[llvm] [KnownBits] Add fast path for udiv by pow2 and overloads for shift ops by constant (PR #189779)

Max Graey via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 4 09:46:41 PDT 2026


https://github.com/MaxGraey updated https://github.com/llvm/llvm-project/pull/189779

>From bc71364b702b686d437e63bd8fb043ff3d001a6b Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Wed, 1 Apr 2026 03:48:37 +0300
Subject: [PATCH 01/24] add fast-path for KnownBits::udiv when RHS is contant

---
 llvm/lib/Support/KnownBits.cpp | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 07e7781d0839d..5809fff3ad6ab 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1209,6 +1209,20 @@ KnownBits KnownBits::udiv(const KnownBits &LHS, const KnownBits &RHS,
     return Known;
   }
 
+  if (RHS.isConstant()) {
+    const APInt Divisor = RHS.getConstant();
+    if (Divisor.isOne())
+      return LHS;
+
+    if (Divisor.isPowerOf2()) {
+      unsigned Shift = Divisor.logBase2();
+      KnownBits Known = LHS;
+      Known.One.lshrInPlace(Shift);
+      Known.Zero.lshrInPlace(Shift);
+      return Known;
+    }
+  }
+
   // We can figure out the minimum number of upper zero bits by doing
   // MaxNumerator / MinDenominator. If the Numerator gets smaller or Denominator
   // gets larger, the number of upper zero bits increases.

>From 98fbc0b1f97150ca44a9fe1b4ca9152665c75868 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Wed, 1 Apr 2026 03:56:48 +0300
Subject: [PATCH 02/24] refactor

---
 llvm/lib/Support/KnownBits.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 5809fff3ad6ab..25530d7e46727 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1216,7 +1216,7 @@ KnownBits KnownBits::udiv(const KnownBits &LHS, const KnownBits &RHS,
 
     if (Divisor.isPowerOf2()) {
       unsigned Shift = Divisor.logBase2();
-      KnownBits Known = LHS;
+      Known = LHS;
       Known.One.lshrInPlace(Shift);
       Known.Zero.lshrInPlace(Shift);
       return Known;

>From f326a707d239257f17684715800cb511c523dda9 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Wed, 1 Apr 2026 05:01:20 +0300
Subject: [PATCH 03/24] fix: preserve high bits of LHS

---
 llvm/lib/Support/KnownBits.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 25530d7e46727..56ec9de6af77e 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1216,9 +1216,15 @@ KnownBits KnownBits::udiv(const KnownBits &LHS, const KnownBits &RHS,
 
     if (Divisor.isPowerOf2()) {
       unsigned Shift = Divisor.logBase2();
+
+      APInt MaxRes = LHS.getMaxValue().lshr(Shift);
+      unsigned LeadZ = MaxRes.countLeadingZeros();
+
       Known = LHS;
       Known.One.lshrInPlace(Shift);
       Known.Zero.lshrInPlace(Shift);
+      Known.Zero.setHighBits(LeadZ);
+
       return Known;
     }
   }

>From 7c6b11aeec5d951c899adbae80635abd721540e9 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Wed, 1 Apr 2026 11:31:59 +0300
Subject: [PATCH 04/24] update mir tests

---
 llvm/test/CodeGen/AArch64/GlobalISel/knownbits-sdiv.mir | 4 ++--
 llvm/test/CodeGen/AArch64/GlobalISel/knownbits-udiv.mir | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-sdiv.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-sdiv.mir
index 7b315205bc2e8..ae56e9097c99f 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-sdiv.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-sdiv.mir
@@ -8,7 +8,7 @@ body: |
   ; CHECK-LABEL: name: @Cst
   ; CHECK-NEXT: %0:_ KnownBits:01100100 SignBits:1
   ; CHECK-NEXT: %1:_ KnownBits:00000100 SignBits:5
-  ; CHECK-NEXT: %2:_ KnownBits:000????? SignBits:3
+  ; CHECK-NEXT: %2:_ KnownBits:00011001 SignBits:3
     %0:_(s8) = G_CONSTANT i8 100
     %1:_(s8) = G_CONSTANT i8 4
     %2:_(s8) = G_SDIV %0, %1
@@ -36,7 +36,7 @@ body: |
   ; CHECK-LABEL: name: @Exact
   ; CHECK-NEXT: %0:_ KnownBits:00001100 SignBits:4
   ; CHECK-NEXT: %1:_ KnownBits:00000100 SignBits:5
-  ; CHECK-NEXT: %2:_ KnownBits:000000?1 SignBits:6
+  ; CHECK-NEXT: %2:_ KnownBits:00000011 SignBits:6
     %0:_(s8) = G_CONSTANT i8 12
     %1:_(s8) = G_CONSTANT i8 4
     %2:_(s8) = exact G_SDIV %0, %1
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-udiv.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-udiv.mir
index dfbcd3f3c6188..0a0a00ce456ae 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-udiv.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-udiv.mir
@@ -8,7 +8,7 @@ body: |
   ; CHECK-LABEL: name: @Cst
   ; CHECK-NEXT: %0:_ KnownBits:01100100 SignBits:1
   ; CHECK-NEXT: %1:_ KnownBits:00000100 SignBits:5
-  ; CHECK-NEXT: %2:_ KnownBits:000????? SignBits:3
+  ; CHECK-NEXT: %2:_ KnownBits:00011001 SignBits:3
     %0:_(s8) = G_CONSTANT i8 100
     %1:_(s8) = G_CONSTANT i8 4
     %2:_(s8) = G_UDIV %0, %1
@@ -36,7 +36,7 @@ body: |
   ; CHECK-LABEL: name: @Exact
   ; CHECK-NEXT: %0:_ KnownBits:00001100 SignBits:4
   ; CHECK-NEXT: %1:_ KnownBits:00000100 SignBits:5
-  ; CHECK-NEXT: %2:_ KnownBits:000000?1 SignBits:6
+  ; CHECK-NEXT: %2:_ KnownBits:00000011 SignBits:6
     %0:_(s8) = G_CONSTANT i8 12
     %1:_(s8) = G_CONSTANT i8 4
     %2:_(s8) = exact G_UDIV %0, %1

>From c223b1f1b92bc4d0c4b90e6e7c9b55fe9b7031e0 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Wed, 1 Apr 2026 13:36:25 +0300
Subject: [PATCH 05/24] simplify

---
 llvm/lib/Support/KnownBits.cpp | 24 ++++++------------------
 1 file changed, 6 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 56ec9de6af77e..97ae3138356ee 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1209,24 +1209,12 @@ KnownBits KnownBits::udiv(const KnownBits &LHS, const KnownBits &RHS,
     return Known;
   }
 
-  if (RHS.isConstant()) {
-    const APInt Divisor = RHS.getConstant();
-    if (Divisor.isOne())
-      return LHS;
-
-    if (Divisor.isPowerOf2()) {
-      unsigned Shift = Divisor.logBase2();
-
-      APInt MaxRes = LHS.getMaxValue().lshr(Shift);
-      unsigned LeadZ = MaxRes.countLeadingZeros();
-
-      Known = LHS;
-      Known.One.lshrInPlace(Shift);
-      Known.Zero.lshrInPlace(Shift);
-      Known.Zero.setHighBits(LeadZ);
-
-      return Known;
-    }
+  if (RHS.isConstant() && RHS.getConstant().isPowerOf2()) {
+    unsigned Shift = RHS.getConstant().logBase2();
+    Known = LHS;
+    Known >>= Shift;
+    Known.Zero.setHighBits(Shift);
+    return Known;
   }
 
   // We can figure out the minimum number of upper zero bits by doing

>From 185302411f5ea48bc3d5c4c48b90566a9da8cb76 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 03:15:09 +0300
Subject: [PATCH 06/24] refactor: intoduce shifted by const KnownBits::lshr and
 KnownBits::ashr versions and reuse it

---
 llvm/include/llvm/Support/KnownBits.h |  8 ++++++
 llvm/lib/Support/KnownBits.cpp        | 38 +++++++++++++--------------
 2 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index d99817dc43e5b..d773339d679cf 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -460,11 +460,19 @@ struct KnownBits {
                                 bool NUW = false, bool NSW = false,
                                 bool ShAmtNonZero = false);
 
+  /// Compute known bits for lshr(LHS, ShiftAmt).
+  /// This is shift by constant variant of lshr(LHS, RHS).
+  LLVM_ABI static KnownBits lshr(const KnownBits &LHS, unsigned ShiftAmt);
+
   /// Compute known bits for lshr(LHS, RHS).
   /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS.
   LLVM_ABI static KnownBits lshr(const KnownBits &LHS, const KnownBits &RHS,
                                  bool ShAmtNonZero = false, bool Exact = false);
 
+  /// Compute known bits for ashr(LHS, ShiftAmt).
+  /// This is shift by constant variant of ashr(LHS, RHS).
+  LLVM_ABI static KnownBits ashr(const KnownBits &LHS, unsigned ShiftAmt);
+
   /// Compute known bits for ashr(LHS, RHS).
   /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS.
   LLVM_ABI static KnownBits ashr(const KnownBits &LHS, const KnownBits &RHS,
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 97ae3138356ee..fb1a9f8638639 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -488,16 +488,17 @@ KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
   return Known;
 }
 
+KnownBits KnownBits::lshr(const KnownBits &LHS, unsigned ShiftAmt) {
+  KnownBits Known = LHS;
+  Known >>= ShiftAmt;
+  // High bits are known zero.
+  Known.Zero.setHighBits(ShiftAmt);
+  return Known;
+}
+
 KnownBits KnownBits::lshr(const KnownBits &LHS, const KnownBits &RHS,
                           bool ShAmtNonZero, bool Exact) {
   unsigned BitWidth = LHS.getBitWidth();
-  auto ShiftByConst = [&](const KnownBits &LHS, unsigned ShiftAmt) {
-    KnownBits Known = LHS;
-    Known >>= ShiftAmt;
-    // High bits are known zero.
-    Known.Zero.setHighBits(ShiftAmt);
-    return Known;
-  };
 
   // Fast path for a common case when LHS is completely unknown.
   KnownBits Known(BitWidth);
@@ -533,7 +534,7 @@ KnownBits KnownBits::lshr(const KnownBits &LHS, const KnownBits &RHS,
     if ((ShiftAmtZeroMask & ShiftAmt) != 0 ||
         (ShiftAmtOneMask | ShiftAmt) != ShiftAmt)
       continue;
-    Known = Known.intersectWith(ShiftByConst(LHS, ShiftAmt));
+    Known = Known.intersectWith(lshr(LHS, ShiftAmt));
     if (Known.isUnknown())
       break;
   }
@@ -544,15 +545,16 @@ KnownBits KnownBits::lshr(const KnownBits &LHS, const KnownBits &RHS,
   return Known;
 }
 
+KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt) {
+  KnownBits Known = LHS;
+  Known.Zero.ashrInPlace(ShiftAmt);
+  Known.One.ashrInPlace(ShiftAmt);
+  return Known;
+}
+
 KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,
                           bool ShAmtNonZero, bool Exact) {
   unsigned BitWidth = LHS.getBitWidth();
-  auto ShiftByConst = [&](const KnownBits &LHS, unsigned ShiftAmt) {
-    KnownBits Known = LHS;
-    Known.Zero.ashrInPlace(ShiftAmt);
-    Known.One.ashrInPlace(ShiftAmt);
-    return Known;
-  };
 
   // Fast path for a common case when LHS is completely unknown.
   KnownBits Known(BitWidth);
@@ -592,7 +594,7 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,
     if ((ShiftAmtZeroMask & ShiftAmt) != 0 ||
         (ShiftAmtOneMask | ShiftAmt) != ShiftAmt)
       continue;
-    Known = Known.intersectWith(ShiftByConst(LHS, ShiftAmt));
+    Known = Known.intersectWith(ashr(LHS, ShiftAmt));
     if (Known.isUnknown())
       break;
   }
@@ -1210,11 +1212,7 @@ KnownBits KnownBits::udiv(const KnownBits &LHS, const KnownBits &RHS,
   }
 
   if (RHS.isConstant() && RHS.getConstant().isPowerOf2()) {
-    unsigned Shift = RHS.getConstant().logBase2();
-    Known = LHS;
-    Known >>= Shift;
-    Known.Zero.setHighBits(Shift);
-    return Known;
+    return lshr(LHS, RHS.getConstant().logBase2());
   }
 
   // We can figure out the minimum number of upper zero bits by doing

>From a045228f606745445eab57adde4dd3730fb992e1 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 14:39:13 +0300
Subject: [PATCH 07/24] add KnownBits::shl with immidiate shift overload

---
 llvm/include/llvm/Support/KnownBits.h |  5 +++
 llvm/lib/Support/KnownBits.cpp        | 44 ++++++++++++++-------------
 2 files changed, 28 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index d773339d679cf..131908d205ddf 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -454,6 +454,11 @@ struct KnownBits {
   /// Compute known bits for abds(LHS, RHS).
   LLVM_ABI static KnownBits abds(KnownBits LHS, KnownBits RHS);
 
+  /// Compute known bits for shl(LHS, ShiftAmt).
+  /// This is shift by constant variant of shl(LHS, RHS).
+  LLVM_ABI static KnownBits shl(const KnownBits &LHS, unsigned ShiftAmt,
+                                bool NUW = false, bool NSW = false);
+
   /// Compute known bits for shl(LHS, RHS).
   /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS.
   LLVM_ABI static KnownBits shl(const KnownBits &LHS, const KnownBits &RHS,
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index fb1a9f8638639..82bf6c1229960 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -404,29 +404,31 @@ static unsigned getMaxShiftAmount(const APInt &MaxValue, unsigned BitWidth) {
   return MaxValue.getLimitedValue(BitWidth - 1);
 }
 
+KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
+                         bool NSW) {
+  KnownBits Known;
+  bool ShiftedOutZero, ShiftedOutOne;
+  Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
+  Known.Zero.setLowBits(ShiftAmt);
+  Known.One = LHS.One.ushl_ov(ShiftAmt, ShiftedOutOne);
+
+  // All cases returning poison have been handled by MaxShiftAmount already.
+  if (NSW) {
+    if (NUW && ShiftAmt != 0)
+      // NUW means we can assume anything shifted out was a zero.
+      ShiftedOutZero = true;
+
+    if (ShiftedOutZero)
+      Known.makeNonNegative();
+    else if (ShiftedOutOne)
+      Known.makeNegative();
+  }
+  return Known;
+}
+
 KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
                          bool NSW, bool ShAmtNonZero) {
   unsigned BitWidth = LHS.getBitWidth();
-  auto ShiftByConst = [&](const KnownBits &LHS, unsigned ShiftAmt) {
-    KnownBits Known;
-    bool ShiftedOutZero, ShiftedOutOne;
-    Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
-    Known.Zero.setLowBits(ShiftAmt);
-    Known.One = LHS.One.ushl_ov(ShiftAmt, ShiftedOutOne);
-
-    // All cases returning poison have been handled by MaxShiftAmount already.
-    if (NSW) {
-      if (NUW && ShiftAmt != 0)
-        // NUW means we can assume anything shifted out was a zero.
-        ShiftedOutZero = true;
-
-      if (ShiftedOutZero)
-        Known.makeNonNegative();
-      else if (ShiftedOutOne)
-        Known.makeNegative();
-    }
-    return Known;
-  };
 
   // Fast path for a common case when LHS is completely unknown.
   KnownBits Known(BitWidth);
@@ -477,7 +479,7 @@ KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
     if ((ShiftAmtZeroMask & ShiftAmt) != 0 ||
         (ShiftAmtOneMask | ShiftAmt) != ShiftAmt)
       continue;
-    Known = Known.intersectWith(ShiftByConst(LHS, ShiftAmt));
+    Known = Known.intersectWith(shl(LHS, ShiftAmt, NUW, NSW));
     if (Known.isUnknown())
       break;
   }

>From d0314c064c6a1ee0df252ca61331fd4654deac9d Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 15:37:33 +0300
Subject: [PATCH 08/24] fix?

---
 llvm/lib/Support/KnownBits.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 82bf6c1229960..5df6123a9b59b 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -406,7 +406,7 @@ static unsigned getMaxShiftAmount(const APInt &MaxValue, unsigned BitWidth) {
 
 KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
                          bool NSW) {
-  KnownBits Known;
+  KnownBits Known(LHS.getBitWidth());
   bool ShiftedOutZero, ShiftedOutOne;
   Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
   Known.Zero.setLowBits(ShiftAmt);

>From a8f161def7554c3d7ba2cff26982464d4bc741a5 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 16:51:39 +0300
Subject: [PATCH 09/24] refactor KnownBits::shl. Address review comments

---
 llvm/lib/Support/KnownBits.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 5df6123a9b59b..15483f00c5b21 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -406,13 +406,13 @@ static unsigned getMaxShiftAmount(const APInt &MaxValue, unsigned BitWidth) {
 
 KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
                          bool NSW) {
-  KnownBits Known(LHS.getBitWidth());
   bool ShiftedOutZero, ShiftedOutOne;
-  Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
+  KnownBits Known(LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero),
+                  LHS.One.ushl_ov(ShiftAmt, ShiftedOutOne));
   Known.Zero.setLowBits(ShiftAmt);
-  Known.One = LHS.One.ushl_ov(ShiftAmt, ShiftedOutOne);
 
-  // All cases returning poison have been handled by MaxShiftAmount already.
+  // This part assumes a valid shift amount and does not check for
+  // cases that would result in poison.
   if (NSW) {
     if (NUW && ShiftAmt != 0)
       // NUW means we can assume anything shifted out was a zero.

>From 4069ae9ff0d4d66f48b56f398acab41cc28a06a3 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 17:35:07 +0300
Subject: [PATCH 10/24] simplify KnownBits add self add path by reusing new
 overloaded KnownBits::shl

---
 llvm/include/llvm/Support/KnownBits.h | 3 +--
 llvm/lib/Support/KnownBits.cpp        | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 131908d205ddf..4041e82c04f97 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -368,8 +368,7 @@ struct KnownBits {
       // Shift amount bitwidth is independent of src bitwidth (and we're
       // just shifting by one so don't have any bounds issues).
       assert(LHS == RHS && "Expected matching knownbits");
-      KnownBits Amt = KnownBits::makeConstant(APInt(8, 1));
-      return KnownBits::shl(LHS, Amt, NUW, NSW, /*ShAmtNonZero=*/true);
+      return KnownBits::shl(LHS, 1, NUW, NSW);
     }
     return computeForAddSub(/*Add=*/true, NSW, NUW, LHS, RHS);
   }
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 15483f00c5b21..5e283758cade3 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -1213,9 +1213,8 @@ KnownBits KnownBits::udiv(const KnownBits &LHS, const KnownBits &RHS,
     return Known;
   }
 
-  if (RHS.isConstant() && RHS.getConstant().isPowerOf2()) {
+  if (RHS.isConstant() && RHS.getConstant().isPowerOf2())
     return lshr(LHS, RHS.getConstant().logBase2());
-  }
 
   // We can figure out the minimum number of upper zero bits by doing
   // MaxNumerator / MinDenominator. If the Numerator gets smaller or Denominator

>From ad7223a3b843cc3c12da7fdeff307eb20cb0b78e Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 17:40:50 +0300
Subject: [PATCH 11/24] add more usages for new overloaded shift ops

---
 .../Transforms/InstCombine/InstCombineSimplifyDemanded.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 28cfa55b968dc..d25e9e3a8c7da 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -787,8 +787,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
       if (SimplifyDemandedBits(I, 0, DemandedMaskIn, Known, Q, Depth + 1))
         return I;
 
-      Known = KnownBits::shl(Known,
-                             KnownBits::makeConstant(APInt(BitWidth, ShiftAmt)),
+      Known = KnownBits::shl(Known, ShiftAmt,
                              /* NUW */ IOp->hasNoUnsignedWrap(),
                              /* NSW */ IOp->hasNoSignedWrap());
     } else {
@@ -931,9 +930,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
         return InsertNewInstWith(LShr, I->getIterator());
       }
 
-      Known = KnownBits::ashr(
-          Known, KnownBits::makeConstant(APInt(BitWidth, ShiftAmt)),
-          ShiftAmt != 0, I->isExact());
+      Known = KnownBits::ashr(Known, ShiftAmt, ShiftAmt != 0, I->isExact());
     } else {
       llvm::computeKnownBits(I, Known, Q, Depth);
     }

>From 56fda86c855ca111f87d82ea42ca065c6472b10f Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 17:46:23 +0300
Subject: [PATCH 12/24] fix

---
 llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index d25e9e3a8c7da..7be7c2f12e355 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -930,7 +930,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
         return InsertNewInstWith(LShr, I->getIterator());
       }
 
-      Known = KnownBits::ashr(Known, ShiftAmt, ShiftAmt != 0, I->isExact());
+      Known = KnownBits::ashr(Known, ShiftAmt);
     } else {
       llvm::computeKnownBits(I, Known, Q, Depth);
     }

>From c6e05d3ae9194ad660019e52d83df5e608cef54e Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 21:01:52 +0300
Subject: [PATCH 13/24] use fallback to general version for overloaded

---
 llvm/lib/Support/KnownBits.cpp | 50 +++++++++++++++++++---------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 5e283758cade3..5748dfb21e7c0 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -404,31 +404,37 @@ static unsigned getMaxShiftAmount(const APInt &MaxValue, unsigned BitWidth) {
   return MaxValue.getLimitedValue(BitWidth - 1);
 }
 
-KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
-                         bool NSW) {
-  bool ShiftedOutZero, ShiftedOutOne;
-  KnownBits Known(LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero),
-                  LHS.One.ushl_ov(ShiftAmt, ShiftedOutOne));
-  Known.Zero.setLowBits(ShiftAmt);
-
-  // This part assumes a valid shift amount and does not check for
-  // cases that would result in poison.
-  if (NSW) {
-    if (NUW && ShiftAmt != 0)
-      // NUW means we can assume anything shifted out was a zero.
-      ShiftedOutZero = true;
-
-    if (ShiftedOutZero)
-      Known.makeNonNegative();
-    else if (ShiftedOutOne)
-      Known.makeNegative();
-  }
-  return Known;
+KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt,
+                         bool NUW, bool NSW) {
+  // TODO: This is simple fallback to generic RHS-based shl.
+  // Add a specialized constant-shift implementation with identical semantics.
+  KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));
+  return shl(LHS, RHS, NUW, NSW);
 }
 
 KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
                          bool NSW, bool ShAmtNonZero) {
   unsigned BitWidth = LHS.getBitWidth();
+  auto ShiftByConst = [&](const KnownBits &LHS, unsigned ShiftAmt) {
+    KnownBits Known;
+    bool ShiftedOutZero, ShiftedOutOne;
+    Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
+    Known.Zero.setLowBits(ShiftAmt);
+    Known.One = LHS.One.ushl_ov(ShiftAmt, ShiftedOutOne);
+
+    // All cases returning poison have been handled by MaxShiftAmount already.
+    if (NSW) {
+      if (NUW && ShiftAmt != 0)
+        // NUW means we can assume anything shifted out was a zero.
+        ShiftedOutZero = true;
+
+      if (ShiftedOutZero)
+        Known.makeNonNegative();
+      else if (ShiftedOutOne)
+        Known.makeNegative();
+    }
+    return Known;
+  };
 
   // Fast path for a common case when LHS is completely unknown.
   KnownBits Known(BitWidth);
@@ -454,7 +460,7 @@ KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
         MaxShiftAmount,
         std::max(LHS.countMaxLeadingZeros(), LHS.countMaxLeadingOnes()) - 1);
 
