Bug type: crash
Component: LoopSimplifyCFG
Bug location: llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp: analyze
run
Issue title: `loop-simplifycfg` optimisation pass segfault
Labels: loopoptim
confirmed
crash
Reported by: triallax

I first had this issue with Zig, corresponding issue on the Zig tracker is https://github.com/ziglang/zig/issues/24383, but I've been told this is likely an LLVM problem so I'm reporting it here as well.

LLVM version is 20.1.7, distro is Chimera Linux.

Failing IR (reduced with `llvm-reduce`):
```
; ModuleID = 'reduced.bc'
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux6.15.2-musl"

define fastcc void @vm.VM.runNoClear() {
Entry:
  indirectbr ptr null, [label %Case, label %Case38]

Case:                                             ; preds = %Then104, %Entry
  ret void

Case38:                                           ; preds = %Then104, %Entry
  br i1 true, label %Then104, label %Else66

Then104:                                          ; preds = %Case38
  indirectbr ptr null, [label %Case, label %Case38]

Else66:                                           ; preds = %Case38
  unreachable
}
```

To get the segfault, run:
```console
$ llvm-as reduced.ll
$ opt -passes=loop-simplifycfg -disable-output reduced.bc
```

Here's the full backtrace:
```
* thread #1, name = 'opt', stop reason = SIGSEGV: address not mapped to object (fault address=0x30)
  * frame #0: 0x00007536157612aa libLLVM.so.20.1`::run() [inlined] getPrev at ilist_node_base.h:27:38
    frame #1: 0x00007536157612aa libLLVM.so.20.1`::run() [inlined] getPrev at ilist_node.h:121:59
    frame #2: 0x00007536157612aa libLLVM.so.20.1`::run() [inlined] empty at ilist_node.h:313:45
    frame #3: 0x00007536157612aa libLLVM.so.20.1`::run() [inlined] empty at simple_ilist.h:139:54
    frame #4: 0x00007536157612aa libLLVM.so.20.1`::run() [inlined] getTerminator at BasicBlock.h:241:18
    frame #5: 0x00007536157612a6 libLLVM.so.20.1`::run() [inlined] getTerminator at BasicBlock.h:247:48
    frame #6: 0x00007536157612a6 libLLVM.so.20.1`::run() [inlined] handleDeadExits at LoopSimplifyCFG.cpp:353:31
    frame #7: 0x000075361576128a libLLVM.so.20.1`::run() [inlined] run at LoopSimplifyCFG.cpp:602:5
    frame #8: 0x0000753615760109 libLLVM.so.20.1`::run() [inlined] constantFoldTerminators at LoopSimplifyCFG.cpp:655:31
    frame #9: 0x000075361575f68b libLLVM.so.20.1`::run() [inlined] simplifyLoopCFG at LoopSimplifyCFG.cpp:701:14
    frame #10: 0x000075361575f68b libLLVM.so.20.1`::run() at LoopSimplifyCFG.cpp:722:8
    frame #11: 0x0000753618bb92b2 libLLVM.so.20.1`llvm::detail::PassModel<llvm::Loop, llvm::LoopSimplifyCFGPass, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&>::run(llvm::Loop&, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>&, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&) at PassManagerInternal.h:91:17
    frame #12: 0x0000753615757aec libLLVM.so.20.1`::runSinglePass<llvm::Loop, std::__1::unique_ptr<llvm::detail::PassConcept<llvm::Loop, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults &>, llvm::LoopStandardAnalysisResults &, llvm::LPMUpdater &>, std::__1::default_delete<llvm::detail::PassConcept<llvm::Loop, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults &>, llvm::LoopStandardAnalysisResults &, llvm::LPMUpdater &> > > >() at LoopPassManager.h:375:32
    frame #13: 0x0000753615757841 libLLVM.so.20.1`::runWithoutLoopNestPasses() at LoopPassManager.cpp:160:9
    frame #14: 0x000075361575657d libLLVM.so.20.1`::run() at LoopPassManager.cpp:31:32
    frame #15: 0x0000753618b3ddf2 libLLVM.so.20.1`llvm::detail::PassModel<llvm::Loop, llvm::PassManager<llvm::Loop, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&>, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&>::run(llvm::Loop&, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>&, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&) at PassManagerInternal.h:91:17
    frame #16: 0x00007536157587a1 libLLVM.so.20.1`::run() at LoopPassManager.cpp:302:38
    frame #17: 0x0000753617201982 libLLVM.so.20.1`llvm::detail::PassModel<llvm::Function, llvm::FunctionToLoopPassAdaptor, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) at PassManagerInternal.h:91:17
    frame #18: 0x000075361419e634 libLLVM.so.20.1`::run() at PassManagerImpl.h:81:38
    frame #19: 0x00007536171d7512 libLLVM.so.20.1`llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) at PassManagerInternal.h:91:17
    frame #20: 0x00007536141a3e94 libLLVM.so.20.1`::run() at PassManager.cpp:124:38
    frame #21: 0x00007536171ec972 libLLVM.so.20.1`llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) at PassManagerInternal.h:91:17
    frame #22: 0x000075361419cc4b libLLVM.so.20.1`::run() at PassManagerImpl.h:81:38
    frame #23: 0x00005af58f144970 opt`::runPassPipeline() at NewPMDriver.cpp:541:7
    frame #24: 0x00005af58f135f34 opt`::optMain() at optdriver.cpp:739:12
    frame #25: 0x000075361d1f0d7d ld-musl-x86_64.so.1`libc_start_main_stage2(main=(opt`main), argc=<unavailable>, argv=0x00007ffc033f4558) at __libc_start_main.c:95:7
    frame #26: 0x00005af58f1314c6 opt`_start + 22
