[llvm] [LoopSimplifyCFG] Add check for missing loop preheader (PR #149743)

Justus Klausecker via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 22 04:53:13 PDT 2025


https://github.com/Justus2308 updated https://github.com/llvm/llvm-project/pull/149743

>From 5ab6c7e575ceda59a6a4226158d03f0e6b10b708 Mon Sep 17 00:00:00 2001
From: Justus Klausecker <justus at klausecker.de>
Date: Sun, 20 Jul 2025 23:54:47 +0200
Subject: [PATCH 1/3] [LoopSimplifyCFG] Add check for missing loop preheader
 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).

---
 .../lib/Transforms/Scalar/LoopSimplifyCFG.cpp | 19 +++++++++++++++++++
 .../enter-through-indirectbr.ll               | 16 ++++++++++++++++
 2 files changed, 35 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll

diff --git a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
index 221094f170ac7..c63578c63e8be 100644
--- a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
@@ -128,6 +128,8 @@ class ConstantTerminatorFoldingImpl {
   // 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,17 @@ class ConstantTerminatorFoldingImpl {
       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 +559,12 @@ class ConstantTerminatorFoldingImpl {
       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(
diff --git a/llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll b/llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll
new file mode 100644
index 0000000000000..bf1b6a70e6c35
--- /dev/null
+++ b/llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll
@@ -0,0 +1,16 @@
+; RUN: opt -S -enable-loop-simplifycfg-term-folding=true -passes='require<domtree>,loop(loop-simplifycfg)' -verify-loop-info -verify-dom-info -verify-loop-lcssa < %s | FileCheck %s
+ 
+define void @test() {
+; CHECK-LABEL: @test(
+
+  indirectbr ptr null, [label %A, label %C]
+
+A:
+  br i1 true, label %B, label %C
+
+B:
+  br i1 true, label %A, label %C
+
+C:
+  unreachable
+}

>From 35c44836fec21a74bc4d3104657aa13c24e7481c Mon Sep 17 00:00:00 2001
From: Justus Klausecker <justus at klausecker.de>
Date: Mon, 21 Jul 2025 18:14:40 +0200
Subject: [PATCH 2/3] address comments

---
 .../enter-through-indirectbr.ll               | 20 +++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll b/llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll
index bf1b6a70e6c35..dd524ab7d140d 100644
--- a/llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll
+++ b/llvm/test/Transforms/LoopSimplifyCFG/enter-through-indirectbr.ll
@@ -1,9 +1,21 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt -S -enable-loop-simplifycfg-term-folding=true -passes='require<domtree>,loop(loop-simplifycfg)' -verify-loop-info -verify-dom-info -verify-loop-lcssa < %s | FileCheck %s
- 
-define void @test() {
-; CHECK-LABEL: @test(
 
-  indirectbr ptr null, [label %A, label %C]
+define void @test(ptr %addr) {
+; CHECK-LABEL: define void @test(
+; CHECK-SAME: ptr [[ADDR:%.*]]) {
+; CHECK-NEXT:    indirectbr ptr [[ADDR]], [label %[[A:.*]], label %C]
+; CHECK:       [[A]]:
+; CHECK-NEXT:    br i1 true, label %[[B:.*]], label %[[C_LOOPEXIT:.*]]
+; CHECK:       [[B]]:
+; CHECK-NEXT:    br i1 true, label %[[A]], label %[[C_LOOPEXIT]]
+; CHECK:       [[C_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[C:.*]]
+; CHECK:       [[C]]:
+; CHECK-NEXT:    unreachable
+;
+
+  indirectbr ptr %addr, [label %A, label %C]
 
 A:
   br i1 true, label %B, label %C

>From 53a43f8c5ac8f8b2e79e686d9a1a77d3cd748f25 Mon Sep 17 00:00:00 2001
From: Justus Klausecker <justus at klausecker.de>
Date: Mon, 21 Jul 2025 18:29:06 +0200
Subject: [PATCH 3/3] fix formatting

---
 llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
index c63578c63e8be..b9546c5fa236b 100644
--- a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp
@@ -222,9 +222,11 @@ class ConstantTerminatorFoldingImpl {
     // 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");
+      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;
     }



More information about the llvm-commits mailing list