-  // Fast path for common case where the shift amount is unknown.
+   // Fast path for common case where the shift amount is unknown.
   if (MinShiftAmount == 0 && MaxShiftAmount == BitWidth - 1 &&
       isPowerOf2_32(BitWidth)) {
     Known.Zero.setLowBits(LHS.countMinTrailingZeros());
@@ -479,7 +485,7 @@ KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
     if ((ShiftAmtZeroMask & ShiftAmt) != 0 ||
         (ShiftAmtOneMask | ShiftAmt) != ShiftAmt)
       continue;
-    Known = Known.intersectWith(shl(LHS, ShiftAmt, NUW, NSW));
+    Known = Known.intersectWith(ShiftByConst(LHS, ShiftAmt));
     if (Known.isUnknown())
       break;
   }

>From 1a099f1280bb6526cf5e8943f04bdf364718b55d Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 21:06:14 +0300
Subject: [PATCH 14/24] format fix

---
 llvm/lib/Support/KnownBits.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 5748dfb21e7c0..f4eb5fb946475 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -460,7 +460,7 @@ KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
         MaxShiftAmount,
         std::max(LHS.countMaxLeadingZeros(), LHS.countMaxLeadingOnes()) - 1);
 
-   // Fast path for common case where the shift amount is unknown.
+  // Fast path for common case where the shift amount is unknown.
   if (MinShiftAmount == 0 && MaxShiftAmount == BitWidth - 1 &&
       isPowerOf2_32(BitWidth)) {
     Known.Zero.setLowBits(LHS.countMinTrailingZeros());

>From a1897b0f9bed0b2e2b1bd4a4d71274e9c9efd414 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 21:06:54 +0300
Subject: [PATCH 15/24] format fix

---
 llvm/lib/Support/KnownBits.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index f4eb5fb946475..8c58ef1db8af3 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -404,8 +404,8 @@ static unsigned getMaxShiftAmount(const APInt &MaxValue, unsigned BitWidth) {
   return MaxValue.getLimitedValue(BitWidth - 1);
 }
 
-KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt,
-                         bool NUW, bool NSW) {
+KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
+                         bool NSW) {
   // TODO: This is simple fallback to generic RHS-based shl.
   // Add a specialized constant-shift implementation with identical semantics.
   KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));

