Skip to content

[IR] Don't allow successors() over block without terminators#186646

Open
aengelke wants to merge 3 commits intollvm:mainfrom
aengelke:no-null-succs
Open

[IR] Don't allow successors() over block without terminators#186646
aengelke wants to merge 3 commits intollvm:mainfrom
aengelke:no-null-succs

Conversation

@aengelke
Copy link
Contributor

There's no point constructing a dominator tree or similar on known-broken IR. Generally, functions should be able to assume that IR is valid (i.e., passes the verifier). Users of this "feature" were:

  • Verifier, fixed by verifying existence of terminators first.
  • FuzzMutate, worked around by temporarily inserting terminators.
  • MergeBlockIntoPredecessor, inadvertently, fixed by adding terminator before updating MemorySSA.
  • Some sloppily written unit tests.

@aengelke aengelke requested a review from nikic March 15, 2026 08:13
@llvmbot llvmbot added llvm:ir llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Mar 15, 2026
@llvmbot
Copy link
Member

llvmbot commented Mar 15, 2026

@llvm/pr-subscribers-flang-openmp
@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-ir

Author: Alexis Engelke (aengelke)

Changes

There's no point constructing a dominator tree or similar on known-broken IR. Generally, functions should be able to assume that IR is valid (i.e., passes the verifier). Users of this "feature" were:

  • Verifier, fixed by verifying existence of terminators first.
  • FuzzMutate, worked around by temporarily inserting terminators.
  • MergeBlockIntoPredecessor, inadvertently, fixed by adding terminator before updating MemorySSA.
  • Some sloppily written unit tests.

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

9 Files Affected:

  • (modified) llvm/include/llvm/IR/CFG.h (+8-8)
  • (modified) llvm/lib/FuzzMutate/RandomIRBuilder.cpp (+14-2)
  • (modified) llvm/lib/IR/Verifier.cpp (+6-3)
  • (modified) llvm/lib/Transforms/Utils/BasicBlockUtils.cpp (+4-2)
  • (added) llvm/test/Transforms/LoopSimplifyCFG/mssa_term.ll (+46)
  • (modified) llvm/unittests/Analysis/BasicAliasAnalysisTest.cpp (+2)
  • (modified) llvm/unittests/Analysis/DomTreeUpdaterTest.cpp (+1)
  • (modified) llvm/unittests/Analysis/MemorySSATest.cpp (+33)
  • (modified) llvm/unittests/Transforms/Utils/SSAUpdaterBulkTest.cpp (+2)
