[llvm] 4db3dce - [llvm] produce a more consistent estimates of bit width needed when parsing an integer (#205947)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 25 14:05:40 PDT 2026
Author: Jeremy Kun
Date: 2026-09-25T14:05:28-07:00
New Revision: 4db3dceb8d7bffa4b69042fe043652d616396237
URL: https://github.com/llvm/llvm-project/commit/4db3dceb8d7bffa4b69042fe043652d616396237
DIFF: https://github.com/llvm/llvm-project/commit/4db3dceb8d7bffa4b69042fe043652d616396237.diff
LOG: [llvm] produce a more consistent estimates of bit width needed when parsing an integer (#205947)
A colleague of mine noticed that `"12535824225335233"` parses in MLIR's
integer attribute parser as a 68-bit integer, even though it is a 53-bit
constant. I traced this back to `StringRef::consumeInteger`'s heuristic
estimate of the bit size. This change replaces that heuristic with a
default 64-bit storage, doubling the storage as more digits are parsed.
This produces a potentially larger over-estimate of the total storage
required, but does so in a less arbitrary manner and reduces the
over-approximation for numbers close to the 64-bit boundary.
Nb., A first iteration of this change tightened that estimate to at most
a 1-bit overapproximation with a lookup-table.
Assisted by Gemini
Added:
Modified:
llvm/lib/Support/StringRef.cpp
llvm/unittests/ADT/StringRefTest.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Support/StringRef.cpp b/llvm/lib/Support/StringRef.cpp
index 02270a6e223af..1741bccd752ff 100644
--- a/llvm/lib/Support/StringRef.cpp
+++ b/llvm/lib/Support/StringRef.cpp
@@ -522,16 +522,17 @@ bool StringRef::consumeInteger(unsigned Radix, APInt &Result) {
return false;
}
- // (Over-)estimate the required number of bits.
unsigned Log2Radix = 0;
while ((1U << Log2Radix) < Radix) Log2Radix++;
bool IsPowerOf2Radix = ((1U << Log2Radix) == Radix);
- unsigned BitWidth = Log2Radix * Str.size();
- if (BitWidth < Result.getBitWidth())
- BitWidth = Result.getBitWidth(); // don't shrink the result
- else if (BitWidth > Result.getBitWidth())
+ // Initialize Result to a reasonable starting width (at least 64 bits),
+ // but do not shrink it if it was already larger.
+ unsigned BitWidth = std::max(64U, Result.getBitWidth());
+
+ if (Result.getBitWidth() < BitWidth) {
Result = Result.zext(BitWidth);
+ }
APInt RadixAP, CharAP; // unused unless !IsPowerOf2Radix
if (!IsPowerOf2Radix) {
@@ -558,6 +559,21 @@ bool StringRef::consumeInteger(unsigned Radix, APInt &Result) {
if (CharVal >= Radix)
break;
+ // Check if one more digit will overflow, and grow if so.
+ if (Result.getActiveBits() + Log2Radix > Result.getBitWidth()) {
+ unsigned NewWidth = Result.getBitWidth() * 2;
+
+ // Guard against overflow of the NewWidth itself
+ if (NewWidth < Result.getBitWidth())
+ return true;
+
+ Result = Result.zext(NewWidth);
+ if (!IsPowerOf2Radix) {
+ RadixAP = RadixAP.zext(NewWidth);
+ CharAP = CharAP.zext(NewWidth);
+ }
+ }
+
// Add in this character.
if (IsPowerOf2Radix) {
Result <<= Log2Radix;
diff --git a/llvm/unittests/ADT/StringRefTest.cpp b/llvm/unittests/ADT/StringRefTest.cpp
index bb5fdd0a98aae..956c7295c3cd9 100644
--- a/llvm/unittests/ADT/StringRefTest.cpp
+++ b/llvm/unittests/ADT/StringRefTest.cpp
@@ -968,6 +968,65 @@ TEST(StringRefTest, consumeIntegerSigned) {
}
}
+TEST(StringRefTest, consumeIntegerAPIntBitWidth) {
+ // Decimal large number (12535824225335233)
+ // Fits in 64 bits, so should not grow beyond initial 64 bits.
+ {
+ APInt U;
+ StringRef Str = "12535824225335233";
+ bool Success = Str.consumeInteger(10, U);
+ ASSERT_FALSE(Success);
+ EXPECT_EQ(U.getZExtValue(), 12535824225335233ULL);
+ EXPECT_EQ(U.getBitWidth(), 64U);
+ }
+
+ // Hex version of same number (2c894405eaf7c1)
+ // Fits in 64 bits, so should not grow beyond initial 64 bits.
+ {
+ APInt U;
+ StringRef Str = "2c894405eaf7c1";
+ bool Success = Str.consumeInteger(16, U);
+ ASSERT_FALSE(Success);
+ EXPECT_EQ(U.getZExtValue(), 12535824225335233ULL);
+ EXPECT_EQ(U.getBitWidth(), 64U);
+ }
+
+ // A very large decimal number (100 digits)
+ // Needs 333 bits. Started at 64, doubled to 128, 256, 512.
+ {
+ APInt U;
+ std::string LargeDec(100, '9');
+ StringRef Str = LargeDec;
+ bool Success = Str.consumeInteger(10, U);
+ ASSERT_FALSE(Success);
+ EXPECT_EQ(U.getBitWidth(), 512U);
+ }
+
+ // Trailing garbage should not trigger growth or failure.
+ {
+ APInt U;
+ StringRef Str = "123g";
+ bool Success = Str.consumeInteger(10, U);
+ ASSERT_FALSE(Success);
+ EXPECT_EQ(U.getZExtValue(), 123ULL);
+ EXPECT_EQ(U.getBitWidth(), 64U);
+ EXPECT_EQ(Str, "g");
+ }
+
+ // Very long trailing garbage should also not trigger growth or failure.
+ {
+ APInt U;
+ std::string LongGarbage = "1";
+ LongGarbage.append(10000, 'g');
+ StringRef Str = LongGarbage;
+ bool Success = Str.consumeInteger(10, U);
+ ASSERT_FALSE(Success);
+ EXPECT_EQ(U.getZExtValue(), 1ULL);
+ EXPECT_EQ(U.getBitWidth(), 64U);
+ EXPECT_EQ(Str.size(), 10000U);
+ }
+}
+
struct GetDoubleStrings {
const char *Str;
bool AllowInexact;
More information about the llvm-commits
mailing list