>From 083641d56fd2c12450fad7e6d57f952145e34787 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 21:22:33 +0300
Subject: [PATCH 16/24] do the same for KnownBits::ashr

---
 llvm/include/llvm/Support/KnownBits.h                 |  5 +++--
 llvm/lib/Support/KnownBits.cpp                        | 11 ++++++-----
 .../InstCombine/InstCombineSimplifyDemanded.cpp       |  2 +-
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 4041e82c04f97..b31ae60ff5800 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -474,8 +474,9 @@ struct KnownBits {
                                  bool ShAmtNonZero = false, bool Exact = false);
 
   /// Compute known bits for ashr(LHS, ShiftAmt).
-  /// This is shift by constant variant of ashr(LHS, RHS).
-  LLVM_ABI static KnownBits ashr(const KnownBits &LHS, unsigned ShiftAmt);
+  /// This is shift by constant variant of ashr(LHS, ShiftAmt).
+  LLVM_ABI static KnownBits ashr(const KnownBits &LHS, unsigned ShiftAmt,
+                                 bool ShAmtNonZero = false, bool Exact = false);
 
   /// Compute known bits for ashr(LHS, RHS).
   /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS.
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 8c58ef1db8af3..b71e24a0eaec7 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -553,11 +553,12 @@ KnownBits KnownBits::lshr(const KnownBits &LHS, const KnownBits &RHS,
   return Known;
 }
 
-KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt) {
-  KnownBits Known = LHS;
-  Known.Zero.ashrInPlace(ShiftAmt);
-  Known.One.ashrInPlace(ShiftAmt);
-  return Known;
+KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt,
+                          bool ShAmtNonZero, bool Exact) {
+  // TODO: This is simple fallback to generic RHS-based ashr.
+  // Add a specialized constant-shift implementation with identical semantics.
+  KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));
+  return ashr(LHS, RHS, ShAmtNonZero, Exact);
 }
 
 KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 7be7c2f12e355..d25e9e3a8c7da 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -930,7 +930,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
         return InsertNewInstWith(LShr, I->getIterator());
       }
 
-      Known = KnownBits::ashr(Known, ShiftAmt);
+      Known = KnownBits::ashr(Known, ShiftAmt, ShiftAmt != 0, I->isExact());
     } else {
       llvm::computeKnownBits(I, Known, Q, Depth);
     }

>From 74e50ef4c55fe80ad830ee97b028500e5ed7031c Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 21:50:15 +0300
Subject: [PATCH 17/24] fix ashr

---
 llvm/lib/Support/KnownBits.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index b71e24a0eaec7..53fdd2435825e 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -564,6 +564,12 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt,
 KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,
                           bool ShAmtNonZero, bool Exact) {
   unsigned BitWidth = LHS.getBitWidth();
+  auto ShiftByConst = [&](const KnownBits &LHS, unsigned ShiftAmt) {
+    KnownBits Known = LHS;
+    Known.Zero.ashrInPlace(ShiftAmt);
+    Known.One.ashrInPlace(ShiftAmt);
+    return Known;
+  };
 
   // Fast path for a common case when LHS is completely unknown.
   KnownBits Known(BitWidth);
@@ -603,7 +609,7 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,
     if ((ShiftAmtZeroMask & ShiftAmt) != 0 ||
         (ShiftAmtOneMask | ShiftAmt) != ShiftAmt)
       continue;
-    Known = Known.intersectWith(ashr(LHS, ShiftAmt));
+    Known = Known.intersectWith(ShiftByConst(LHS, ShiftAmt));
     if (Known.isUnknown())
       break;
   }

