[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