Index: lib/Analysis/ValueTracking.cpp =================================================================== --- lib/Analysis/ValueTracking.cpp +++ lib/Analysis/ValueTracking.cpp @@ -4299,6 +4299,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); @@ -4329,7 +4343,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 @@ -688,3 +688,57 @@ %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 min idiom. +define zeroext i8 @look_through_cast1(i32 %x) { +; CHECK-LABEL: @look_through_cast1( +; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 511 +; CHECK-NEXT: [[RES1:%.*]] = select i1 [[TMP1]], i32 [[X]], i32 511 +; CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[RES1]] to i8 +; CHECK-NEXT: ret i8 [[TMP2]] +; + %cmp1 = icmp slt i32 %x, 511 + %x_trunc = trunc i32 %x to i8 + %res = select i1 %cmp1, i8 %x_trunc, i8 255 + ret i8 %res +} + +; Check that we look through cast but min is not recognized. +define zeroext i8 @look_through_cast2(i32 %x) { +; CHECK-LABEL: @look_through_cast2( +; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[X:%.*]], 510 +; CHECK-NEXT: [[X_TRUNC:%.*]] = trunc i32 [[X]] to i8 +; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP1]], i8 [[X_TRUNC]], i8 -1 +; CHECK-NEXT: ret i8 [[RES]] +; + %cmp1 = icmp slt i32 %x, 510 + %x_trunc = trunc i32 %x to i8 + %res = select i1 %cmp1, i8 %x_trunc, i8 255 + ret i8 %res +} + +define <2 x i8> @min_through_cast_vec1(<2 x i32> %x) { +; CHECK-LABEL: @min_through_cast_vec1( +; CHECK-NEXT: [[CMP:%.*]] = icmp slt <2 x i32> [[X:%.*]], +; CHECK-NEXT: [[X_TRUNC:%.*]] = trunc <2 x i32> [[X]] to <2 x i8> +; CHECK-NEXT: [[RES:%.*]] = select <2 x i1> [[CMP]], <2 x i8> [[X_TRUNC]], <2 x i8> +; CHECK-NEXT: ret <2 x i8> [[RES]] +; + %cmp = icmp slt <2 x i32> %x, + %x_trunc = trunc <2 x i32> %x to <2 x i8> + %res = select <2 x i1> %cmp, <2 x i8> %x_trunc, <2 x i8> + ret <2 x i8> %res +} + +define <2 x i8> @min_through_cast_vec2(<2 x i32> %x) { +; CHECK-LABEL: @min_through_cast_vec2( +; CHECK-NEXT: [[TMP1:%.*]] = icmp slt <2 x i32> [[X:%.*]], +; CHECK-NEXT: [[RES1:%.*]] = select <2 x i1> [[TMP1]], <2 x i32> [[X]], <2 x i32> +; CHECK-NEXT: [[TMP2:%.*]] = trunc <2 x i32> [[RES1]] to <2 x i8> +; CHECK-NEXT: ret <2 x i8> [[TMP2]] +; + %cmp = icmp slt <2 x i32> %x, + %x_trunc = trunc <2 x i32> %x to <2 x i8> + %res = select <2 x i1> %cmp, <2 x i8> %x_trunc, <2 x i8> + ret <2 x i8> %res +}