>From a28cb3e78fad37859fdff0046ceef2a4e1fe7ffe Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 2 Apr 2026 21:54:34 +0300
Subject: [PATCH 18/24] fix

---
 llvm/include/llvm/Support/KnownBits.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index b31ae60ff5800..34b978a189686 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -368,7 +368,7 @@ struct KnownBits {
       // Shift amount bitwidth is independent of src bitwidth (and we're
       // just shifting by one so don't have any bounds issues).
       assert(LHS == RHS && "Expected matching knownbits");
-      return KnownBits::shl(LHS, 1, NUW, NSW);
+      return KnownBits::shl(LHS, 1, NUW, NSW, /*ShAmtNonZero=*/true);
     }
     return computeForAddSub(/*Add=*/true, NSW, NUW, LHS, RHS);
   }

>From d8e2f5e80fc3dcb7924116de021468b5c094215e Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 3 Apr 2026 10:44:20 +0300
Subject: [PATCH 19/24] fix

---
 llvm/include/llvm/Support/KnownBits.h | 2 +-
 llvm/lib/Support/KnownBits.cpp        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 34b978a189686..b31ae60ff5800 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -368,7 +368,7 @@ struct KnownBits {
       // Shift amount bitwidth is independent of src bitwidth (and we're
       // just shifting by one so don't have any bounds issues).
       assert(LHS == RHS && "Expected matching knownbits");
-      return KnownBits::shl(LHS, 1, NUW, NSW, /*ShAmtNonZero=*/true);
+      return KnownBits::shl(LHS, 1, NUW, NSW);
     }
     return computeForAddSub(/*Add=*/true, NSW, NUW, LHS, RHS);
   }
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 53fdd2435825e..61999b56e2cdf 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -409,7 +409,7 @@ KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
   // TODO: This is simple fallback to generic RHS-based shl.
   // Add a specialized constant-shift implementation with identical semantics.
   KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));
