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

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 09:35:56 PDT 2026


================
@@ -301,73 +304,86 @@ 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
   // slots for its own region [Cursor, Cursor + count). Its children take the
   // 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;
----------------
MaskRay wrote:

Yes, should be feasible during the process making `GenericCycle`  an implementation detail!

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


More information about the llvm-commits mailing list