[Mlir-commits] [llvm] [mlir] [LoopInfo] Store blocks using Euler tour representation (PR #211485)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Jul 23 01:59:27 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-support
@llvm/pr-subscribers-mlir
@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-bolt

@llvm/pr-subscribers-llvm-analysis

Author: Fangrui Song (MaskRay)

<details>
<summary>Changes</summary>

Block list construction appends each block to all enclosing loops
(O(depth) per block), and each loop owns a separately allocated vector.

Switch to an Euler tour representation: one BlockLayout array per
LoopInfo, each loop's list a [begin, end) slice of it, subloop slices
nested inside their parent's.

Mutations first copy a borrowed slice into private storage from
LoopInfo's allocator. `contains(BlockT *)` remains map-based: a
materialized loop's slice is not a membership test.

Previously a loop's list was the function's reverse postorder restricted
to its members, so a subloop's blocks were interleaved with the parent's
own blocks at their RPO positions. Now a subloop's blocks are contiguous
after its header within every ancestor's list. Headers remain first;
`SubLoops`/`TopLevelLoops` orders are unchanged. A few tests observe the
order and are updated.

AllocateLoop() no longer forwards constructor arguments; analyze() uses
allocateLoop(Header), removing the header-taking loop constructors.

Aided by Claude Fable 5


---

Patch is 30.04 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/211485.diff


11 Files Affected:

- (modified) bolt/include/bolt/Core/BinaryLoop.h (-5) 
- (modified) bolt/test/X86/loop-nest.test (+1-1) 
- (modified) llvm/include/llvm/Analysis/LoopInfo.h (-1) 
- (modified) llvm/include/llvm/CodeGen/MachineLoopInfo.h (-3) 
- (modified) llvm/include/llvm/Support/GenericLoopInfo.h (+92-34) 
- (modified) llvm/include/llvm/Support/GenericLoopInfoImpl.h (+74-79) 
- (modified) llvm/test/Transforms/LoopVectorize/early_exit_with_outer_loop.ll (+2-2) 
- (modified) llvm/test/Transforms/SimpleLoopUnswitch/nontrivial-unswitch-freeze.ll (+13-13) 
- (modified) llvm/test/Transforms/SimpleLoopUnswitch/nontrivial-unswitch.ll (+3-3) 
- (modified) mlir/include/mlir/Analysis/CFGLoopInfo.h (+1-1) 
- (modified) mlir/lib/Analysis/CFGLoopInfo.cpp (-3) 


``````````diff
diff --git a/bolt/include/bolt/Core/BinaryLoop.h b/bolt/include/bolt/Core/BinaryLoop.h
index b425c75715d8b..1c52a051a84cc 100644
--- a/bolt/include/bolt/Core/BinaryLoop.h
+++ b/bolt/include/bolt/Core/BinaryLoop.h
@@ -36,11 +36,6 @@ class BinaryLoop : public LoopBase<BinaryBasicBlock, BinaryLoop> {
   uint64_t ExitCount{0};
 
   // Most of the public interface is provided by LoopBase.
-
-protected:
-  friend class LoopInfoBase<BinaryBasicBlock, BinaryLoop>;
-  explicit BinaryLoop(BinaryBasicBlock *BB)
-      : LoopBase<BinaryBasicBlock, BinaryLoop>(BB) {}
 };
 
 class BinaryLoopInfo : public LoopInfoBase<BinaryBasicBlock, BinaryLoop> {
diff --git a/bolt/test/X86/loop-nest.test b/bolt/test/X86/loop-nest.test
index 51c8fcdb32eaa..208cc9ebdfaaf 100644
--- a/bolt/test/X86/loop-nest.test
+++ b/bolt/test/X86/loop-nest.test
@@ -18,6 +18,6 @@ CHECK-NEXT: Loop basic blocks: .Ltmp[[#MAIN_OUTER_HDR]], .Ltmp[[#]]
 CHECK:      Binary Function "foo" after building cfg
 CHECK:      Loop Info for Function "foo"
 CHECK:      Outer loop header: .Ltmp[[#FOO_OUTER_HDR:]]
-CHECK-NEXT: Loop basic blocks: .Ltmp[[#FOO_OUTER_HDR]], .Ltmp[[#]], .LFT[[#]], .Ltmp[[#]], .Ltmp[[#FOO_INNER_HDR:]], .Ltmp[[#]], .Ltmp[[#FOO_INNER_BB:]]
+CHECK-NEXT: Loop basic blocks: .Ltmp[[#FOO_OUTER_HDR]], .Ltmp[[#]], .LFT[[#]], .Ltmp[[#]], .Ltmp[[#FOO_INNER_HDR:]], .Ltmp[[#FOO_INNER_BB:]], .Ltmp[[#]]
 CHECK:      Nested loop header: .Ltmp[[#FOO_INNER_HDR]]
 CHECK-NEXT: Loop basic blocks: .Ltmp[[#FOO_INNER_HDR]], .Ltmp[[#FOO_INNER_BB]]
diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h
index 13b404505b5e2..15c22e43c44bf 100644
--- a/llvm/include/llvm/Analysis/LoopInfo.h
+++ b/llvm/include/llvm/Analysis/LoopInfo.h
@@ -417,7 +417,6 @@ class LLVM_ABI Loop : public LoopBase<BasicBlock, Loop> {
 
   friend class LoopInfoBase<BasicBlock, Loop>;
   friend class LoopBase<BasicBlock, Loop>;
-  explicit Loop(BasicBlock *BB) : LoopBase<BasicBlock, Loop>(BB) {}
   ~Loop() = default;
 };
 
diff --git a/llvm/include/llvm/CodeGen/MachineLoopInfo.h b/llvm/include/llvm/CodeGen/MachineLoopInfo.h
index 60b3f6c6a3ba5..7893f70cd353c 100644
--- a/llvm/include/llvm/CodeGen/MachineLoopInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineLoopInfo.h
@@ -96,9 +96,6 @@ class MachineLoop : public LoopBase<MachineBasicBlock, MachineLoop> {
   /// Returns true if the given physreg has no defs inside the loop.
   bool isLoopInvariantImplicitPhysReg(Register Reg) const;
 
-  explicit MachineLoop(MachineBasicBlock *MBB)
-    : LoopBase<MachineBasicBlock, MachineLoop>(MBB) {}
-
   MachineLoop() = default;
 };
 
diff --git a/llvm/include/llvm/Support/GenericLoopInfo.h b/llvm/include/llvm/Support/GenericLoopInfo.h
index 0bdfce7dc889e..cda6577deb31a 100644
--- a/llvm/include/llvm/Support/GenericLoopInfo.h
+++ b/llvm/include/llvm/Support/GenericLoopInfo.h
@@ -51,7 +51,6 @@ namespace llvm {
 
 template <class N, class M> class LoopInfoBase;
 template <class N, class M> class LoopBase;
-template <class N, class M> class PopulateLoopsDFS;
 
 //===----------------------------------------------------------------------===//
 /// Instances of this class are used to represent loops that are detected in the
@@ -62,8 +61,12 @@ template <class BlockT, class LoopT> class LoopBase {
   // Loops contained entirely within this one.
   std::vector<LoopT *> SubLoops;
 
-  // The list of blocks in this loop. First entry is the header node.
-  std::vector<BlockT *> Blocks;
+  // The list of blocks in this loop; first entry is the header. Either borrows
+  // from the owning LoopInfo's BlockLayout with BlockCapacity == BlockLen, or
+  // is a private allocation of BlockCapacity slots from its allocator.
+  BlockT **BlockData = nullptr;
+  unsigned BlockLen = 0;
+  unsigned BlockCapacity = 0;
 
   // The LoopInfo that owns this loop. Used to answer contains(BlockT *) from
   // the central block-to-loop map.
@@ -186,7 +189,7 @@ template <class BlockT, class LoopT> class LoopBase {
   /// Get a list of the basic blocks which make up this loop.
   ArrayRef<BlockT *> getBlocks() const {
     assert(!isInvalid() && "Loop not in a valid state!");
-    return Blocks;
+    return ArrayRef<BlockT *>(BlockData, BlockLen);
   }
   using block_iterator = typename ArrayRef<BlockT *>::const_iterator;
   block_iterator block_begin() const { return getBlocks().begin(); }
@@ -200,7 +203,7 @@ template <class BlockT, class LoopT> class LoopBase {
   /// Invalidate the loop, indicating that it is no longer a loop.
   unsigned getNumBlocks() const {
     assert(!isInvalid() && "Loop not in a valid state!");
-    return Blocks.size();
+    return BlockLen;
   }
 
   /// Return true if this loop is no longer valid.  The only valid use of this
@@ -400,19 +403,17 @@ template <class BlockT, class LoopT> class LoopBase {
   /// transformations should use addBasicBlockToLoop.
   void addBlockEntry(BlockT *BB) {
     assert(!isInvalid() && "Loop not in a valid state!");
-    Blocks.push_back(BB);
-  }
-
-  /// interface to reverse Blocks[from, end of loop] in this loop
-  void reverseBlock(unsigned from) {
-    assert(!isInvalid() && "Loop not in a valid state!");
-    std::reverse(Blocks.begin() + from, Blocks.end());
+    if (BlockLen == BlockCapacity)
+      LI->reallocBlocks(*static_cast<LoopT *>(this),
+                        std::max(2 * BlockLen, 4u));
+    BlockData[BlockLen++] = BB;
   }
 
   /// interface to do reserve() for Blocks
-  void reserveBlocks(unsigned size) {
+  void reserveBlocks(unsigned Size) {
     assert(!isInvalid() && "Loop not in a valid state!");
-    Blocks.reserve(size);
+    if (BlockCapacity < Size)
+      LI->reallocBlocks(*static_cast<LoopT *>(this), Size);
   }
 
   /// interface to do reserve() for SubLoops
@@ -421,21 +422,18 @@ template <class BlockT, class LoopT> class LoopBase {
     SubLoops.reserve(Size);
   }
 
-  /// Capacity of the block list; input to an enclosing loop's reserveBlocks()
-  /// during construction, when this loop's list is not yet fully populated.
-  unsigned getBlocksCapacity() const { return Blocks.capacity(); }
-
   /// This method is used to move BB (which must be part of this loop) to be the
   /// loop header of the loop (the block that dominates all others).
   void moveToHeader(BlockT *BB) {
     assert(!isInvalid() && "Loop not in a valid state!");
-    if (Blocks[0] == BB)
+    if (BlockData[0] == BB)
       return;
+    LI->materializeBlocks(*static_cast<LoopT *>(this));
     for (unsigned i = 0;; ++i) {
-      assert(i != Blocks.size() && "Loop does not contain BB!");
-      if (Blocks[i] == BB) {
-        Blocks[i] = Blocks[0];
-        Blocks[0] = BB;
+      assert(i != BlockLen && "Loop does not contain BB!");
+      if (BlockData[i] == BB) {
+        BlockData[i] = BlockData[0];
+        BlockData[0] = BB;
         return;
       }
     }
@@ -446,9 +444,12 @@ template <class BlockT, class LoopT> class LoopBase {
   /// class.
   void removeBlockFromLoop(BlockT *BB) {
     assert(!isInvalid() && "Loop not in a valid state!");
-    auto I = find(Blocks, BB);
+    LI->materializeBlocks(*static_cast<LoopT *>(this));
+    MutableArrayRef<BlockT *> Blocks(BlockData, BlockLen);
+    auto *I = llvm::find(Blocks, BB);
     assert(I != Blocks.end() && "N is not in this list!");
-    Blocks.erase(I);
+    std::move(I + 1, Blocks.end(), I);
+    --BlockLen;
   }
 
   /// Verify loop structure
@@ -469,15 +470,10 @@ template <class BlockT, class LoopT> class LoopBase {
 
 protected:
   friend class LoopInfoBase<BlockT, LoopT>;
-  friend class PopulateLoopsDFS<BlockT, LoopT>;
 
   /// This creates an empty loop.
   LoopBase() : ParentLoop(nullptr) {}
 
-  explicit LoopBase(BlockT *BB) : ParentLoop(nullptr) {
-    Blocks.push_back(BB);
-  }
-
   // Since loop passes like SCEV are allowed to key analysis results off of
   // `Loop` pointers, we cannot re-use pointers within a loop pass manager.
   // This means loop passes should not be `delete` ing `Loop` objects directly
@@ -495,7 +491,10 @@ template <class BlockT, class LoopT> class LoopBase {
     IsInvalid = true;
 #endif
     SubLoops.clear();
-    Blocks.clear();
+    // The block storage is reclaimed by the owning LoopInfo.
+    BlockData = nullptr;
+    BlockLen = 0;
+    BlockCapacity = 0;
     ParentLoop = nullptr;
   }
 };
@@ -525,6 +524,12 @@ template <class BlockT, class LoopT> class LoopInfoBase {
   unsigned BlockNumberEpoch;
 
   std::vector<LoopT *> TopLevelLoops;
+
+  // Shared reverse postorder layout of all blocks. Each initial loop is a slice
+  // of this array, subloop slices nested inside their parent's.
+  std::unique_ptr<BlockT *[]> BlockLayout;
+  unsigned BlockLayoutLen = 0;
+
   BumpPtrAllocator LoopAllocator;
 
   friend class LoopBase<BlockT, LoopT>;
@@ -540,9 +545,11 @@ template <class BlockT, class LoopT> class LoopInfoBase {
   LoopInfoBase(LoopInfoBase &&Arg)
       : BBMap(std::move(Arg.BBMap)),
         TopLevelLoops(std::move(Arg.TopLevelLoops)),
+        BlockLayout(std::move(Arg.BlockLayout)),
         LoopAllocator(std::move(Arg.LoopAllocator)) {
     ParentPtr = Arg.ParentPtr;
     BlockNumberEpoch = Arg.BlockNumberEpoch;
+    BlockLayoutLen = Arg.BlockLayoutLen;
     resetLoopInfoOwners();
     // We have to clear the arguments top level loops as we've taken ownership.
     Arg.TopLevelLoops.clear();
@@ -556,9 +563,12 @@ template <class BlockT, class LoopT> class LoopInfoBase {
       L->~LoopT();
 
     TopLevelLoops = std::move(RHS.TopLevelLoops);
+    BlockLayout = std::move(RHS.BlockLayout);
+    BlockLayoutLen = RHS.BlockLayoutLen;
     LoopAllocator = std::move(RHS.LoopAllocator);
     resetLoopInfoOwners();
     RHS.TopLevelLoops.clear();
+    RHS.BlockLayoutLen = 0;
     return *this;
   }
 
@@ -568,12 +578,14 @@ template <class BlockT, class LoopT> class LoopInfoBase {
     for (auto *L : TopLevelLoops)
       L->~LoopT();
     TopLevelLoops.clear();
+    BlockLayout.reset();
+    BlockLayoutLen = 0;
     LoopAllocator.Reset();
   }
 
-  template <typename... ArgsTy> LoopT *AllocateLoop(ArgsTy &&...Args) {
+  LoopT *AllocateLoop() {
     LoopT *Storage = LoopAllocator.Allocate<LoopT>();
-    LoopT *L = new (Storage) LoopT(std::forward<ArgsTy>(Args)...);
+    LoopT *L = new (Storage) LoopT();
     L->LI = this;
     return L;
   }
@@ -636,6 +648,49 @@ template <class BlockT, class LoopT> class LoopInfoBase {
     return Number < BBMap.size() ? BBMap[Number] : nullptr;
   }
 
+  /// AllocateLoop for analyze(): stash \p Header (see pendingHeader).
+  /// getHeader() only works once the layout carve has replaced the stash with
+  /// the loop's block list.
+  LoopT *allocateLoop(BlockT *Header) {
+    LoopT *L = AllocateLoop();
+    L->BlockData = reinterpret_cast<BlockT **>(Header);
+    return L;
+  }
+
+  /// The header of a loop under construction, stashed in BlockData until the
+  /// layout carve builds the block list.
+  static BlockT *pendingHeader(const LoopT *L) {
+    return reinterpret_cast<BlockT *>(L->BlockData);
+  }
+
+  void discoverAndMapSubloop(LoopT *L, BlockT *Header,
+                             ArrayRef<BlockT *> Backedges,
+                             const DominatorTreeBase<BlockT, false> &DomTree);
+
+  /// True if \p L's block list is a borrowed slice of BlockLayout rather than
+  /// a private allocation.
+  bool hasBorrowedBlocks(const LoopT &L) const {
+    return L.BlockData >= BlockLayout.get() &&
+           L.BlockData < BlockLayout.get() + BlockLayoutLen;
+  }
+
+  /// Replace \p L's block list with a private allocation of NewCapacity
+  /// slots. The old storage is abandoned in place so slices sharing it stay
+  /// intact; it is reclaimed when this LoopInfo is cleared.
+  void reallocBlocks(LoopT &L, unsigned NewCapacity) {
+    assert(NewCapacity >= L.BlockLen && "capacity below size");
+    BlockT **New = LoopAllocator.Allocate<BlockT *>(NewCapacity);
+    llvm::copy(L.getBlocks(), New);
+    L.BlockData = New;
+    L.BlockCapacity = NewCapacity;
+  }
+
+  /// Copy \p L's borrowed block list into private storage before a mutation.
+  void materializeBlocks(LoopT &L) {
+    if (hasBorrowedBlocks(L))
+      reallocBlocks(L, L.BlockLen);
+  }
+
 public:
   /// Return the inner most loop that BB lives in. If a basic block is in no
   /// loop (for example the entry node), null is returned.
@@ -672,7 +727,10 @@ template <class BlockT, class LoopT> class LoopInfoBase {
   /// ancestors or descendants, and not the block-to-loop mapping.
   template <typename PredicateT>
   void removeBlocksIf(LoopT &L, PredicateT Pred) {
-    llvm::erase_if(L.Blocks, Pred);
+    materializeBlocks(L);
+    L.BlockLen = llvm::remove_if(
+                     MutableArrayRef<BlockT *>(L.BlockData, L.BlockLen), Pred) -
+                 L.BlockData;
   }
 
   /// Remove every block satisfying \p Pred from \p Start and each of its
diff --git a/llvm/include/llvm/Support/GenericLoopInfoImpl.h b/llvm/include/llvm/Support/GenericLoopInfoImpl.h
index 170f05e9d3a49..ad36d3343c8a8 100644
--- a/llvm/include/llvm/Support/GenericLoopInfoImpl.h
+++ b/llvm/include/llvm/Support/GenericLoopInfoImpl.h
@@ -282,7 +282,7 @@ void LoopBase<BlockT, LoopT>::addBasicBlockToLoop(
     BlockT *NewBB, LoopInfoBase<BlockT, LoopT> &LIB) {
   assert(!isInvalid() && "Loop not in a valid state!");
 #ifndef NDEBUG
-  if (!Blocks.empty()) {
+  if (!getBlocks().empty()) {
     auto SameHeader = LIB[getHeader()];
     assert(contains(SameHeader) && getHeader() == SameHeader->getHeader() &&
            "Incorrect LI specified for this loop!");
@@ -325,7 +325,7 @@ template <class BlockT, class LoopT>
 void LoopBase<BlockT, LoopT>::verifyLoop() const {
   assert(!isInvalid() && "Loop not in a valid state!");
 #ifndef NDEBUG
-  assert(!Blocks.empty() && "Loop header is missing");
+  assert(!getBlocks().empty() && "Loop header is missing");
 
   // Setup for using a depth-first iterator to visit every block in the loop.
   SmallVector<BlockT *, 8> ExitBBs;
@@ -371,7 +371,7 @@ void LoopBase<BlockT, LoopT>::verifyLoop() const {
 
   if (VisitedBBs.size() != getNumBlocks()) {
     dbgs() << "The following blocks are unreachable in the loop: ";
-    for (auto *BB : Blocks) {
+    for (auto *BB : getBlocks()) {
       if (!VisitedBBs.count(BB)) {
         dbgs() << *BB << "\n";
       }
@@ -455,12 +455,11 @@ void LoopBase<BlockT, LoopT>::print(raw_ostream &OS, bool Verbose,
 /// this loop are mapped to this loop or a subloop. And all subloops within this
 /// loop have their parent loop set to this loop or a subloop.
 template <class BlockT, class LoopT>
-static void discoverAndMapSubloop(LoopT *L, ArrayRef<BlockT *> Backedges,
-                                  LoopInfoBase<BlockT, LoopT> *LI,
-                                  const DomTreeBase<BlockT> &DomTree) {
+void LoopInfoBase<BlockT, LoopT>::discoverAndMapSubloop(
+    LoopT *L, BlockT *Header, ArrayRef<BlockT *> Backedges,
+    const DominatorTreeBase<BlockT, false> &DomTree) {
   using InvBlockTraits = GraphTraits<Inverse<BlockT *>>;
 
-  unsigned NumBlocks = 0;
   unsigned NumSubloops = 0;
 
   // Perform a backward CFG traversal using a worklist.
@@ -469,15 +468,14 @@ static void discoverAndMapSubloop(LoopT *L, ArrayRef<BlockT *> Backedges,
     BlockT *PredBB = ReverseCFGWorklist.back();
     ReverseCFGWorklist.pop_back();
 
-    LoopT *Subloop = LI->getLoopFor(PredBB);
+    LoopT *Subloop = getLoopFor(PredBB);
     if (!Subloop) {
       if (!DomTree.isReachableFromEntry(PredBB))
         continue;
 
       // This is an undiscovered block. Map it to the current loop.
-      LI->changeLoopFor(PredBB, L);
-      ++NumBlocks;
-      if (PredBB == L->getHeader())
+      changeLoopFor(PredBB, L);
+      if (PredBB == Header)
         continue;
       // Push all block predecessors on the worklist.
       ReverseCFGWorklist.insert(ReverseCFGWorklist.end(),
@@ -494,84 +492,31 @@ static void discoverAndMapSubloop(LoopT *L, ArrayRef<BlockT *> Backedges,
       // Discover a subloop of this loop.
       Subloop->setParentLoop(L);
       ++NumSubloops;
-      NumBlocks += Subloop->getBlocksCapacity();
-      PredBB = Subloop->getHeader();
+      PredBB = pendingHeader(Subloop);
       // Continue traversal along predecessors that are not loop-back edges from
       // within this subloop tree itself. Note that a predecessor may directly
       // reach another subloop that is not yet discovered to be a subloop of
       // this loop, which we must traverse.
       for (const auto Pred : inverse_children<BlockT *>(PredBB)) {
-        if (LI->getLoopFor(Pred) != Subloop)
+        if (getLoopFor(Pred) != Subloop)
           ReverseCFGWorklist.push_back(Pred);
       }
     }
   }
   L->reserveSubLoops(NumSubloops);
-  L->reserveBlocks(NumBlocks);
-}
-
-/// Populate all loop data in a stable order during a single forward DFS.
-template <class BlockT, class LoopT> class PopulateLoopsDFS {
-  using BlockTraits = GraphTraits<BlockT *>;
-  using SuccIterTy = typename BlockTraits::ChildIteratorType;
-
-  LoopInfoBase<BlockT, LoopT> *LI;
-
-public:
-  PopulateLoopsDFS(LoopInfoBase<BlockT, LoopT> *li) : LI(li) {}
-
-  void traverse(BlockT *EntryBlock);
-
-protected:
-  void insertIntoLoop(BlockT *Block);
-};
-
-/// Top-level driver for the forward DFS within the loop.
-template <class BlockT, class LoopT>
-void PopulateLoopsDFS<BlockT, LoopT>::traverse(BlockT *EntryBlock) {
-  for (BlockT *BB : post_order(EntryBlock))
-    insertIntoLoop(BB);
-}
-
-/// Add a single Block to its ancestor loops in PostOrder. If the block is a
-/// subloop header, add the subloop to its parent in PostOrder, then reverse the
-/// Block and Subloop vectors of the now complete subloop to achieve RPO.
-template <class BlockT, class LoopT>
-void PopulateLoopsDFS<BlockT, LoopT>::insertIntoLoop(BlockT *Block) {
-  LoopT *Subloop = LI->getLoopFor(Block);
-  if (Subloop && Block == Subloop->getHeader()) {
-    // We reach this point once per subloop after processing all the blocks in
-    // the subloop.
-    if (!Subloop->isOutermost())
-      Subloop->getParentLoop()->SubLoops.push_back(Subloop);
-    else
-      LI->addTopLevelLoop(Subloop);
-
-    // For convenience, Blocks and Subloops are inserted in postorder. Reverse
-    // the lists, except for the loop header, which is always at the beginning.
-    Subloop->reverseBlock(1);
-    std::reverse(Subloop->SubLoops.begin(), Subloop->SubLoops.end());
-
-    Subloop = Subloop->getParentLoop();
-  }
-  for (; Subloop; Subloop = Subloop->getParentLoop())
-    Subloop->addBlockEntry(Block);
 }
 
 /// Analyze LoopInfo discovers loops during a reverse preorder DominatorTree
 /// traversal interleaved with backward CFG traversals within each subloop
 /// (discoverAndMapSubloop). The backward traversal skips inner subloops, so
-/// this part of the algorithm is linear in the number of CFG edges. Subloop and
-/// Block vectors are then populated during a single forward CFG traversal
-/// (PopulateLoopDFS).
+/// this part of the algorithm is linear in the number of CFG edges.
 ///
-/// During the two CFG traversals each block is seen three times:
-/// 1) Discovered and mapped by a reverse CFG traversal.
-/// 2) Visited during a forward DFS CFG traversal.
-/// 3) Reverse-inserted in the loop in postorder following forward DFS.
-///
-/// The Block vectors are inclusive, so step 3 requires loop-depth number of
-/// insertions per block.
+/// The block lists are then built in one shared layout: a forward CFG postorder
+/// records the in-loop blocks, and a reverse walk (RPO) carves each loop a
+/// contiguous slice of its parent's, writing each block once to its innermost
+/// loop. Lists are header-first with each subloop's blocks contiguous, ordered
+/// by first appearance in RPO; SubLoops keep program order, TopLevelLoops
+/// reverse program order.
 template <class BlockT, class LoopT>
 void LoopInfoBase<BlockT, LoopT>::analyze(const DomTreeBase<BlockT> &DomTree) {
   const DomTreeNodeBase<BlockT> *DomRoot = DomTree.getRootNode();
@@ -587,6 +532,7 @@ void LoopInfoBase<BlockT, LoopT>::analyze(const DomTreeBase<BlockT> &DomTree) {
   for (const DomTreeNodeBase<BlockT> *Node : DomTree.nodes())
     PreorderNodes[Node->getDFSNumIn()] = Node;
 
+  bool HasLoops = false;
   for (const DomTreeNodeBase<BlockT> *DomNode : llvm::reverse(PreorderNodes)) {
     BlockT *Header = DomNode->getBlock();
     SmallVector<BlockT *, 4> Backedges;
@@ -600,14 +546,63 @@ void LoopInfoBase<BlockT, LoopT>::analyze(const D...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/211485


More information about the Mlir-commits mailing list