diff --git a/llvm/include/llvm/IR/CFG.h b/llvm/include/llvm/IR/CFG.h
index c0d2218c24322..e8158fdc243b1 100644
--- a/llvm/include/llvm/IR/CFG.h
+++ b/llvm/include/llvm/IR/CFG.h
@@ -141,16 +141,16 @@ using succ_range = iterator_range<succ_iterator>;
 using const_succ_range = iterator_range<const_succ_iterator>;
 
 inline succ_iterator succ_begin(Instruction *I) {
-  return I ? I->successors().begin() : succ_iterator(nullptr);
+  return I->successors().begin();
 }
 inline const_succ_iterator succ_begin(const Instruction *I) {
-  return I ? I->successors().begin() : const_succ_iterator(nullptr);
+  return I->successors().begin();
 }
 inline succ_iterator succ_end(Instruction *I) {
-  return I ? I->successors().end() : succ_iterator(nullptr);
+  return I->successors().end();
 }
 inline const_succ_iterator succ_end(const Instruction *I) {
-  return I ? I->successors().end() : const_succ_iterator(nullptr);
+  return I->successors().end();
 }
 inline bool succ_empty(const Instruction *I) {
   return succ_begin(I) == succ_end(I);
@@ -159,10 +159,10 @@ inline unsigned succ_size(const Instruction *I) {
   return std::distance(succ_begin(I), succ_end(I));
 }
 inline succ_range successors(Instruction *I) {
-  return succ_range(succ_begin(I), succ_end(I));
+  return I->successors();
 }
 inline const_succ_range successors(const Instruction *I) {
-  return const_succ_range(succ_begin(I), succ_end(I));
+  return I->successors();
 }
 
 inline succ_iterator succ_begin(BasicBlock *BB) {
@@ -184,10 +184,10 @@ inline unsigned succ_size(const BasicBlock *BB) {
   return std::distance(succ_begin(BB), succ_end(BB));
 }
 inline succ_range successors(BasicBlock *BB) {
-  return succ_range(succ_begin(BB), succ_end(BB));
+  return successors(BB->getTerminator());
 }
 inline const_succ_range successors(const BasicBlock *BB) {
-  return const_succ_range(succ_begin(BB), succ_end(BB));
+  return successors(BB->getTerminator());
 }
 
 //===--------------------------------------------------------------------===//
diff --git a/llvm/lib/FuzzMutate/RandomIRBuilder.cpp b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp
index 4e4732ae08c1b..b5946b98bb12b 100644
--- a/llvm/lib/FuzzMutate/RandomIRBuilder.cpp
+++ b/llvm/lib/FuzzMutate/RandomIRBuilder.cpp
@@ -21,11 +21,23 @@
 using namespace llvm;
 using namespace fuzzerop;
 
+static DominatorTree getDomTree(Function &F) {
+  // Dominator tree construction requires that all blocks have terminators.
+  SmallVector<Instruction *> AddedInsts;
+  for (BasicBlock &BB : F)
+    if (!BB.getTerminator())
+      AddedInsts.push_back(new UnreachableInst(F.getContext(), &BB));
+  DominatorTree DT(F);
+  for (Instruction *I : AddedInsts)
+    I->eraseFromParent();
+  return DT;
+}
+
 /// Return a vector of Blocks that dominates this block, excluding current
 /// block.
 static std::vector<BasicBlock *> getDominators(BasicBlock *BB) {
   std::vector<BasicBlock *> ret;
-  DominatorTree DT(*BB->getParent());
+  DominatorTree DT = getDomTree(*BB->getParent());
   DomTreeNode *Node = DT.getNode(BB);
   // It's possible that an orphan block is not in the dom tree. In that case we
   // just return nothing.
@@ -43,7 +55,7 @@ static std::vector<BasicBlock *> getDominators(BasicBlock *BB) {
 /// Return a vector of Blocks that is dominated by this block, excluding current
 /// block
 static std::vector<BasicBlock *> getDominatees(BasicBlock *BB) {
-  DominatorTree DT(*BB->getParent());
+  DominatorTree DT = getDomTree(*BB->getParent());
   std::vector<BasicBlock *> ret;
   DomTreeNode *Parent = DT.getNode(BB);
   // It's possible that an orphan block is not in the dom tree. In that case we
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 3cdc75ca9869e..593595f0a831e 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -410,10 +410,9 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
     // pass manager to provide this as it isolates us from a potentially
     // out-of-date dominator tree and makes it significantly more complex to run
     // this code outside of a pass manager.
-    // FIXME: It's really gross that we have to cast away constness here.
-    if (!F.empty())
-      DT.recalculate(const_cast<Function &>(F));
 
+    // First check that every basic block has a terminator, otherwise we can't
+    // even inspect the CFG.
     for (const BasicBlock &BB : F) {
       if (!BB.empty() && BB.back().isTerminator())
         continue;
@@ -427,6 +426,10 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
       return false;
     }
 
+    // FIXME: It's really gross that we have to cast away constness here.
+    if (!F.empty())
+      DT.recalculate(const_cast<Function &>(F));
+
     auto FailureCB = [this](const Twine &Message) {
       this->CheckFailed(Message);
     };
diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index f8e0ad2b7b661..17aa7cc185b66 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -346,6 +346,8 @@ bool llvm::MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU,
   if (PredecessorWithTwoSuccessors) {
     // Delete the unconditional branch from BB.
     BB->back().eraseFromParent();
+    // Add unreachable to now empty BB.
+    new UnreachableInst(BB->getContext(), BB);
 
     // Update branch in the predecessor.
     PredBB_BI->setSuccessor(FallThruPath, NewSucc);
@@ -355,6 +357,8 @@ bool llvm::MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU,
 
     // Move terminator instruction.
     BB->back().moveBeforePreserving(*PredBB, PredBB->end());
+    // Add unreachable to now empty BB.
+    new UnreachableInst(BB->getContext(), BB);
 
     // Terminator may be a memory accessing instruction too.
     if (MSSAU)
@@ -362,8 +366,6 @@ bool llvm::MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU,
               MSSAU->getMemorySSA()->getMemoryAccess(PredBB->getTerminator())))
         MSSAU->moveToPlace(MUD, PredBB, MemorySSA::End);
   }
-  // Add unreachable to now empty BB.
-  new UnreachableInst(BB->getContext(), BB);
 
   // Inherit predecessors name if it exists.
   if (!PredBB->hasName())
diff --git a/llvm/test/Transforms/LoopSimplifyCFG/mssa_term.ll b/llvm/test/Transforms/LoopSimplifyCFG/mssa_term.ll
new file mode 100644
index 0000000000000..a2936727ea6ad
--- /dev/null
+++ b/llvm/test/Transforms/LoopSimplifyCFG/mssa_term.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes="loop-mssa(loop-simplifycfg,simple-loop-unswitch)" < %s | FileCheck %s
+
+; Check that IR is valid when MemorySSA is updated during MergeBlockIntoPredecessor.
+
+define i32 @f1(i1 %cond) personality ptr null {
+; CHECK-LABEL: define i32 @f1(
+; CHECK-SAME: i1 [[COND:%.*]]) personality ptr null {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[ENTRY_SPLIT:.*]], label %[[COMMON_RET_LOOPEXIT:.*]]
+; CHECK:       [[ENTRY_SPLIT]]:
+; CHECK-NEXT:    br label %[[FOR_COND:.*]]
+; CHECK:       [[FOR_COND]]:
+; CHECK-NEXT:    [[CALL26:%.*]] = invoke i32 @f2(ptr null, ptr null, ptr null)
+; CHECK-NEXT:            to label %[[FOR_COND]] unwind label %[[LPAD24:.*]]
+; CHECK:       [[COMMON_RET_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[COMMON_RET:.*]]
+; CHECK:       [[COMMON_RET]]:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ 0, %[[LPAD24]] ], [ 0, %[[COMMON_RET_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       [[LPAD24]]:
+; CHECK-NEXT:    [[LPAD:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT:            cleanup
+; CHECK-NEXT:    br label %[[COMMON_RET]]
+;
+entry:
+  br label %for.cond
+
+for.cond:                                         ; preds = %if.end19, %entry
+  br i1 %cond, label %if.end19, label %common.ret
+
+common.ret:                                       ; preds = %lpad24, %for.cond
+  %common.ret.op = phi i32 [ 0, %lpad24 ], [ 0, %for.cond ]
+  ret i32 %common.ret.op
+
+if.end19:                                         ; preds = %for.cond
+  %call26 = invoke i32 @f2(ptr null, ptr null, ptr null)
+  to label %for.cond unwind label %lpad24
+
+lpad24:                                           ; preds = %if.end19
+  %lpad = landingpad { ptr, i32 }
+  cleanup
+  br label %common.ret
+}
+
+declare i32 @f2(ptr, ptr, ptr)
diff --git a/llvm/unittests/Analysis/BasicAliasAnalysisTest.cpp b/llvm/unittests/Analysis/BasicAliasAnalysisTest.cpp
index bae1f1c508af3..0541f257b20d2 100644
--- a/llvm/unittests/Analysis/BasicAliasAnalysisTest.cpp
+++ b/llvm/unittests/Analysis/BasicAliasAnalysisTest.cpp
@@ -79,6 +79,7 @@ TEST_F(BasicAATest, AliasInstWithObjectOfImpreciseSize) {
 
   BasicBlock *Entry(BasicBlock::Create(C, "", F));
   B.SetInsertPoint(Entry);
+  B.CreateRetVoid();
 
   Value *IncomingI32Ptr = F->arg_begin();
 
@@ -119,6 +120,7 @@ TEST_F(BasicAATest, AliasInstWithFullObjectOfImpreciseSize) {
   AllocaInst *I8 = B.CreateAlloca(B.getInt8Ty(), B.getInt32(2));
   auto *I8AtUncertainOffset =
       cast<GetElementPtrInst>(B.CreatePtrAdd(I8, ArbitraryI32));
+  B.CreateRetVoid();
 
   auto &AllAnalyses = setupAnalyses();
   BasicAAResult &BasicAA = AllAnalyses.BAA;
diff --git a/llvm/unittests/Analysis/DomTreeUpdaterTest.cpp b/llvm/unittests/Analysis/DomTreeUpdaterTest.cpp
index 1a8160f14a0a8..4fe3e701d9f83 100644
--- a/llvm/unittests/Analysis/DomTreeUpdaterTest.cpp
+++ b/llvm/unittests/Analysis/DomTreeUpdaterTest.cpp
@@ -776,6 +776,7 @@ TEST(DomTreeUpdater, LazyUpdateDeduplicationTest) {
   // CFG Change: remove bb0 -> bb1.
   EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
   BB0->getTerminator()->eraseFromParent();
+  new UnreachableInst(Context, BB0);
 
   // Update the DTU and simulate invalid updates.
   DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB0, BB1},
diff --git a/llvm/unittests/Analysis/MemorySSATest.cpp b/llvm/unittests/Analysis/MemorySSATest.cpp
index a2e4f99a07d22..38c2e41776ed8 100644
--- a/llvm/unittests/Analysis/MemorySSATest.cpp
+++ b/llvm/unittests/Analysis/MemorySSATest.cpp
@@ -91,6 +91,7 @@ TEST_F(MemorySSATest, CreateALoad) {
   B.CreateStore(B.getInt8(16), PointerArg);
   UncondBrInst::Create(Merge, Left);
   UncondBrInst::Create(Merge, Right);
+  ReturnInst::Create(C, Merge);
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -129,6 +130,7 @@ TEST_F(MemorySSATest, CreateLoadsAndStoreUpdater) {
   B.CreateBr(Merge);
   B.SetInsertPoint(Right);
   B.CreateBr(Merge);
+  ReturnInst::Create(C, Merge);
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -218,6 +220,7 @@ TEST_F(MemorySSATest, CreateALoadUpdater) {
   B.CreateBr(Merge);
   B.SetInsertPoint(Right);
   B.CreateBr(Merge);
+  ReturnInst::Create(C, Merge);
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -261,6 +264,7 @@ TEST_F(MemorySSATest, SinkLoad) {
   B.CreateBr(Merge);
   B.SetInsertPoint(Right);
   B.CreateBr(Merge);
+  ReturnInst::Create(C, Merge);
 
   // Load in left block
   B.SetInsertPoint(Left, Left->begin());
@@ -312,6 +316,8 @@ TEST_F(MemorySSATest, MoveAStore) {
   UncondBrInst::Create(Merge, Right);
   B.SetInsertPoint(Merge);
   B.CreateLoad(B.getInt8Ty(), PointerArg);
+  B.CreateRetVoid();
+
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
   MemorySSAUpdater Updater(&MSSA);
@@ -347,6 +353,8 @@ TEST_F(MemorySSATest, MoveAStoreUpdater) {
   UncondBrInst::Create(Merge, Right);
   B.SetInsertPoint(Merge);
   auto *MergeLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);
+  B.CreateRetVoid();
+
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
   MemorySSAUpdater Updater(&MSSA);
@@ -392,6 +400,8 @@ TEST_F(MemorySSATest, MoveAStoreUpdaterMove) {
   UncondBrInst::Create(Merge, Right);
   B.SetInsertPoint(Merge);
   auto *MergeLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);
+  B.CreateRetVoid();
+
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
   MemorySSAUpdater Updater(&MSSA);
@@ -435,6 +445,8 @@ TEST_F(MemorySSATest, MoveAStoreAllAround) {
   UncondBrInst::Create(Merge, Right);
   B.SetInsertPoint(Merge);
   auto *MergeLoad = B.CreateLoad(B.getInt8Ty(), PointerArg);
+  B.CreateRetVoid();
+
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
   MemorySSAUpdater Updater(&MSSA);
@@ -488,6 +500,7 @@ TEST_F(MemorySSATest, RemoveAPhi) {
   UncondBrInst::Create(Merge, Right);
   B.SetInsertPoint(Merge);
   LoadInst *LoadInst = B.CreateLoad(B.getInt8Ty(), PointerArg);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -532,6 +545,7 @@ TEST_F(MemorySSATest, RemoveMemoryAccess) {
   UncondBrInst::Create(Merge, Right);
   B.SetInsertPoint(Merge);
   LoadInst *LoadInst = B.CreateLoad(B.getInt8Ty(), PointerArg);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -598,6 +612,7 @@ TEST_F(MemorySSATest, TestTripleStore) {
   StoreInst *S1 = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);
   StoreInst *S2 = B.CreateStore(ConstantInt::get(Int8, 1), Alloca);
   StoreInst *S3 = B.CreateStore(ConstantInt::get(Int8, 2), Alloca);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -628,6 +643,7 @@ TEST_F(MemorySSATest, TestStoreAndLoad) {
   Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");
   Instruction *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);
   Instruction *LI = B.CreateLoad(Int8, Alloca);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -657,6 +673,7 @@ TEST_F(MemorySSATest, TestStoreDoubleQuery) {
   Type *Int8 = Type::getInt8Ty(C);
   Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A");
   StoreInst *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -721,6 +738,7 @@ TEST_F(MemorySSATest, PartialWalkerCacheWithPhis) {
 
   B.SetInsertPoint(IfEnd);
   Instruction *BelowPhi = B.CreateStore(Zero, AllocA);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -768,6 +786,7 @@ TEST_F(MemorySSATest, WalkerInvariantLoadOpt) {
 
   Instruction *Store = B.CreateStore(One, AllocA);
   Instruction *Load = B.CreateLoad(Int8, AllocA);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -797,6 +816,7 @@ TEST_F(MemorySSATest, WalkerReopt) {
   Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");
   Instruction *SIB = B.CreateStore(ConstantInt::get(Int8, 0), AllocaB);
   Instruction *LIA = B.CreateLoad(Int8, AllocaA);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -835,6 +855,7 @@ TEST_F(MemorySSATest, MoveAboveMemoryDef) {
   StoreInst *StoreC = B.CreateStore(ConstantInt::get(Int8, 4), C);
   StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 4), A);
   LoadInst *LoadC = B.CreateLoad(Int8, C);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -891,6 +912,7 @@ TEST_F(MemorySSATest, Irreducible) {
   B.SetInsertPoint(LoopMainBB);
   B.CreateCondBr(B.getTrue(), LoopStartBB, AfterLoopBB);
   B.SetInsertPoint(AfterLoopBB);
+  B.CreateRetVoid();
   Argument *FirstArg = &*F->arg_begin();
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -923,6 +945,7 @@ TEST_F(MemorySSATest, MoveToBeforeLiveOnEntryInvalidatesCache) {
   Value *A = B.CreateAlloca(B.getInt8Ty());
   StoreInst *StoreA = B.CreateStore(B.getInt8(0), A);
   StoreInst *StoreB = B.CreateStore(B.getInt8(0), A);
+  B.CreateRetVoid();
 
   setupAnalyses();
 
@@ -969,6 +992,7 @@ TEST_F(MemorySSATest, RemovingDefInvalidatesCache) {
   StoreInst *StoreX1 = B.CreateStore(B.getInt8(0), X);
   StoreInst *StoreY = B.CreateStore(B.getInt8(0), Y);
   StoreInst *StoreX2 = B.CreateStore(B.getInt8(0), X);
+  B.CreateRetVoid();
 
   setupAnalyses();
 
@@ -1005,6 +1029,7 @@ TEST_F(MemorySSATest, TestStoreMustAlias) {
   StoreInst *SB2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaB);
   StoreInst *SA3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaA);
   StoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaB);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -1056,6 +1081,7 @@ TEST_F(MemorySSATest, TestStoreMayAlias) {
   StoreInst *SC2 = B.CreateStore(ConstantInt::get(Int8, 5), AllocaC);
   // Store into arg2, must alias store to arg2 => must
   StoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 6), PointerB);
+  B.CreateRetVoid();
   std::initializer_list<StoreInst *> Sts = {SA1, SB1, SC1, SA2, SB2, SC2, SB3};
 
   setupAnalyses();
@@ -1126,6 +1152,7 @@ TEST_F(MemorySSATest, LifetimeMarkersAreClobbers) {
   Instruction *FooStore = B.CreateStore(B.getInt8(0), Foo);
   Instruction *BarStore = B.CreateStore(B.getInt8(0), Bar);
   Instruction *BazMemSet = B.CreateMemSet(Baz, B.getInt8(0), 1, Align(1));
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -1236,6 +1263,7 @@ TEST_F(MemorySSATest, TestOptimizedDefsAreProperUses) {
   StoreInst *StoreA = B.CreateStore(ConstantInt::get(Int8, 0), AllocA);
   StoreInst *StoreB = B.CreateStore(ConstantInt::get(Int8, 1), AllocB);
   StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocA);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -1319,6 +1347,7 @@ TEST_F(MemorySSATest, TestAddedEdgeToBlockWithPhiNotOpt) {
   UncondBrInst::Create(Exit, Body);
   B.SetInsertPoint(Exit);
   StoreInst *S1 = B.CreateStore(B.getInt8(16), PointerArg);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -1380,6 +1409,7 @@ TEST_F(MemorySSATest, TestAddedEdgeToBlockWithPhiOpt) {
 
   B.SetInsertPoint(Exit);
   StoreInst *S2 = B.CreateStore(B.getInt8(16), PointerArg);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -1452,6 +1482,7 @@ TEST_F(MemorySSATest, TestAddedEdgeToBlockWithNoPhiAddNewPhis) {
   B.SetInsertPoint(FBlock);
   B.CreateStore(B.getInt8(16), PointerArg);
   UncondBrInst::Create(EBlock, FBlock);
+  ReturnInst::Create(C, EBlock);
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -1486,6 +1517,7 @@ TEST_F(MemorySSATest, TestCallClobber) {
   Instruction *StorePointer1 = B.CreateStore(B.getInt8(0), Pointer1);
   Instruction *StorePointer2 = B.CreateStore(B.getInt8(0), Pointer2);
   Instruction *MemSet = B.CreateMemSet(Pointer2, B.getInt8(0), 1, Align(1));
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
@@ -1519,6 +1551,7 @@ TEST_F(MemorySSATest, TestLoadClobber) {
       B.CreateLoad(B.getInt8Ty(), Pointer1, /* Volatile */ true);
   Instruction *LoadPointer2 =
       B.CreateLoad(B.getInt8Ty(), Pointer2, /* Volatile */ true);
+  B.CreateRetVoid();
 
   setupAnalyses();
   MemorySSA &MSSA = *Analyses->MSSA;
diff --git a/llvm/unittests/Transforms/Utils/SSAUpdaterBulkTest.cpp b/llvm/unittests/Transforms/Utils/SSAUpdaterBulkTest.cpp
index 503d3a0f9bfa9..0c7720e9c4f1f 100644
--- a/llvm/unittests/Transforms/Utils/SSAUpdaterBulkTest.cpp
+++ b/llvm/unittests/Transforms/Utils/SSAUpdaterBulkTest.cpp
@@ -44,6 +44,7 @@ TEST(SSAUpdaterBulk, SimpleMerge) {
   //     %6 = add i32 %3, 6
   //     %7 = add i32 %2, %4
   //     %8 = sub i32 %2, %4
+  //     ret void
   Argument *FirstArg = &*(F->arg_begin());
   BasicBlock *IfBB = BasicBlock::Create(C, "if", F);
   BasicBlock *TrueBB = BasicBlock::Create(C, "true", F);
@@ -68,6 +69,7 @@ TEST(SSAUpdaterBulk, SimpleMerge) {
   auto *I2 = cast<Instruction>(B.CreateAdd(AddOp2, ConstantInt::get(I32Ty, 6)));
   auto *I3 = cast<Instruction>(B.CreateAdd(SubOp1, SubOp2));
   auto *I4 = cast<Instruction>(B.CreateSub(SubOp1, SubOp2));
+  B.CreateRetVoid();
 
   // Now rewrite uses in instructions %5, %6, %7. They need to use a phi, which
   // SSAUpdater should insert into %merge.

@github-actions
Copy link

github-actions bot commented Mar 15, 2026

✅ With the latest revision this PR passed the C/C++ code formatter.

@nikic
Copy link
Contributor

nikic commented Mar 15, 2026

Looks like there are some Clang/MLIR test failures related to OpenMP.

@aengelke
Copy link
Contributor Author

sigh. I know ~nothing how this OpenMP code works and don't care enough to fix it at this point... Polly is also affected.

@nikic
Copy link
Contributor

nikic commented Mar 15, 2026

Something we could do is to move the null check to GraphTraits<BasicBlock*> instead -- I assume that's the one that's relevant for DT construction. That at least limits the scope of this null special case.

Though I think we should land the compatibility fixes in any case.

@llvmbot llvmbot added flang:openmp clang:openmp OpenMP related changes to Clang labels Mar 15, 2026
@aengelke
Copy link
Contributor Author

aengelke commented Mar 15, 2026

So the OpenMP IR builder does sketchy things also on other occasions and works around limitations by temporarily adding unreachable instructions. Can't hurt to do this three more times... same for Polly.

The c-t impact is 0.10% stage2-O3 -- more than I expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:openmp OpenMP related changes to Clang flang:openmp llvm:analysis Includes value tracking, cost tables and constant folding llvm:ir llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants