[llvm] [ConstantRange] Bail out early in unsignedMulMayOverflow for wide integer types (PR #192275)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 15 08:53:08 PDT 2026


https://github.com/Firebear518 updated https://github.com/llvm/llvm-project/pull/192275

>From 6ac4714876cbbd68ac841c699f4ac607e5fa988a Mon Sep 17 00:00:00 2001
From: Firebear518 <phj040518 at gmail.com>
Date: Thu, 16 Apr 2026 00:15:11 +0900
Subject: [PATCH] [ConstantRange] Bail out early in unsignedMulMayOverflow for
 wide integer types

ConstantRange::unsignedMulMayOverflow calls APInt::umul_ov on the range
bounds to determine overflow. APInt multiplication is O(n^2) in the word
count, so integer types wider than ~1024 bits cause multi-second hangs in
InstCombine.

For example, an IR function with i5754496 arguments and two add instructions
causes opt to hang for 30+ seconds via the add->mul fold path:

  add i5754496 %x, %x  ->  mul i5754496 %x, 2
    -> visitMul -> willNotOverflowUnsignedMul
      -> ConstantRange::unsignedMulMayOverflow
        -> APInt::umul_ov  (89914 words, ~4 billion word ops)

Fix: return a conservative MayOverflow early when getBitWidth() > 1024.
MayOverflow is always a valid upper bound; callers that receive it simply
forgo adding 'nuw' to the folded instruction, which is safe.

The sibling functions (unsignedAddMayOverflow, signedSubMayOverflow, etc.)
are unaffected because APInt addition/subtraction is O(n), not O(n^2).

Add a regression test UnsignedMulOverflowWideTypeBailout that completes in
0 ms with the fix (and would hang >30 s without it).

Discovered via llvm-opt-fuzzer.
---
 llvm/lib/IR/ConstantRange.cpp           |  6 ++++++
 llvm/unittests/IR/ConstantRangeTest.cpp | 23 +++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index 4c10dfb140cbe..9e12ac0c03f3e 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -2261,6 +2261,12 @@ ConstantRange::OverflowResult ConstantRange::unsignedMulMayOverflow(
   if (isEmptySet() || Other.isEmptySet())
     return OverflowResult::MayOverflow;
 
+  // Bail out for very wide integer types: APInt multiplication is O(n^2) in
+  // the number of words, so types wider than ~1024 bits can cause multi-second
+  // hangs. Return a conservative result instead.
+  if (getBitWidth() > 1024)
+    return OverflowResult::MayOverflow;
+
   APInt Min = getUnsignedMin(), Max = getUnsignedMax();
   APInt OtherMin = Other.getUnsignedMin(), OtherMax = Other.getUnsignedMax();
   bool Overflow;
diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index 13712a76d3edf..8c774d522b122 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -2506,6 +2506,29 @@ TEST_F(ConstantRangeTest, UnsignedMulOverflowExhaustive) {
       });
 }
 
+// Regression test: very wide integer types (>1024 bits) triggered an O(n^2)
+// APInt multiplication inside unsignedMulMayOverflow via APInt::umul_ov,
+// causing multi-second hangs in InstCombine (discovered via llvm-opt-fuzzer
+// with i5754496). The fix returns a conservative MayOverflow early for bit
+// widths above 1024.
+TEST_F(ConstantRangeTest, UnsignedMulOverflowWideTypeBailout) {
+  // 2048 bits is above the 1024-bit bailout threshold.
+  const unsigned W = 2048;
+  ConstantRange WFull = ConstantRange::getFull(W);
+  ConstantRange WOne(APInt(W, 1));
+  ConstantRange WMax(APInt::getMaxValue(W));
+  ConstantRange WEmpty = ConstantRange::getEmpty(W);
+
+  // For types wider than 1024 bits all non-empty pairs return MayOverflow
+  // (conservative but always correct).
+  EXPECT_MAY_OVERFLOW(WFull.unsignedMulMayOverflow(WFull));
+  EXPECT_MAY_OVERFLOW(WOne.unsignedMulMayOverflow(WMax));
+  EXPECT_MAY_OVERFLOW(WMax.unsignedMulMayOverflow(WOne));
+  // Empty-set cases are still handled before the width check.
+  EXPECT_MAY_OVERFLOW(WEmpty.unsignedMulMayOverflow(WFull));
+  EXPECT_MAY_OVERFLOW(WFull.unsignedMulMayOverflow(WEmpty));
+}
+
 TEST_F(ConstantRangeTest, SignedAddOverflowExhaustive) {
   TestOverflowExhaustive(
       [](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {



More information about the llvm-commits mailing list