[llvm] [ValueTracking] Add subtraction support for setLimitsForBinOp (PR #143618)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 14 07:50:54 PDT 2025


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/143618

>From b0e78b78994414bf45823e545df9277aacd126e6 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 10 Jun 2025 16:44:34 -0400
Subject: [PATCH 1/4] Add subtraction support for setLimitsForBinOp

We can determine the range from a subtraction if it has nsw or nuw.
Alive2: https://alive2.llvm.org/ce/z/tXAKVV
---
 llvm/lib/Analysis/ValueTracking.cpp          | 39 ++++++++++++++++++--
 llvm/test/Transforms/InstCombine/div.ll      |  8 +---
 llvm/test/Transforms/InstCombine/icmp-sub.ll |  3 +-
 3 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d8c1096049dce..90d2334788208 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9576,15 +9576,48 @@ static void setLimitsForBinOp(const BinaryOperator &BO, APInt &Lower,
   unsigned Width = Lower.getBitWidth();
   const APInt *C;
   switch (BO.getOpcode()) {
-  case Instruction::Add:
-    if (match(BO.getOperand(1), m_APInt(C)) && !C->isZero()) {
+  case Instruction::Sub:
+    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:
-      // "add nuw nsw i8 X, -2" is unsigned [254,255] vs. signed [-128, 125].
+      // "sub nuw nsw i8 -2, x" is unsigned [0, 254] vs. signed [-128, 126].
+      // "sub nuw nsw i8 2, x" is unsigned [0, 2] vs. signed [-125, 127].
+      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 - 1)].
+          // Because to be negative, C must be - 1, and the highest result is
+          // INT_MIN, so -INT_MIN - 1 is INT_MAX, so it is SINT_MAX - (C - 1),
+          // or SINT_MAX - C + 1
+          Lower = APInt::getSignedMinValue(Width);
+          Upper = APInt::getSignedMaxValue(Width) + *C + 2;
+        } 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);
+      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: "add nuw
+      // nsw i8 X, -2" is unsigned [254,255] vs. signed [-128, 125].
       if (PreferSignedRange && HasNSW && HasNUW)
         HasNUW = false;
 
diff --git a/llvm/test/Transforms/InstCombine/div.ll b/llvm/test/Transforms/InstCombine/div.ll
index 7e93612150e8c..f0fdc5f54366a 100644
--- a/llvm/test/Transforms/InstCombine/div.ll
+++ b/llvm/test/Transforms/InstCombine/div.ll
@@ -494,9 +494,7 @@ define <2 x i8> @sdiv_exact_negated_dividend_constant_divisor_vec_splat(<2 x i8>
 
 define i8 @sdiv_negated_dividend_constant_divisor_smin(i8 %x) {
 ; CHECK-LABEL: @sdiv_negated_dividend_constant_divisor_smin(
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[X:%.*]], -128
-; CHECK-NEXT:    [[D:%.*]] = zext i1 [[TMP1]] to i8
-; CHECK-NEXT:    ret i8 [[D]]
+; CHECK-NEXT:    ret i8 0
 ;
   %neg = sub nsw i8 0, %x
   %d = sdiv i8 %neg, -128
@@ -505,9 +503,7 @@ define i8 @sdiv_negated_dividend_constant_divisor_smin(i8 %x) {
 
 define <2 x i8> @sdiv_negated_dividend_constant_divisor_vec_splat_smin(<2 x i8> %x) {
 ; CHECK-LABEL: @sdiv_negated_dividend_constant_divisor_vec_splat_smin(
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq <2 x i8> [[X:%.*]], splat (i8 -128)
-; CHECK-NEXT:    [[D:%.*]] = zext <2 x i1> [[TMP1]] to <2 x i8>
-; CHECK-NEXT:    ret <2 x i8> [[D]]
+; CHECK-NEXT:    ret <2 x i8> zeroinitializer
 ;
   %neg = sub nsw <2 x i8> zeroinitializer, %x
   %d = sdiv <2 x i8> %neg, <i8 -128, i8 -128>
diff --git a/llvm/test/Transforms/InstCombine/icmp-sub.ll b/llvm/test/Transforms/InstCombine/icmp-sub.ll
index 4143902bc9c46..13ed7ba0c1703 100644
--- a/llvm/test/Transforms/InstCombine/icmp-sub.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-sub.ll
@@ -290,8 +290,7 @@ define i1 @subC_nsw_ne(i32 %x) {
 ; CHECK-LABEL: @subC_nsw_ne(
 ; CHECK-NEXT:    [[SUBX:%.*]] = sub nsw i32 -2147483647, [[X:%.*]]
 ; CHECK-NEXT:    call void @use(i32 [[SUBX]])
-; CHECK-NEXT:    [[R:%.*]] = icmp ne i32 [[X]], 2147483603
-; CHECK-NEXT:    ret i1 [[R]]
+; CHECK-NEXT:    ret i1 true
 ;
   %subx = sub nsw i32 -2147483647, %x
   call void @use(i32 %subx)

>From b28f157a06763770b3f5af07fb24bd0ea8de83c6 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Sat, 14 Jun 2025 08:24:46 -0400
Subject: [PATCH 2/4] Suggested Change

Co-authored-by: Yingwei Zheng <dtcxzyw at qq.com>
---
 llvm/lib/Analysis/ValueTracking.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 90d2334788208..e2f895f75f1e4 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9603,8 +9603,8 @@ static void setLimitsForBinOp(const BinaryOperator &BO, APInt &Lower,
         } 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;
+          Lower = *C - APInt::getSignedMaxValue(Width);
+          Upper = APInt::getSignedMinValue(Width);
         }
       }
     }

>From b0904ca7e8cb3488783447fc4bc6b677b1ee9aa3 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Sat, 14 Jun 2025 08:24:59 -0400
Subject: [PATCH 3/4] Update ValueTracking.cpp

Co-authored-by: Yingwei Zheng <dtcxzyw at qq.com>
---
 llvm/lib/Analysis/ValueTracking.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index e2f895f75f1e4..062ee693ad8e4 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9599,7 +9599,7 @@ static void setLimitsForBinOp(const BinaryOperator &BO, APInt &Lower,
           // INT_MIN, so -INT_MIN - 1 is INT_MAX, so it is SINT_MAX - (C - 1),
           // or SINT_MAX - C + 1
           Lower = APInt::getSignedMinValue(Width);
-          Upper = APInt::getSignedMaxValue(Width) + *C + 2;
+          Upper = *C - APInt::getSignedMaxValue(Width);
         } 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].

>From 99c0c6c168d581e57f8fae9f432fd6f09c3785b8 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Sat, 14 Jun 2025 10:50:36 -0400
Subject: [PATCH 4/4] Fix comments

---
 llvm/lib/Analysis/ValueTracking.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 062ee693ad8e4..b4694b75b8c9a 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9594,15 +9594,12 @@ static void setLimitsForBinOp(const BinaryOperator &BO, APInt &Lower,
         Upper = *C + 1;
       } else if (HasNSW) {
         if (C->isNegative()) {
-          // 'sub nsw -C, x' produces [SINT_MIN, SINT_MAX - (C - 1)].
-          // Because to be negative, C must be - 1, and the highest result is
-          // INT_MIN, so -INT_MIN - 1 is INT_MAX, so it is SINT_MAX - (C - 1),
-          // or SINT_MAX - C + 1
+          // 'sub nsw -C, x' produces [SINT_MIN, -C - SINT_MIN].
           Lower = APInt::getSignedMinValue(Width);
           Upper = *C - APInt::getSignedMaxValue(Width);
         } 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].
+          // 'sub nsw C, x' produces [C - SINT_MAX, SINT_MAX].
           Lower = *C - APInt::getSignedMaxValue(Width);
           Upper = APInt::getSignedMinValue(Width);
         }



More information about the llvm-commits mailing list