From b0e78b78994414bf45823e545df9277aacd126e6 Mon Sep 17 00:00:00 2001 From: Rose 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, 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 Date: Sat, 14 Jun 2025 08:24:46 -0400 Subject: [PATCH 2/4] Suggested Change Co-authored-by: Yingwei Zheng --- 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 Date: Sat, 14 Jun 2025 08:24:59 -0400 Subject: [PATCH 3/4] Update ValueTracking.cpp Co-authored-by: Yingwei Zheng --- 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 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); }