[llvm] [KnownBits] Fix add() SelfAdd assertion for bitwidths >= 512 (PR #202769)
Paweł Bylica via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 10 08:22:48 PDT 2026
https://github.com/chfast updated https://github.com/llvm/llvm-project/pull/202769
>From 72de6b4532237ea275a70bf2c6312b16a27931dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Wed, 10 Jun 2026 17:22:00 +0200
Subject: [PATCH] [KnownBits] Fix getMaxShiftAmount assertion for narrow shift
amounts
KnownBits::{shl,lshr,ashr} call getMaxShiftAmount(RHS.getMaxValue(),
BitWidth), which for power-of-two BitWidth does
MaxValue.extractBitsAsZExtValue(Log2_32(BitWidth), 0). Nothing requires
the shift amount (RHS) to be as wide as the shifted value, so when its
bitwidth is smaller than Log2_32(BitWidth) the extraction trips the
"Illegal bit extraction" assertion in APInt::extractBitsAsZExtValue.
This is reachable from KnownBits::add() with SelfAdd=true, which lowers
X+X to shl(X, 1) using a fixed 8-bit shift amount: for source widths
>= 512, Log2_32(BitWidth) >= 9 > 8 and the assertion fires.
Fix it centrally in getMaxShiftAmount by clamping the extraction width
to the shift amount's bitwidth. A shift amount that narrow is already
< BitWidth, so the clamped value remains a valid upper bound. This
protects all three shift callers rather than just the add() path.
Found via fuzzing. Adds a SelfAddWide regression test covering widths
256/512/1024 with both minimal and maximal constants.
---
llvm/lib/Support/KnownBits.cpp | 7 ++++++-
llvm/unittests/Support/KnownBitsTest.cpp | 18 ++++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index b1b985b80cffd..d1a2145ad17b0 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -399,7 +399,12 @@ KnownBits KnownBits::abds(KnownBits LHS, KnownBits RHS) {
static unsigned getMaxShiftAmount(const APInt &MaxValue, unsigned BitWidth) {
if (isPowerOf2_32(BitWidth))
- return MaxValue.extractBitsAsZExtValue(Log2_32(BitWidth), 0);
+ // The shift amount may be narrower than Log2_32(BitWidth) bits (e.g. the
+ // X+X self-add lowering in KnownBits::add uses an 8-bit amount). Clamp the
+ // extraction width to the shift amount's bitwidth; a value that narrow is
+ // already < BitWidth, so this stays a valid upper bound.
+ return MaxValue.extractBitsAsZExtValue(
+ std::min(Log2_32(BitWidth), MaxValue.getBitWidth()), 0);
// This is only an approximate upper bound.
return MaxValue.getLimitedValue(BitWidth - 1);
}
diff --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp
index 0303b2f121c3b..2d73160603995 100644
--- a/llvm/unittests/Support/KnownBitsTest.cpp
+++ b/llvm/unittests/Support/KnownBitsTest.cpp
@@ -179,6 +179,24 @@ TEST(KnownBitsTest, SelfAddExhaustive) {
});
}
+TEST(KnownBitsTest, SelfAddWide) {
+ // SelfAdd lowers to shl(X, 1), whose getMaxShiftAmount extracts
+ // Log2_32(BitWidth) bits from the shift amount. For source widths >= 512 that
+ // is more than the 8-bit shift amount used by the lowering, which used to
+ // assert in APInt::extractBitsAsZExtValue.
+ for (unsigned Bits : {256u, 512u, 1024u}) {
+ for (const APInt &X : {APInt(Bits, 1), APInt::getMaxValue(Bits)}) {
+ KnownBits Known = KnownBits::makeConstant(X);
+ KnownBits Computed = KnownBits::add(Known, Known, /*NSW=*/false,
+ /*NUW=*/false, /*SelfAdd=*/true);
+ // X + X == X << 1 (truncated to Bits); the result is fully known.
+ APInt Doubled = X << 1;
+ EXPECT_EQ(Computed.One, Doubled);
+ EXPECT_EQ(Computed.Zero, ~Doubled);
+ }
+ }
+}
+
TEST(KnownBitsTest, AddCarryExhaustive) {
unsigned Bits = 4;
ForeachKnownBits(Bits, [&](const KnownBits &Known1) {
More information about the llvm-commits
mailing list