-  return shl(LHS, RHS, NUW, NSW);
+  return shl(LHS, RHS, NUW, NSW, ShiftAmt != 0);
 }
 
 KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,

>From dcf1bbd802fc535e96c305ce9d16f5d2f8eb0171 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 3 Apr 2026 17:17:40 +0300
Subject: [PATCH 20/24] New attempt of proper implantation specialised shl with
 const RHS

---
 llvm/lib/Support/KnownBits.cpp | 40 ++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 61999b56e2cdf..1e1e208085116 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -406,10 +406,42 @@ static unsigned getMaxShiftAmount(const APInt &MaxValue, unsigned BitWidth) {
 
 KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
                          bool NSW) {
-  // TODO: This is simple fallback to generic RHS-based shl.
-  // Add a specialized constant-shift implementation with identical semantics.
-  KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));
-  return shl(LHS, RHS, NUW, NSW, ShiftAmt != 0);
+  if (ShiftAmt == 0)
+    return LHS;
+
+  unsigned BitWidth = LHS.getBitWidth();
+  KnownBits Known(BitWidth);
+
+  if (ShiftAmt >= BitWidth)
+    return Known;
+
+  if (LHS.isUnknown()) {
+    Known.Zero.setLowBits(ShiftAmt);
+    if (NUW && NSW)
+      Known.makeNonNegative();
+    return Known;
+  }
+
+  bool ShiftedOutZero, ShiftedOutOne;
+  Known.One = LHS.One.ushl_ov(ShiftAmt, ShiftedOutOne);
+  if (NUW && ShiftedOutOne) {
+    // ones shifted out means this is always poison.
+    Known.setAllZero();
+    return Known;
+  }
+  Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
+  Known.Zero.setLowBits(ShiftAmt);
+
+  if (NSW) {
+    if (NUW)
+      ShiftedOutZero = true;
+    if (ShiftedOutZero)
+      Known.makeNonNegative();
+    else if (ShiftedOutOne)
+      Known.makeNegative();
+  }
+
+  return Known;
 }
 
 KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,

>From 1c1b635867dcfb4c67f6606ee210562af1e5cc4c Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 3 Apr 2026 18:10:10 +0300
Subject: [PATCH 21/24] fix

---
 llvm/lib/Support/KnownBits.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 1e1e208085116..ae5155a4a4b60 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -412,9 +412,6 @@ KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
   unsigned BitWidth = LHS.getBitWidth();
   KnownBits Known(BitWidth);
 
-  if (ShiftAmt >= BitWidth)
-    return Known;
-
   if (LHS.isUnknown()) {
     Known.Zero.setLowBits(ShiftAmt);
     if (NUW && NSW)
@@ -430,7 +427,7 @@ KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
     return Known;
   }
   Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
-  Known.Zero.setLowBits(ShiftAmt);
+  Known.Zero.setLowBits(std::min(ShiftAmt, BitWidth));
 
   if (NSW) {
     if (NUW)

>From b082a1adaa57efebc4d3ce389c585505a5b642d2 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 3 Apr 2026 19:30:05 +0300
Subject: [PATCH 22/24] specialize ashr is similar way

---
 llvm/lib/Support/KnownBits.cpp | 44 ++++++++++++++++++++++++++++++++--
 1 file changed, 42 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index ae5155a4a4b60..d1cd2c475bf21 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -526,6 +526,17 @@ KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
 }
 
 KnownBits KnownBits::lshr(const KnownBits &LHS, unsigned ShiftAmt) {
+   if (ShiftAmt == 0)
+    return LHS;
+
+  unsigned BitWidth = LHS.getBitWidth();
+
+  if (ShiftAmt >= BitWidth) {
+    KnownBits Known(BitWidth);
+    Known.setAllZero();
+    return Known;
+  }
+
   KnownBits Known = LHS;
   Known >>= ShiftAmt;
   // High bits are known zero.
@@ -586,8 +597,37 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt,
                           bool ShAmtNonZero, bool Exact) {
   // TODO: This is simple fallback to generic RHS-based ashr.
   // Add a specialized constant-shift implementation with identical semantics.
-  KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));
-  return ashr(LHS, RHS, ShAmtNonZero, Exact);
+  // KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));
+  // return ashr(LHS, RHS, ShAmtNonZero, Exact);
+
+  unsigned BitWidth = LHS.getBitWidth();
+  KnownBits Known(BitWidth);
+
+  if (ShiftAmt == 0) {
+    if (!ShAmtNonZero)
+      return LHS;
+
+    Known.setAllZero();
+    return Known;
+  }
+
+  if (ShiftAmt >= BitWidth) {
+    Known.setAllZero();
+    return Known;
+  }
+
+  if (LHS.isUnknown())
+    return Known;
+
+  if (Exact && LHS.countMaxTrailingZeros() < ShiftAmt) {
+    Known.setAllZero();
+    return Known;
+  }
+
+  Known = LHS;
+  Known.Zero.ashrInPlace(ShiftAmt);
+  Known.One.ashrInPlace(ShiftAmt);
+  return Known;
 }
 
 KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,

>From 8237c151b4d3e087db5c7fa4780e8754b245fd47 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 3 Apr 2026 21:11:35 +0300
Subject: [PATCH 23/24] cleanups

