Index: lib/Analysis/ValueTracking.cpp =================================================================== --- lib/Analysis/ValueTracking.cpp +++ lib/Analysis/ValueTracking.cpp @@ -4298,6 +4298,20 @@ return matchFastFloatClamp(Pred, CmpLHS, CmpRHS, TrueVal, FalseVal, LHS, RHS); } +/// Helps to match a select pattern in case of a type mismatch. +/// +/// The function processes the case when type of true and false values of a +/// select instruction differs from type of the cmp instruction operands because +/// of a cast instructon. The function checks if it is legal to move the cast +/// operation after "select". If yes, it returns the new second value of +/// "select" (with the assumption that cast is moved): +/// 1. As operand of cast instruction when both values of "select" are same cast +/// instructions. +/// 2. As restored constant (by applying reverse cast operation) when the first +/// value of the inital "select" is a cast operation and the second value is a +/// constant. +/// NOTE: We return only the new second value because the first value could be +/// accessed as operand of cast instruction. static Value *lookThroughCast(CmpInst *CmpI, Value *V1, Value *V2, Instruction::CastOps *CastOp) { auto *Cast1 = dyn_cast(V1); @@ -4328,7 +4342,40 @@ CastedTo = ConstantExpr::getTrunc(C, SrcTy, true); break; case Instruction::Trunc: - CastedTo = ConstantExpr::getIntegerCast(C, SrcTy, CmpI->isSigned()); + const APInt *CmpConst, *SelectConst; + if (match(CmpI->getOperand(1), m_APInt(CmpConst)) && + match(C, m_APInt(SelectConst))) { + // Here we have the following case: + // + // %c = cmp iN %x, a + // %t = trunc iN %y to iK + // %sel = select i1 %c, iK %t, iK b + // + // where a and b are constants. We can always move trunc after select + // operation: + // + // %c = cmp iN %x, a + // %sel = select i1 %c, iN %y, iN B + // %res = trunc iN %sel to iK + // + // Note that b could be extended in any way because we don't care about + // upper bits after truncation. It can't be abs pattern, because it would + // look like: + // + // select i1 %c, x, -x. + // + // So only min/max pattern could be matched. Such match requires B == a. + // That is why copy upper bits from a. + auto SrcWidth = SrcTy->getScalarSizeInBits(); + auto DstWidth = C->getType()->getScalarSizeInBits(); + // Extend to source type with clear upper bits + APInt WidenedConst = SelectConst->zext(SrcWidth); + // Copy upper bits from constant in cmp instruction + WidenedConst |= CmpConst->getHiBits(SrcWidth - DstWidth).shl(DstWidth); + CastedTo = ConstantInt::getIntegerValue(SrcTy, WidenedConst); + } else { + CastedTo = ConstantExpr::getIntegerCast(C, SrcTy, CmpI->isSigned()); + } break; case Instruction::FPTrunc: CastedTo = ConstantExpr::getFPExtend(C, SrcTy, true); Index: test/Transforms/InstCombine/minmax-fold.ll =================================================================== --- test/Transforms/InstCombine/minmax-fold.ll +++ test/Transforms/InstCombine/minmax-fold.ll @@ -586,3 +586,39 @@ %sel = select <8 x i1> %cmp, <8 x float> %x, <8 x float> %y ret <8 x float> %sel } + +; Check that we look through cast and recognize clamp +define zeroext i8 @look_through_cast1(i32 %x) { +; CHECK-LABEL: @look_through_cast1( +; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X:%.*]], 0 +; CHECK-NEXT: [[MAX:%.*]] = select i1 [[CMP1]], i32 [[X]], i32 0 +; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[MAX]], 511 +; CHECK-NEXT: [[RES1:%.*]] = select i1 [[TMP1]], i32 [[MAX]], i32 511 +; CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[RES1]] to i8 +; CHECK-NEXT: ret i8 [[TMP2]] +; + %cmp1 = icmp sgt i32 %x, 0 + %max = select i1 %cmp1, i32 %x, i32 0 + %cmp2 = icmp slt i32 %max, 511 + %max_trunc = trunc i32 %max to i8 + %res = select i1 %cmp2, i8 %max_trunc, i8 255 + ret i8 %res +} + +; Check that we look through cast but clamp is not recognized. +define zeroext i8 @look_through_cast2(i32 %x) { +; CHECK-LABEL: @look_through_cast2( +; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X:%.*]], 0 +; CHECK-NEXT: [[MAX:%.*]] = select i1 [[CMP1]], i32 [[X]], i32 0 +; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[MAX]], 510 +; CHECK-NEXT: [[MAX_TRUNC:%.*]] = trunc i32 [[MAX]] to i8 +; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP2]], i8 [[MAX_TRUNC]], i8 -1 +; CHECK-NEXT: ret i8 [[RES]] +; + %cmp1 = icmp sgt i32 %x, 0 + %max = select i1 %cmp1, i32 %x, i32 0 + %cmp2 = icmp slt i32 %max, 510 + %max_trunc = trunc i32 %max to i8 + %res = select i1 %cmp2, i8 %max_trunc, i8 255 + ret i8 %res +}