Index: llvm/trunk/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/trunk/lib/Analysis/ValueTracking.cpp +++ llvm/trunk/lib/Analysis/ValueTracking.cpp @@ -4320,6 +4320,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 "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); @@ -4350,7 +4364,33 @@ CastedTo = ConstantExpr::getTrunc(C, SrcTy, true); break; case Instruction::Trunc: - CastedTo = ConstantExpr::getIntegerCast(C, SrcTy, CmpI->isSigned()); + Constant *CmpConst; + if (match(CmpI->getOperand(1), m_Constant(CmpConst))) { + // Here we have the following case: + // + // %cond = cmp iN %x, CmpConst + // %tr = trunc iN %x to iK + // %narrowsel = select i1 %cond, iK %t, iK C + // + // We can always move trunc after select operation: + // + // %cond = cmp iN %x, CmpConst + // %widesel = select i1 %cond, iN %x, iN CmpConst + // %tr = trunc iN %widesel to iK + // + // Note that C 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 %cond, x, -x. + // + // So only min/max pattern could be matched. Such match requires widened C + // == CmpConst. That is why set widened C = CmpConst, condition trunc + // CmpConst == C is checked below. + CastedTo = CmpConst; + } else { + CastedTo = ConstantExpr::getIntegerCast(C, SrcTy, CmpI->isSigned()); + } break; case Instruction::FPTrunc: CastedTo = ConstantExpr::getFPExtend(C, SrcTy, true); Index: llvm/trunk/test/Transforms/InstCombine/minmax-fold.ll =================================================================== --- llvm/trunk/test/Transforms/InstCombine/minmax-fold.ll +++ llvm/trunk/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: [[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 +} + +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 +}