---
 llvm/include/llvm/Support/KnownBits.h         |  5 ++-
 llvm/lib/Support/KnownBits.cpp                | 34 ++++---------------
 .../InstCombineSimplifyDemanded.cpp           |  2 +-
 3 files changed, 9 insertions(+), 32 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index b31ae60ff5800..4041e82c04f97 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -474,9 +474,8 @@ struct KnownBits {
                                  bool ShAmtNonZero = false, bool Exact = false);
 
   /// Compute known bits for ashr(LHS, ShiftAmt).
-  /// This is shift by constant variant of ashr(LHS, ShiftAmt).
-  LLVM_ABI static KnownBits ashr(const KnownBits &LHS, unsigned ShiftAmt,
-                                 bool ShAmtNonZero = false, bool Exact = false);
+  /// This is shift by constant variant of ashr(LHS, RHS).
+  LLVM_ABI static KnownBits ashr(const KnownBits &LHS, unsigned ShiftAmt);
 
   /// Compute known bits for ashr(LHS, RHS).
   /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS.
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index d1cd2c475bf21..b8d88bf469dcb 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -526,7 +526,7 @@ KnownBits KnownBits::shl(const KnownBits &LHS, const KnownBits &RHS, bool NUW,
 }
 
 KnownBits KnownBits::lshr(const KnownBits &LHS, unsigned ShiftAmt) {
-   if (ShiftAmt == 0)
+  if (ShiftAmt == 0)
     return LHS;
 
   unsigned BitWidth = LHS.getBitWidth();
@@ -593,23 +593,13 @@ KnownBits KnownBits::lshr(const KnownBits &LHS, const KnownBits &RHS,
   return Known;
 }
 
-KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt,
-                          bool ShAmtNonZero, bool Exact) {
-  // TODO: This is simple fallback to generic RHS-based ashr.
-  // Add a specialized constant-shift implementation with identical semantics.
-  // KnownBits RHS = KnownBits::makeConstant(APInt(LHS.getBitWidth(), ShiftAmt));
-  // return ashr(LHS, RHS, ShAmtNonZero, Exact);
-
+KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt) {
   unsigned BitWidth = LHS.getBitWidth();
-  KnownBits Known(BitWidth);
 
-  if (ShiftAmt == 0) {
-    if (!ShAmtNonZero)
-      return LHS;
+  if (ShiftAmt == 0)
+    return LHS;
 
-    Known.setAllZero();
-    return Known;
-  }
+  KnownBits Known(BitWidth);
 
   if (ShiftAmt >= BitWidth) {
     Known.setAllZero();
@@ -619,11 +609,6 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt,
   if (LHS.isUnknown())
     return Known;
 
-  if (Exact && LHS.countMaxTrailingZeros() < ShiftAmt) {
-    Known.setAllZero();
-    return Known;
-  }
-
   Known = LHS;
   Known.Zero.ashrInPlace(ShiftAmt);
   Known.One.ashrInPlace(ShiftAmt);
@@ -633,13 +618,6 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt,
 KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,
                           bool ShAmtNonZero, bool Exact) {
   unsigned BitWidth = LHS.getBitWidth();
-  auto ShiftByConst = [&](const KnownBits &LHS, unsigned ShiftAmt) {
-    KnownBits Known = LHS;
-    Known.Zero.ashrInPlace(ShiftAmt);
-    Known.One.ashrInPlace(ShiftAmt);
-    return Known;
-  };
-
   // Fast path for a common case when LHS is completely unknown.
   KnownBits Known(BitWidth);
   unsigned MinShiftAmount = RHS.getMinValue().getLimitedValue(BitWidth);
@@ -678,7 +656,7 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS,
     if ((ShiftAmtZeroMask & ShiftAmt) != 0 ||
         (ShiftAmtOneMask | ShiftAmt) != ShiftAmt)
       continue;
-    Known = Known.intersectWith(ShiftByConst(LHS, ShiftAmt));
+    Known = Known.intersectWith(ashr(LHS, ShiftAmt));
     if (Known.isUnknown())
       break;
   }
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index d25e9e3a8c7da..7be7c2f12e355 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -930,7 +930,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
         return InsertNewInstWith(LShr, I->getIterator());
       }
 
-      Known = KnownBits::ashr(Known, ShiftAmt, ShiftAmt != 0, I->isExact());
+      Known = KnownBits::ashr(Known, ShiftAmt);
     } else {
       llvm::computeKnownBits(I, Known, Q, Depth);
     }

>From 8ea869fbe410acd21bfeb808332371504de6447b Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 4 Apr 2026 19:46:16 +0300
Subject: [PATCH 24/24] add unit tests and minor fixes

---
 llvm/lib/Support/KnownBits.cpp           |  19 ++++-
 llvm/unittests/Support/KnownBitsTest.cpp | 102 +++++++++++++++++++++++
 2 files changed, 118 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index b8d88bf469dcb..00bc14c2c1fc0 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -412,6 +412,13 @@ KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
   unsigned BitWidth = LHS.getBitWidth();
   KnownBits Known(BitWidth);
 
+  if (ShiftAmt >= BitWidth) {
+    Known.setAllZero();
+    if (NUW && NSW)
+      Known.makeNonNegative();
+    return Known;
+  }
+
   if (LHS.isUnknown()) {
     Known.Zero.setLowBits(ShiftAmt);
     if (NUW && NSW)
@@ -428,6 +435,11 @@ KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
   }
   Known.Zero = LHS.Zero.ushl_ov(ShiftAmt, ShiftedOutZero);
   Known.Zero.setLowBits(std::min(ShiftAmt, BitWidth));
+  if (NSW && ShiftedOutZero && ShiftedOutOne) {
+    // mixed shifted out means this is always poison.
+    Known.setAllZero();
+    return Known;
+  }
 
   if (NSW) {
     if (NUW)
@@ -438,6 +450,9 @@ KnownBits KnownBits::shl(const KnownBits &LHS, unsigned ShiftAmt, bool NUW,
       Known.makeNegative();
   }
 
+  if (Known.hasConflict())
+    Known.setAllZero();
+
   return Known;
 }
 
