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

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 18 13:53:48 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]);
+    Cursor += OwnCount[C];
+    Flat.IdxBegin = Cursor;
+    Stack.push_back({ID, ChildHead[C]});
   };
-  for (CycleT *TLC : TopLevelCycles) {
-    enter(TLC, nullptr);
+  for (auto TLC = TopHead; TLC != NoCycle; TLC = NextSibling[TLC]) {
+    enter(TLC, CycleRef());
     while (!Stack.empty()) {
       Frame &F = Stack.back();
-      if (F.ChildCur != F.ChildEnd) {
-        enter(AttachedChildren[F.ChildCur++], F.Flat);
+      if (F.Child != NoCycle) {
+        unsigned C = F.Child;
+        F.Child = NextSibling[C];
+        enter(C, CycleRef(F.Flat));
       } else {
-        F.Flat->IdxEnd = Cursor;
-        F.Flat->NumDescendants = NextID - F.ID - 1;
+        CycleT &Flat = Info.Cycles[F.Flat];
+        Flat.IdxEnd = Cursor;
+        Flat.NumDescendants = NextID - F.Flat - 1;
         Stack.pop_back();
       }
     }
   }
 
-  // Place every block into its innermost cycle's own region, remapping its
-  // BlockMap entry from a creation-order index to the flat preorder index.
+  // Place every block into its innermost cycle's own region.
   Info.BlockLayout.resize_for_overwrite(Cursor);
-  for (BlockT *B : llvm::reverse(Order)) {
-    unsigned Number = GraphTraits<const BlockT *>::getNumber(B);
-    unsigned Created = Info.BlockMap[Number];
-    if (Created != CycleInfoT::NoCycle) {
-      // Created indexes AllCycles; enter() stashed the flat preorder index in
-      // that temporary node's IdxBegin.
-      unsigned Flat = AllCycles[Created].IdxBegin;
-      Info.BlockMap[Number] = Flat;
-      CycleT &FlatCycle = Info.Cycles[Flat];
-      Info.BlockLayout[--FlatCycle.IdxBegin] = B;
-    }
+  for (unsigned N : llvm::reverse(Preorder)) {
+    BlockInfo &BI = info(N);
+    if (BI.CycleIdx == NoCycle)
+      continue;
+    unsigned Flat = FlatIdx[BI.CycleIdx];
+    Info.BlockMap[N] = CycleRef(Flat);
+    Info.BlockLayout[--Info.Cycles[Flat].IdxBegin] = BI.Block;
   }
 }
 
 /// \brief Main function of the cycle info computations.
 template <typename ContextT>
 void GenericCycleInfoCompute<ContextT>::run(FunctionT *F) {
   BlockT *EntryBlock = GraphTraits<FunctionT *>::getEntryNode(F);
-  LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock)
-                    << "\n");
-  dfs(F, EntryBlock);
-
-  SmallVector<BlockT *, 8> Worklist;
-
-  for (BlockT *HeaderCandidate : llvm::reverse(BlockPreorder)) {
-    const DFSInfo CandidateInfo = getDFSInfo(HeaderCandidate);
-
-    for (BlockT *Pred : predecessors(HeaderCandidate)) {
-      const DFSInfo PredDFSInfo = getDFSInfo(Pred);
-      // This automatically ignores unreachable predecessors since they have
-      // zeros in their DFSInfo.
-      if (CandidateInfo.isAncestorOf(PredDFSInfo))
-        Worklist.push_back(Pred);
-    }
-    if (Worklist.empty()) {
-      continue;
+  BlockInfos.assign(GraphTraits<FunctionT *>::getMaxNumber(F), BlockInfo{});
----------------
MaskRay wrote:

I have shrunk `struct BlockInfo` and `BlockInfos` initialization uses `memset` now.

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


More information about the llvm-commits mailing list