[llvm] [CycleInfo] Identify cycles with a single-pass DFS algorithm (PR #210491)

Alexis Engelke via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 18 01:22:26 PDT 2026


================
@@ -329,257 +323,204 @@ void GenericCycleInfo<ContextT>::addBlockToCycle(BlockT *Block, CycleRef C) {
       ++X.IdxEnd;
     }
   }
-  addToBlockMap(Block, &Cyc);
+  addToBlockMap(Block, C);
   // Cyc 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 (unsigned I = getCycleIndex(Cyc); I != NoCycle;
-       I = Cycles[I].ParentIndex) {
-    ++Cycles[I].IdxEnd;
+  for (CycleRef I = C; I; I = deref(I).Parent) {
+    ++deref(I).IdxEnd;
     if (!ExitBlocksCaches.empty())
-      ExitBlocksCaches[I].clear();
+      ExitBlocksCaches[I.Index].clear();
   }
 }
 
-/// Move the discovered forest into Info's flat preorder array. Assigns preorder
-/// IDs, depths and descendant counts, remaps BlockMap from creation-order to
-/// preorder indices, and lays out every cycle's blocks in BlockLayout.
+/// Lay the discovered cycle forest out into Info's flat preorder array: number
+/// the cycles in Euler-tour order, set each one's parent, depth and descendant
+/// count, place every block into its innermost cycle's region of BlockLayout,
+/// and fill BlockMap. Arrays are keyed by header-preorder rank.
 template <typename ContextT>
-void GenericCycleInfoCompute<ContextT>::flatten(ArrayRef<BlockT *> Order) {
-  unsigned N = AllCycles.size();
+void GenericCycleInfoCompute<ContextT>::flatten(ArrayRef<BlockT *> Headers,
+                                                ArrayRef<unsigned> ChildHead,
+                                                ArrayRef<unsigned> NextSibling,
+                                                ArrayRef<unsigned> OwnCount,
+                                                unsigned TopHead) {
+  unsigned N = Headers.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.
+  // Walk the cycle forest as an Euler tour. On entry a cycle reserves [Cursor,
+  // Cursor + OwnCount) for its own blocks (IdxBegin temporarily holds that
+  // region's end; the fill loop below walks it back down); its descendants take
+  // the following slots, so on exit Cursor is its IdxEnd.
+  SmallVector<unsigned, 8> FlatIdx(N);
   struct Frame {
-    CycleT *Flat;
-    unsigned ChildCur, ChildEnd;
-    unsigned ID;
+    unsigned Flat;
+    unsigned Child; // Next child to enter, NoCycle once exhausted.
   };
   SmallVector<Frame, 8> Stack;
-  unsigned Cursor = 0;
-  unsigned NextID = 0;
-  auto enter = [&](CycleT *Temp, CycleT *Parent) {
+  unsigned Cursor = 0, NextID = 0;
+  auto enter = [&](unsigned C, CycleRef Parent) {
     unsigned ID = NextID++;
+    FlatIdx[C] = ID;
     CycleT &Flat = Info.Cycles[ID];
-    Flat.ParentIndex =
-        Parent ? Info.getCycleIndex(*Parent) : CycleInfoT::NoCycle;
-    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;
+    Flat.Parent = Parent;
+    Flat.Depth = Parent ? Info.deref(Parent).Depth + 1 : 1;
+    Flat.appendEntry(Headers[C]);
----------------
aengelke wrote:

For natural loops, the header is always the first block of a cycle in a pre-order traversal (necessary as it dominates all blocks of the loop). Irreducible entries are handled separately below. I think we can avoid the Headers array completely: in a CycleT, store HeaderBegin, HeaderEnd as indices into BlockLayout, initialize with HeaderBegin = IdxBegin, HeaderEnd = IdxBegin + 1. For irreducible loops (uncommon case), append extra header slices to BlockLayout. (This would also have the nice side effect that CycleT is trivially destructible.)

This can be a separate later change.

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


More information about the llvm-commits mailing list