Skip to content

Conversation

dianqk
Copy link
Member

@dianqk dianqk commented Sep 27, 2025

Fixes #157113.

Take the following IR as an example; we know the destination of the [1, 3] cases is %else.

define i32 @src(i8 range(i8 0, 6) %arg) {
  switch i8 %arg, label %else [
    i8 0, label %if
    i8 4, label %if
    i8 5, label %if
  ]

if:
  ret i32 0

else:
  ret i32 1
}

We can first try the non-wrapping range for both destinations, but I don't see how that would be any better.

Proof: https://alive2.llvm.org/ce/z/acdWD4.

@llvmbot
Copy link
Member

llvmbot commented Sep 27, 2025

@llvm/pr-subscribers-coroutines

@llvm/pr-subscribers-llvm-transforms

Author: dianqk (dianqk)

Changes

Fixes #157113.

Take the following IR as an example; we know the destination of the [1, 3] cases is %else.

define i32 @<!-- -->src(i8 range(i8 0, 6) %arg) {
  switch i8 %arg, label %else [
    i8 0, label %if
    i8 4, label %if
    i8 5, label %if
  ]

if:
  ret i32 0

else:
  ret i32 1
}

We can first try the non-wrapping range for both destinations, but I don't see how that would be any better.

Proof: https://alive2.llvm.org/ce/z/acdWD4.


Full diff: https://github.com/llvm/llvm-project/pull/161000.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/SimplifyCFG.cpp (+58-15)
  • (modified) llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll (+93)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 5e719c6c8cbb7..b02aed3f8f5ea 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5761,15 +5761,49 @@ bool SimplifyCFGOpt::simplifyUnreachable(UnreachableInst *UI) {
   return Changed;
 }
 
