[llvm] [InstCombine] Fold smin(-a, x - a) + a to smin(x, 0) (PR #167109)

Aneesh Kadiyala via llvm-commits llvm-commits at lists.llvm.org
Sat Nov 8 09:31:52 PST 2025


https://github.com/ARandomDev99 updated https://github.com/llvm/llvm-project/pull/167109

>From 20147c22840a9296dd3db586dda6a2f54eaab251 Mon Sep 17 00:00:00 2001
From: Aneesh K <notspaceboy at pm.me>
Date: Sat, 8 Nov 2025 12:27:52 +0530
Subject: [PATCH] [InstCombine] Fold binary operators into single minmax
 intrinsic

---
 .../InstCombine/InstCombineAddSub.cpp         | 20 +++++++++++++++++++
 .../InstCombine/InstCombineInternal.h         |  4 ++++
 .../InstCombine/InstructionCombining.cpp      | 19 ++++++++++++++++++
 .../Transforms/InstCombine/add-min-max.ll     | 15 ++++++++++++++
 4 files changed, 58 insertions(+)

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<MinMaxIntrinsic>(LHS);
+  Value* otherOp = RHS;
+  if (!MinMax) {
+    MinMax = dyn_cast<MinMaxIntrinsic>(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



More information about the llvm-commits mailing list