[Mlir-commits] [llvm] [mlir] [LoopInfo] Build dominator tree only for irreducible CFG (PR #212098)

Fangrui Song llvmlistbot at llvm.org
Sun Jul 26 14:52:12 PDT 2026


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/212098

>From 2b3ce5ac2897a8614e7a5953896e859616d5d352 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 25 Jul 2026 11:08:14 -0700
Subject: [PATCH 1/3] [LoopInfo] Build dominator tree only for irreducible CFG

analyze() requires a dominator tree, so LoopAnalysis and MachineLoopAnalysis
request one for every function, though only an irreducible CFG queries it.
Clients that build their own, from InlineCost to XRayInstrumentation, need it
for nothing else.

Take the function and a callback returning the tree instead, and call it when
an edge re-enters a loop. Add an analyze(F) overload for a client that holds
no tree.

The number of dominator tree builds does not change in an -O2 pipeline
building sqlite3.bc, where SROA and InstCombine cache one before
LoopAnalysis runs.

Tests that observed the tree through LoopAnalysis now require it explicitly.

MachineLoopInfoWrapperPass keeps requiring one: the legacy pass manager cannot
provide it on demand.

Aided by Claude Opus 5
---
 llvm/include/llvm/CodeGen/MachineLoopInfo.h   |  6 ++++++
 llvm/include/llvm/Support/GenericLoopInfo.h   | 13 +++++++++++-
 .../llvm/Support/GenericLoopInfoImpl.h        | 21 ++++++++++++++++++-
 llvm/lib/Analysis/InlineCost.cpp              | 11 +++++-----
 llvm/lib/Analysis/LoopInfo.cpp                |  5 ++++-
 llvm/lib/CodeGen/MachineLoopInfo.cpp          | 14 ++++++++++++-
 llvm/lib/CodeGen/XRayInstrumentation.cpp      | 17 ++++++++-------
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp |  5 ++---
 llvm/lib/Transforms/IPO/PartialInlining.cpp   |  4 ++--
 .../Instrumentation/InstrProfiling.cpp        |  5 ++---
 .../Instrumentation/PGOInstrumentation.cpp    | 11 +++++-----
 ...ate-branch-lowering-preserve-loop-info.mir |  2 +-
 ...i-pre-emit-peephole-preserve-loop-info.mir |  4 ++--
 .../HLSLControlFlowHint-pass-check.ll         |  2 +-
 .../test/Transforms/SCCP/preserve-analysis.ll |  5 +++--
 15 files changed, 88 insertions(+), 37 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/MachineLoopInfo.h b/llvm/include/llvm/CodeGen/MachineLoopInfo.h
index 7893f70cd353c..8ca01a54532d9 100644
--- a/llvm/include/llvm/CodeGen/MachineLoopInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineLoopInfo.h
@@ -131,6 +131,12 @@ class MachineLoopInfo : public LoopInfoBase<MachineBasicBlock, MachineLoop> {
 
   /// Calculate the natural loop information.
   LLVM_ABI void calculate(MachineDominatorTree &MDT);
+
+  /// Rebuild the loop forest. \p GetDomTree is called only for an irreducible
+  /// CFG.
+  LLVM_ABI void
+  calculate(MachineFunction &MF,
+            function_ref<const DomTreeBase<MachineBasicBlock> &()> GetDomTree);
 };
 
 /// Analysis pass that exposes the \c MachineLoopInfo for a machine function.
diff --git a/llvm/include/llvm/Support/GenericLoopInfo.h b/llvm/include/llvm/Support/GenericLoopInfo.h
index f3ebef97c9e8b..19f725f29d4cc 100644
--- a/llvm/include/llvm/Support/GenericLoopInfo.h
+++ b/llvm/include/llvm/Support/GenericLoopInfo.h
@@ -840,8 +840,19 @@ template <class BlockT, class LoopT> class LoopInfoBase {
     return isNotAlreadyContainedIn(SubLoop->getParentLoop(), ParentLoop);
   }
 
-  /// Create the loop forest using a stable algorithm.
+  /// Create the loop forest for a function. A dominator tree is needed only for
+  /// an irreducible CFG, where dominance reduces a loop that an edge re-enters
+  /// to the natural loop of its header's backedges.
+  ///@{
+  /// Build a dominator tree if one is needed.
+  void analyze(ParentT F);
+  /// Call \p GetDomTree if a dominator tree is needed.
+  void
+  analyze(ParentT F,
+          function_ref<const DominatorTreeBase<BlockT, false> &()> GetDomTree);
+  /// Analyze the function \p DomTree describes.
   void analyze(const DominatorTreeBase<BlockT, false> &DomTree);
+  ///@}
 
   // Debugging
   void print(raw_ostream &OS) const;
diff --git a/llvm/include/llvm/Support/GenericLoopInfoImpl.h b/llvm/include/llvm/Support/GenericLoopInfoImpl.h
index c89bdb7c0f566..4b0cc1b460681 100644
--- a/llvm/include/llvm/Support/GenericLoopInfoImpl.h
+++ b/llvm/include/llvm/Support/GenericLoopInfoImpl.h
@@ -461,12 +461,28 @@ void LoopBase<BlockT, LoopT>::print(raw_ostream &OS, bool Verbose,
 /// program order.
 template <class BlockT, class LoopT>
 void LoopInfoBase<BlockT, LoopT>::analyze(const DomTreeBase<BlockT> &DomTree) {
+  analyze(DomTree.getRootNode()->getBlock()->getParent(),
+          [&]() -> const DomTreeBase<BlockT> & { return DomTree; });
+}
+
+template <class BlockT, class LoopT>
+void LoopInfoBase<BlockT, LoopT>::analyze(ParentT F) {
+  DomTreeBase<BlockT> DomTree;
+  analyze(F, [&]() -> const DomTreeBase<BlockT> & {
+    DomTree.recalculate(*F);
+    return DomTree;
+  });
+}
+
+template <class BlockT, class LoopT>
+void LoopInfoBase<BlockT, LoopT>::analyze(
+    ParentT F, function_ref<const DomTreeBase<BlockT> &()> GetDomTree) {
   using BlockTraits = GraphTraits<BlockT *>;
   auto num = [](const BlockT *BB) {
     return GraphTraits<const BlockT *>::getNumber(BB);
   };
 
-  ParentPtr = DomTree.getRootNode()->getBlock()->getParent();
+  ParentPtr = F;
   BlockNumberEpoch = GraphTraits<ParentT>::getNumberEpoch(ParentPtr);
   unsigned MaxNumber = GraphTraits<ParentT>::getMaxNumber(ParentPtr);
 
@@ -593,6 +609,9 @@ void LoopInfoBase<BlockT, LoopT>::analyze(const DomTreeBase<BlockT> &DomTree) {
     // splice the header out of the chain of every other block.
     for (unsigned H : Reentries)
       Info[H].Pos = IsReentered;
+    const DomTreeBase<BlockT> &DomTree = GetDomTree();
+    assert(DomTree.getRootNode()->getBlock() ==
+           GraphTraits<ParentT>::getEntryNode(ParentPtr));
     DomTree.updateDFSNumbers();
     SmallVector<unsigned, 0> Mark(MaxNumber, NoBlock);
     SmallVector<BlockT *, 8> Worklist;
diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index 67fd5f6495c5a..6cd84376d8a98 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -34,7 +34,6 @@
 #include "llvm/IR/AssemblyAnnotationWriter.h"
 #include "llvm/IR/CallingConv.h"
 #include "llvm/IR/DataLayout.h"
-#include "llvm/IR/Dominators.h"
 #include "llvm/IR/GetElementPtrTypeIterator.h"
 #include "llvm/IR/GlobalAlias.h"
 #include "llvm/IR/InlineAsm.h"
@@ -1083,11 +1082,11 @@ class InlineCostCallAnalyzer final : public CallAnalyzer {
     // movement, require a certain amount of setup, etc. So when optimising for
     // size, we penalise any call sites that perform loops. We do this after all
     // other costs here, so will likely only be dealing with relatively small
-    // functions (and hence DT and LI will hopefully be cheap).
+    // functions (and hence LI will hopefully be cheap).
     auto *Caller = CandidateCall.getFunction();
     if (Caller->hasMinSize()) {
-      DominatorTree DT(F);
-      LoopInfo LI(DT);
+      LoopInfo LI;
+      LI.analyze(&F);
       int NumLoops = 0;
       for (Loop *L : LI) {
         // Ignore loops that will not be executed
@@ -1395,8 +1394,8 @@ class InlineCostFeaturesAnalyzer final : public CallAnalyzer {
   InlineResult finalizeAnalysis() override {
     auto *Caller = CandidateCall.getFunction();
     if (Caller->hasMinSize()) {
-      DominatorTree DT(F);
-      LoopInfo LI(DT);
+      LoopInfo LI;
+      LI.analyze(&F);
       for (Loop *L : LI) {
         // Ignore loops that will not be executed
         if (DeadBlocks.count(L->getHeader()))
diff --git a/llvm/lib/Analysis/LoopInfo.cpp b/llvm/lib/Analysis/LoopInfo.cpp
index f7f577784f762..96faf5b242426 100644
--- a/llvm/lib/Analysis/LoopInfo.cpp
+++ b/llvm/lib/Analysis/LoopInfo.cpp
@@ -1011,7 +1011,10 @@ LoopInfo LoopAnalysis::run(Function &F, FunctionAnalysisManager &AM) {
   // objects. I don't want to add that kind of complexity until the scope of
   // the problem is better understood.
   LoopInfo LI;
-  LI.analyze(AM.getResult<DominatorTreeAnalysis>(F));
+  // The dominator tree is needed only for an irreducible CFG.
+  LI.analyze(&F, [&]() -> const DominatorTree & {
+    return AM.getResult<DominatorTreeAnalysis>(F);
+  });
   return LI;
 }
 
diff --git a/llvm/lib/CodeGen/MachineLoopInfo.cpp b/llvm/lib/CodeGen/MachineLoopInfo.cpp
index 9479b41f57b17..3fb0d043b01a1 100644
--- a/llvm/lib/CodeGen/MachineLoopInfo.cpp
+++ b/llvm/lib/CodeGen/MachineLoopInfo.cpp
@@ -37,7 +37,12 @@ AnalysisKey MachineLoopAnalysis::Key;
 MachineLoopAnalysis::Result
 MachineLoopAnalysis::run(MachineFunction &MF,
                          MachineFunctionAnalysisManager &MFAM) {
-  return MachineLoopInfo(MFAM.getResult<MachineDominatorTreeAnalysis>(MF));
+  MachineLoopInfo LI;
+  // The dominator tree is needed only for an irreducible CFG.
+  LI.calculate(MF, [&]() -> const MachineDominatorTree & {
+    return MFAM.getResult<MachineDominatorTreeAnalysis>(MF);
+  });
+  return LI;
 }
 
 PreservedAnalyses
@@ -80,6 +85,13 @@ void MachineLoopInfo::calculate(MachineDominatorTree &MDT) {
   analyze(MDT);
 }
 
+void MachineLoopInfo::calculate(
+    MachineFunction &MF,
+    function_ref<const DomTreeBase<MachineBasicBlock> &()> GetDomTree) {
+  releaseMemory();
+  analyze(&MF, GetDomTree);
+}
+
 void MachineLoopInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
   AU.setPreservesAll();
   AU.addRequired<MachineDominatorTreeWrapperPass>();
diff --git a/llvm/lib/CodeGen/XRayInstrumentation.cpp b/llvm/lib/CodeGen/XRayInstrumentation.cpp
index ce13abbc2145f..b9cc60820be8c 100644
--- a/llvm/lib/CodeGen/XRayInstrumentation.cpp
+++ b/llvm/lib/CodeGen/XRayInstrumentation.cpp
@@ -228,17 +228,18 @@ bool XRayInstrumentation::run(MachineFunction &MF) {
     bool TooFewInstrs = MICount < XRayThreshold;
 
     if (!IgnoreLoops) {
-      // Get MachineDominatorTree or compute it on the fly if it's unavailable
+      // Get MachineLoopInfo or compute it on the fly if it's unavailable,
+      // which needs a MachineDominatorTree only for an irreducible CFG.
       MachineDominatorTree ComputedMDT;
-      if (!MDT) {
-        ComputedMDT.recalculate(MF);
-        MDT = &ComputedMDT;
-      }
-
-      // Get MachineLoopInfo or compute it on the fly if it's unavailable
       MachineLoopInfo ComputedMLI;
       if (!MLI) {
-        ComputedMLI.analyze(*MDT);
+        ComputedMLI.calculate(MF, [&]() -> const MachineDominatorTree & {
+          if (!MDT) {
+            ComputedMDT.recalculate(MF);
+            MDT = &ComputedMDT;
+          }
+          return *MDT;
+        });
         MLI = &ComputedMLI;
       }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index e5afaa5c63e4b..793c48ceb0f84 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -22,7 +22,6 @@
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Analysis/LoopInfo.h"
-#include "llvm/IR/Dominators.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/IR/InstVisitor.h"
@@ -3590,8 +3589,8 @@ void SPIRVEmitIntrinsicsImpl::emitUnstructuredLoopControls(Function &F,
 
   // For non-shader targets without the Intel extension, emit OpLoopMerge
   // using spv_loop_merge intrinsics, mirroring the structurizer approach.
-  DominatorTree DT(F);
-  LoopInfo LI(DT);
+  LoopInfo LI;
+  LI.analyze(&F);
   if (LI.empty())
     return;
 
diff --git a/llvm/lib/Transforms/IPO/PartialInlining.cpp b/llvm/lib/Transforms/IPO/PartialInlining.cpp
index e01bb9f78fca3..2dfb7411413db 100644
--- a/llvm/lib/Transforms/IPO/PartialInlining.cpp
+++ b/llvm/lib/Transforms/IPO/PartialInlining.cpp
@@ -905,10 +905,10 @@ void PartialInlinerImpl::computeCallsiteToProfCountMap(
   auto ComputeCurrBFI = [&,this](Function *Caller) {
       // For the old pass manager:
       if (!GetBFI) {
-        DominatorTree DT(*Caller);
         CycleInfo CI;
         CI.compute(*Caller);
-        LoopInfo LI(DT);
+        LoopInfo LI;
+        LI.analyze(Caller);
         BranchProbabilityInfo BPI(*Caller, CI);
         TempBFI.reset(new BlockFrequencyInfo(*Caller, BPI, LI));
         CurrentCallerBFI = TempBFI.get();
diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
index ef97dac7a5f0a..fa46d10277ab4 100644
--- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
@@ -33,7 +33,6 @@
 #include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/DiagnosticInfo.h"
-#include "llvm/IR/Dominators.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/GlobalAlias.h"
 #include "llvm/IR/GlobalValue.h"
@@ -973,10 +972,10 @@ void InstrLowerer::promoteCounterLoadStores(Function *F) {
   if (!isCounterPromotionEnabled())
     return;
 
-  DominatorTree DT(*F);
   CycleInfo CI;
   CI.compute(*F);
-  LoopInfo LI(DT);
+  LoopInfo LI;
+  LI.analyze(F);
   DenseMap<Loop *, SmallVector<LoadStorePair, 8>> LoopPromotionCandidates;
 
   std::unique_ptr<BlockFrequencyInfo> BFI;
diff --git a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
index 47f98d98a50e9..92cd9b33f2ef1 100644
--- a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
+++ b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
@@ -74,7 +74,6 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/CycleInfo.h"
 #include "llvm/IR/DiagnosticInfo.h"
-#include "llvm/IR/Dominators.h"
 #include "llvm/IR/EHPersonalities.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/GlobalAlias.h"
@@ -1582,10 +1581,10 @@ void PGOUseFunc::populateCoverage() {
   }
 
   unsigned NumCorruptCoverage = 0;
-  DominatorTree DT(F);
   CycleInfo CI;
   CI.compute(F);
-  LoopInfo LI(DT);
+  LoopInfo LI;
+  LI.analyze(&F);
   BranchProbabilityInfo BPI(F, CI);
   BlockFrequencyInfo BFI(F, BPI, LI);
   auto IsBlockDead = [&](const BasicBlock &BB) -> std::optional<bool> {
@@ -2342,7 +2341,8 @@ static bool annotateAllFunctions(
     if (PGOViewCounts != PGOVCT_None &&
         (ViewBlockFreqFuncName.empty() ||
          F.getName() == ViewBlockFreqFuncName)) {
-      LoopInfo LI{DominatorTree(F)};
+      LoopInfo LI;
+      LI.analyze(&F);
       CycleInfo CI;
       CI.compute(F);
       std::unique_ptr<BranchProbabilityInfo> NewBPI =
@@ -2373,7 +2373,8 @@ static bool annotateAllFunctions(
     if (PGOVerifyBFI || PGOVerifyHotBFI || PGOFixEntryCount) {
       CycleInfo CI;
       CI.compute(F);
-      LoopInfo LI{DominatorTree(F)};
+      LoopInfo LI;
+      LI.analyze(&F);
       BranchProbabilityInfo NBPI(F, CI);
 
       // Fix func entry count.
diff --git a/llvm/test/CodeGen/AMDGPU/si-late-branch-lowering-preserve-loop-info.mir b/llvm/test/CodeGen/AMDGPU/si-late-branch-lowering-preserve-loop-info.mir
index 87925e41c2e71..fa7852e0b93b5 100644
--- a/llvm/test/CodeGen/AMDGPU/si-late-branch-lowering-preserve-loop-info.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-late-branch-lowering-preserve-loop-info.mir
@@ -4,8 +4,8 @@
 # due to early termination handling.
 
 # CHECK: Running analysis: MachineLoopAnalysis on early_term_in_loop
-# CHECK-NEXT: Running analysis: MachineDominatorTreeAnalysis on early_term_in_loop
 # CHECK-NEXT: Running pass: SILateBranchLoweringPass on early_term_in_loop
+# CHECK-NEXT: Running analysis: MachineDominatorTreeAnalysis on early_term_in_loop
 # CHECK-NEXT: Running pass: MachineLoopPrinterPass on early_term_in_loop
 # CHECK-NEXT: Machine loop info for machine function 'early_term_in_loop':
 # CHECK-NOT: Running analysis: MachineLoopAnalysis on early_term_in_loop
diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir
index 12eff8da0e477..89424af721277 100644
--- a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir
@@ -1,7 +1,7 @@
-# RUN: llc -mtriple=amdgpu9.00-amd-amdhsa -passes="require<machine-loops>,si-pre-emit-peephole,print<machine-loops>" -debug-pass-manager -filetype=null %s 2>&1 | FileCheck %s
+# RUN: llc -mtriple=amdgpu9.00-amd-amdhsa -passes="require<machine-dom-tree>,require<machine-loops>,si-pre-emit-peephole,print<machine-loops>" -debug-pass-manager -filetype=null %s 2>&1 | FileCheck %s
 
+# CHECK: Running analysis: MachineDominatorTreeAnalysis on vcc_and_removal_preserves_mli
 # CHECK: Running analysis: MachineLoopAnalysis on vcc_and_removal_preserves_mli
-# CHECK-NEXT: Running analysis: MachineDominatorTreeAnalysis on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Running pass: SIPreEmitPeepholePass on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Invalidating analysis: MachineDominatorTreeAnalysis on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Running pass: MachineLoopPrinterPass on vcc_and_removal_preserves_mli
diff --git a/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll
index 07a31b6c26f52..78c5d6e263087 100644
--- a/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll
+++ b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll
@@ -5,8 +5,8 @@
 ; RUN: opt -passes='spirv-structurizer' -disable-output -debug-pass-manager \
 ; RUN:   -mtriple=spirv-unknown-unknown %s 2>&1 | FileCheck %s --check-prefix=INVALIDATE
 ; INVALIDATE: Running pass: SPIRVStructurizerWrapper on test_branch
-; INVALIDATE: Invalidating analysis: DominatorTreeAnalysis on test_branch
 ; INVALIDATE: Invalidating analysis: LoopAnalysis on test_branch
+; INVALIDATE: Invalidating analysis: DominatorTreeAnalysis on test_branch
 ; INVALIDATE: Invalidating analysis: SPIRVConvergenceRegionAnalysis on test_branch
 
 ; CHECK-LABEL: define spir_func noundef i32 @test_branch
diff --git a/llvm/test/Transforms/SCCP/preserve-analysis.ll b/llvm/test/Transforms/SCCP/preserve-analysis.ll
index 7e61f9ba5fb78..c633eb004d58d 100644
--- a/llvm/test/Transforms/SCCP/preserve-analysis.ll
+++ b/llvm/test/Transforms/SCCP/preserve-analysis.ll
@@ -1,7 +1,8 @@
-; RUN: opt < %s -debug-pass-manager -passes='loop-vectorize,sccp,loop-vectorize' 2>&1 -S | FileCheck --check-prefix=NEW-PM %s
+; RUN: opt < %s -debug-pass-manager -passes='require<domtree>,loop-vectorize,sccp,loop-vectorize,require<domtree>' 2>&1 -S | FileCheck --check-prefix=NEW-PM %s
 
 ; Check that DT is preserved by SCCP by running it between 2
-; loop-vectorize runs.
+; loop-vectorize runs. LoopAnalysis needs a dominator tree only for an
+; irreducible CFG, so the pipeline requires one on either side.
 
 ; NEW-PM-DAG: Running analysis: LoopAnalysis on test
 ; NEW-PM-DAG: Running analysis: DominatorTreeAnalysis on test

>From 54442d971f297b7ddaf209a755654e206de86863 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 26 Jul 2026 11:06:25 -0700
Subject: [PATCH 2/3] fix mlir-opt with explicit instantiation

---
 mlir/lib/IR/Dominance.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mlir/lib/IR/Dominance.cpp b/mlir/lib/IR/Dominance.cpp
index 79fb41f2e6b30..714c01f6f2468 100644
--- a/mlir/lib/IR/Dominance.cpp
+++ b/mlir/lib/IR/Dominance.cpp
@@ -23,6 +23,10 @@ template class llvm::DominatorTreeBase<Block, /*IsPostDom=*/false>;
 template class llvm::DominatorTreeBase<Block, /*IsPostDom=*/true>;
 template class llvm::DomTreeNodeBase<Block>;
 
+template void llvm::DomTreeBuilder::Calculate<
+    llvm::DominatorTreeBase<Block, /*IsPostDom=*/false>>(
+    llvm::DominatorTreeBase<Block, /*IsPostDom=*/false> &DT);
+
 //===----------------------------------------------------------------------===//
 // DominanceInfoBase
 //===----------------------------------------------------------------------===//

>From 4e39a6e65e3bb4e26a027d112532e6d97eafe8ba Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 26 Jul 2026 14:52:00 -0700
Subject: [PATCH 3/3] migrate 2 users; leave the rest to a follow-up cleanup

---
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp    | 19 +++++++++---------
 .../CodeGen/LazyMachineBlockFrequencyInfo.cpp | 20 +++++++++----------
 .../AArch64/arm64-opt-remarks-lazy-bfi.ll     |  1 -
 3 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 11d39fcff0ff7..c3ac38508319f 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2054,19 +2054,20 @@ void AsmPrinter::emitFunctionBody() {
   emitFunctionBodyStart();
 
   if (isVerbose()) {
-    // Get MachineDominatorTree or compute it on the fly if it's unavailable
     MDT = GetMDT(*MF);
-    if (!MDT) {
-      OwnedMDT = std::make_unique<MachineDominatorTree>();
-      OwnedMDT->recalculate(*MF);
-      MDT = OwnedMDT.get();
-    }
-
-    // Get MachineLoopInfo or compute it on the fly if it's unavailable
+    // Get MachineLoopInfo or compute it on the fly if it's unavailable, which
+    // needs a MachineDominatorTree only for an irreducible CFG.
     MLI = GetMLI(*MF);
     if (!MLI) {
       OwnedMLI = std::make_unique<MachineLoopInfo>();
-      OwnedMLI->analyze(*MDT);
+      OwnedMLI->calculate(*MF, [&]() -> const MachineDominatorTree & {
+        if (!MDT) {
+          OwnedMDT = std::make_unique<MachineDominatorTree>();
+          OwnedMDT->recalculate(*MF);
+          MDT = OwnedMDT.get();
+        }
+        return *MDT;
+      });
       MLI = OwnedMLI.get();
     }
   }
diff --git a/llvm/lib/CodeGen/LazyMachineBlockFrequencyInfo.cpp b/llvm/lib/CodeGen/LazyMachineBlockFrequencyInfo.cpp
index 64e906ad8198c..bbec23fca9bdd 100644
--- a/llvm/lib/CodeGen/LazyMachineBlockFrequencyInfo.cpp
+++ b/llvm/lib/CodeGen/LazyMachineBlockFrequencyInfo.cpp
@@ -65,19 +65,19 @@ LazyMachineBlockFrequencyInfoPass::calculateIfNotAvailable() const {
 
   if (!MLI) {
     LLVM_DEBUG(dbgs() << "Building LoopInfo on the fly\n");
-    // First create a dominator tree.
     LLVM_DEBUG(if (MDT) dbgs() << "DominatorTree is available\n");
 
-    if (!MDT) {
-      LLVM_DEBUG(dbgs() << "Building DominatorTree on the fly\n");
-      OwnedMDT = std::make_unique<MachineDominatorTree>();
-      OwnedMDT->recalculate(*MF);
-      MDT = OwnedMDT.get();
-    }
-
-    // Generate LoopInfo from it.
+    // A dominator tree is needed only for an irreducible CFG.
     OwnedMLI = std::make_unique<MachineLoopInfo>();
-    OwnedMLI->analyze(*MDT);
+    OwnedMLI->calculate(*MF, [&]() -> const MachineDominatorTree & {
+      if (!MDT) {
+        LLVM_DEBUG(dbgs() << "Building DominatorTree on the fly\n");
+        OwnedMDT = std::make_unique<MachineDominatorTree>();
+        OwnedMDT->recalculate(*MF);
+        MDT = OwnedMDT.get();
+      }
+      return *MDT;
+    });
     MLI = OwnedMLI.get();
   }
 
diff --git a/llvm/test/CodeGen/AArch64/arm64-opt-remarks-lazy-bfi.ll b/llvm/test/CodeGen/AArch64/arm64-opt-remarks-lazy-bfi.ll
index 6654b58d306eb..a074d1b4dad5f 100644
--- a/llvm/test/CodeGen/AArch64/arm64-opt-remarks-lazy-bfi.ll
+++ b/llvm/test/CodeGen/AArch64/arm64-opt-remarks-lazy-bfi.ll
@@ -66,7 +66,6 @@
 ; HOTNESS-NEXT: Executing Pass 'Machine Optimization Remark Emitter'
 ; HOTNESS-NEXT: Building MachineBlockFrequencyInfo on the fly
 ; HOTNESS-NEXT: Building LoopInfo on the fly
-; HOTNESS-NEXT: Building DominatorTree on the fly
 ; HOTNESS-NOT: Executing Pass
 ; HOTNESS: block-frequency: empty_func
 ; HOTNESS-NOT: Executing Pass



More information about the Mlir-commits mailing list