[llvm] [ValueTracking] Recognize non-negative subtraction patterns (PR #181964)

via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 17 22:20:23 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Abu (user1342234)

<details>
<summary>Changes</summary>

Fix missed optimization for nonnegative `b - a` (issue #<!-- -->146131)

The optimizer currently fails to recognize that `b - a` (signed subtraction) is provably nonnegative in patterns like:

- `if (b < a) a = b; return (int32_t)(b - a);`
- Equivalently, `return (b < a ? 0 : (int32_t)(b - a));`

On x86-64, this should generate shorter/better code in affected cases

Fixes #<!-- -->146131

---
Full diff: https://github.com/llvm/llvm-project/pull/181964.diff


2 Files Affected:

- (modified) llvm/lib/Analysis/ValueTracking.cpp (+63) 
- (added) llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll (+28) 


``````````diff
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 2d78aa4be3455..0623bc7019732 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -483,6 +483,21 @@ static void computeKnownBitsFromLerpPattern(const Value *Op0, const Value *Op1,
   KnownOut.Zero.setHighBits(MinimumNumberOfLeadingZeros);
 }
 
+static bool isSubtractionKnownNonNegative(const Value *LHS, const Value *RHS) {
+  // Pattern: LHS - smin(LHS, X) = max(0, LHS - X) >= 0
+  if (auto *MinCall = dyn_cast<IntrinsicInst>(RHS)) {
+    if (MinCall->getIntrinsicID() == Intrinsic::smin) {
+      Value *MinOp0 = MinCall->getOperand(0);
+      Value *MinOp1 = MinCall->getOperand(1);
+
+      // If LHS appears in the smin, then LHS - smin(LHS, X) >= 0
+      if (MinOp0 == LHS || MinOp1 == LHS)
+        return true;
+    }
+  }
+  return false;
+}
+
 static void computeKnownBitsAddSub(bool Add, const Value *Op0, const Value *Op1,
                                    bool NSW, bool NUW,
                                    const APInt &DemandedElts,
@@ -503,6 +518,10 @@ static void computeKnownBitsAddSub(bool Add, const Value *Op0, const Value *Op1,
           .value_or(false))
     KnownOut.makeNonNegative();
 
+    if (!Add && !KnownOut.isNonNegative() &&
+    isSubtractionKnownNonNegative(Op0, Op1))
+  KnownOut.makeNonNegative();
+
   if (Add)
     // Try to match lerp pattern and combine results
     computeKnownBitsFromLerpPattern(Op0, Op1, DemandedElts, KnownOut, Q, Depth);
@@ -1401,6 +1420,46 @@ static void unionWithMinMaxIntrinsicClamp(const IntrinsicInst *II,
         ConstantRange::getNonEmpty(*CLow, *CHigh + 1).toKnownBits());
 }
 
+static bool isSelectKnownNonNegative(const SelectInst *SI) {
+
+  if (auto *Cmp = dyn_cast<ICmpInst>(SI->getCondition())) {
+    const Value *TrueVal = SI->getTrueValue();
+    const Value *FalseVal = SI->getFalseValue();
+    const Value *CmpLHS = Cmp->getOperand(0);
+    const Value *CmpRHS = Cmp->getOperand(1);
+    ICmpInst::Predicate Pred = Cmp->getPredicate();
+
+    // Determine which value is zero and which is sub based on predicate
+    const Value *ZeroVal, *SubVal;
+    if (Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SLE) {
+      // condition is (b < a) or (b <= a)
+      // zero is on true side, sub is on false side
+      ZeroVal = TrueVal;
+      SubVal = FalseVal;
+    } else if (Pred == ICmpInst::ICMP_SGT || Pred == ICmpInst::ICMP_SGE) {
+      // condition is (b > a) or (b >= a)
+      // zero is on false side, sub is on true side
+      ZeroVal = FalseVal;
+      SubVal = TrueVal;
+    } else {
+      return false; // predicate we don't handle
+    }
+
+    // Now just check once
+    if (auto *ConstInt = dyn_cast<ConstantInt>(ZeroVal)) {
+      if (ConstInt->isZero()) {
+        if (auto *Sub = dyn_cast<BinaryOperator>(SubVal)) {
+          if (Sub->getOpcode() == Instruction::Sub &&
+              Sub->getOperand(0) == CmpLHS && Sub->getOperand(1) == CmpRHS)
+            return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
 static void computeKnownBitsFromOperator(const Operator *I,
                                          const APInt &DemandedElts,
                                          KnownBits &Known,
@@ -1466,6 +1525,10 @@ static void computeKnownBitsFromOperator(const Operator *I,
     Known =
         ComputeForArm(I->getOperand(1), /*Invert=*/false)
             .intersectWith(ComputeForArm(I->getOperand(2), /*Invert=*/true));
+
+    if (isSelectKnownNonNegative(cast<SelectInst>(I)))
+      Known.makeNonNegative();
+    
     break;
   }
   case Instruction::FPTrunc:
diff --git a/llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll b/llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll
new file mode 100644
index 0000000000000..a9dc5959c06bc
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll
@@ -0,0 +1,28 @@
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Test that b - smin(b, a) is recognized as non-negative
+define i64 @func1(i32 %a, i32 %b) {
+; CHECK-LABEL: @func1
+; CHECK: zext nneg i32
+; CHECK-NOT: sext i32
+entry:
+  %spec.select = tail call i32 @llvm.smin.i32(i32 %b, i32 %a)
+  %sub = sub nsw i32 %b, %spec.select
+  %conv = sext i32 %sub to i64
+  ret i64 %conv
+}
+
+; Test that select (b < a), 0, (b - a) is recognized as non-negative
+define i64 @func3(i32 %a, i32 %b) {
+; CHECK-LABEL: @func3
+; CHECK: zext nneg i32
+; CHECK-NOT: sext i32
+entry:
+  %cmp = icmp slt i32 %b, %a
+  %sub = sub nsw i32 %b, %a
+  %cond = select i1 %cmp, i32 0, i32 %sub
+  %conv = sext i32 %cond to i64
+  ret i64 %conv
+}
+
+declare i32 @llvm.smin.i32(i32, i32)
\ No newline at end of file

``````````

</details>


https://github.com/llvm/llvm-project/pull/181964


More information about the llvm-commits mailing list