[llvm] [CycleInfo] Store cycles in a flat preorder array. NFC (PR #209981)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 23:37:25 PDT 2026


https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/209981

Store cycles by value in one array in cycle-forest preorder, each cycle
immediately followed by its descendants, instead of heap-allocating each
cycle and holding its children in a std::vector<std::unique_ptr<>>.
Child and top-level iteration become pointer arithmetic that skips a
subtree via a new NumDescendants count, and sizeof(GenericCycle) drops
from 72 to 48. GenericCycleInfoCompute builds the forest with temporary
nodes, then flatten() moves it into the array.

GenericCycle still exposes raw pointers into this array. The eventual
goal is to replace them with an opaque handle, so all access goes
through GenericCycleInfo and the storage stays an implementation detail.

Aided by Fable 5


>From 30c140cc30ef429cc59f8fef54c4e4655b61b3b7 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 15 Jul 2026 23:37:12 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.5-bogner
---
 llvm/include/llvm/ADT/GenericCycleImpl.h     | 209 +++++++++----------
 llvm/include/llvm/ADT/GenericCycleInfo.h     | 125 +++++------
 llvm/lib/Transforms/Utils/FixIrreducible.cpp |  11 +-
 3 files changed, 153 insertions(+), 192 deletions(-)

diff --git a/llvm/include/llvm/ADT/GenericCycleImpl.h b/llvm/include/llvm/ADT/GenericCycleImpl.h
index 9f83efc2aa946..043b7f48501b8 100644
--- a/llvm/include/llvm/ADT/GenericCycleImpl.h
+++ b/llvm/include/llvm/ADT/GenericCycleImpl.h
@@ -27,6 +27,7 @@
 #include "llvm/ADT/DepthFirstIterator.h"
 #include "llvm/ADT/GenericCycleInfo.h"
 #include "llvm/ADT/StringExtras.h"
+#include <deque>
 #include <iterator>
 
 #define DEBUG_TYPE "generic-cycle-impl"
@@ -38,7 +39,7 @@ void GenericCycleInfo<ContextT>::getExitBlocks(
     const CycleT &C, SmallVectorImpl<BlockT *> &TmpStorage) const {
   if (ExitBlocksCaches.empty())
     ExitBlocksCaches.resize(NumCycles);
-  auto &Cache = ExitBlocksCaches[C.ID];
+  auto &Cache = ExitBlocksCaches[getCycleIndex(C)];
   if (!Cache.empty()) {
     TmpStorage.append(Cache.begin(), Cache.end());
     return;
@@ -240,6 +241,16 @@ template <typename ContextT> class GenericCycleInfoCompute {
   SmallVector<DFSInfo, 8> BlockDFSInfo;
   SmallVector<BlockT *, 8> BlockPreorder;
 
+  /// Append-only cycles discovered so far, in creation order.
+  std::deque<CycleT> AllCycles;
+
+  /// Flat log of child attachments, in attach order. Cycles are only ever
+  /// attached to the newest cycle, so each cycle's children occupy the
+  /// contiguous slice [IdxBegin, Depth) of this log.
+  SmallVector<CycleT *, 8> AttachedChildren;
+
+  SmallVector<CycleT *, 8> TopLevelCycles;
+
   GenericCycleInfoCompute(const GenericCycleInfoCompute &) = delete;
   GenericCycleInfoCompute &operator=(const GenericCycleInfoCompute &) = delete;
 
@@ -253,38 +264,30 @@ template <typename ContextT> class GenericCycleInfoCompute {
     return BlockDFSInfo[Number];
   }
 
+  /// Make top-level cycle \p Child a child of \p NewParent.
+  void moveTopLevelCycleToNewParent(CycleT *NewParent, CycleT *Child) {
+    assert((!Child->ParentCycle && !NewParent->ParentCycle) &&
+           "NewParent and Child must be both top level cycle!\n");
+    assert(NewParent == &AllCycles.back() &&
+           "attach slices in AttachedChildren must stay contiguous");
+    auto Pos = llvm::find(TopLevelCycles, Child);
+    assert(Pos != TopLevelCycles.end());
+    *Pos = TopLevelCycles.back();
+    TopLevelCycles.pop_back();
+    AttachedChildren.push_back(Child);
+    Child->ParentCycle = NewParent;
+  }
+
 public:
   GenericCycleInfoCompute(CycleInfoT &Info) : Info(Info) {}
 
   void run(FunctionT *F);
 
-  static void updateDepth(CycleT *SubTree);
-
 private:
   void dfs(FunctionT *F, BlockT *EntryBlock);
+  void flatten(ArrayRef<BlockT *> Order);
 };
 
-template <typename ContextT>
-void GenericCycleInfo<ContextT>::moveTopLevelCycleToNewParent(CycleT *NewParent,
-                                                              CycleT *Child) {
-  assert((!Child->ParentCycle && !NewParent->ParentCycle) &&
-         "NewParent and Child must be both top level cycle!\n");
-  auto &CurrentContainer =
-      Child->ParentCycle ? Child->ParentCycle->Children : TopLevelCycles;
-  auto Pos = llvm::find_if(CurrentContainer, [=](const auto &Ptr) -> bool {
-    return Child == Ptr.get();
-  });
-  assert(Pos != CurrentContainer.end());
-  NewParent->Children.push_back(std::move(*Pos));
-  *Pos = std::move(CurrentContainer.back());
-  CurrentContainer.pop_back();
-  Child->ParentCycle = NewParent;
-  // This only relinks the cycle tree and does NOT touch BlockLayout, so it
-  // leaves every cycle's [IdxBegin, IdxEnd) range stale, i.e. BlockLayout is
-  // left invalid. The caller must call layoutBlocks() before any
-  // range-dependent query is used.
-}
-
 template <typename ContextT>
 void GenericCycleInfo<ContextT>::addToBlockMap(BlockT *Block, CycleT *Cycle) {
   // The caller should ensure that BlockMap is large enough.
@@ -301,36 +304,35 @@ void GenericCycleInfo<ContextT>::addBlockToCycle(BlockT *Block, CycleT *Cycle) {
     BlockMap.resize(GraphTraits<FunctionT *>::getMaxNumber(Block->getParent()));
 
   // Insert Block at the end of Cycle's slice and shift every later cycle's
-  // range right. contain it below. The forest is an Euler tour, so a subtree
-  // ending at or before Pos is entirely earlier and is skipped.
+  // range right. Ranges straddling Pos belong to Cycle's ancestors and are
+  // extended below.
   unsigned Pos = Cycle->IdxEnd;
   BlockLayout.insert(BlockLayout.begin() + Pos, Block);
-  SmallVector<CycleT *, 8> Worklist(toplevel_cycles());
-  while (!Worklist.empty()) {
-    CycleT *C = Worklist.pop_back_val();
-    if (C->IdxEnd <= Pos)
-      continue;
-    if (C->IdxBegin >= Pos) {
-      ++C->IdxBegin;
-      ++C->IdxEnd;
+  for (CycleT &C : cycles())
+    if (C.IdxBegin >= Pos) {
+      ++C.IdxBegin;
+      ++C.IdxEnd;
     }
-    for (auto &Child : C->Children)
-      Worklist.push_back(Child.get());
-  }
   addToBlockMap(Block, Cycle);
   // Cycle and its ancestors gain the new block: extend each one's slice and
   // invalidate its exit-block cache in a single walk up the tree.
   for (CycleT *C = Cycle; C; C = C->getParentCycle()) {
     ++C->IdxEnd;
     if (!ExitBlocksCaches.empty())
-      ExitBlocksCaches[C->ID].clear();
+      ExitBlocksCaches[getCycleIndex(*C)].clear();
   }
 }
 
+/// Move the discovered forest into Info's flat preorder array. Assigns preorder
+/// IDs, depths and descendant counts, remaps BlockMap from the temporary nodes,
+/// and lays out every cycle's blocks in BlockLayout.
 template <typename ContextT>
-void GenericCycleInfo<ContextT>::layoutBlocks(ArrayRef<BlockT *> Order) {
-  if (TopLevelCycles.empty())
+void GenericCycleInfoCompute<ContextT>::flatten(ArrayRef<BlockT *> Order) {
+  unsigned N = AllCycles.size();
+  Info.NumCycles = N;
+  if (!N)
     return;
+  Info.Cycles = std::make_unique<CycleT[]>(N);
 
   // Walk the cycle forest as an Euler tour. On entry, a cycle's IdxEnd still
   // holds its own-block count (accumulated during run()); reserve that many
@@ -338,36 +340,50 @@ void GenericCycleInfo<ContextT>::layoutBlocks(ArrayRef<BlockT *> Order) {
   // following slots, so on leaving, Cursor is the cycle's real range end, which
   // overwrites the now-consumed count in IdxEnd.
   struct Frame {
-    CycleT *C;
-    typename CycleT::const_child_iterator ChildCur, ChildEnd;
+    CycleT *Flat;
+    unsigned ChildCur, ChildEnd;
+    unsigned ID;
   };
   SmallVector<Frame, 8> Stack;
   unsigned Cursor = 0;
-  NumCycles = 0;
-  auto enter = [&](CycleT *C) {
-    C->ID = NumCycles++;
-    Cursor += C->IdxEnd; // IdxEnd currently holds C's own-block count.
-    C->IdxBegin = Cursor;
-    Stack.push_back({C, C->child_begin(), C->child_end()});
+  unsigned NextID = 0;
+  auto enter = [&](CycleT *Temp, CycleT *Parent) {
+    unsigned ID = NextID++;
+    CycleT &Flat = Info.Cycles[ID];
+    Flat.ParentCycle = Parent;
+    Flat.Depth = Parent ? Parent->Depth + 1 : 1;
+    Flat.Entries = std::move(Temp->Entries);
+    Cursor += Temp->IdxEnd; // IdxEnd currently holds Temp's own-block count.
+    Flat.IdxBegin = Cursor; // Real begin restored by the fill loop below.
+    Stack.push_back({&Flat, Temp->IdxBegin, Temp->Depth, ID});
+    // Temp's IdxBegin now holds the flat index, for the BlockMap remap below.
+    Temp->IdxBegin = ID;
   };
-  for (CycleT *TLC : toplevel_cycles()) {
-    enter(TLC);
+  for (CycleT *TLC : TopLevelCycles) {
+    enter(TLC, nullptr);
     while (!Stack.empty()) {
       Frame &F = Stack.back();
       if (F.ChildCur != F.ChildEnd) {
-        enter(*F.ChildCur++);
+        enter(AttachedChildren[F.ChildCur++], F.Flat);
       } else {
-        F.C->IdxEnd = Cursor;
+        F.Flat->IdxEnd = Cursor;
+        F.Flat->NumDescendants = NextID - F.ID - 1;
         Stack.pop_back();
       }
     }
   }
 
-  // Place every block into its innermost cycle's own region.
-  BlockLayout.resize_for_overwrite(Cursor);
-  for (BlockT *B : llvm::reverse(Order))
-    if (CycleT *C = getCycle(B))
-      BlockLayout[--C->IdxBegin] = B;
+  // Place every block into its innermost cycle's own region, remapping its
+  // BlockMap entry from the temporary node to the flat one.
+  Info.BlockLayout.resize_for_overwrite(Cursor);
+  for (BlockT *B : llvm::reverse(Order)) {
+    unsigned Number = GraphTraits<const BlockT *>::getNumber(B);
+    if (CycleT *Temp = Info.BlockMap[Number]) {
+      CycleT *Flat = &Info.Cycles[Temp->IdxBegin];
+      Info.BlockMap[Number] = Flat;
+      Info.BlockLayout[--Flat->IdxBegin] = B;
+    }
+  }
 }
 
 /// \brief Main function of the cycle info computations.
@@ -397,12 +413,13 @@ void GenericCycleInfoCompute<ContextT>::run(FunctionT *F) {
     // Found a cycle with the candidate as its header.
     LLVM_DEBUG(errs() << "Found cycle for header: "
                       << Info.Context.print(HeaderCandidate) << "\n");
-    std::unique_ptr<CycleT> NewCycle = std::make_unique<CycleT>();
+    CycleT *NewCycle = &AllCycles.emplace_back();
+    NewCycle->IdxBegin = AttachedChildren.size(); // Attach-log slice start.
     NewCycle->appendEntry(HeaderCandidate);
-    Info.addToBlockMap(HeaderCandidate, NewCycle.get());
-    // The header is this cycle's first own block. Until layoutBlocks runs,
+    Info.addToBlockMap(HeaderCandidate, NewCycle);
+    // The header is this cycle's first own block. Until flatten() runs,
     // IdxEnd accumulates this cycle's own-block count (see the IdxBegin/
-    // IdxEnd doc comment), so layoutBlocks needs no separate counting pass.
+    // IdxEnd doc comment), so flatten() needs no separate counting pass.
     ++NewCycle->IdxEnd;
 
     // Helper function to process (non-back-edge) predecessors of a discovered
@@ -444,12 +461,12 @@ void GenericCycleInfoCompute<ContextT>::run(FunctionT *F) {
       if (auto *BlockParent = Info.getTopLevelParentCycle(Block)) {
         LLVM_DEBUG(errs() << "  block " << Info.Context.print(Block) << ": ");
 
-        if (BlockParent != NewCycle.get()) {
+        if (BlockParent != NewCycle) {
           LLVM_DEBUG(errs()
                      << "discovered child cycle "
                      << Info.Context.print(BlockParent->getHeader()) << "\n");
           // Make BlockParent the child of NewCycle.
-          Info.moveTopLevelCycleToNewParent(NewCycle.get(), BlockParent);
+          moveTopLevelCycleToNewParent(NewCycle, BlockParent);
 
           for (auto *ChildEntry : BlockParent->entries())
             ProcessPredecessors(ChildEntry);
@@ -459,39 +476,20 @@ void GenericCycleInfoCompute<ContextT>::run(FunctionT *F) {
                      << Info.Context.print(BlockParent->getHeader()) << "\n");
         }
       } else {
-        Info.addToBlockMap(Block, NewCycle.get());
+        Info.addToBlockMap(Block, NewCycle);
         ++NewCycle->IdxEnd; // Block's innermost cycle is NewCycle.
         ProcessPredecessors(Block);
       }
     } while (!Worklist.empty());
 
-    Info.TopLevelCycles.push_back(std::move(NewCycle));
-  }
-
-  // Fix top-level cycle links and compute cycle depths.
-  for (auto *TLC : Info.toplevel_cycles()) {
-    LLVM_DEBUG(errs() << "top-level cycle: "
-                      << Info.Context.print(TLC->getHeader()) << "\n");
-
-    TLC->ParentCycle = nullptr;
-    updateDepth(TLC);
+    NewCycle->Depth = AttachedChildren.size(); // Attach-log slice end.
+    TopLevelCycles.push_back(NewCycle);
   }
 
-  // The cycle tree and the block-to-innermost-cycle map are complete; lay out
-  // every cycle's blocks into the shared contiguous BlockLayout.
-  Info.layoutBlocks(BlockPreorder);
-}
-
-/// \brief Recompute depth values of \p SubTree and all descendants.
-template <typename ContextT>
-void GenericCycleInfoCompute<ContextT>::updateDepth(CycleT *SubTree) {
-  SmallVector<CycleT *, 8> Worklist = {SubTree};
-  while (!Worklist.empty()) {
-    CycleT *Cycle = Worklist.pop_back_val();
-    Cycle->Depth = Cycle->ParentCycle ? Cycle->ParentCycle->Depth + 1 : 1;
-    for (CycleT *Child : Cycle->children())
-      Worklist.push_back(Child);
-  }
+  // The cycle forest and the block-to-innermost-cycle map are complete; move
+  // the forest into Info's flat preorder array and lay out every cycle's
+  // blocks into the shared contiguous BlockLayout.
+  flatten(BlockPreorder);
 }
 
 /// \brief Compute a DFS of basic blocks starting at the function entry.
@@ -557,9 +555,9 @@ void GenericCycleInfoCompute<ContextT>::dfs(FunctionT *F, BlockT *EntryBlock) {
 
 /// \brief Reset the object to its initial state.
 template <typename ContextT> void GenericCycleInfo<ContextT>::clear() {
-  TopLevelCycles.clear();
   BlockMap.clear();
   BlockLayout.clear();
+  Cycles.reset();
   NumCycles = 0;
   ExitBlocksCaches.clear();
 }
@@ -639,23 +637,19 @@ void GenericCycleInfo<ContextT>::verifyCycleNest(bool VerifyFull) const {
 #ifndef NDEBUG
   DenseSet<BlockT *> CycleHeaders;
 
-  SmallVector<CycleT *, 8> Worklist(toplevel_begin(), toplevel_end());
-  while (!Worklist.empty()) {
-    CycleT *Cycle = Worklist.pop_back_val();
-    BlockT *Header = Cycle->getHeader();
+  for (const CycleT &Cycle : cycles()) {
+    BlockT *Header = Cycle.getHeader();
     assert(CycleHeaders.insert(Header).second);
     if (VerifyFull)
-      verifyCycle(*Cycle);
+      verifyCycle(Cycle);
     else
-      verifyCycleNest(*Cycle);
+      verifyCycleNest(Cycle);
     // Check the block map entries for blocks contained in this cycle.
-    for (BlockT *BB : getBlocks(*Cycle)) {
+    for (BlockT *BB : getBlocks(Cycle)) {
       CycleT *CycleInBlockMap = getCycle(BB);
       assert(CycleInBlockMap != nullptr);
-      assert(Cycle->contains(CycleInBlockMap));
+      assert(Cycle.contains(CycleInBlockMap));
     }
-    for (CycleT *Child : Cycle->children())
-      Worklist.push_back(Child);
   }
 #endif
 }
@@ -668,18 +662,11 @@ template <typename ContextT> void GenericCycleInfo<ContextT>::verify() const {
 /// \brief Print the cycle info.
 template <typename ContextT>
 void GenericCycleInfo<ContextT>::print(raw_ostream &Out) const {
-  SmallVector<const CycleT *, 8> Stack;
-  for (const CycleT *TLC : toplevel_cycles()) {
-    Stack.push_back(TLC);
-    while (!Stack.empty()) {
-      const CycleT *Cycle = Stack.pop_back_val();
-      for (unsigned I = 0; I < Cycle->Depth; ++I)
-        Out << "    ";
+  for (const CycleT &Cycle : cycles()) {
+    for (unsigned I = 0; I < Cycle.Depth; ++I)
+      Out << "    ";
 
-      Out << print(Cycle) << '\n';
-      for (const auto &Child : reverse(Cycle->Children))
-        Stack.push_back(Child.get());
-    }
+    Out << print(&Cycle) << '\n';
   }
 }
 
diff --git a/llvm/include/llvm/ADT/GenericCycleInfo.h b/llvm/include/llvm/ADT/GenericCycleInfo.h
index 96b5fb1f4e66a..07995faffefc5 100644
--- a/llvm/include/llvm/ADT/GenericCycleInfo.h
+++ b/llvm/include/llvm/ADT/GenericCycleInfo.h
@@ -33,8 +33,10 @@
 #include "llvm/ADT/GraphTraits.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/iterator.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
+#include <memory>
 
 namespace llvm {
 
@@ -42,6 +44,10 @@ template <typename ContextT> class GenericCycleInfo;
 template <typename ContextT> class GenericCycleInfoCompute;
 
 /// A possibly irreducible generalization of a \ref Loop.
+///
+/// Cycles are stored by value in GenericCycleInfo::Cycles in preorder of the
+/// cycle forest: a cycle is immediately followed by its descendants. Child
+/// iteration is therefore pointer arithmetic over that array.
 template <typename ContextT> class GenericCycle {
 public:
   using BlockT = typename ContextT::BlockT;
@@ -50,37 +56,31 @@ template <typename ContextT> class GenericCycle {
   template <typename> friend class GenericCycleInfoCompute;
 
 private:
-  /// The parent cycle. Is null for the root "cycle". Top-level cycles point
-  /// at the root.
+  /// The parent cycle. Is null for top-level cycles.
   GenericCycle *ParentCycle = nullptr;
 
   /// The entry block(s) of the cycle. The header is the only entry if
-  /// this is a loop. Is empty for the root "cycle", to avoid
-  /// unnecessary memory use.
+  /// this is a loop.
   SmallVector<BlockT *, 1> Entries;
 
-  /// Child cycles, if any.
-  std::vector<std::unique_ptr<GenericCycle>> Children;
-
   /// This cycle's blocks (its own and its nested cycles') occupy the half-open
   /// range [IdxBegin, IdxEnd) of GenericCycleInfo::BlockLayout. The
   /// ranges are nested like an Euler tour of the cycle tree, so containment is
   /// an interval test (see contains()).
   ///
-  /// During construction (before layoutBlocks), IdxEnd accumulates the number
-  /// of this cycle's own blocks (those whose innermost cycle is this one).
+  /// During construction (before the forest is flattened), IdxEnd accumulates
+  /// the number of this cycle's own blocks (those whose innermost cycle is
+  /// this one).
   unsigned IdxBegin = 0, IdxEnd = 0;
 
-  /// Depth of the cycle in the tree. The root "cycle" is at depth 0.
-  ///
-  /// \note Depths are not necessarily contiguous. However, child loops always
-  ///       have strictly greater depth than their parents, and sibling loops
-  ///       always have the same depth.
+  /// Depth of the cycle in the tree: top-level cycles are at depth 1 and each
+  /// nested cycle is one deeper (getCycleDepth() returns 0 for blocks outside
+  /// any cycle). Sibling cycles share a depth.
   unsigned Depth = 0;
 
-  /// Preorder number of this cycle in the forest, assigned by layoutBlocks.
-  /// Indexes per-cycle side tables in GenericCycleInfo.
-  unsigned ID = 0;
+  /// Number of cycles nested inside this one: the subtree occupies
+  /// [this, this + 1 + NumDescendants) of GenericCycleInfo::Cycles.
+  unsigned NumDescendants = 0;
 
   void appendEntry(BlockT *Block) { Entries.push_back(Block); }
 
@@ -125,36 +125,37 @@ template <typename ContextT> class GenericCycle {
 
   size_t getNumBlocks() const { return IdxEnd - IdxBegin; }
 
-  /// Iteration over child cycles.
+  /// Iteration over child cycles: the first child (if any) immediately
+  /// follows this cycle in the preorder array, and each next sibling follows
+  /// the previous child's subtree.
   //@{
-  using const_child_iterator_base =
-      typename std::vector<std::unique_ptr<GenericCycle>>::const_iterator;
   struct const_child_iterator
-      : iterator_adaptor_base<const_child_iterator, const_child_iterator_base,
-                              std::random_access_iterator_tag, GenericCycle *,
-                              std::ptrdiff_t, GenericCycle *, GenericCycle *> {
-    using Base =
-        iterator_adaptor_base<const_child_iterator, const_child_iterator_base,
-                              std::random_access_iterator_tag, GenericCycle *,
-                              std::ptrdiff_t, GenericCycle *, GenericCycle *>;
+      : iterator_facade_base<const_child_iterator, std::forward_iterator_tag,
+                             GenericCycle *, std::ptrdiff_t, GenericCycle *,
+                             GenericCycle *> {
+    const GenericCycle *C = nullptr;
 
     const_child_iterator() = default;
-    explicit const_child_iterator(const_child_iterator_base I) : Base(I) {}
-
-    const const_child_iterator_base &wrapped() { return Base::wrapped(); }
-    GenericCycle *operator*() const { return Base::I->get(); }
+    explicit const_child_iterator(const GenericCycle *C) : C(C) {}
+
+    GenericCycle *operator*() const { return const_cast<GenericCycle *>(C); }
+    const_child_iterator &operator++() {
+      C += 1 + C->NumDescendants;
+      return *this;
+    }
+    bool operator==(const const_child_iterator &Other) const {
+      return C == Other.C;
+    }
   };
 
   const_child_iterator child_begin() const {
-    return const_child_iterator{Children.begin()};
+    return const_child_iterator{this + 1};
   }
   const_child_iterator child_end() const {
-    return const_child_iterator{Children.end()};
+    return const_child_iterator{this + 1 + NumDescendants};
   }
-  size_t getNumChildren() const { return Children.size(); }
   iterator_range<const_child_iterator> children() const {
-    return llvm::make_range(const_child_iterator{Children.begin()},
-                            const_child_iterator{Children.end()});
+    return llvm::make_range(child_begin(), child_end());
   }
   //@}
 
@@ -202,23 +203,18 @@ template <typename ContextT> class GenericCycleInfo {
   /// slice [IdxBegin, IdxEnd) of this array, nested inside its parent's.
   SmallVector<BlockT *, 8> BlockLayout;
 
+  /// All cycles in forest preorder: every cycle is immediately followed by
+  /// its descendants, and skipping a top-level cycle's subtree lands on the
+  /// next top-level cycle.
+  std::unique_ptr<CycleT[]> Cycles;
   unsigned NumCycles = 0;
 
-  /// getExitBlocks caches, indexed by CycleT::ID. Empty until the first
-  /// query, then sized to NumCycles.
+  /// getExitBlocks caches, indexed by the cycle's preorder index. Empty until
+  /// the first query, then sized to NumCycles.
   mutable SmallVector<SmallVector<BlockT *, 0>, 0> ExitBlocksCaches;
 
-  /// Top-level cycles discovered by any DFS.
-  ///
-  /// Note: The implementation treats the nullptr as the parent of
-  /// every top-level cycle. See \ref contains for an example.
-  std::vector<std::unique_ptr<CycleT>> TopLevelCycles;
-
-  /// Move \p Child to \p NewParent by manipulating Children vectors.
-  ///
-  /// Note: This is an incomplete operation that does not update the depth of
-  /// the subtree.
-  void moveTopLevelCycleToNewParent(CycleT *NewParent, CycleT *Child);
+  /// The preorder index of \p C, i.e. its offset in the Cycles array.
+  unsigned getCycleIndex(const CycleT &C) const { return &C - Cycles.get(); }
 
   void verifyBlockNumberEpoch(const FunctionT *Fn) const {
     assert(BlockNumberEpoch ==
@@ -227,10 +223,6 @@ template <typename ContextT> class GenericCycleInfo {
   }
   void addToBlockMap(BlockT *Block, CycleT *Cycle);
 
-  /// Build BlockLayout and every cycle's [IdxBegin, IdxEnd) slice
-  /// from the innermost-cycle map and the current cycle tree.
-  void layoutBlocks(ArrayRef<BlockT *> Order);
-
 public:
   GenericCycleInfo() = default;
   GenericCycleInfo(GenericCycleInfo &&) = default;
@@ -243,6 +235,10 @@ template <typename ContextT> class GenericCycleInfo {
   const FunctionT *getFunction() const { return Context.getFunction(); }
   const ContextT &getSSAContext() const { return Context; }
 
+  /// All cycles in forest preorder.
+  MutableArrayRef<CycleT> cycles() { return {Cycles.get(), NumCycles}; }
+  ArrayRef<CycleT> cycles() const { return {Cycles.get(), NumCycles}; }
+
   /// \brief Find the innermost cycle containing \p Block.
   ///
   /// \returns the innermost cycle containing \p Block or nullptr if
@@ -325,32 +321,17 @@ template <typename ContextT> class GenericCycleInfo {
 
   /// Iteration over top-level cycles.
   //@{
-  using const_toplevel_iterator_base =
-      typename std::vector<std::unique_ptr<CycleT>>::const_iterator;
-  struct const_toplevel_iterator
-      : iterator_adaptor_base<const_toplevel_iterator,
-                              const_toplevel_iterator_base> {
-    using Base = iterator_adaptor_base<const_toplevel_iterator,
-                                       const_toplevel_iterator_base>;
-
-    const_toplevel_iterator() = default;
-    explicit const_toplevel_iterator(const_toplevel_iterator_base I)
-        : Base(I) {}
-
-    const const_toplevel_iterator_base &wrapped() { return Base::wrapped(); }
-    CycleT *operator*() const { return Base::I->get(); }
-  };
+  using const_toplevel_iterator = typename CycleT::const_child_iterator;
 
   const_toplevel_iterator toplevel_begin() const {
-    return const_toplevel_iterator{TopLevelCycles.begin()};
+    return const_toplevel_iterator{Cycles.get()};
   }
   const_toplevel_iterator toplevel_end() const {
-    return const_toplevel_iterator{TopLevelCycles.end()};
+    return const_toplevel_iterator{Cycles.get() + NumCycles};
   }
 
   iterator_range<const_toplevel_iterator> toplevel_cycles() const {
-    return llvm::make_range(const_toplevel_iterator{TopLevelCycles.begin()},
-                            const_toplevel_iterator{TopLevelCycles.end()});
+    return llvm::make_range(toplevel_begin(), toplevel_end());
   }
   //@}
 };
diff --git a/llvm/lib/Transforms/Utils/FixIrreducible.cpp b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
index 81feb34eb953d..4fd08a90e86bc 100644
--- a/llvm/lib/Transforms/Utils/FixIrreducible.cpp
+++ b/llvm/lib/Transforms/Utils/FixIrreducible.cpp
@@ -429,15 +429,8 @@ static bool FixIrreducibleImpl(Function &F, CycleInfo &CI, DominatorTree &DT,
                     << F.getName() << "\n");
 
   bool Changed = false;
-  SmallVector<Cycle *, 8> Worklist;
-  for (Cycle *TopCycle : CI.toplevel_cycles()) {
-    Worklist.push_back(TopCycle);
-    while (!Worklist.empty()) {
-      Cycle *C = Worklist.pop_back_val();
-      Changed |= fixIrreducible(*C, CI, DT, LI);
-      llvm::append_range(Worklist, reverse(C->children()));
-    }
-  }
+  for (Cycle &C : CI.cycles())
+    Changed |= fixIrreducible(C, CI, DT, LI);
 
   if (!Changed)
     return false;



More information about the llvm-commits mailing list