Skip to content

Conversation

@AZero13
Copy link
Contributor

@AZero13 AZero13 commented Jun 10, 2025

We can determine the range from a subtraction if it has nsw or nuw.

https://alive2.llvm.org/ce/z/tXAKVV

@AZero13 AZero13 requested a review from nikic as a code owner June 10, 2025 22:36
@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Jun 10, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 10, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-analysis

Author: AZero13 (AZero13)

Changes

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

1 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+54)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d8c1096049dce..88e8ea4878fe1 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9576,6 +9576,60 @@ static void setLimitsForBinOp(const BinaryOperator &BO, APInt &Lower,
   unsigned Width = Lower.getBitWidth();
   const APInt *C;
   switch (BO.getOpcode()) {
+  case Instruction::Sub:
+    if (match(BO.getOperand(1), m_APInt(C)) && !C->isZero()) {
+      bool HasNSW = IIQ.hasNoSignedWrap(&BO);
+      bool HasNUW = IIQ.hasNoUnsignedWrap(&BO);
+
+      // If the caller expects a signed compare, then try to use a signed range.
+      // Otherwise if both no-wraps are set, use the unsigned range because it
+      // is never larger than the signed range. Example:
+      // "sub nuw nsw i8 X, -2" is unsigned [0, 127] vs. signed [-128, 126].
+      if (PreferSignedRange && HasNSW && HasNUW)
+        HasNUW = false;
+
+      if (HasNUW) {
+        // 'sub nuw x, C' produces [0, UINT_MAX - C].
+        Upper = APInt::getAllOnes(Width) - *C + 1;
+      } else if (HasNSW) {
+        if (C->isNegative()) {
+          // 'sub nsw x, -C' produces [SINT_MIN + C, SINT_MAX].
+          Lower = APInt::getSignedMinValue(Width) + *C;
+          Upper = APInt::getSignedMaxValue(Width) + 1;
+        } else {
+          // 'sub nsw x, +C' produces [SINT_MIN, SINT_MAX - C].
+          Lower = APInt::getSignedMinValue(Width);
+          Upper = APInt::getSignedMaxValue(Width) - *C + 1;
+        }
+      }
+    } else if (match(BO.getOperand(0), m_APInt(C))) {
+      bool HasNSW = IIQ.hasNoSignedWrap(&BO);
+      bool HasNUW = IIQ.hasNoUnsignedWrap(&BO);
+
+      // If the caller expects a signed compare, then try to use a signed range.
+      // Otherwise if both no-wraps are set, use the unsigned range because it
+      // is never larger than the signed range. Example:
+      // "sub nuw nsw i8 X, -2" is unsigned [0, 127] vs. signed [-128, 126].
+      if (PreferSignedRange && HasNSW && HasNUW)
+        HasNUW = false;
+
+      if (HasNUW) {
+        // 'sub nuw c, x' produces [0, C].
+        Upper = *C + 1;
+      } else if (HasNSW) {
+        if (C->isNegative()) {
+          // 'sub nsw C, x' produces [SINT_MIN, SINT_MAX - C].
+          Lower = APInt::getSignedMinValue(Width);
+          Upper = APInt::getSignedMaxValue(Width) - *C + 1;
+        } else {
+          // Note that sub 0, INT_MIN is not NSW. It techically is a signed wrap
+          // 'sub nsw C, x' produces [SINT_MIN + 1 + C, SINT_MAX].
+          Lower = APInt::getSignedMinValue(Width) + *C + 1;
+          Upper = APInt::getSignedMaxValue(Width) + 1;
+        }
+      }
+    }
+    break;
   case Instruction::Add:
     if (match(BO.getOperand(1), m_APInt(C)) && !C->isZero()) {
       bool HasNSW = IIQ.hasNoSignedWrap(&BO);

@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Jun 10, 2025
@AZero13 AZero13 force-pushed the csel branch 2 times, most recently from a859101 to ab86d08 Compare June 11, 2025 00:23
@AZero13 AZero13 force-pushed the csel branch 2 times, most recently from c4d225b to e8e025a Compare June 13, 2025 15:02
@AZero13 AZero13 requested a review from dtcxzyw June 13, 2025 16:44
@AZero13 AZero13 force-pushed the csel branch 3 times, most recently from 2ae5bb4 to 0b9dd31 Compare June 13, 2025 23:16
We can determine the range from a subtraction if it has nsw or nuw.
Alive2: https://alive2.llvm.org/ce/z/tXAKVV
@dtcxzyw
Copy link
Member

dtcxzyw commented Jun 14, 2025

@AZero13 Please avoid rebasing and force-pushing on your branches... If you don't need pre-commit tests from newer commits, just append a new commit for your change.

@dtcxzyw dtcxzyw changed the title Add subtraction support for setLimitsForBinOp [ValueTracking] Add subtraction support for setLimitsForBinOp Jun 14, 2025
AZero13 and others added 2 commits June 14, 2025 08:24
Co-authored-by: Yingwei Zheng <[email protected]>
Co-authored-by: Yingwei Zheng <[email protected]>
Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG

@dtcxzyw dtcxzyw merged commit 30a41a6 into llvm:main Jun 15, 2025
7 checks passed
@AZero13 AZero13 deleted the csel branch June 15, 2025 12:46
akuhlens pushed a commit to akuhlens/llvm-project that referenced this pull request Jun 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants