[llvm] Add subtraction support for setLimitsForBinOp (PR #143618)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 13 16:10:42 PDT 2025
https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/143618
>From 150aeff98d577cc11d839ef31cc86362d1761f21 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 10 Jun 2025 16:44:34 -0400
Subject: [PATCH] Add subtraction support for setLimitsForBinOp
We can determine the range from a subtraction if it has nsw or nuw.
---
llvm/lib/Analysis/ValueTracking.cpp | 41 +++++++++++++++++---
llvm/test/Transforms/InstCombine/div.ll | 8 +---
llvm/test/Transforms/InstCombine/icmp-sub.ll | 3 +-
3 files changed, 39 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d8c1096049dce..3e1838fb21233 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;
@@ -9683,8 +9716,6 @@ static void setLimitsForBinOp(const BinaryOperator &BO, APInt &Lower,
// sequence of consecutive ones is shifted to the highbits (breaking
// ties for which sequence is higher). At the moment we take a liberal
// upper bound on this by just popcounting the constant.
- // TODO: There may be a bitwise trick for it longest/highest
- // consecutative sequence of ones (naive method is O(Width) loop).
Upper = APInt::getHighBitsSet(Width, C->popcount()) + 1;
}
} else if (match(BO.getOperand(1), m_APInt(C)) && C->ult(Width)) {
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)
More information about the llvm-commits
mailing list