[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