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

via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 18 14:47:12 PST 2026


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

>From 803deb9145f5321932b63a91c42fba9f13eab809 Mon Sep 17 00:00:00 2001
From: abu <ayywarepremium at gmail.com>
Date: Wed, 18 Feb 2026 14:32:25 -0800
Subject: [PATCH 1/2] Pre-commit baseline tests for sext-nonneg-sub (Fix
 #146131)

---
 .../Transforms/InstCombine/sext-nonneg-sub.ll | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll

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 00000000000000..cce695133e95ff
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll
@@ -0,0 +1,40 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; 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: define i64 @func1(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = tail call i32 @llvm.smin.i32(i32 [[B]], i32 [[A]])
+; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i32 [[B]], [[SPEC_SELECT]]
+; CHECK-NEXT:    [[CONV:%.*]] = zext nneg i32 [[SUB]] to i64
+; CHECK-NEXT:    ret i64 [[CONV]]
+;
+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: define i64 @func3(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B]], [[A]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i32 [[B]], [[A]]
+; CHECK-NEXT:    [[COND:%.*]] = select i1 [[CMP]], i32 0, i32 [[SUB]]
+; CHECK-NEXT:    [[CONV:%.*]] = zext nneg i32 [[COND]] to i64
+; CHECK-NEXT:    ret i64 [[CONV]]
+;
+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)

>From e52c042c0907a6af49b5597e963ed056f1eaca2c Mon Sep 17 00:00:00 2001
From: abu <ayywarepremium at gmail.com>
Date: Wed, 18 Feb 2026 14:45:36 -0800
Subject: [PATCH 2/2] [ValueTracking] Recognize non-negative subtraction
 patterns

---
 llvm/lib/Analysis/ValueTracking.cpp | 62 +++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 2d78aa4be34551..967e808463e306 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,9 @@ 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:



More information about the llvm-commits mailing list