From 5ab6c7e575ceda59a6a4226158d03f0e6b10b708 Mon Sep 17 00:00:00 2001 From: Justus Klausecker 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(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,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 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,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 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(Pred->getTerminator()); - }) && "Loop should have preheader if it is not entered indirectly"); + assert(any_of(predecessors(L.getHeader()), + [&](BasicBlock *Pred) { + return isa(Pred->getTerminator()); + }) && + "Loop should have preheader if it is not entered indirectly"); HasIndirectEntry = true; return; }