-static bool casesAreContiguous(SmallVectorImpl<ConstantInt *> &Cases) {
+static bool casesAreContiguous(Value *Condition,
+                               SmallVectorImpl<ConstantInt *> &Cases,
+                               ConstantInt *&ContiguousCasesMin,
+                               ConstantInt *&ContiguousCasesMax,
+                               bool &IsWrapping) {
   assert(Cases.size() >= 1);
 
   array_pod_sort(Cases.begin(), Cases.end(), constantIntSortPredicate);
-  for (size_t I = 1, E = Cases.size(); I != E; ++I) {
-    if (Cases[I - 1]->getValue() != Cases[I]->getValue() + 1)
+  auto Min = Cases.back()->getValue();
+  auto Max = Cases.front()->getValue();
+  auto Offset = Max - Min;
+  auto ContiguousOffset = Cases.size() - 1;
+  if (Offset == ContiguousOffset) {
+    ContiguousCasesMin = Cases.back();
+    ContiguousCasesMax = Cases.front();
+    return true;
+  }
+  ConstantRange CR = computeConstantRange(Condition, /*ForSigned=*/false);
+  // If this is a wrapping contiguous range, that is, [Min, OtherMin] +
+  // [OtherMax, Max] (also [OtherMax, OtherMin]), [OtherMin+1, OtherMax-1] is a
+  // contiguous range for the other destination. N.B. If CR is not a full range,
+  // Max+1 is not equal to Min. It's not continuous in arithmetic.
+  if (Max == CR.getUnsignedMax() && Min == CR.getUnsignedMin()) {
+    assert(Cases.size() >= 2);
+    auto *It =
+        std::adjacent_find(Cases.begin(), Cases.end(), [](auto L, auto R) {
+          return L->getValue() != R->getValue() + 1;
+        });
+    if (It == Cases.end())
       return false;
+    auto *OtherMax = *It;
+    auto *OtherMin = *(It + 1);
+    if ((Max - OtherMax->getValue()) + (OtherMin->getValue() - Min) ==
+        Cases.size() - 2) {
+      ContiguousCasesMin = cast<ConstantInt>(
+          ConstantInt::get(OtherMin->getType(), OtherMin->getValue() + 1));
+      ContiguousCasesMax = cast<ConstantInt>(
+          ConstantInt::get(OtherMax->getType(), OtherMax->getValue() - 1));
+      IsWrapping = true;
+      return true;
+    }
   }
-  return true;
+  return false;
 }
 
 static void createUnreachableSwitchDefault(SwitchInst *Switch,
@@ -5840,25 +5874,34 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   assert(!CasesA.empty() || HasDefault);
 
   // Figure out if one of the sets of cases form a contiguous range.
-  SmallVectorImpl<ConstantInt *> *ContiguousCases = nullptr;
+  ConstantInt *ContiguousCasesMin = nullptr;
+  ConstantInt *ContiguousCasesMax = nullptr;
   BasicBlock *ContiguousDest = nullptr;
   BasicBlock *OtherDest = nullptr;
-  if (!CasesA.empty() && casesAreContiguous(CasesA)) {
-    ContiguousCases = &CasesA;
+  bool IsWrapping = false;
+  if (!CasesA.empty() &&
+      casesAreContiguous(SI->getCondition(), CasesA, ContiguousCasesMin,
+                         ContiguousCasesMax, IsWrapping)) {
     ContiguousDest = DestA;
     OtherDest = DestB;
-  } else if (casesAreContiguous(CasesB)) {
-    ContiguousCases = &CasesB;
+  } else if (casesAreContiguous(SI->getCondition(), CasesB, ContiguousCasesMin,
+                                ContiguousCasesMax, IsWrapping)) {
     ContiguousDest = DestB;
     OtherDest = DestA;
   } else
     return false;
 
+  if (IsWrapping)
+    std::swap(ContiguousDest, OtherDest);
+
   // Start building the compare and branch.
 
-  Constant *Offset = ConstantExpr::getNeg(ContiguousCases->back());
-  Constant *NumCases =
-      ConstantInt::get(Offset->getType(), ContiguousCases->size());
+  auto ContiguousCasesSize =
+      (ContiguousCasesMax->getValue() - ContiguousCasesMin->getValue())
+          .getZExtValue() +
+      1;
+  Constant *Offset = ConstantExpr::getNeg(ContiguousCasesMin);
+  Constant *NumCases = ConstantInt::get(Offset->getType(), ContiguousCasesSize);
 
   Value *Sub = SI->getCondition();
   if (!Offset->isNullValue())
@@ -5866,7 +5909,7 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
 
   Value *Cmp;
   // If NumCases overflowed, then all possible values jump to the successor.
-  if (NumCases->isNullValue() && !ContiguousCases->empty())
+  if (NumCases->isNullValue() && ContiguousCasesSize != 0)
     Cmp = ConstantInt::getTrue(SI->getContext());
   else
     Cmp = Builder.CreateICmpULT(Sub, NumCases, "switch");
@@ -5895,14 +5938,14 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
 
   // Prune obsolete incoming values off the successors' PHI nodes.
   for (auto BBI = ContiguousDest->begin(); isa<PHINode>(BBI); ++BBI) {
-    unsigned PreviousEdges = ContiguousCases->size();
+    unsigned PreviousEdges = ContiguousCasesSize;
     if (ContiguousDest == SI->getDefaultDest())
       ++PreviousEdges;
     for (unsigned I = 0, E = PreviousEdges - 1; I != E; ++I)
       cast<PHINode>(BBI)->removeIncomingValue(SI->getParent());
   }
   for (auto BBI = OtherDest->begin(); isa<PHINode>(BBI); ++BBI) {
-    unsigned PreviousEdges = SI->getNumCases() - ContiguousCases->size();
+    unsigned PreviousEdges = SI->getNumCases() - ContiguousCasesSize;
     if (OtherDest == SI->getDefaultDest())
       ++PreviousEdges;
     for (unsigned I = 0, E = PreviousEdges - 1; I != E; ++I)
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index 8f2ae2d054f1e..c6904fe273f09 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -188,4 +188,97 @@ exit:
   ret void
 }
 
+define i32 @wrapping_known_range(i8 range(i8 0, 6) %arg) {
+; CHECK-LABEL: @wrapping_known_range(
+; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[ARG_OFF]], 3
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[ELSE:%.*]], label [[IF:%.*]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       if:
+; CHECK-NEXT:    [[I0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    [[I1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 4, label %if
+  i8 5, label %if
+  ]
+
+if:
+  %i0 = call i32 @f(i32 0)
+  ret i32 %i0
+
+else:
+  %i1 = call i32 @f(i32 1)
+  ret i32 %i1
+}
+
+define i32 @wrapping_range(i8 %arg) {
+; CHECK-LABEL: @wrapping_range(
+; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[ARG_OFF]], -4
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[ELSE:%.*]], label [[IF:%.*]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       if:
+; CHECK-NEXT:    [[I0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    [[I1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 -3, label %if
+  i8 -2, label %if
+  i8 -1, label %if
+  ]
+
+if:
+  %i0 = call i32 @f(i32 0)
+  ret i32 %i0
+
+else:
+  %i1 = call i32 @f(i32 1)
+  ret i32 %i1
+}
+
+define i32 @no_continuous_wrapping_range(i8 %arg) {
+; CHECK-LABEL: @no_continuous_wrapping_range(
+; CHECK-NEXT:    switch i8 [[ARG:%.*]], label [[ELSE:%.*]] [
+; CHECK-NEXT:      i8 0, label [[IF:%.*]]
+; CHECK-NEXT:      i8 -3, label [[IF]]
+; CHECK-NEXT:      i8 -1, label [[IF]]
+; CHECK-NEXT:    ]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       if:
+; CHECK-NEXT:    [[I0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    [[I1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 -3, label %if
+  i8 -1, label %if
+  ]
+
+if:
+  %i0 = call i32 @f(i32 0)
+  ret i32 %i0
+
+else:
+  %i1 = call i32 @f(i32 1)
+  ret i32 %i1
+}
+
 declare void @bar(ptr nonnull dereferenceable(4))

@dtcxzyw
Copy link
Member

dtcxzyw commented Sep 28, 2025

@zyw-bot csmith-quick-fuzz

@nikic
Copy link
Contributor

nikic commented Sep 28, 2025

Maybe limit this to >2 cases to start to avoid regressions?

@llvmbot llvmbot added the coroutines C++20 coroutines label Oct 3, 2025
@dianqk dianqk force-pushed the switch-wrapping-range branch from bb9720c to 9864e17 Compare October 3, 2025 10:47
@dianqk
Copy link
Member Author

dianqk commented Oct 3, 2025

Maybe limit this to >2 cases to start to avoid regressions?

This is still needed, some 2 cases have not been transformed: https://llvm.godbolt.org/z/Y71EovY4M.

I think it's fine because it runs later in the passes pipeline.

@dianqk
Copy link
Member Author

dianqk commented Oct 3, 2025

I improved this with only one case also.

// Max+1 is not equal to Min. It's not continuous in arithmetic.
if (Max == CR.getUnsignedMax() && Min == CR.getUnsignedMin()) {
assert(Cases.size() >= 2);
auto *It =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about something as follows before the logic here?

// Find the first non-consecutive pair, and ensure this pair
// happens to be unique.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I meant this before the std::adjacent_find, which is not appearing)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is what you want. std::adjacent_find finds one non-consecutive pair and checks their distance to check if this pair is unique.

Comment on lines 5729 to 5732
auto Min = Cases.back()->getValue();
auto Max = Cases.front()->getValue();
auto Offset = Max - Min;
auto ContiguousOffset = Cases.size() - 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for auto here, having the type explicit (const APInt&, unsigned) would make the code more readable.

Comment on lines 5751 to 5752
auto *OtherMax = *It;
auto *OtherMin = *(It + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto *OtherMax = *It;
auto *OtherMin = *(It + 1);
auto [OtherMax, OtherMin] = std::make_pair(*It, *std::next(It));


static bool casesAreContiguous(SmallVectorImpl<ConstantInt *> &Cases) {
static bool casesAreContiguous(Value *Condition,
SmallVectorImpl<ConstantInt *> &Cases,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the function name would deserve an update, e.g., findContiguousCases (and maybe return a std::optional struct instead of the three values as out parameters)?

Comment on lines 5874 to 5876
if (auto Result = findContiguousCases(SI->getCondition(), CasesA, CasesB,
DestA, DestB))
ContiguousCases = *Result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto Result = findContiguousCases(SI->getCondition(), CasesA, CasesB,
DestA, DestB))
ContiguousCases = *Result;
ContiguousCases = findContiguousCases(SI->getCondition(), CasesA, CasesB,
DestA, DestB);

Comment on lines 5879 to 5881
if (auto Result = findContiguousCases(SI->getCondition(), CasesB, CasesA,
DestB, DestA))
ContiguousCases = *Result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto Result = findContiguousCases(SI->getCondition(), CasesB, CasesA,
DestB, DestA))
ContiguousCases = *Result;
ContiguousCases = findContiguousCases(SI->getCondition(), CasesB, CasesA,
DestB, DestA);


// Start building the compare and branch.
// Correctness: Cases to the default destination cannot be contiguous cases.
if (!ContiguousCases && !HasDefault && !CasesA.empty())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!ContiguousCases && !HasDefault && !CasesA.empty())
else if (!HasDefault)

!HasDefault implies !CasesA.empty().

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. TBH the cases are unnecessary to be contiguous. There can be holes between them (unreachable cases).

@antoniofrighetto
Copy link
Contributor

@zyw-bot csmith-fuzz

@dianqk dianqk enabled auto-merge (squash) October 6, 2025 04:33
@dianqk dianqk merged commit bbdcba9 into llvm:main Oct 6, 2025
9 checks passed
@dianqk dianqk deleted the switch-wrapping-range branch October 6, 2025 05:47
@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 6, 2025

LLVM Buildbot has detected a new failure on builder sanitizer-x86_64-linux-fast running on sanitizer-buildbot3 while building llvm at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/169/builds/15667

Here is the relevant piece of the build log for the reference
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using lld-link: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/lld-link
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using ld64.lld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/ld64.lld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using wasm-ld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/wasm-ld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using ld.lld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/ld.lld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using lld-link: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/lld-link
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using ld64.lld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/ld64.lld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using wasm-ld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/wasm-ld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/main.py:74: note: The test suite configuration requested an individual test timeout of 0 seconds but a timeout of 900 seconds was requested on the command line. Forcing timeout to be 900 seconds.
-- Testing: 92406 tests, 64 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90.
FAIL: Clang :: Interpreter/global-dtor.cpp (68187 of 92406)
******************** TEST 'Clang :: Interpreter/global-dtor.cpp' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
cat /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp | /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/clang-repl | /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/FileCheck /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp # RUN: at line 5
+ cat /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp
+ /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/clang-repl
+ /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/FileCheck /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp
JIT session error: In graph incr_module_9-jitted-objectbuffer, section .text.startup: relocation target 0x71dbac40bffc (__dso_handle:0x71dbac40c000 + 0xfffffffffffffffc) is out of range of Delta32 fixup at address 0x71dbac40c000 (<anonymous block> @ 0x75dbad8c7010 + 0x1f)
error: Failed to materialize symbols: { (main, { __clang_call_terminate, $.incr_module_9.__inits.0, DW.ref.__gxx_personality_v0, _ZN1DC2Ev, _ZN1DD2Ev, d, __orc_init_func.incr_module_9 }) }
error: Failed to materialize symbols: { (main, { __orc_init_func.incr_module_9 }) }
/home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp:10:11: error: CHECK: expected string not found in input
// CHECK: D[f=1.000000, m=0x0]
          ^
<stdin>:1:1: note: scanning from here
clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> 
^
<stdin>:1:11: note: possible intended match here
clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> 
          ^

Input file: <stdin>
Check file: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp

-dump-input=help explains the following input dump.

Input was:
<<<<<<
            1: clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl>  
check:10'0     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
check:10'1               ?                                                                                                                                       possible intended match
>>>>>>

--

********************
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90.. 
Step 10 (stage2/asan_ubsan check) failure: stage2/asan_ubsan check (failure)
...
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using lld-link: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/lld-link
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using ld64.lld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/ld64.lld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using wasm-ld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/wasm-ld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using ld.lld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/ld.lld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using lld-link: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/lld-link
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using ld64.lld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/ld64.lld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/llvm/config.py:530: note: using wasm-ld: /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/wasm-ld
llvm-lit: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/llvm/utils/lit/lit/main.py:74: note: The test suite configuration requested an individual test timeout of 0 seconds but a timeout of 900 seconds was requested on the command line. Forcing timeout to be 900 seconds.
-- Testing: 92406 tests, 64 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90.
FAIL: Clang :: Interpreter/global-dtor.cpp (68187 of 92406)
******************** TEST 'Clang :: Interpreter/global-dtor.cpp' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
cat /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp | /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/clang-repl | /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/FileCheck /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp # RUN: at line 5
+ cat /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp
+ /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/clang-repl
+ /home/b/sanitizer-x86_64-linux-fast/build/llvm_build_asan_ubsan/bin/FileCheck /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp
JIT session error: In graph incr_module_9-jitted-objectbuffer, section .text.startup: relocation target 0x71dbac40bffc (__dso_handle:0x71dbac40c000 + 0xfffffffffffffffc) is out of range of Delta32 fixup at address 0x71dbac40c000 (<anonymous block> @ 0x75dbad8c7010 + 0x1f)
error: Failed to materialize symbols: { (main, { __clang_call_terminate, $.incr_module_9.__inits.0, DW.ref.__gxx_personality_v0, _ZN1DC2Ev, _ZN1DD2Ev, d, __orc_init_func.incr_module_9 }) }
error: Failed to materialize symbols: { (main, { __orc_init_func.incr_module_9 }) }
/home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp:10:11: error: CHECK: expected string not found in input
// CHECK: D[f=1.000000, m=0x0]
          ^
<stdin>:1:1: note: scanning from here
clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> 
^
<stdin>:1:11: note: possible intended match here
clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> 
          ^

Input file: <stdin>
Check file: /home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang/test/Interpreter/global-dtor.cpp

-dump-input=help explains the following input dump.

Input was:
<<<<<<
            1: clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl> clang-repl>  
check:10'0     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
check:10'1               ?                                                                                                                                       possible intended match
>>>>>>

--

********************
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90.. 

dtcxzyw added a commit that referenced this pull request Oct 6, 2025
This patch implements the todo discussed in
#158495 (comment).
It also fixes a regression introduced by
#161000. See also
dtcxzyw/llvm-opt-benchmark#2890 (comment).

IR diff: dtcxzyw/llvm-opt-benchmark#2892
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Oct 6, 2025
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 6, 2025
…1000)

Fixes llvm#157113.

Take the following IR as an example; we know the destination of the `[1,
3]` cases is `%else`.

```llvm
define i32 @src(i8 range(i8 0, 6) %arg) {
  switch i8 %arg, label %else [
    i8 0, label %if
    i8 4, label %if
    i8 5, label %if
  ]

if:
  ret i32 0

else:
  ret i32 1
}
```

We can first try the non-wrapping range for both destinations, but I
don't see how that would be any better.

Proof: https://alive2.llvm.org/ce/z/acdWD4.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 6, 2025
@nathanchance
Copy link
Member

I am seeing a "Broken module" error when building the Linux kernel with full LTO (it shows up for AArch64 / ARCH=arm64 and RISCV / ARCH=riscv) after this change (original report: ClangBuiltLinux/linux#2126)

PHINode should have one entry for each predecessor of its parent basic block!
  %97 = phi i32 [ %94, %91 ], [ -38, undef ], [ -38, %78 ]
LLVM ERROR: Broken module found, compilation aborted!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.	Program arguments: ld.lld -EL -maarch64elf -z norelro -mllvm -import-instr-limit=5 -z noexecstack -r -o drivers/gpu/drm/nouveau/nouveau.o @drivers/gpu/drm/nouveau/nouveau.mod
1.	Running pass "verify" on module "ld-temp.o"
 #0 0x0000762378bfe41a llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4a3241a)
 #1 0x0000762378bfbc27 llvm::sys::RunSignalHandlers() (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4a2fc27)
 #2 0x0000762378bff184 (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4a33184)
 #3 0x0000762373c90df0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0)
 #4 0x0000762373ce595c (/lib/x86_64-linux-gnu/libc.so.6+0x9495c)
 #5 0x0000762373c90cc2 raise (/lib/x86_64-linux-gnu/libc.so.6+0x3fcc2)
 #6 0x0000762373c794ac abort (/lib/x86_64-linux-gnu/libc.so.6+0x284ac)
 #7 0x0000762378b3ed1b llvm::report_fatal_error(llvm::Twine const&, bool) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4972d1b)
 #8 0x0000762378b3eb6a (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4972b6a)
 #9 0x0000762378ddb941 (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4c0f941)
#10 0x0000762378da70e7 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4bdb0e7)
#11 0x000076237a61cbb7 llvm::lto::opt(llvm::lto::Config const&, llvm::TargetMachine*, unsigned int, llvm::Module&, bool, llvm::ModuleSummaryIndex*, llvm::ModuleSummaryIndex const*, std::vector<unsigned char, std::allocator<unsigned char>> const&) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x6450bb7)
#12 0x000076237a61d352 llvm::lto::backend(llvm::lto::Config const&, std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, unsigned int, llvm::Module&, llvm::ModuleSummaryIndex&) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x6451352)
#13 0x000076237a607acd llvm::lto::LTO::runRegularLTO(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x643bacd)
#14 0x000076237a6070a8 llvm::lto::LTO::run(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, llvm::FileCache) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x643b0a8)
#15 0x00006426634c129d lld::elf::BitcodeCompiler::compile() (/usr/lib/llvm-22/bin/lld+0x34029d)
#16 0x000064266341809c void lld::elf::LinkerDriver::compileBitcodeFiles<llvm::object::ELFType<(llvm::endianness)1, true>>(bool) (/usr/lib/llvm-22/bin/lld+0x29709c)
#17 0x00006426633fd468 void lld::elf::LinkerDriver::link<llvm::object::ELFType<(llvm::endianness)1, true>>(llvm::opt::InputArgList&) (/usr/lib/llvm-22/bin/lld+0x27c468)
#18 0x00006426633e5457 lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) (/usr/lib/llvm-22/bin/lld+0x264457)
#19 0x00006426633e1041 lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) (/usr/lib/llvm-22/bin/lld+0x260041)
#20 0x00006426633040ce lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) (/usr/lib/llvm-22/bin/lld+0x1830ce)
#21 0x00006426633032f2 lld_main(int, char**, llvm::ToolContext const&) (/usr/lib/llvm-22/bin/lld+0x1822f2)
#22 0x00006426633039aa main (/usr/lib/llvm-22/bin/lld+0x1829aa)
#23 0x0000762373c7aca8 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca8)
#24 0x0000762373c7ad65 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d65)
#25 0x0000642663302f31 _start (/usr/lib/llvm-22/bin/lld+0x181f31)

I was able to find two .o files within this module that reproduced this issue when linked together and reduced each of them with cvise.

$ cat gr.i
struct nvkm_subdev {
  int *device;
};
enum nvkm_memory_target {
  NVKM_MEM_TARGET_INST
} nvkm_memory_new(int *, enum nvkm_memory_target, long, int, _Bool, int **);
struct {
  struct nvkm_subdev base;
  struct {
    _Bool init;
  } ctxbuf[];
} *r535_gr_promote_ctx_gr;
typedef struct {
  char bNonmapped;
} NV2080_CTRL_GPU_PROMOTE_CTX_BUFFER_ENTRY;
typedef struct {
  NV2080_CTRL_GPU_PROMOTE_CTX_BUFFER_ENTRY promoteEntry[];
} NV2080_CTRL_GPU_PROMOTE_CTX_PARAMS;
int *r535_gr_promote_ctx_pmem_0;
NV2080_CTRL_GPU_PROMOTE_CTX_PARAMS r535_gr_promote_ctx_ctrl;
int r535_gr_promote_ctx_ctrl_0, r535_gr_promote_ctx_gr_1_0_0;
char r535_gr_promote_ctx_gr_1_0_1;
void r535_gr_promote_ctx() {
  struct nvkm_subdev subdev = r535_gr_promote_ctx_gr->base;
  int *device = subdev.device;
  for (int i = 0;;) {
    NV2080_CTRL_GPU_PROMOTE_CTX_BUFFER_ENTRY *entry =
        &r535_gr_promote_ctx_ctrl.promoteEntry[r535_gr_promote_ctx_ctrl_0];
    nvkm_memory_new(device, r535_gr_promote_ctx_gr->ctxbuf[i].init,
                    r535_gr_promote_ctx_gr_1_0_0, r535_gr_promote_ctx_gr_1_0_1,
                    r535_gr_promote_ctx_gr, &r535_gr_promote_ctx_pmem_0);
    entry->bNonmapped = 1;
  }
}

$ cat memory.i
enum { false, true };
enum nvkm_memory_target {
  NVKM_MEM_TARGET_INST_SR_LOST,
  NVKM_MEM_TARGET_INST
} nvkm_instobj_new(int *, _Bool, int **);
int nvkm_memory_new_pmemory, nvkm_memory_new_ret, nvkm_memory_new_imem;
int *nvkm_memory_new_memory;
int nvkm_memory_new(int *device, enum nvkm_memory_target target) {
  _Bool preserve = true;
  switch (target) {
  case NVKM_MEM_TARGET_INST_SR_LOST:
    preserve = false;
  case NVKM_MEM_TARGET_INST:
    break;
  default:
    return 8;
  }
  nvkm_instobj_new(&nvkm_memory_new_imem, preserve, &nvkm_memory_new_memory);
  if (nvkm_memory_new_ret)
    nvkm_memory_new_pmemory = *nvkm_memory_new_memory;
  return 0;
}

$ clang --target=aarch64-linux -O2 -flto -Wall -Wextra -c -o gr.{o,i}

$ clang --target=aarch64-linux -O2 -flto -Wall -Wextra -c -o memory.{o,i}
memory.i:8:26: warning: unused parameter 'device' [-Wunused-parameter]
    8 | int nvkm_memory_new(int *device, enum nvkm_memory_target target) {
      |                          ^
1 warning generated.

$ ld.lld -EL -maarch64elf -z norelro -z noexecstack -r -o /dev/null {gr,memory}.o
PHINode should have one entry for each predecessor of its parent basic block!
  %20 = phi i32 [ %4, %3 ], [ %15, %3 ], [ %15, %16 ]
LLVM ERROR: Broken module found, compilation aborted!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.      Program arguments: ld.lld -EL -maarch64elf -z norelro -z noexecstack -r -o /dev/null gr.o memory.o
1.      Running pass "verify" on module "ld-temp.o"
 #0 0x000055dd2972c9d8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (ld.lld+0x355f9d8)
 #1 0x000055dd29729fc5 llvm::sys::RunSignalHandlers() (ld.lld+0x355cfc5)
 #2 0x000055dd2972d741 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007f3f2363e540 (/usr/lib/libc.so.6+0x3e540)
 #4 0x00007f3f2369894c (/usr/lib/libc.so.6+0x9894c)
 #5 0x00007f3f2363e410 raise (/usr/lib/libc.so.6+0x3e410)
 #6 0x00007f3f2362557a abort (/usr/lib/libc.so.6+0x2557a)
 #7 0x000055dd2969b385 llvm::report_fatal_error(llvm::Twine const&, bool) (ld.lld+0x34ce385)
 #8 0x000055dd2969b1c6 (ld.lld+0x34ce1c6)
 #9 0x000055dd2d34baa6 (ld.lld+0x717eaa6)
#10 0x000055dd2d314ab7 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (ld.lld+0x7147ab7)
#11 0x000055dd2af45fae llvm::lto::opt(llvm::lto::Config const&, llvm::TargetMachine*, unsigned int, llvm::Module&, bool, llvm::ModuleSummaryIndex*, llvm::ModuleSummaryIndex const*, std::vector<unsigned char, std::allocator<unsigned char>> const&) (ld.lld+0x4d78fae)
#12 0x000055dd2af46988 llvm::lto::backend(llvm::lto::Config const&, std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, unsigned int, llvm::Module&, llvm::ModuleSummaryIndex&) (ld.lld+0x4d79988)
#13 0x000055dd2af2c3d1 llvm::lto::LTO::runRegularLTO(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>) (ld.lld+0x4d5f3d1)
#14 0x000055dd2af2b5cc llvm::lto::LTO::run(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, llvm::FileCache) (ld.lld+0x4d5e5cc)
#15 0x000055dd2995d217 lld::elf::BitcodeCompiler::compile() (ld.lld+0x3790217)
#16 0x000055dd2989029f void lld::elf::LinkerDriver::compileBitcodeFiles<llvm::object::ELFType<(llvm::endianness)1, true>>(bool) (ld.lld+0x36c329f)
#17 0x000055dd2986f050 void lld::elf::LinkerDriver::link<llvm::object::ELFType<(llvm::endianness)1, true>>(llvm::opt::InputArgList&) (ld.lld+0x36a2050)
#18 0x000055dd2985369d lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) (ld.lld+0x368669d)
#19 0x000055dd2984fbca lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) (ld.lld+0x3682bca)
#20 0x000055dd29731752 lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) (ld.lld+0x3564752)
#21 0x000055dd296791a5 lld_main(int, char**, llvm::ToolContext const&) (ld.lld+0x34ac1a5)
#22 0x000055dd296797e7 main (ld.lld+0x34ac7e7)
#23 0x00007f3f23627675 (/usr/lib/libc.so.6+0x27675)
#24 0x00007f3f23627729 __libc_start_main (/usr/lib/libc.so.6+0x27729)
#25 0x000055dd29678da5 _start (ld.lld+0x34abda5)

If there is any other information I can provide, I am more than happy to do so.

@dtcxzyw
Copy link
Member

dtcxzyw commented Oct 9, 2025

I am seeing a "Broken module" error when building the Linux kernel with full LTO (it shows up for AArch64 / ARCH=arm64 and RISCV / ARCH=riscv) after this change (original report: ClangBuiltLinux/linux#2126)

PHINode should have one entry for each predecessor of its parent basic block!
  %97 = phi i32 [ %94, %91 ], [ -38, undef ], [ -38, %78 ]
LLVM ERROR: Broken module found, compilation aborted!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.	Program arguments: ld.lld -EL -maarch64elf -z norelro -mllvm -import-instr-limit=5 -z noexecstack -r -o drivers/gpu/drm/nouveau/nouveau.o @drivers/gpu/drm/nouveau/nouveau.mod
1.	Running pass "verify" on module "ld-temp.o"
 #0 0x0000762378bfe41a llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4a3241a)
 #1 0x0000762378bfbc27 llvm::sys::RunSignalHandlers() (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4a2fc27)
 #2 0x0000762378bff184 (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4a33184)
 #3 0x0000762373c90df0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0)
 #4 0x0000762373ce595c (/lib/x86_64-linux-gnu/libc.so.6+0x9495c)
 #5 0x0000762373c90cc2 raise (/lib/x86_64-linux-gnu/libc.so.6+0x3fcc2)
 #6 0x0000762373c794ac abort (/lib/x86_64-linux-gnu/libc.so.6+0x284ac)
 #7 0x0000762378b3ed1b llvm::report_fatal_error(llvm::Twine const&, bool) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4972d1b)
 #8 0x0000762378b3eb6a (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4972b6a)
 #9 0x0000762378ddb941 (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4c0f941)
#10 0x0000762378da70e7 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x4bdb0e7)
#11 0x000076237a61cbb7 llvm::lto::opt(llvm::lto::Config const&, llvm::TargetMachine*, unsigned int, llvm::Module&, bool, llvm::ModuleSummaryIndex*, llvm::ModuleSummaryIndex const*, std::vector<unsigned char, std::allocator<unsigned char>> const&) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x6450bb7)
#12 0x000076237a61d352 llvm::lto::backend(llvm::lto::Config const&, std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, unsigned int, llvm::Module&, llvm::ModuleSummaryIndex&) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x6451352)
#13 0x000076237a607acd llvm::lto::LTO::runRegularLTO(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x643bacd)
#14 0x000076237a6070a8 llvm::lto::LTO::run(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, llvm::FileCache) (/lib/x86_64-linux-gnu/libLLVM.so.22.0+0x643b0a8)
#15 0x00006426634c129d lld::elf::BitcodeCompiler::compile() (/usr/lib/llvm-22/bin/lld+0x34029d)
#16 0x000064266341809c void lld::elf::LinkerDriver::compileBitcodeFiles<llvm::object::ELFType<(llvm::endianness)1, true>>(bool) (/usr/lib/llvm-22/bin/lld+0x29709c)
#17 0x00006426633fd468 void lld::elf::LinkerDriver::link<llvm::object::ELFType<(llvm::endianness)1, true>>(llvm::opt::InputArgList&) (/usr/lib/llvm-22/bin/lld+0x27c468)
#18 0x00006426633e5457 lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) (/usr/lib/llvm-22/bin/lld+0x264457)
#19 0x00006426633e1041 lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) (/usr/lib/llvm-22/bin/lld+0x260041)
#20 0x00006426633040ce lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) (/usr/lib/llvm-22/bin/lld+0x1830ce)
#21 0x00006426633032f2 lld_main(int, char**, llvm::ToolContext const&) (/usr/lib/llvm-22/bin/lld+0x1822f2)
#22 0x00006426633039aa main (/usr/lib/llvm-22/bin/lld+0x1829aa)
#23 0x0000762373c7aca8 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca8)
#24 0x0000762373c7ad65 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d65)
#25 0x0000642663302f31 _start (/usr/lib/llvm-22/bin/lld+0x181f31)

I was able to find two .o files within this module that reproduced this issue when linked together and reduced each of them with cvise.

$ cat gr.i
struct nvkm_subdev {
  int *device;
};
enum nvkm_memory_target {
  NVKM_MEM_TARGET_INST
} nvkm_memory_new(int *, enum nvkm_memory_target, long, int, _Bool, int **);
struct {
  struct nvkm_subdev base;
  struct {
    _Bool init;
  } ctxbuf[];
} *r535_gr_promote_ctx_gr;
typedef struct {
  char bNonmapped;
} NV2080_CTRL_GPU_PROMOTE_CTX_BUFFER_ENTRY;
typedef struct {
  NV2080_CTRL_GPU_PROMOTE_CTX_BUFFER_ENTRY promoteEntry[];
} NV2080_CTRL_GPU_PROMOTE_CTX_PARAMS;
int *r535_gr_promote_ctx_pmem_0;
NV2080_CTRL_GPU_PROMOTE_CTX_PARAMS r535_gr_promote_ctx_ctrl;
int r535_gr_promote_ctx_ctrl_0, r535_gr_promote_ctx_gr_1_0_0;
char r535_gr_promote_ctx_gr_1_0_1;
void r535_gr_promote_ctx() {
  struct nvkm_subdev subdev = r535_gr_promote_ctx_gr->base;
  int *device = subdev.device;
  for (int i = 0;;) {
    NV2080_CTRL_GPU_PROMOTE_CTX_BUFFER_ENTRY *entry =
        &r535_gr_promote_ctx_ctrl.promoteEntry[r535_gr_promote_ctx_ctrl_0];
    nvkm_memory_new(device, r535_gr_promote_ctx_gr->ctxbuf[i].init,
                    r535_gr_promote_ctx_gr_1_0_0, r535_gr_promote_ctx_gr_1_0_1,
                    r535_gr_promote_ctx_gr, &r535_gr_promote_ctx_pmem_0);
    entry->bNonmapped = 1;
  }
}

$ cat memory.i
enum { false, true };
enum nvkm_memory_target {
  NVKM_MEM_TARGET_INST_SR_LOST,
  NVKM_MEM_TARGET_INST
} nvkm_instobj_new(int *, _Bool, int **);
int nvkm_memory_new_pmemory, nvkm_memory_new_ret, nvkm_memory_new_imem;
int *nvkm_memory_new_memory;
int nvkm_memory_new(int *device, enum nvkm_memory_target target) {
  _Bool preserve = true;
  switch (target) {
  case NVKM_MEM_TARGET_INST_SR_LOST:
    preserve = false;
  case NVKM_MEM_TARGET_INST:
    break;
  default:
    return 8;
  }
  nvkm_instobj_new(&nvkm_memory_new_imem, preserve, &nvkm_memory_new_memory);
  if (nvkm_memory_new_ret)
    nvkm_memory_new_pmemory = *nvkm_memory_new_memory;
  return 0;
}

$ clang --target=aarch64-linux -O2 -flto -Wall -Wextra -c -o gr.{o,i}

$ clang --target=aarch64-linux -O2 -flto -Wall -Wextra -c -o memory.{o,i}
memory.i:8:26: warning: unused parameter 'device' [-Wunused-parameter]
    8 | int nvkm_memory_new(int *device, enum nvkm_memory_target target) {
      |                          ^
1 warning generated.

$ ld.lld -EL -maarch64elf -z norelro -z noexecstack -r -o /dev/null {gr,memory}.o
PHINode should have one entry for each predecessor of its parent basic block!
  %20 = phi i32 [ %4, %3 ], [ %15, %3 ], [ %15, %16 ]
LLVM ERROR: Broken module found, compilation aborted!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.      Program arguments: ld.lld -EL -maarch64elf -z norelro -z noexecstack -r -o /dev/null gr.o memory.o
1.      Running pass "verify" on module "ld-temp.o"
 #0 0x000055dd2972c9d8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (ld.lld+0x355f9d8)
 #1 0x000055dd29729fc5 llvm::sys::RunSignalHandlers() (ld.lld+0x355cfc5)
 #2 0x000055dd2972d741 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007f3f2363e540 (/usr/lib/libc.so.6+0x3e540)
 #4 0x00007f3f2369894c (/usr/lib/libc.so.6+0x9894c)
 #5 0x00007f3f2363e410 raise (/usr/lib/libc.so.6+0x3e410)
 #6 0x00007f3f2362557a abort (/usr/lib/libc.so.6+0x2557a)
 #7 0x000055dd2969b385 llvm::report_fatal_error(llvm::Twine const&, bool) (ld.lld+0x34ce385)
 #8 0x000055dd2969b1c6 (ld.lld+0x34ce1c6)
 #9 0x000055dd2d34baa6 (ld.lld+0x717eaa6)
#10 0x000055dd2d314ab7 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (ld.lld+0x7147ab7)
#11 0x000055dd2af45fae llvm::lto::opt(llvm::lto::Config const&, llvm::TargetMachine*, unsigned int, llvm::Module&, bool, llvm::ModuleSummaryIndex*, llvm::ModuleSummaryIndex const*, std::vector<unsigned char, std::allocator<unsigned char>> const&) (ld.lld+0x4d78fae)
#12 0x000055dd2af46988 llvm::lto::backend(llvm::lto::Config const&, std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, unsigned int, llvm::Module&, llvm::ModuleSummaryIndex&) (ld.lld+0x4d79988)
#13 0x000055dd2af2c3d1 llvm::lto::LTO::runRegularLTO(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>) (ld.lld+0x4d5f3d1)
#14 0x000055dd2af2b5cc llvm::lto::LTO::run(std::function<llvm::Expected<std::unique_ptr<llvm::CachedFileStream, std::default_delete<llvm::CachedFileStream>>> (unsigned int, llvm::Twine const&)>, llvm::FileCache) (ld.lld+0x4d5e5cc)
#15 0x000055dd2995d217 lld::elf::BitcodeCompiler::compile() (ld.lld+0x3790217)
#16 0x000055dd2989029f void lld::elf::LinkerDriver::compileBitcodeFiles<llvm::object::ELFType<(llvm::endianness)1, true>>(bool) (ld.lld+0x36c329f)
#17 0x000055dd2986f050 void lld::elf::LinkerDriver::link<llvm::object::ELFType<(llvm::endianness)1, true>>(llvm::opt::InputArgList&) (ld.lld+0x36a2050)
#18 0x000055dd2985369d lld::elf::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) (ld.lld+0x368669d)
#19 0x000055dd2984fbca lld::elf::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) (ld.lld+0x3682bca)
#20 0x000055dd29731752 lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) (ld.lld+0x3564752)
#21 0x000055dd296791a5 lld_main(int, char**, llvm::ToolContext const&) (ld.lld+0x34ac1a5)
#22 0x000055dd296797e7 main (ld.lld+0x34ac7e7)
#23 0x00007f3f23627675 (/usr/lib/libc.so.6+0x27675)
#24 0x00007f3f23627729 __libc_start_main (/usr/lib/libc.so.6+0x27729)
#25 0x000055dd29678da5 _start (ld.lld+0x34abda5)

If there is any other information I can provide, I am more than happy to do so.

Reproduced. I will provide a smaller IR reproducer.

dianqk added a commit that referenced this pull request Oct 9, 2025
…is unreachable (#162677)

Fixes #162585.

#161000 changed `br i1 true, label %if, label %else` to `br label %if`,
so we should remove one more incoming value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[SimplifyCFG] Fold switch to wrapping range check
8 participants