[llvm] [InstCombine] Recognize X - 1 as nonnegative when X is positive (PR #223168)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 13:02:01 PDT 2026
https://github.com/addmisol updated https://github.com/llvm/llvm-project/pull/223168
>From dec1143f913a3080923b8e6fdfec86cf9ade8d98 Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Sun, 13 Sep 2026 00:45:41 +0530
Subject: [PATCH 1/2] [InstCombine] Recognize X - 1 as nonnegative when X is
positive
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
llvm/lib/Analysis/ValueTracking.cpp | 24 ++++++++++++++++-----
llvm/test/Transforms/InstCombine/bitcast.ll | 22 +++++++++++++++++++
llvm/test/Transforms/InstCombine/divceil.ll | 2 +-
3 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1580571a01451..74c078867ee14 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -531,11 +531,25 @@ static void computeKnownBitsAddSub(bool Add, const Value *Op0, const Value *Op1,
computeKnownBits(Op0, DemandedElts, Known2, Q, Depth + 1);
KnownOut = KnownBits::computeForAddSub(Add, NSW, NUW, Known2, KnownOut);
- if (!Add && NSW && !KnownOut.isNonNegative() &&
- (isImpliedByDomCondition(ICmpInst::ICMP_SLE, Op1, Op0, Q.CxtI, Q.DL)
- .value_or(false) ||
- match(Op1, m_c_SMin(m_Specific(Op0), m_Value()))))
- KnownOut.makeNonNegative();
+ if (NSW && !KnownOut.isNonNegative()) {
+ if (!Add) {
+ // X - Y is nonnegative if Y <= X (signed).
+ if (isImpliedByDomCondition(ICmpInst::ICMP_SLE, Op1, Op0, Q.CxtI, Q.DL)
+ .value_or(false) ||
+ match(Op1, m_c_SMin(m_Specific(Op0), m_Value())))
+ KnownOut.makeNonNegative();
+ // X - 1 is nonnegative if X is known positive (X > 0 implies X >= 1).
+ else if (match(Op1, m_One()) && Known2.isNonNegative() &&
+ (Known2.isNonZero() || isKnownNonZero(Op0, Q, Depth + 1)))
+ KnownOut.makeNonNegative();
+ } else {
+ // X + (-1) is nonnegative if X is known positive (X > 0 implies X >= 1).
+ // This handles "add nsw X, -1" which is semantically "X - 1".
+ if (match(Op1, m_AllOnes()) && Known2.isNonNegative() &&
+ (Known2.isNonZero() || isKnownNonZero(Op0, Q, Depth + 1)))
+ KnownOut.makeNonNegative();
+ }
+ }
if (Add)
// Try to match lerp pattern and combine results
diff --git a/llvm/test/Transforms/InstCombine/bitcast.ll b/llvm/test/Transforms/InstCombine/bitcast.ll
index a81eb5000f63c..34e75a7e90951 100644
--- a/llvm/test/Transforms/InstCombine/bitcast.ll
+++ b/llvm/test/Transforms/InstCombine/bitcast.ll
@@ -903,3 +903,25 @@ end: ; preds = %unreachable, %entry
%3 = extractelement <2 x i16> %2, i64 0
ret i16 %3
}
+
+; Issue #221943: X - 1 should be nonnegative when X > 0.
+; The assume that %magx > 0 should allow InstCombine to recognize that
+; %mag = %magx - 1 is nonnegative, enabling the copysign idiom fold.
+define float @copysign_idiom_sub1_positive(float %x, i32 %magx) {
+; CHECK-LABEL: @copysign_idiom_sub1_positive(
+; CHECK-NEXT: [[MAG:%.*]] = add nsw i32 [[MAGX:%.*]], -1
+; CHECK-NEXT: [[POSITIVE:%.*]] = icmp sgt i32 [[MAGX]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[POSITIVE]])
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[MAG]] to float
+; CHECK-NEXT: [[Y:%.*]] = call float @llvm.copysign.f32(float [[TMP1]], float [[X:%.*]])
+; CHECK-NEXT: ret float [[Y]]
+;
+ %mag = add nsw i32 %magx, -1
+ %positive = icmp sgt i32 %magx, 0
+ call void @llvm.assume(i1 %positive)
+ %bits = bitcast float %x to i32
+ %sign = and i32 %bits, -2147483648
+ %res = or i32 %mag, %sign
+ %y = bitcast i32 %res to float
+ ret float %y
+}
diff --git a/llvm/test/Transforms/InstCombine/divceil.ll b/llvm/test/Transforms/InstCombine/divceil.ll
index c9f3ba750d7ec..e43d6c28fcd27 100644
--- a/llvm/test/Transforms/InstCombine/divceil.ll
+++ b/llvm/test/Transforms/InstCombine/divceil.ll
@@ -138,7 +138,7 @@ define i32 @divceil_i16_var_divisor_zext(i16 range(i16 0, 101) %x, i16 range(i16
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i16 [[Y:%.*]], -1
; CHECK-NEXT: [[TMP2:%.*]] = add nuw nsw i16 [[X:%.*]], [[TMP1]]
; CHECK-NEXT: [[TMP3:%.*]] = udiv i16 [[TMP2]], [[Y]]
-; CHECK-NEXT: [[RESULT:%.*]] = zext i16 [[TMP3]] to i32
+; CHECK-NEXT: [[RESULT:%.*]] = zext nneg i16 [[TMP3]] to i32
; CHECK-NEXT: ret i32 [[RESULT]]
;
%q = udiv i16 %x, %y
>From 793e43272a58b53de1eeff7dd700ffeae650b7c0 Mon Sep 17 00:00:00 2001
From: Addmisol <addmisol9 at gmail.com>
Date: Sun, 13 Sep 2026 01:31:51 +0530
Subject: [PATCH 2/2] fix: add tests | fix issues
---
llvm/test/Transforms/InstCombine/bitcast.ll | 83 ++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/InstCombine/bitcast.ll b/llvm/test/Transforms/InstCombine/bitcast.ll
index 34e75a7e90951..ee61761221a93 100644
--- a/llvm/test/Transforms/InstCombine/bitcast.ll
+++ b/llvm/test/Transforms/InstCombine/bitcast.ll
@@ -916,7 +916,7 @@ define float @copysign_idiom_sub1_positive(float %x, i32 %magx) {
; CHECK-NEXT: [[Y:%.*]] = call float @llvm.copysign.f32(float [[TMP1]], float [[X:%.*]])
; CHECK-NEXT: ret float [[Y]]
;
- %mag = add nsw i32 %magx, -1
+ %mag = add i32 %magx, -1
%positive = icmp sgt i32 %magx, 0
call void @llvm.assume(i1 %positive)
%bits = bitcast float %x to i32
@@ -925,3 +925,84 @@ define float @copysign_idiom_sub1_positive(float %x, i32 %magx) {
%y = bitcast i32 %res to float
ret float %y
}
+
+; Test with sub instruction instead of add with -1.
+define float @copysign_idiom_sub1_positive_sub(float %x, i32 %magx) {
+; CHECK-LABEL: @copysign_idiom_sub1_positive_sub(
+; CHECK-NEXT: [[MAG:%.*]] = add nsw i32 [[MAGX:%.*]], -1
+; CHECK-NEXT: [[POSITIVE:%.*]] = icmp sgt i32 [[MAGX]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[POSITIVE]])
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[MAG]] to float
+; CHECK-NEXT: [[Y:%.*]] = call float @llvm.copysign.f32(float [[TMP1]], float [[X:%.*]])
+; CHECK-NEXT: ret float [[Y]]
+;
+ %mag = sub i32 %magx, 1
+ %positive = icmp sgt i32 %magx, 0
+ call void @llvm.assume(i1 %positive)
+ %bits = bitcast float %x to i32
+ %sign = and i32 %bits, -2147483648
+ %res = or i32 %mag, %sign
+ %y = bitcast i32 %res to float
+ ret float %y
+}
+
+; Test with i64 for double precision.
+define double @copysign_idiom_sub1_positive_f64(double %x, i64 %magx) {
+; CHECK-LABEL: @copysign_idiom_sub1_positive_f64(
+; CHECK-NEXT: [[MAG:%.*]] = add nsw i64 [[MAGX:%.*]], -1
+; CHECK-NEXT: [[POSITIVE:%.*]] = icmp sgt i64 [[MAGX]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[POSITIVE]])
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast i64 [[MAG]] to double
+; CHECK-NEXT: [[Y:%.*]] = call double @llvm.copysign.f64(double [[TMP1]], double [[X:%.*]])
+; CHECK-NEXT: ret double [[Y]]
+;
+ %mag = add i64 %magx, -1
+ %positive = icmp sgt i64 %magx, 0
+ call void @llvm.assume(i1 %positive)
+ %bits = bitcast double %x to i64
+ %sign = and i64 %bits, -9223372036854775808
+ %res = or i64 %mag, %sign
+ %y = bitcast i64 %res to double
+ ret double %y
+}
+
+; Negative test: %magx >= 0 (nonnegative) is not enough, need %magx > 0 (positive).
+; %magx could be 0, so %mag = %magx - 1 could be -1.
+define float @copysign_idiom_sub1_nonnegative_not_positive(float %x, i32 %magx) {
+; CHECK-LABEL: @copysign_idiom_sub1_nonnegative_not_positive(
+; CHECK-NEXT: [[MAG:%.*]] = add nsw i32 [[MAGX:%.*]], -1
+; CHECK-NEXT: [[NONNEG:%.*]] = icmp sgt i32 [[MAGX]], -1
+; CHECK-NEXT: call void @llvm.assume(i1 [[NONNEG]])
+; CHECK-NEXT: [[BITS:%.*]] = bitcast float [[X:%.*]] to i32
+; CHECK-NEXT: [[SIGN:%.*]] = and i32 [[BITS]], -2147483648
+; CHECK-NEXT: [[RES:%.*]] = or i32 [[MAG]], [[SIGN]]
+; CHECK-NEXT: [[Y:%.*]] = bitcast i32 [[RES]] to float
+; CHECK-NEXT: ret float [[Y]]
+;
+ %mag = add i32 %magx, -1
+ %nonneg = icmp sgt i32 %magx, -1
+ call void @llvm.assume(i1 %nonneg)
+ %bits = bitcast float %x to i32
+ %sign = and i32 %bits, -2147483648
+ %res = or i32 %mag, %sign
+ %y = bitcast i32 %res to float
+ ret float %y
+}
+
+; Negative test: no assume, so we don't know if %magx is positive.
+define float @copysign_idiom_sub1_no_assume(float %x, i32 %magx) {
+; CHECK-LABEL: @copysign_idiom_sub1_no_assume(
+; CHECK-NEXT: [[MAG:%.*]] = add i32 [[MAGX:%.*]], -1
+; CHECK-NEXT: [[BITS:%.*]] = bitcast float [[X:%.*]] to i32
+; CHECK-NEXT: [[SIGN:%.*]] = and i32 [[BITS]], -2147483648
+; CHECK-NEXT: [[RES:%.*]] = or i32 [[MAG]], [[SIGN]]
+; CHECK-NEXT: [[Y:%.*]] = bitcast i32 [[RES]] to float
+; CHECK-NEXT: ret float [[Y]]
+;
+ %mag = add i32 %magx, -1
+ %bits = bitcast float %x to i32
+ %sign = and i32 %bits, -2147483648
+ %res = or i32 %mag, %sign
+ %y = bitcast i32 %res to float
+ ret float %y
+}
More information about the llvm-commits
mailing list