diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp index 9bee523c7b7e5..a811fd6fba9c2 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -1535,6 +1535,9 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) { if (Instruction *Phi = foldBinopWithPhiOperands(I)) return Phi; + if (Instruction *R = foldBinOpIntoMinMax(I)) + return R; + // (A*B)+(A*C) -> A*(B+C) etc if (Value *V = foldUsingDistributiveLaws(I)) return replaceInstUsesWith(I, V); @@ -2037,6 +2040,20 @@ Instruction *InstCombinerImpl::visitFAdd(BinaryOperator &I) { return BinaryOperator::CreateFSubFMF(Z, XY, &I); } + // smin(-a, x - a) + a --> smin(x, 0) [2 commuted variants] + // smin(x - a, -a) + a --> smin(x, 0) [2 commuted variants] + if (match(&I, + m_c_FAdd(m_SMin(m_FNeg(m_Value(A)), m_FSub(m_Value(X), m_Value(A))), + m_Value(A))) || + match(&I, + m_c_FAdd(m_SMin(m_FSub(m_Value(X), m_Value(A)), m_FNeg(m_Value(A))), + m_Value(A)))) { + Constant *Zero = Constant::getNullValue(I.getType()); + return replaceInstUsesWith( + I, + Builder.CreateIntrinsic(Intrinsic::smin, {I.getType()}, {X, Zero}, &I)); + } + // Check for (fadd double (sitofp x), y), see if we can merge this into an // integer add followed by a promotion. if (Instruction *R = foldFBinOpOfIntCasts(I)) @@ -2310,6 +2327,9 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) { return Res; } + if (Instruction *R = foldBinOpIntoMinMax(I)) + return R; + // Try this before Negator to preserve NSW flag. if (Instruction *R = factorizeMathWithShlOps(I, Builder)) return R; diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 9bdd8cb71f7f3..8d3064a4eb56a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -670,6 +670,10 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final /// This is a convenience wrapper function for the above two functions. Instruction *foldBinOpIntoSelectOrPhi(BinaryOperator &I); + /// Given a binary operator with min/max intrinsic as one operand, + /// try to fold it into a single min/max intrinsic call. + Instruction *foldBinOpIntoMinMax(BinaryOperator &I); + Instruction *foldAddWithConstant(BinaryOperator &Add); Instruction *foldSquareSumInt(BinaryOperator &I); diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index b158e0f626850..3a99295f645b2 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -1833,6 +1833,25 @@ Instruction *InstCombinerImpl::FoldOpIntoSelect(Instruction &Op, SelectInst *SI, return SelectInst::Create(SI->getCondition(), NewTV, NewFV, "", nullptr, SI); } +Instruction *InstCombinerImpl::foldBinOpIntoMinMax(BinaryOperator &I) { + Value *LHS = I.getOperand(0); + Value *RHS = I.getOperand(1); + MinMaxIntrinsic *MinMax = dyn_cast(LHS); + Value* otherOp = RHS; + if (!MinMax) { + MinMax = dyn_cast(RHS); + otherOp = LHS; + } + if (!MinMax) return nullptr; + Value* X = MinMax->getLHS(); + Value* Y = MinMax->getRHS(); + Value* NewX = BinaryOperator::Create(I.getOpcode(), X, otherOp); + Value* NewY = BinaryOperator::Create(I.getOpcode(), Y, otherOp); + Intrinsic::ID InvID = getInverseMinMaxIntrinsic(MinMax->getIntrinsicID()); + Function *F = Intrinsic::getOrInsertDeclaration(I.getModule(), InvID, I.getType()); + return CallInst::Create(F, {NewX, NewY}); +} + static Value *simplifyInstructionWithPHI(Instruction &I, PHINode *PN, Value *InValue, BasicBlock *InBB, const DataLayout &DL, diff --git a/llvm/test/Transforms/InstCombine/add-min-max.ll b/llvm/test/Transforms/InstCombine/add-min-max.ll index 2117a55e2a490..cbf960d32cb90 100644 --- a/llvm/test/Transforms/InstCombine/add-min-max.ll +++ b/llvm/test/Transforms/InstCombine/add-min-max.ll @@ -83,3 +83,18 @@ entry: %res = add nuw nsw i32 %min, %max ret i32 %res } + + +define i32 @sadd_min_neg(i32 %x, i32 %a) { +; CHECK-LABEL: @sadd_min_neg( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = call i32 @llvm.smin.i32(i32 [[A:%.*]], i64 5) +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %neg_a = sub nsw i32 0, %a + %x_minus_a = sub nsw i32 %x, %a + %smin = call i32 @llvm.smin.i32(i32 %neg_a, i32 %x_minus_a) + %res = add nsw i32 %smin, %a + ret i32 %res +} \ No newline at end of file