[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