@@ -530,7 +545,6 @@ KnownBits KnownBits::lshr(const KnownBits &LHS, unsigned ShiftAmt) {
     return LHS;
 
   unsigned BitWidth = LHS.getBitWidth();
-
   if (ShiftAmt >= BitWidth) {
     KnownBits Known(BitWidth);
     Known.setAllZero();
@@ -594,11 +608,10 @@ KnownBits KnownBits::lshr(const KnownBits &LHS, const KnownBits &RHS,
 }
 
 KnownBits KnownBits::ashr(const KnownBits &LHS, unsigned ShiftAmt) {
-  unsigned BitWidth = LHS.getBitWidth();
-
   if (ShiftAmt == 0)
     return LHS;
 
+  unsigned BitWidth = LHS.getBitWidth();
   KnownBits Known(BitWidth);
 
   if (ShiftAmt >= BitWidth) {
diff --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp
index dc4a47d7cc562..4651a56d5d9e7 100644
--- a/llvm/unittests/Support/KnownBitsTest.cpp
+++ b/llvm/unittests/Support/KnownBitsTest.cpp
@@ -517,6 +517,30 @@ TEST(KnownBitsTest, BinaryExhaustive) {
         return N1.shl(N2);
       },
       /*CheckOptimality=*/true, /*RefinePoisonToZero=*/true);
+  testBinaryOpExhaustive(
+      "shl const",
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        KnownBits Generic = KnownBits::shl(Known1, Known2);
+
+        if (!Known2.isConstant() || Known1.hasConflict() || Known2.hasConflict())
+          return Generic;
+
+        unsigned ShiftAmt = Known2.getConstant().getLimitedValue();
+        KnownBits Const = KnownBits::shl(Known1, ShiftAmt);
+
+        EXPECT_EQ(Generic, Const) << "shl const mismatch for\n"
+                                  << "Known1 = " << Known1 << "\n"
+                                  << "Known2 = " << Known2
+                                  << " (" << ShiftAmt << ")\n\n";
+
+        return Const;
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.uge(N2.getBitWidth()))
+          return std::nullopt;
+        return N1.shl(N2);
+      },
+      /*CheckOptimality=*/true, /*RefinePoisonToZero=*/true);
   testBinaryOpExhaustive(
       "ushl_ov",
       [](const KnownBits &Known1, const KnownBits &Known2) {
@@ -543,6 +567,34 @@ TEST(KnownBitsTest, BinaryExhaustive) {
         return Res;
       },
       /*CheckOptimality=*/true, /*RefinePoisonToZero=*/true);
+  testBinaryOpExhaustive(
+      "shl nsw const",
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        KnownBits Generic =
+          KnownBits::shl(Known1, Known2, /*NUW=*/false, /*NSW=*/true);
+
+        if (!Known2.isConstant() || Known1.hasConflict() || Known2.hasConflict())
+          return Generic;
+
+        unsigned ShiftAmt = Known2.getConstant().getLimitedValue();
+        KnownBits Const =
+          KnownBits::shl(Known1, ShiftAmt, /*NUW=*/false, /*NSW=*/true);
+
+        EXPECT_EQ(Generic, Const) << "shl const mismatch for\n"
+                                  << "Known1 = " << Known1 << "\n"
+                                  << "Known2 = " << Known2
+                                  << " (" << ShiftAmt << ")\n\n";
+
+        return Const;
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        bool Overflow;
+        APInt Res = N1.sshl_ov(N2, Overflow);
+        if (Overflow)
+          return std::nullopt;
+        return Res;
+      },
+      /*CheckOptimality=*/true, /*RefinePoisonToZero=*/true);
   testBinaryOpExhaustive(
       "shl nuw",
       [](const KnownBits &Known1, const KnownBits &Known2) {
@@ -583,6 +635,31 @@ TEST(KnownBitsTest, BinaryExhaustive) {
         return N1.lshr(N2);
       },
       /*CheckOptimality=*/true, /*RefinePoisonToZero=*/true);
+  testBinaryOpExhaustive(
+      "lshr const",
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        KnownBits Generic = KnownBits::lshr(Known1, Known2);
+
+        if (!Known2.isConstant() || Known1.hasConflict() || Known2.hasConflict())
+          return Generic;
+
+        unsigned ShiftAmt = Known2.getConstant().getLimitedValue();
+        KnownBits Const = KnownBits::lshr(Known1, ShiftAmt);
+
+        EXPECT_EQ(Generic, Const) << "lshr const mismatch for\n"
+                                  << "Known1 = " << Known1 << "\n"
+                                  << "Known2 = " << Known2
+                                  << " (" << ShiftAmt << ")\n\n";
+
+        return Const;
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.uge(N2.getBitWidth()))
+          return std::nullopt;
+        return N1.lshr(N2);
+      },
+      /*CheckOptimality=*/false,
+      /*RefinePoisonToZero=*/true);
   testBinaryOpExhaustive(
       "ashr",
       [](const KnownBits &Known1, const KnownBits &Known2) {
@@ -608,6 +685,31 @@ TEST(KnownBitsTest, BinaryExhaustive) {
         return N1.ashr(N2);
       },
       /*CheckOptimality=*/true, /*RefinePoisonToZero=*/true);
+  testBinaryOpExhaustive(
+      "ashr const",
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        KnownBits Generic = KnownBits::ashr(Known1, Known2);
+
+        if (!Known2.isConstant() || Known1.hasConflict() || Known2.hasConflict())
+          return Generic;
+
+        unsigned ShiftAmt = Known2.getConstant().getLimitedValue();
+        KnownBits Const = KnownBits::ashr(Known1, ShiftAmt);
+
+        EXPECT_EQ(Generic, Const) << "ashr const mismatch for\n"
+                                  << "Known1 = " << Known1 << "\n"
+                                  << "Known2 = " << Known2
+                                  << " (" << ShiftAmt << ")\n\n";
+
+        return Const;
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.uge(N2.getBitWidth()))
+          return std::nullopt;
+        return N1.ashr(N2);
+      },
+      /*CheckOptimality=*/false,
+      /*RefinePoisonToZero=*/true);
   testBinaryOpExhaustive(
       "mul",
       [](const KnownBits &Known1, const KnownBits &Known2) {



More information about the llvm-commits mailing list