```

Comments: author: hstk30-hw
body: https://godbolt.org/z/nvMrcWaE6

Fix patch:
commit 04107209856bb39e041aa38cf40de0afa90a6b2d
Author: Justus Klausecker <117751770+Justus2308@users.noreply.github.com>
Date:   Tue Jul 22 16:01:49 2025 +0200

    [LoopSimplifyCFG] Add check for missing loop preheader (#149743)
    
    Closes #147869
    Closes #149679
    
    Adds a check for a missing loop preheader during analysis. This fixes a
    nullptr dereference that happened whenever LoopSimplify was unable to
    generate a preheader because the loop was entered by an indirectbr
    instruction (as stated in the LoopSimplify.cpp doc comment).

diff --git a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
index 221094f170ac..b9546c5fa236 100644
--- a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
@@ -128,6 +128,8 @@ private:
   // from any other block. So this variable set to true means that loop's latch
   // has become unreachable from loop header.
   bool DeleteCurrentLoop = false;
+  // Whether or not we enter the loop through an indirectbr.
+  bool HasIndirectEntry = false;
 
   // The blocks of the original loop that will still be reachable from entry
   // after the constant folding.
@@ -216,6 +218,19 @@ private:
       return;
     }
 
+    // We need a loop preheader to split in handleDeadExits(). If LoopSimplify
+    // wasn't able to form one because the loop can be entered through an
+    // indirectbr we cannot continue.
+    if (!L.getLoopPreheader()) {
+      assert(any_of(predecessors(L.getHeader()),
+                    [&](BasicBlock *Pred) {
+                      return isa<IndirectBrInst>(Pred->getTerminator());
+                    }) &&
+             "Loop should have preheader if it is not entered indirectly");
+      HasIndirectEntry = true;
+      return;
+    }
+
     // Collect live and dead loop blocks and exits.
     LiveLoopBlocks.insert(L.getHeader());
     for (auto I = DFS.beginRPO(), E = DFS.endRPO(); I != E; ++I) {
@@ -546,6 +561,12 @@ public:
       return false;
     }
 
+    if (HasIndirectEntry) {
+      LLVM_DEBUG(dbgs() << "Loops which can be entered indirectly are not"
+                           " supported!\n");
+      return false;
+    }
+
     // Nothing to constant-fold.
     if (FoldCandidates.empty()) {
       LLVM_DEBUG(


Regression tests:
file: llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll
commands: opt -S -enable-loop-simplifycfg-term-folding=true -passes='require<domtree>,loop(loop-simplifycfg)' -verify-loop-info -verify-dom-info -verify-loop-lcssa < %s
tests: test_name: <module>
test_body: 
define void @test(ptr %addr) {
;

  indirectbr ptr %addr, [label %A, label %C]

A:
  br i1 true, label %B, label %C

B:
  br i1 true, label %A, label %C

C:
  unreachable
}