Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -880,26 +880,39 @@ } /// If this min/max has a constant operand and an operand that is a matching -/// min/max with a constant operand, constant-fold the 2 constant operands. -static Instruction *reassociateMinMaxWithConstants(IntrinsicInst *II) { +/// min/max, try to push the constant operand into the inner (first) min/max. +/// This may enable simplifications. +static Instruction * +reassociateMinMaxWithConstant(IntrinsicInst *II, + InstCombiner::BuilderTy &Builder) { Intrinsic::ID MinMaxID = II->getIntrinsicID(); auto *LHS = dyn_cast(II->getArgOperand(0)); - if (!LHS || LHS->getIntrinsicID() != MinMaxID) + Constant *C; + if (!LHS || LHS->getIntrinsicID() != MinMaxID || + !match(II->getArgOperand(1), m_ImmConstant(C))) return nullptr; - Constant *C0, *C1; - if (!match(LHS->getArgOperand(1), m_ImmConstant(C0)) || - !match(II->getArgOperand(1), m_ImmConstant(C1))) + // Avoid infinite loop if the inner min/max is not in canonical form yet. + Value *X = LHS->getArgOperand(0); + Value *Y = LHS->getArgOperand(1); + if (match(X, m_Constant())) return nullptr; - // max (max X, C0), C1 --> max X, (max C0, C1) --> max X, NewC - ICmpInst::Predicate Pred = MinMaxIntrinsic::getPredicate(MinMaxID); - Constant *CondC = ConstantExpr::getICmp(Pred, C0, C1); - Constant *NewC = ConstantExpr::getSelect(CondC, C0, C1); + // If the inner min/max has a constant operand, it will constant-fold with + // the outer constant operand, so an extra use is fine in that case. + if (!LHS->hasOneUse() && !match(Y, m_ImmConstant())) + return nullptr; + // max (max X, Y), C --> max (max Y, C), X + // This may allow the inner op to be simplified. + // TODO: This always chooses X (operand 0 of the inner instruction) as the + // the operand to swap, but we may want to choose Y instead to enable + // more folds (in the case where Y is not a constant). + Value *NewInner = Builder.CreateBinaryIntrinsic(MinMaxID, Y, C); + NewInner->takeName(LHS); Module *Mod = II->getModule(); Function *MinMax = Intrinsic::getDeclaration(Mod, MinMaxID, II->getType()); - return CallInst::Create(MinMax, {LHS->getArgOperand(0), NewC}); + return CallInst::Create(MinMax, {NewInner, X}); } /// Reduce a sequence of min/max intrinsics with a common operand. @@ -1247,7 +1260,7 @@ if (Instruction *R = FoldOpIntoSelect(*II, Sel)) return R; - if (Instruction *NewMinMax = reassociateMinMaxWithConstants(II)) + if (Instruction *NewMinMax = reassociateMinMaxWithConstant(II, Builder)) return NewMinMax; if (Instruction *NewMinMax = factorizeMinMaxTree(II)) Index: llvm/test/Transforms/InstCombine/minmax-intrinsics.ll =================================================================== --- llvm/test/Transforms/InstCombine/minmax-intrinsics.ll +++ llvm/test/Transforms/InstCombine/minmax-intrinsics.ll @@ -2176,8 +2176,8 @@ define i8 @umin_umin_reassoc_constants(i8 %x) { ; CHECK-LABEL: @umin_umin_reassoc_constants( -; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 -116) -; CHECK-NEXT: call void @use(i8 [[M1]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 -116) +; CHECK-NEXT: call void @use(i8 [[TMP1]]) ; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 42) ; CHECK-NEXT: ret i8 [[M2]] ; @@ -2202,8 +2202,8 @@ define i8 @smax_smax_reassoc_constant(i8 %x, i8 %y) { ; CHECK-LABEL: @smax_smax_reassoc_constant( -; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 [[Y:%.*]]) -; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[M1]], i8 42) +; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.smax.i8(i8 [[Y:%.*]], i8 42) +; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[M1]], i8 [[X:%.*]]) ; CHECK-NEXT: ret i8 [[M2]] ; %m1 = call i8 @llvm.smax.i8(i8 %x, i8 %y) @@ -2213,8 +2213,8 @@ define <3 x i8> @smin_smin_reassoc_constant(<3 x i8> %x, <3 x i8> %y) { ; CHECK-LABEL: @smin_smin_reassoc_constant( -; CHECK-NEXT: [[M1:%.*]] = call <3 x i8> @llvm.smin.v3i8(<3 x i8> [[X:%.*]], <3 x i8> [[Y:%.*]]) -; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.smin.v3i8(<3 x i8> [[M1]], <3 x i8> ) +; CHECK-NEXT: [[M1:%.*]] = call <3 x i8> @llvm.smin.v3i8(<3 x i8> [[Y:%.*]], <3 x i8> ) +; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.smin.v3i8(<3 x i8> [[M1]], <3 x i8> [[X:%.*]]) ; CHECK-NEXT: ret <3 x i8> [[M2]] ; %m1 = call <3 x i8> @llvm.smin.v3i8(<3 x i8> %x, <3 x i8> %y) @@ -2224,8 +2224,8 @@ define i8 @umax_umax_reassoc_constant(i8 %x, i8 %y) { ; CHECK-LABEL: @umax_umax_reassoc_constant( -; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 [[Y:%.*]]) -; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umax.i8(i8 [[M1]], i8 42) +; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.umax.i8(i8 [[Y:%.*]], i8 42) +; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umax.i8(i8 [[M1]], i8 [[X:%.*]]) ; CHECK-NEXT: ret i8 [[M2]] ; %m1 = call i8 @llvm.umax.i8(i8 %x, i8 %y) @@ -2235,8 +2235,8 @@ define <3 x i8> @umin_smin_reassoc_constant(<3 x i8> %x, <3 x i8> %y) { ; CHECK-LABEL: @umin_smin_reassoc_constant( -; CHECK-NEXT: [[M1:%.*]] = call <3 x i8> @llvm.umin.v3i8(<3 x i8> [[X:%.*]], <3 x i8> [[Y:%.*]]) -; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.umin.v3i8(<3 x i8> [[M1]], <3 x i8> ) +; CHECK-NEXT: [[M1:%.*]] = call <3 x i8> @llvm.umin.v3i8(<3 x i8> [[Y:%.*]], <3 x i8> ) +; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.umin.v3i8(<3 x i8> [[M1]], <3 x i8> [[X:%.*]]) ; CHECK-NEXT: ret <3 x i8> [[M2]] ; %m1 = call <3 x i8> @llvm.umin.v3i8(<3 x i8> %x, <3 x i8> %y) @@ -2259,9 +2259,8 @@ define i8 @smax_smax_smax_reassoc_constants(i8 %x, i8 %y) { ; CHECK-LABEL: @smax_smax_smax_reassoc_constants( -; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 42) -; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[Y:%.*]], i8 [[M1]]) -; CHECK-NEXT: [[M3:%.*]] = call i8 @llvm.smax.i8(i8 [[M2]], i8 126) +; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 126) +; CHECK-NEXT: [[M3:%.*]] = call i8 @llvm.smax.i8(i8 [[M2]], i8 [[Y:%.*]]) ; CHECK-NEXT: ret i8 [[M3]] ; %m1 = call i8 @llvm.smax.i8(i8 %x, i8 42) @@ -2273,8 +2272,8 @@ define i8 @smax_smax_smax_reassoc_constants_swap(i8 %x, i8 %y) { ; CHECK-LABEL: @smax_smax_smax_reassoc_constants_swap( ; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 42) -; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[M1]], i8 [[Y:%.*]]) -; CHECK-NEXT: [[M3:%.*]] = call i8 @llvm.smax.i8(i8 [[M2]], i8 126) +; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[Y:%.*]], i8 126) +; CHECK-NEXT: [[M3:%.*]] = call i8 @llvm.smax.i8(i8 [[M2]], i8 [[M1]]) ; CHECK-NEXT: ret i8 [[M3]] ; %m1 = call i8 @llvm.smax.i8(i8 %x, i8 42) @@ -2286,8 +2285,7 @@ define i8 @smin_smin_smin_reassoc_constants(i8 %x, i8 %y) { ; CHECK-LABEL: @smin_smin_smin_reassoc_constants( ; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 42) -; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smin.i8(i8 [[Y:%.*]], i8 [[M1]]) -; CHECK-NEXT: [[M3:%.*]] = call i8 @llvm.smin.i8(i8 [[M2]], i8 126) +; CHECK-NEXT: [[M3:%.*]] = call i8 @llvm.smin.i8(i8 [[M1]], i8 [[Y:%.*]]) ; CHECK-NEXT: ret i8 [[M3]] ; %m1 = call i8 @llvm.smin.i8(i8 %x, i8 42)