[llvm] [BasicBlockUtils] Fix loopinfo header in split block before (PR #179408)

Yevgeny Rouban via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 3 01:03:44 PST 2026


https://github.com/yrouban updated https://github.com/llvm/llvm-project/pull/179408

>From c0e312713d7e99bb44a0eb75a27cd096decc934f Mon Sep 17 00:00:00 2001
From: Yevgeny Rouban <yrouban at gmail.com>
Date: Tue, 3 Feb 2026 09:02:27 +0300
Subject: [PATCH 1/2] [BasicBlockUtilsTests] SplitBlock breaks LoopInfo. NFC

Splitting a basic block BB into a pair of blocks NewBB->BB
SplitBlock(... DT, &LI,... /* Before */ true) does not change
NewBB to be the new head of the loop if BB was the loop head
before the split.

This change just introduces a new unit test that demonstrates
the bug.
---
 .../Transforms/Utils/BasicBlockUtilsTest.cpp  | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
index 00d9e9ff81e05..37b20d00d18d9 100644
--- a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
@@ -26,6 +26,8 @@
 #include "llvm/Support/SourceMgr.h"
 #include "gtest/gtest.h"
 
+#define DEBUG_TYPE "basic-block-utils-tests"
+
 using namespace llvm;
 
 static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
@@ -354,6 +356,38 @@ define void @foo() {
 }
 #endif
 
+#ifndef NDEBUG
+TEST(BasicBlockUtils, SplitBlockBefore) {
+  LLVMContext C;
+  std::unique_ptr<Module> M = parseIR(C, R"IR(
+define void @split-block-before-test(i1 %flag) {
+entry:
+  br label %loop
+
+loop:
+  br i1 %flag, label %loop, label %exit
+
+exit:
+  ret void
+}
+)IR");
+  Function *F = M->getFunction("split-block-before-test");
+  DominatorTree DT(*F);
+  LoopInfo LI(DT);
+
+  EXPECT_TRUE(DT.verify());
+  LI.verify(DT);
+  auto *LoopBB = getBasicBlockByName(*F, "loop");
+  SplitBlock(LoopBB, LoopBB->getFirstInsertionPt(), &DT, &LI,
+              /* MemorySSAUpdater */ nullptr, LoopBB->getName() + ".split",
+              /* Before */ true);
+
+  EXPECT_TRUE(DT.verify());
+  LLVM_DEBUG(F->dump(); LI.print(dbgs()));
+  EXPECT_DEATH({LI.verify(DT);}, "Assertion.*Loop is unreachable!");
+}
+#endif
+
 TEST(BasicBlockUtils, NoUnreachableBlocksToEliminate) {
   LLVMContext C;
   std::unique_ptr<Module> M = parseIR(C, R"IR(

>From ef0bf05db95cb097cba0e94f0c732cd1019f718a Mon Sep 17 00:00:00 2001
From: Yevgeny Rouban <yrouban at gmail.com>
Date: Tue, 3 Feb 2026 10:38:06 +0300
Subject: [PATCH 2/2] [BasicBlockUtils] Fix loop header in splitBlockBefore()

If BB was the loop header before NewBB = splitBlockBefore(BB, ...)
then the NewBB must become the new header of the loop.
---
 llvm/lib/Transforms/Utils/BasicBlockUtils.cpp          |  5 ++++-
 .../unittests/Transforms/Utils/BasicBlockUtilsTest.cpp | 10 ++++++----
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index b0c04086f5e84..381cb7170c0f6 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -1080,8 +1080,11 @@ BasicBlock *llvm::splitBlockBefore(BasicBlock *Old, BasicBlock::iterator SplitPt
   // The new block lives in whichever loop the old one did. This preserves
   // LCSSA as well, because we force the split point to be after any PHI nodes.
   if (LI)
-    if (Loop *L = LI->getLoopFor(Old))
+    if (Loop *L = LI->getLoopFor(Old)) {
       L->addBasicBlockToLoop(New, *LI);
+      if (L->getHeader() == Old)
+        L->moveToHeader(New);
+    }
 
   if (DTU) {
     SmallVector<DominatorTree::UpdateType, 8> DTUpdates;
diff --git a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
index 37b20d00d18d9..198518e130551 100644
--- a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
@@ -378,13 +378,15 @@ define void @split-block-before-test(i1 %flag) {
   EXPECT_TRUE(DT.verify());
   LI.verify(DT);
   auto *LoopBB = getBasicBlockByName(*F, "loop");
-  SplitBlock(LoopBB, LoopBB->getFirstInsertionPt(), &DT, &LI,
-              /* MemorySSAUpdater */ nullptr, LoopBB->getName() + ".split",
-              /* Before */ true);
+  auto *New =
+      SplitBlock(LoopBB, LoopBB->getFirstInsertionPt(), &DT, &LI,
+                 /* MemorySSAUpdater */ nullptr, LoopBB->getName() + ".split",
+                 /* Before */ true);
 
   EXPECT_TRUE(DT.verify());
   LLVM_DEBUG(F->dump(); LI.print(dbgs()));
-  EXPECT_DEATH({LI.verify(DT);}, "Assertion.*Loop is unreachable!");
+  LI.verify(DT);
+  EXPECT_EQ(LI.getLoopFor(New)->getHeader(), New);
 }
 #endif
 



More information about the llvm-commits mailing list