diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -879,6 +879,29 @@ return SelectInst::Create(Cmp, ConstantInt::get(II->getType(), *C0), I1); } +/// 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) { + Intrinsic::ID MinMaxID = II->getIntrinsicID(); + auto *LHS = dyn_cast(II->getArgOperand(0)); + if (!LHS || LHS->getIntrinsicID() != MinMaxID) + return nullptr; + + Constant *C0, *C1; + if (!match(LHS->getArgOperand(1), m_ImmConstant(C0)) || + !match(II->getArgOperand(1), m_ImmConstant(C1))) + 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); + + Module *Mod = II->getModule(); + Function *MinMax = Intrinsic::getDeclaration(Mod, MinMaxID, II->getType()); + return CallInst::Create(MinMax, {LHS->getArgOperand(0), NewC}); +} + /// Reduce a sequence of min/max intrinsics with a common operand. static Instruction *factorizeMinMaxTree(IntrinsicInst *II) { // Match 3 of the same min/max ops. Example: umin(umin(), umin()). @@ -1224,6 +1247,9 @@ if (Instruction *R = FoldOpIntoSelect(*II, Sel)) return R; + if (Instruction *NewMinMax = reassociateMinMaxWithConstants(II)) + return NewMinMax; + if (Instruction *NewMinMax = factorizeMinMaxTree(II)) return NewMinMax; diff --git a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll --- a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll +++ b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll @@ -2144,8 +2144,7 @@ define <3 x i8> @smax_smax_reassoc_constants(<3 x i8> %x) { ; CHECK-LABEL: @smax_smax_reassoc_constants( -; CHECK-NEXT: [[M1:%.*]] = call <3 x i8> @llvm.smax.v3i8(<3 x i8> [[X:%.*]], <3 x i8> ) -; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.smax.v3i8(<3 x i8> [[M1]], <3 x i8> ) +; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.smax.v3i8(<3 x i8> [[X:%.*]], <3 x i8> ) ; CHECK-NEXT: ret <3 x i8> [[M2]] ; %m1 = call <3 x i8> @llvm.smax.v3i8(<3 x i8> %x, <3 x i8> ) @@ -2155,8 +2154,7 @@ define i8 @smin_smin_reassoc_constants(i8 %x) { ; CHECK-LABEL: @smin_smin_reassoc_constants( -; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 97) -; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smin.i8(i8 [[M1]], i8 -3) +; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 -3) ; CHECK-NEXT: ret i8 [[M2]] ; %m1 = call i8 @llvm.smin.i8(i8 %x, i8 97) @@ -2166,8 +2164,7 @@ define <3 x i8> @umax_umax_reassoc_constants(<3 x i8> %x) { ; CHECK-LABEL: @umax_umax_reassoc_constants( -; CHECK-NEXT: [[M1:%.*]] = call <3 x i8> @llvm.umax.v3i8(<3 x i8> [[X:%.*]], <3 x i8> ) -; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.umax.v3i8(<3 x i8> [[M1]], <3 x i8> ) +; CHECK-NEXT: [[M2:%.*]] = call <3 x i8> @llvm.umax.v3i8(<3 x i8> [[X:%.*]], <3 x i8> ) ; CHECK-NEXT: ret <3 x i8> [[M2]] ; %m1 = call <3 x i8> @llvm.umax.v3i8(<3 x i8> %x, <3 x i8> ) @@ -2175,11 +2172,13 @@ ret <3 x i8> %m2 } +; extra use is ok + 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: [[M2:%.*]] = call i8 @llvm.umin.i8(i8 [[M1]], i8 42) +; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 42) ; CHECK-NEXT: ret i8 [[M2]] ; %m1 = call i8 @llvm.umin.i8(i8 140, i8 %x) @@ -2188,6 +2187,8 @@ ret i8 %m2 } +; negative test - must have matching intrinsics + define i8 @smin_smax_reassoc_constants(i8 %x) { ; CHECK-LABEL: @smin_smax_reassoc_constants( ; CHECK-NEXT: [[M1:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 97)