[llvm] [GenericDomTreeConstruction] Use 0-based DFS numbering and a trivial InfoRec (PR #207524)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 4 15:54:20 PDT 2026


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/207524

>From 5b99dcd1f6ea1871e17af1e53049f518336e0995 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 4 Jul 2026 09:11:06 -0700
Subject: [PATCH 1/4] [GenericDomTreeConstruction] Switch to 0-based DFS
 numbering

... and drop the unused index-0 sentinel from NumToNote/NumToInfo/IDoms.
`Unvisited` (-1u) marks unvisited nodes by DFS. 0 is now the DFS root,
or the virtual root for postdominators.
---
 .../llvm/Support/GenericDomTreeConstruction.h | 83 ++++++++++---------
 1 file changed, 42 insertions(+), 41 deletions(-)

diff --git a/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 869087cda219a..8aff074d6a2a4 100644
--- a/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -59,9 +59,12 @@ template <typename DomTreeT> struct SemiNCAInfo {
   static constexpr bool IsPostDom = DomTreeT::IsPostDominator;
   using GraphDiffT = GraphDiff<NodePtr, IsPostDom>;
 
+  // Marks a node that hasn't been visited by DFS.
+  static constexpr unsigned Unvisited = -1u;
+
   // Information record used by Semi-NCA during tree construction.
   struct InfoRec {
-    unsigned DFSNum = 0;
+    unsigned DFSNum = Unvisited;
     unsigned Parent = 0;
     unsigned Semi = 0;
     unsigned Label = 0;
@@ -69,9 +72,9 @@ template <typename DomTreeT> struct SemiNCAInfo {
     SmallVector<unsigned, 4> ReverseChildren;
   };
 
-  // Number to node mapping is 1-based. Initialize the mapping to start with
-  // a dummy element.
-  SmallVector<NodePtr, 64> NumToNode = {nullptr};
+  // Map a 0-based DFS number to the node. 0 is the DFS root, or the virtual
+  // root for postdominators.
+  SmallVector<NodePtr, 64> NumToNode;
   // If blocks have numbers (e.g., BasicBlock, MachineBasicBlock), store node
   // infos in a vector. Otherwise, store them in a map.
   std::conditional_t<GraphHasNodeNumbers<NodePtr>, SmallVector<InfoRec, 64>,
@@ -101,7 +104,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
   SemiNCAInfo(BatchUpdatePtr BUI) : BatchUpdates(BUI) {}
 
   void clear() {
-    NumToNode = {nullptr}; // Restore to initial state with a dummy start node.
+    NumToNode.clear();
     NodeInfos.clear();
     // Don't reset the pointer to BatchUpdateInfo here -- if there's an update
     // in progress, we need this information to continue it.
@@ -211,11 +214,11 @@ template <typename DomTreeT> struct SemiNCAInfo {
       auto &BBInfo = getNodeInfo(BB);
       BBInfo.ReverseChildren.push_back(ParentNum);
 
-      // Visited nodes always have positive DFS numbers.
-      if (BBInfo.DFSNum != 0)
+      if (BBInfo.DFSNum != Unvisited)
         continue;
       BBInfo.Parent = ParentNum;
-      BBInfo.DFSNum = BBInfo.Semi = BBInfo.Label = ++LastNum;
+      unsigned Num = LastNum++;
+      BBInfo.DFSNum = BBInfo.Semi = BBInfo.Label = Num;
       NumToNode.push_back(BB);
 
       constexpr bool Direction = IsReverse != IsPostDom; // XOR.
@@ -225,7 +228,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       if (!SuccOrder && !BatchUpdates) {
         for (const NodePtr Succ : getChildren<Direction>(BB))
           if (Condition(BB, Succ))
-            WorkList.push_back({Succ, LastNum});
+            WorkList.push_back({Succ, Num});
         continue;
       }
 
@@ -240,7 +243,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
         if (!Condition(BB, Succ))
           continue;
 
-        WorkList.push_back({Succ, LastNum});
+        WorkList.push_back({Succ, Num});
       }
     }
 
@@ -294,14 +297,14 @@ template <typename DomTreeT> struct SemiNCAInfo {
   // This function requires DFS to be run before calling it.
   void runSemiNCA() {
     const unsigned NextDFSNum(NumToNode.size());
-    // NumToInfo and IDoms are indexed by DFS number; index 0 is an unused
-    // sentinel. IDoms holds immediate dominators in DFS-number space,
-    // initialized below to spanning tree parents.
+    // NumToInfo and IDoms are indexed by DFS number; 0 is the root. IDoms holds
+    // immediate dominators in DFS-number space, initialized below to spanning
+    // tree parents.
     SmallVector<InfoRec *, 32> NumToInfo;
     NumToInfo.resize_for_overwrite(NextDFSNum);
     SmallVector<unsigned, 32> IDoms;
     IDoms.resize_for_overwrite(NextDFSNum);
-    for (unsigned i = 1; i < NextDFSNum; ++i) {
+    for (unsigned i = 0; i < NextDFSNum; ++i) {
       auto &VInfo = getNodeInfo(NumToNode[i]);
       IDoms[i] = VInfo.Parent;
       NumToInfo[i] = &VInfo;
@@ -309,7 +312,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
     // Step #1: Calculate the semidominators of all vertices.
     SmallVector<InfoRec *, 32> EvalStack;
-    for (unsigned i = NextDFSNum - 1; i >= 2; --i) {
+    for (unsigned i = NextDFSNum; --i;) {
       auto &WInfo = *NumToInfo[i];
 
       // Initialize the semi dominator to point to the parent node.
@@ -324,9 +327,8 @@ template <typename DomTreeT> struct SemiNCAInfo {
     // Step #2: Explicitly define the immediate dominator of each vertex.
     //          IDom[i] = NCA(SDom[i], SpanningTreeParent(i)).
     // SDom[i]'s DFS number is just Semi.
-    for (unsigned i = 2; i < NextDFSNum; ++i) {
+    for (unsigned i = 1; i < NextDFSNum; ++i) {
       auto &WInfo = *NumToInfo[i];
-      assert(WInfo.Semi != 0);
       unsigned WIDom = IDoms[i];
       while (WIDom > WInfo.Semi)
         WIDom = IDoms[WIDom];
@@ -342,12 +344,12 @@ template <typename DomTreeT> struct SemiNCAInfo {
   // This functions maps a nullptr CFG node to the virtual root tree node.
   void addVirtualRoot() {
     assert(IsPostDom && "Only postdominators have a virtual root");
-    assert(NumToNode.size() == 1 && "SNCAInfo must be freshly constructed");
+    assert(NumToNode.empty() && "SNCAInfo must be freshly constructed");
 
     auto &BBInfo = getNodeInfo(nullptr);
-    BBInfo.DFSNum = BBInfo.Semi = BBInfo.Label = 1;
+    BBInfo.DFSNum = BBInfo.Semi = BBInfo.Label = 0;
 
-    NumToNode.push_back(nullptr); // NumToNode[1] = nullptr;
+    NumToNode.push_back(nullptr); // NumToNode[0] = nullptr;
   }
 
   // For postdominators, nodes with no forward successors are trivial roots that
@@ -398,11 +400,11 @@ template <typename DomTreeT> struct SemiNCAInfo {
       if (!HasForwardSuccessors(N, BUI)) {
         Roots.push_back(N);
         // Run DFS not to walk this part of CFG later.
-        Num = SNCA.runDFS(N, Num, AlwaysDescend, 1);
+        Num = SNCA.runDFS(N, Num, AlwaysDescend, 0);
         LLVM_DEBUG(dbgs() << "Found a new trivial root: " << BlockNamePrinter(N)
                           << "\n");
         LLVM_DEBUG(dbgs() << "Last visited node: "
-                          << BlockNamePrinter(SNCA.NumToNode[Num]) << "\n");
+                          << BlockNamePrinter(SNCA.NumToNode[Num - 1]) << "\n");
       }
     }
 
@@ -426,7 +428,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       auto InitSuccOrderOnce = [&]() {
         SuccOrder = NodeOrderMap();
         for (const auto Node : nodes(DT.Parent))
-          if (SNCA.getNodeInfo(Node).DFSNum == 0)
+          if (SNCA.getNodeInfo(Node).DFSNum == Unvisited)
             for (const auto Succ : getChildren<false>(Node, SNCA.BatchUpdates))
               SuccOrder->try_emplace(Succ, 0);
 
@@ -450,7 +452,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       // unreachable node once, we may just visit it in two directions,
       // depending on how lucky we get.
       for (const NodePtr I : nodes(DT.Parent)) {
-        if (SNCA.getNodeInfo(I).DFSNum == 0) {
+        if (SNCA.getNodeInfo(I).DFSNum == Unvisited) {
           LLVM_DEBUG(dbgs()
                      << "\t\t\tVisiting node " << BlockNamePrinter(I) << "\n");
           // Find the furthest away we can get by following successors, then
@@ -471,14 +473,14 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
           const unsigned NewNum =
               SNCA.runDFS<true>(I, Num, AlwaysDescend, Num, &*SuccOrder);
-          const NodePtr FurthestAway = SNCA.NumToNode[NewNum];
+          const NodePtr FurthestAway = SNCA.NumToNode[NewNum - 1];
           LLVM_DEBUG(dbgs() << "\t\t\tFound a new furthest away node "
                             << "(non-trivial root): "
                             << BlockNamePrinter(FurthestAway) << "\n");
           Roots.push_back(FurthestAway);
           LLVM_DEBUG(dbgs() << "\t\t\tPrev DFSNum: " << Num << ", new DFSNum: "
                             << NewNum << "\n\t\t\tRemoving DFS info\n");
-          for (unsigned i = NewNum; i > Num; --i) {
+          for (unsigned i = NewNum; i-- > Num;) {
             const NodePtr N = SNCA.NumToNode[i];
             LLVM_DEBUG(dbgs() << "\t\t\t\tRemoving DFS info for "
                               << BlockNamePrinter(N) << "\n");
@@ -487,8 +489,8 @@ template <typename DomTreeT> struct SemiNCAInfo {
           }
           const unsigned PrevNum = Num;
           LLVM_DEBUG(dbgs() << "\t\t\tRunning reverse DFS\n");
-          Num = SNCA.runDFS(FurthestAway, Num, AlwaysDescend, 1);
-          for (unsigned i = PrevNum + 1; i <= Num; ++i)
+          Num = SNCA.runDFS(FurthestAway, Num, AlwaysDescend, 0);
+          for (unsigned i = PrevNum; i < Num; ++i)
             LLVM_DEBUG(dbgs() << "\t\t\t\tfound node "
                               << BlockNamePrinter(SNCA.NumToNode[i]) << "\n");
         }
@@ -497,7 +499,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
     LLVM_DEBUG(dbgs() << "Total: " << Total << ", Num: " << Num << "\n");
     LLVM_DEBUG(dbgs() << "Discovered CFG nodes:\n");
-    LLVM_DEBUG(for (size_t i = 0; i <= Num; ++i) dbgs()
+    LLVM_DEBUG(for (size_t i = 0; i < Num; ++i) dbgs()
                << i << ": " << BlockNamePrinter(SNCA.NumToNode[i]) << "\n");
 
     assert((Total + 1 == Num) && "Everything should have been visited");
@@ -539,9 +541,8 @@ template <typename DomTreeT> struct SemiNCAInfo {
       SNCA.clear();
       // Do a forward walk looking for the other roots.
       const unsigned Num = SNCA.runDFS<true>(Root, 0, AlwaysDescend, 0);
-      // Skip the start node and begin from the second one (note that DFS uses
-      // 1-based indexing).
-      for (unsigned x = 2; x <= Num; ++x) {
+      // Skip the start node (DFS number 0).
+      for (unsigned x = 1; x < Num; ++x) {
         const NodePtr N = SNCA.NumToNode[x];
         // If we wound another root in a (forward) DFS walk, remove the current
         // root from the set of roots, as it is reverse-reachable from the other
@@ -573,7 +574,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
     addVirtualRoot();
     unsigned Num = 1;
     for (const NodePtr Root : DT.Roots)
-      Num = runDFS(Root, Num, DC, 1);
+      Num = runDFS(Root, Num, DC, 0);
   }
 
   static void CalculateFromScratch(DomTreeT &DT, BatchUpdatePtr BUI) {
@@ -618,9 +619,9 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
   void attachNewSubtree(DomTreeT &DT, const TreeNodePtr AttachTo) {
     // Attach the first unreachable block to AttachTo.
-    getNodeInfo(NumToNode[1]).IDom = AttachTo->getBlock();
+    getNodeInfo(NumToNode[0]).IDom = AttachTo->getBlock();
     // Loop over all of the discovered blocks in the function...
-    for (NodePtr W : llvm::drop_begin(NumToNode)) {
+    for (NodePtr W : NumToNode) {
       if (DT.getNode(W))
         continue; // Already calculated the node before
 
@@ -636,8 +637,8 @@ template <typename DomTreeT> struct SemiNCAInfo {
   }
 
   void reattachExistingSubtree(DomTreeT &DT, const TreeNodePtr AttachTo) {
-    getNodeInfo(NumToNode[1]).IDom = AttachTo->getBlock();
-    for (const NodePtr N : llvm::drop_begin(NumToNode)) {
+    getNodeInfo(NumToNode[0]).IDom = AttachTo->getBlock();
+    for (const NodePtr N : NumToNode) {
       const TreeNodePtr TN = DT.getNode(N);
       assert(TN);
       const TreeNodePtr NewIDom = DT.getNode(getNodeInfo(N).IDom);
@@ -1122,7 +1123,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
     // Erase the unreachable subtree in reverse preorder to process all children
     // before deleting their parent.
-    for (unsigned i = LastDFSNum; i > 0; --i) {
+    for (unsigned i = LastDFSNum; i-- > 0;) {
       const NodePtr N = SNCA.NumToNode[i];
       LLVM_DEBUG(dbgs() << "Erasing node " << BlockNamePrinter(DT.getNode(N))
                         << "\n");
@@ -1290,7 +1291,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       if (DT.isVirtualRoot(TN))
         continue;
 
-      if (getNodeInfo(BB).DFSNum == 0) {
+      if (getNodeInfo(BB).DFSNum == Unvisited) {
         errs() << "DomTree node " << BlockNamePrinter(BB)
                << " not found by DFS walk!\n";
         errs().flush();
@@ -1503,7 +1504,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       });
 
       for (TreeNodePtr Child : TN->children())
-        if (getNodeInfo(Child->getBlock()).DFSNum != 0) {
+        if (getNodeInfo(Child->getBlock()).DFSNum != Unvisited) {
           errs() << "Child " << BlockNamePrinter(Child)
                  << " reachable after its parent " << BlockNamePrinter(BB)
                  << " is removed!\n";
@@ -1541,7 +1542,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
           if (S == N)
             continue;
 
-          if (getNodeInfo(S->getBlock()).DFSNum == 0) {
+          if (getNodeInfo(S->getBlock()).DFSNum == Unvisited) {
             errs() << "Node " << BlockNamePrinter(S)
                    << " not reachable when its sibling " << BlockNamePrinter(N)
                    << " is removed!\n";

>From d5799da9b8f86d1e1c7634dbeaae659b29901081 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 4 Jul 2026 13:30:20 -0700
Subject: [PATCH 2/4] use Chain Forward Star

---
 .../llvm/Support/GenericDomTreeConstruction.h | 44 ++++++++++++-------
 1 file changed, 29 insertions(+), 15 deletions(-)

diff --git a/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 8aff074d6a2a4..8ed8a781bca30 100644
--- a/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -60,16 +60,18 @@ template <typename DomTreeT> struct SemiNCAInfo {
   using GraphDiffT = GraphDiff<NodePtr, IsPostDom>;
 
   // Marks a node that hasn't been visited by DFS.
-  static constexpr unsigned Unvisited = -1u;
+  static constexpr unsigned Unvisited = 0;
 
-  // Information record used by Semi-NCA during tree construction.
+  // std::is_trivial information used by Semi-NCA during tree construction.
+  // DFSNum is the DFS number + 1, so a zero-initialized InfoRec is unvisited.
   struct InfoRec {
-    unsigned DFSNum = Unvisited;
-    unsigned Parent = 0;
-    unsigned Semi = 0;
-    unsigned Label = 0;
-    NodePtr IDom = nullptr;
-    SmallVector<unsigned, 4> ReverseChildren;
+    unsigned DFSNum;
+    unsigned Parent;
+    unsigned Semi;
+    unsigned Label;
+    NodePtr IDom;
+    // Head index + 1 into ReverseChildren; 0 = empty.
+    unsigned ReverseChildrenHead;
   };
 
   // Map a 0-based DFS number to the node. 0 is the DFS root, or the virtual
@@ -80,6 +82,9 @@ template <typename DomTreeT> struct SemiNCAInfo {
   std::conditional_t<GraphHasNodeNumbers<NodePtr>, SmallVector<InfoRec, 64>,
                      DenseMap<NodePtr, InfoRec>>
       NodeInfos;
+  // Reverse children of nodes; pairs of (DFSNum (predecessor), next-or-zero);
+  // forms a linked list in this vector.
+  SmallVector<std::pair<unsigned, unsigned>> ReverseChildren;
 
   using UpdateT = typename DomTreeT::UpdateType;
   using UpdateKind = typename DomTreeT::UpdateKind;
@@ -106,6 +111,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
   void clear() {
     NumToNode.clear();
     NodeInfos.clear();
+    ReverseChildren.clear();
     // Don't reset the pointer to BatchUpdateInfo here -- if there's an update
     // in progress, we need this information to continue it.
   }
@@ -192,8 +198,8 @@ template <typename DomTreeT> struct SemiNCAInfo {
   using NodeOrderMap = DenseMap<NodePtr, unsigned>;
 
   // Custom DFS implementation which can skip nodes based on a provided
-  // predicate. It also collects ReverseChildren so that we don't have to spend
-  // time getting predecessors in SemiNCA.
+  // predicate. It also builds the ReverseChildren lists to avoid re-fetching
+  // predecessors in SemiNCA.
   //
   // If IsReverse is set to true, the DFS walk will be performed backwards
   // relative to IsPostDom -- using reverse edges for dominators and forward
@@ -212,13 +218,16 @@ template <typename DomTreeT> struct SemiNCAInfo {
     while (!WorkList.empty()) {
       const auto [BB, ParentNum] = WorkList.pop_back_val();
       auto &BBInfo = getNodeInfo(BB);
-      BBInfo.ReverseChildren.push_back(ParentNum);
+      // Prepend the reverse edge (predecessor ParentNum) to BB's list.
+      ReverseChildren.emplace_back(ParentNum, BBInfo.ReverseChildrenHead);
+      BBInfo.ReverseChildrenHead = ReverseChildren.size();
 
       if (BBInfo.DFSNum != Unvisited)
         continue;
       BBInfo.Parent = ParentNum;
       unsigned Num = LastNum++;
-      BBInfo.DFSNum = BBInfo.Semi = BBInfo.Label = Num;
+      BBInfo.Semi = BBInfo.Label = Num;
+      BBInfo.DFSNum = Num + 1; // stored as Num+1
       NumToNode.push_back(BB);
 
       constexpr bool Direction = IsReverse != IsPostDom; // XOR.
@@ -297,6 +306,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
   // This function requires DFS to be run before calling it.
   void runSemiNCA() {
     const unsigned NextDFSNum(NumToNode.size());
+
     // NumToInfo and IDoms are indexed by DFS number; 0 is the root. IDoms holds
     // immediate dominators in DFS-number space, initialized below to spanning
     // tree parents.
@@ -317,10 +327,13 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
       // Initialize the semi dominator to point to the parent node.
       WInfo.Semi = WInfo.Parent;
-      for (unsigned N : WInfo.ReverseChildren) {
-        unsigned SemiU = NumToInfo[eval(N, i + 1, EvalStack, NumToInfo)]->Semi;
+      for (unsigned RCIdx = WInfo.ReverseChildrenHead; RCIdx != 0;) {
+        auto [Pred, NextIdx] = ReverseChildren[RCIdx - 1];
+        unsigned SemiU =
+            NumToInfo[eval(Pred, i + 1, EvalStack, NumToInfo)]->Semi;
         if (SemiU < WInfo.Semi)
           WInfo.Semi = SemiU;
+        RCIdx = NextIdx;
       }
     }
 
@@ -347,7 +360,8 @@ template <typename DomTreeT> struct SemiNCAInfo {
     assert(NumToNode.empty() && "SNCAInfo must be freshly constructed");
 
     auto &BBInfo = getNodeInfo(nullptr);
-    BBInfo.DFSNum = BBInfo.Semi = BBInfo.Label = 0;
+    BBInfo.Semi = BBInfo.Label = 0;
+    BBInfo.DFSNum = 1; // DFS number 0, stored as 1.
 
     NumToNode.push_back(nullptr); // NumToNode[0] = nullptr;
   }

>From b08062cf568a334a3c6e2064c8a91ff98a3a9f55 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 4 Jul 2026 13:51:21 -0700
Subject: [PATCH 3/4] Use more accurate DFSNumPlus1

---
 .../llvm/Support/GenericDomTreeConstruction.h | 25 ++++++++++---------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 8ed8a781bca30..408202e97ca7d 100644
--- a/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -62,15 +62,16 @@ template <typename DomTreeT> struct SemiNCAInfo {
   // Marks a node that hasn't been visited by DFS.
   static constexpr unsigned Unvisited = 0;
 
-  // std::is_trivial information used by Semi-NCA during tree construction.
-  // DFSNum is the DFS number + 1, so a zero-initialized InfoRec is unvisited.
+  // Information used by Semi-NCA during tree construction; trivial type for
+  // resize efficiency. DFSNumPlus1 is the DFS number + 1, so a zeroed InfoRec
+  // is unvisited.
   struct InfoRec {
-    unsigned DFSNum;
+    unsigned DFSNumPlus1;
     unsigned Parent;
     unsigned Semi;
     unsigned Label;
     NodePtr IDom;
-    // Head index + 1 into ReverseChildren; 0 = empty.
+    // Head index + 1 into ReverseChildren; 0: empty list.
     unsigned ReverseChildrenHead;
   };
 
@@ -222,12 +223,12 @@ template <typename DomTreeT> struct SemiNCAInfo {
       ReverseChildren.emplace_back(ParentNum, BBInfo.ReverseChildrenHead);
       BBInfo.ReverseChildrenHead = ReverseChildren.size();
 
-      if (BBInfo.DFSNum != Unvisited)
+      if (BBInfo.DFSNumPlus1 != Unvisited)
         continue;
       BBInfo.Parent = ParentNum;
       unsigned Num = LastNum++;
       BBInfo.Semi = BBInfo.Label = Num;
-      BBInfo.DFSNum = Num + 1; // stored as Num+1
+      BBInfo.DFSNumPlus1 = Num + 1;
       NumToNode.push_back(BB);
 
       constexpr bool Direction = IsReverse != IsPostDom; // XOR.
@@ -361,7 +362,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
     auto &BBInfo = getNodeInfo(nullptr);
     BBInfo.Semi = BBInfo.Label = 0;
-    BBInfo.DFSNum = 1; // DFS number 0, stored as 1.
+    BBInfo.DFSNumPlus1 = 1;
 
     NumToNode.push_back(nullptr); // NumToNode[0] = nullptr;
   }
@@ -442,7 +443,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       auto InitSuccOrderOnce = [&]() {
         SuccOrder = NodeOrderMap();
         for (const auto Node : nodes(DT.Parent))
-          if (SNCA.getNodeInfo(Node).DFSNum == Unvisited)
+          if (SNCA.getNodeInfo(Node).DFSNumPlus1 == Unvisited)
             for (const auto Succ : getChildren<false>(Node, SNCA.BatchUpdates))
               SuccOrder->try_emplace(Succ, 0);
 
@@ -466,7 +467,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       // unreachable node once, we may just visit it in two directions,
       // depending on how lucky we get.
       for (const NodePtr I : nodes(DT.Parent)) {
-        if (SNCA.getNodeInfo(I).DFSNum == Unvisited) {
+        if (SNCA.getNodeInfo(I).DFSNumPlus1 == Unvisited) {
           LLVM_DEBUG(dbgs()
                      << "\t\t\tVisiting node " << BlockNamePrinter(I) << "\n");
           // Find the furthest away we can get by following successors, then
@@ -1305,7 +1306,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       if (DT.isVirtualRoot(TN))
         continue;
 
-      if (getNodeInfo(BB).DFSNum == Unvisited) {
+      if (getNodeInfo(BB).DFSNumPlus1 == Unvisited) {
         errs() << "DomTree node " << BlockNamePrinter(BB)
                << " not found by DFS walk!\n";
         errs().flush();
@@ -1518,7 +1519,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
       });
 
       for (TreeNodePtr Child : TN->children())
-        if (getNodeInfo(Child->getBlock()).DFSNum != Unvisited) {
+        if (getNodeInfo(Child->getBlock()).DFSNumPlus1 != Unvisited) {
           errs() << "Child " << BlockNamePrinter(Child)
                  << " reachable after its parent " << BlockNamePrinter(BB)
                  << " is removed!\n";
@@ -1556,7 +1557,7 @@ template <typename DomTreeT> struct SemiNCAInfo {
           if (S == N)
             continue;
 
-          if (getNodeInfo(S->getBlock()).DFSNum == Unvisited) {
+          if (getNodeInfo(S->getBlock()).DFSNumPlus1 == Unvisited) {
             errs() << "Node " << BlockNamePrinter(S)
                    << " not reachable when its sibling " << BlockNamePrinter(N)
                    << " is removed!\n";

>From a8b04c17e58f9636646a1dc7e8e3c90100220587 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 4 Jul 2026 15:54:08 -0700
Subject: [PATCH 4/4] inline capacity 32

---
 llvm/include/llvm/Support/GenericDomTreeConstruction.h | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 408202e97ca7d..94ca2fb6c2f4b 100644
--- a/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -77,15 +77,15 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
   // Map a 0-based DFS number to the node. 0 is the DFS root, or the virtual
   // root for postdominators.
-  SmallVector<NodePtr, 64> NumToNode;
+  SmallVector<NodePtr, 32> NumToNode;
   // If blocks have numbers (e.g., BasicBlock, MachineBasicBlock), store node
   // infos in a vector. Otherwise, store them in a map.
-  std::conditional_t<GraphHasNodeNumbers<NodePtr>, SmallVector<InfoRec, 64>,
+  std::conditional_t<GraphHasNodeNumbers<NodePtr>, SmallVector<InfoRec, 32>,
                      DenseMap<NodePtr, InfoRec>>
       NodeInfos;
   // Reverse children of nodes; pairs of (DFSNum (predecessor), next-or-zero);
   // forms a linked list in this vector.
-  SmallVector<std::pair<unsigned, unsigned>> ReverseChildren;
+  SmallVector<std::pair<unsigned, unsigned>, 32> ReverseChildren;
 
   using UpdateT = typename DomTreeT::UpdateType;
   using UpdateKind = typename DomTreeT::UpdateKind;
@@ -307,7 +307,6 @@ template <typename DomTreeT> struct SemiNCAInfo {
   // This function requires DFS to be run before calling it.
   void runSemiNCA() {
     const unsigned NextDFSNum(NumToNode.size());
-
     // NumToInfo and IDoms are indexed by DFS number; 0 is the root. IDoms holds
     // immediate dominators in DFS-number space, initialized below to spanning
     // tree parents.



More information about the llvm-commits mailing list