[llvm] [KnownBits] Improve minimum denominator bounds for sdiv (PR #227264)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 22:02:22 PDT 2026
https://github.com/anandkvinu updated https://github.com/llvm/llvm-project/pull/227264
>From 7ab84dbcbbf9cfd881e0c2a9b3a22fa64079485a Mon Sep 17 00:00:00 2001
From: Anand K Vinu <20cs113anan at ug.cusat.ac.in>
Date: Tue, 29 Sep 2026 13:42:26 +0530
Subject: [PATCH 1/4] [KnownBits] Fix SDiv to set lowest unknown bit to get the
minimum non-zero denominator
---
llvm/lib/Support/KnownBits.cpp | 6 +++++-
llvm/unittests/Support/KnownBitsTest.cpp | 23 +++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 70f7a2f4fc887..81221081a4339 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1255,7 +1255,11 @@ KnownBits KnownBits::sdiv(const KnownBits &LHS, const KnownBits &RHS,
if (Exact || (-LHS.getSignedMaxValue()).uge(RHS.getSignedMaxValue())) {
APInt Denom = RHS.getSignedMinValue();
APInt Num = LHS.getSignedMinValue();
- Res = Denom.isZero() ? Num : Num.sdiv(Denom);
+ // fix to set the lowest unknown bit to get the minimum non-zero
+ // denominator
+ if (Denom.isZero())
+ Denom.setBit(RHS.countMinTrailingZeros());
+ Res = Num.sdiv(Denom);
}
} else if (LHS.isStrictlyPositive() && RHS.isNegative()) {
// Result is negative if Exact OR LHS u>= -RHS.
diff --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp
index d4b8dff54f3d6..5033fc415b520 100644
--- a/llvm/unittests/Support/KnownBitsTest.cpp
+++ b/llvm/unittests/Support/KnownBitsTest.cpp
@@ -693,6 +693,29 @@ TEST(KnownBitsTest, UnaryExhaustive) {
},
[](const APInt &N) { return N * N; }, /*CheckOptimality=*/false);
}
+// implementing googletest for SDIV having unknown bit in denominator.
+// To set the lowest unknown bit to get the minimum non-zero denominator.
+
+TEST(KnownBitsTest, NonZeroDenomSDiv) {
+
+ KnownBits Num(4);
+ KnownBits Denom(4);
+ // setting the masks for Ones and Zeros Num = -8.
+ Num.One.setBit(3);
+ Num.Zero.setBit(2);
+ Num.Zero.setBit(1);
+ Num.Zero.setBit(0);
+ // comment to test each edge cases. Denom = 0b0?00.
+ Denom.Zero.setBit(3);
+ // Denom.Zero.setBit(2);
+ Denom.Zero.setBit(1);
+ Denom.Zero.setBit(0);
+ KnownBits Result = KnownBits::sdiv(Num, Denom);
+ KnownBits Answer(4);
+ Answer.One.setHighBits(3);
+
+ EXPECT_EQ(Result, Answer);
+}
TEST(KnownBitsTest, FunnelShiftExhaustive) {
unsigned Bits = 4;
>From a999dc72ff4cb4382bb68036e87bc0313b6e27d6 Mon Sep 17 00:00:00 2001
From: "Anand.k.vinu" <41266843+anandkvinu at users.noreply.github.com>
Date: Tue, 29 Sep 2026 17:43:10 +0530
Subject: [PATCH 2/4] Update llvm/unittests/Support/KnownBitsTest.cpp
Co-authored-by: Matt Arsenault <arsenm2 at gmail.com>
---
llvm/unittests/Support/KnownBitsTest.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp
index 5033fc415b520..0639aa9f6421b 100644
--- a/llvm/unittests/Support/KnownBitsTest.cpp
+++ b/llvm/unittests/Support/KnownBitsTest.cpp
@@ -693,8 +693,6 @@ TEST(KnownBitsTest, UnaryExhaustive) {
},
[](const APInt &N) { return N * N; }, /*CheckOptimality=*/false);
}
-// implementing googletest for SDIV having unknown bit in denominator.
-// To set the lowest unknown bit to get the minimum non-zero denominator.
TEST(KnownBitsTest, NonZeroDenomSDiv) {
>From 0b705575a0b172e246ba66231e1650572d0d077a Mon Sep 17 00:00:00 2001
From: Anand K Vinu <20cs113anan at ug.cusat.ac.in>
Date: Thu, 1 Oct 2026 00:07:29 +0530
Subject: [PATCH 3/4] Add lit test, remove unit test, reword comment
---
llvm/lib/Support/KnownBits.cpp | 4 ++--
.../Transforms/InstCombine/sdiv-knownbits.ll | 20 ++++++++++++++++++
llvm/unittests/Support/KnownBitsTest.cpp | 21 -------------------
3 files changed, 22 insertions(+), 23 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/sdiv-knownbits.ll
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 81221081a4339..7a88dc01254b2 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1255,8 +1255,8 @@ KnownBits KnownBits::sdiv(const KnownBits &LHS, const KnownBits &RHS,
if (Exact || (-LHS.getSignedMaxValue()).uge(RHS.getSignedMaxValue())) {
APInt Denom = RHS.getSignedMinValue();
APInt Num = LHS.getSignedMinValue();
- // fix to set the lowest unknown bit to get the minimum non-zero
- // denominator
+ // If the denominator may be zero, use the lowest unknown bit as the
+ // minimum non-zero denominator, matching udiv.
if (Denom.isZero())
Denom.setBit(RHS.countMinTrailingZeros());
Res = Num.sdiv(Denom);
diff --git a/llvm/test/Transforms/InstCombine/sdiv-knownbits.ll b/llvm/test/Transforms/InstCombine/sdiv-knownbits.ll
new file mode 100644
index 0000000000000..e61ae1687beba
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sdiv-knownbits.ll
@@ -0,0 +1,20 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; The denominator has the pattern 0b??00, so it can be {0, 4, 8, 12}.
+; Division by zero is UB, so the minimum non-zero denominator is 4.
+; The numerator is a negative multiple of 16 in the range [-128, -16].
+; Therefore, the result is in the range [-32, -1], so the top three
+; bits are always set and the final 'and' can be removed.
+define i8 @sdiv_maybe_zero_denominator(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_maybe_zero_denominator(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -32
+;
+ %n = and i8 %a, -16
+ %n.neg = or i8 %n, -128
+ %d = and i8 %x, 12
+ %r = sdiv i8 %n.neg, %d
+ %m = and i8 %r, -32
+ ret i8 %m
+}
diff --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp
index 0639aa9f6421b..d4b8dff54f3d6 100644
--- a/llvm/unittests/Support/KnownBitsTest.cpp
+++ b/llvm/unittests/Support/KnownBitsTest.cpp
@@ -694,27 +694,6 @@ TEST(KnownBitsTest, UnaryExhaustive) {
[](const APInt &N) { return N * N; }, /*CheckOptimality=*/false);
}
-TEST(KnownBitsTest, NonZeroDenomSDiv) {
-
- KnownBits Num(4);
- KnownBits Denom(4);
- // setting the masks for Ones and Zeros Num = -8.
- Num.One.setBit(3);
- Num.Zero.setBit(2);
- Num.Zero.setBit(1);
- Num.Zero.setBit(0);
- // comment to test each edge cases. Denom = 0b0?00.
- Denom.Zero.setBit(3);
- // Denom.Zero.setBit(2);
- Denom.Zero.setBit(1);
- Denom.Zero.setBit(0);
- KnownBits Result = KnownBits::sdiv(Num, Denom);
- KnownBits Answer(4);
- Answer.One.setHighBits(3);
-
- EXPECT_EQ(Result, Answer);
-}
-
TEST(KnownBitsTest, FunnelShiftExhaustive) {
unsigned Bits = 4;
ForeachKnownBits(Bits, [&](const KnownBits &Known1) {
>From bd9a6f61932137027691ba79524b4152d7cf1e52 Mon Sep 17 00:00:00 2001
From: Anand K Vinu <20cs113anan at ug.cusat.ac.in>
Date: Thu, 1 Oct 2026 10:31:17 +0530
Subject: [PATCH 4/4] Add more sdiv known-bits lit tests
---
.../Transforms/InstCombine/sdiv-knownbits.ll | 157 ++++++++++++++++--
1 file changed, 147 insertions(+), 10 deletions(-)
diff --git a/llvm/test/Transforms/InstCombine/sdiv-knownbits.ll b/llvm/test/Transforms/InstCombine/sdiv-knownbits.ll
index e61ae1687beba..bce4fb0d9613d 100644
--- a/llvm/test/Transforms/InstCombine/sdiv-knownbits.ll
+++ b/llvm/test/Transforms/InstCombine/sdiv-knownbits.ll
@@ -1,20 +1,157 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
-; The denominator has the pattern 0b??00, so it can be {0, 4, 8, 12}.
-; Division by zero is UB, so the minimum non-zero denominator is 4.
-; The numerator is a negative multiple of 16 in the range [-128, -16].
-; Therefore, the result is in the range [-32, -1], so the top three
-; bits are always set and the final 'and' can be removed.
-define i8 @sdiv_maybe_zero_denominator(i8 %a, i8 %x) {
-; CHECK-LABEL: define i8 @sdiv_maybe_zero_denominator(
+; Division by zero is UB, so sdiv uses the lowest unknown bit of the
+; denominator as the minimum non-zero denominator when it may be zero.
+; Each test masks the quotient with its known high bits, so it folds to a
+; constant only if the quotient range is tight enough.
+
+; i8, denominator 0b0???????, numerator in [-128, -16] (exact):
+; minimum denominator 1, quotient in [-128, -1].
+define i8 @sdiv_exact_denom_0b0_______(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_exact_denom_0b0_______(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -128
+;
+ %n = and i8 %a, -16
+ %n.neg = or i8 %n, -128
+ %d = and i8 %x, 127
+ %r = sdiv exact i8 %n.neg, %d
+ %m = and i8 %r, -128
+ ret i8 %m
+}
+
+; i8, denominator 0b0??????0, numerator in [-128, -16] (exact):
+; minimum denominator 2, quotient in [-64, -1].
+define i8 @sdiv_exact_denom_0b0______0(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_exact_denom_0b0______0(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -64
+;
+ %n = and i8 %a, -16
+ %n.neg = or i8 %n, -128
+ %d = and i8 %x, 126
+ %r = sdiv exact i8 %n.neg, %d
+ %m = and i8 %r, -64
+ ret i8 %m
+}
+
+; i8, denominator 0b0000000?, numerator in [-128, -16]:
+; minimum denominator 1, quotient in [-128, -1].
+define i8 @sdiv_denom_0b0000000_(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_denom_0b0000000_(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -128
+;
+ %n = and i8 %a, -16
+ %n.neg = or i8 %n, -128
+ %d = and i8 %x, 1
+ %r = sdiv i8 %n.neg, %d
+ %m = and i8 %r, -128
+ ret i8 %m
+}
+
+; i8, denominator 0b000000??, numerator in [-128, -16]:
+; minimum denominator 1, quotient in [-128, -1].
+define i8 @sdiv_denom_0b000000__(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_denom_0b000000__(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -128
+;
+ %n = and i8 %a, -16
+ %n.neg = or i8 %n, -128
+ %d = and i8 %x, 3
+ %r = sdiv i8 %n.neg, %d
+ %m = and i8 %r, -128
+ ret i8 %m
+}
+
+; i8, denominator 0b00000?0?, numerator in [-128, -16]:
+; minimum denominator 1, quotient in [-128, -1].
+define i8 @sdiv_denom_0b00000_0_(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_denom_0b00000_0_(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -128
+;
+ %n = and i8 %a, -16
+ %n.neg = or i8 %n, -128
+ %d = and i8 %x, 5
+ %r = sdiv i8 %n.neg, %d
+ %m = and i8 %r, -128
+ ret i8 %m
+}
+
+; i8, denominator 0b000000?0, numerator in [-128, -16]:
+; minimum denominator 2, quotient in [-64, -1].
+define i8 @sdiv_denom_0b000000_0(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_denom_0b000000_0(
; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
-; CHECK-NEXT: ret i8 -32
+; CHECK-NEXT: ret i8 -64
;
%n = and i8 %a, -16
%n.neg = or i8 %n, -128
- %d = and i8 %x, 12
+ %d = and i8 %x, 2
%r = sdiv i8 %n.neg, %d
- %m = and i8 %r, -32
+ %m = and i8 %r, -64
ret i8 %m
}
+
+; i8, denominator 0b0?000000, numerator in [-128, -65]:
+; minimum denominator 64, quotient in [-2, -1].
+define i8 @sdiv_denom_0b0_000000(i8 %a, i8 %x) {
+; CHECK-LABEL: define i8 @sdiv_denom_0b0_000000(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 -2
+;
+ %n = or i8 %a, -128
+ %n.neg = and i8 %n, -65
+ %d = and i8 %x, 64
+ %r = sdiv i8 %n.neg, %d
+ %m = and i8 %r, -2
+ ret i8 %m
+}
+
+; i32, denominator in {0, 4, 8, 12}, numerator in [-128, -16]:
+; minimum denominator 4, quotient in [-32, -1].
+define i32 @sdiv_i32_maybe_zero_denominator(i32 %a, i32 %x) {
+; CHECK-LABEL: define i32 @sdiv_i32_maybe_zero_denominator(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: ret i32 -32
+;
+ %n = and i32 %a, -16
+ %n.neg = or i32 %n, -128
+ %d = and i32 %x, 12
+ %r = sdiv i32 %n.neg, %d
+ %m = and i32 %r, -32
+ ret i32 %m
+}
+
+; i32, denominator in {0, 2, 4, 6}, numerator in [-128, -16]:
+; minimum denominator 2, quotient in [-64, -1].
+define i32 @sdiv_i32_maybe_zero_denominator_min_2(i32 %a, i32 %x) {
+; CHECK-LABEL: define i32 @sdiv_i32_maybe_zero_denominator_min_2(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: ret i32 -64
+;
+ %n = and i32 %a, -16
+ %n.neg = or i32 %n, -128
+ %d = and i32 %x, 6
+ %r = sdiv i32 %n.neg, %d
+ %m = and i32 %r, -64
+ ret i32 %m
+}
+
+; i32, denominator in {0, 1, 2, 3}, numerator in [-128, -16]:
+; minimum denominator 1, quotient in [-128, -1].
+define i32 @sdiv_i32_maybe_zero_denominator_min_1(i32 %a, i32 %x) {
+; CHECK-LABEL: define i32 @sdiv_i32_maybe_zero_denominator_min_1(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: ret i32 -128
+;
+ %n = and i32 %a, -16
+ %n.neg = or i32 %n, -128
+ %d = and i32 %x, 3
+ %r = sdiv i32 %n.neg, %d
+ %m = and i32 %r, -128
+ ret i32 %m
+}
More information about the llvm-commits
mailing list