[llvm] [GenericDomTreeConstruction] Speed up Semi-NCA construction (PR #207474)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 4 08:27:11 PDT 2026


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

>From 48b0a2a9551bf183dd5c9db9ed329897063d334c Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 1 Jul 2026 10:45:07 -0700
Subject: [PATCH 1/2] [GenericDomTreeConstruction] Speed up Semi-NCA
 construction

Run the immediate-dominator step in DFS-number space instead of chasing
NodePtr IDom through getNodeInfo()/getNumber(), and drop the redundant
NumToInfo[Semi]->DFSNum lookup. Use an inline capacity 32 (sqlite3's p90
is ~29 blocks), with no measurable gain from a larger inline buffer.

Make getChildren return a lazy filtered range and iterate it directly in
runDFS's common case, instead of materializing a SmallVector per node just to
push its successors onto the worklist.

clang::CFGBlock::AdjacentBlock still needs special handling: it is a proxy
convertible to CFGBlock* that can be null for unreachable edges (also see
commit 7ea0ee30588e), and it is not easy to migrate off.
---
 .../llvm/Support/GenericDomTreeConstruction.h | 63 +++++++++++--------
 1 file changed, 37 insertions(+), 26 deletions(-)

diff --git a/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 511c28400e347..1c196f04acb7b 100644
--- a/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -111,19 +111,25 @@ template <typename DomTreeT> struct SemiNCAInfo {
   static SmallVector<NodePtr, 8> getChildren(NodePtr N, BatchUpdatePtr BUI) {
     if (BUI)
       return BUI->PreViewCFG.template getChildren<Inversed>(N);
-    return getChildren<Inversed>(N);
+    // Force the element type to NodePtr. some graphs (clang's
+    // CFGBlock::AdjacentBlock) yield a proxy convertible to NodePtr rather than
+    // NodePtr itself.
+    auto Children = getChildren<Inversed>(N);
+    return SmallVector<NodePtr, 8>(Children.begin(), Children.end());
   }
 
-  template <bool Inversed>
-  static SmallVector<NodePtr, 8> getChildren(NodePtr N) {
+  // Returns a lazy range over N's children, reversed for non-inverted graphs so
+  // a LIFO worklist visits them in their natural order. Graphs whose children
+  // aren't NodePtr (clang's CFGBlock::AdjacentBlock) may include nullptr, which
+  // are filtered out.
+  template <bool Inversed> static auto getChildren(NodePtr N) {
     using DirectedNodeT =
         std::conditional_t<Inversed, Inverse<NodePtr>, NodePtr>;
-    auto R = children<DirectedNodeT>(N);
-    SmallVector<NodePtr, 8> Res(detail::reverse_if<!Inversed>(R));
-
-    // Remove nullptr children for clang.
-    llvm::erase(Res, nullptr);
-    return Res;
+    auto R = detail::reverse_if<!Inversed>(children<DirectedNodeT>(N));
+    if constexpr (std::is_same_v<std::decay_t<decltype(*R.begin())>, NodePtr>)
+      return R;
+    else
+      return llvm::make_filter_range(R, [](NodePtr C) { return C != nullptr; });
   }
 
   InfoRec &getNodeInfo(NodePtr BB) {
@@ -212,6 +218,16 @@ template <typename DomTreeT> struct SemiNCAInfo {
       NumToNode.push_back(BB);
 
       constexpr bool Direction = IsReverse != IsPostDom; // XOR.
+      // Common case: iterate the lazy successor range directly. Materializing
+      // is only needed to reorder by SuccOrder or to consult a batch update
+      // view.
+      if (!SuccOrder && !BatchUpdates) {
+        for (const NodePtr Succ : getChildren<Direction>(BB))
+          if (Condition(BB, Succ))
+            WorkList.push_back({Succ, LastNum});
+        continue;
+      }
+
       auto Successors = getChildren<Direction>(BB, BatchUpdates);
       if (SuccOrder && Successors.size() > 1)
         llvm::sort(
@@ -277,13 +293,14 @@ template <typename DomTreeT> struct SemiNCAInfo {
   // This function requires DFS to be run before calling it.
   void runSemiNCA() {
     const unsigned NextDFSNum(NumToNode.size());
-    SmallVector<InfoRec *, 8> NumToInfo = {nullptr};
+    SmallVector<InfoRec *, 32> NumToInfo = {nullptr};
     NumToInfo.reserve(NextDFSNum);
-    // Initialize IDoms to spanning tree parents.
+    // Immediate dominators in DFS-number space, initialized to spanning tree
+    // parents.
+    SmallVector<unsigned, 32> IDoms(NextDFSNum);
     for (unsigned i = 1; i < NextDFSNum; ++i) {
-      const NodePtr V = NumToNode[i];
-      auto &VInfo = getNodeInfo(V);
-      VInfo.IDom = NumToNode[VInfo.Parent];
+      auto &VInfo = getNodeInfo(NumToNode[i]);
+      IDoms[i] = VInfo.Parent;
       NumToInfo.push_back(&VInfo);
     }
 
@@ -303,21 +320,15 @@ template <typename DomTreeT> struct SemiNCAInfo {
 
     // Step #2: Explicitly define the immediate dominator of each vertex.
     //          IDom[i] = NCA(SDom[i], SpanningTreeParent(i)).
-    // Note that the parents were stored in IDoms and later got invalidated
-    // during path compression in Eval.
+    // SDom[i]'s DFS number is just Semi.
     for (unsigned i = 2; i < NextDFSNum; ++i) {
       auto &WInfo = *NumToInfo[i];
       assert(WInfo.Semi != 0);
-      const unsigned SDomNum = NumToInfo[WInfo.Semi]->DFSNum;
-      NodePtr WIDomCandidate = WInfo.IDom;
-      while (true) {
-        auto &WIDomCandidateInfo = getNodeInfo(WIDomCandidate);
-        if (WIDomCandidateInfo.DFSNum <= SDomNum)
-          break;
-        WIDomCandidate = WIDomCandidateInfo.IDom;
-      }
-
-      WInfo.IDom = WIDomCandidate;
+      unsigned WIDom = IDoms[i];
+      while (WIDom > WInfo.Semi)
+        WIDom = IDoms[WIDom];
+      IDoms[i] = WIDom;
+      WInfo.IDom = NumToNode[WIDom];
     }
   }
 

>From 25747c97e0728a8993f400683c43b2b21e016072 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 4 Jul 2026 08:27:00 -0700
Subject: [PATCH 2/2] use resize_for_overwrite; improve clang CFGBlock comments

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

diff --git a/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 1c196f04acb7b..869087cda219a 100644
--- a/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -119,13 +119,14 @@ template <typename DomTreeT> struct SemiNCAInfo {
   }
 
   // Returns a lazy range over N's children, reversed for non-inverted graphs so
-  // a LIFO worklist visits them in their natural order. Graphs whose children
-  // aren't NodePtr (clang's CFGBlock::AdjacentBlock) may include nullptr, which
-  // are filtered out.
+  // a LIFO worklist visits them in their natural order.
   template <bool Inversed> static auto getChildren(NodePtr N) {
     using DirectedNodeT =
         std::conditional_t<Inversed, Inverse<NodePtr>, NodePtr>;
     auto R = detail::reverse_if<!Inversed>(children<DirectedNodeT>(N));
+    // Most graphs' iterators yield NodePtr directly; return the range as is.
+    // clang's CFGBlock instead yields a CFGBlock::AdjacentBlock proxy that is
+    // convertible to NodePtr but can be null for AB_Unreachable.
     if constexpr (std::is_same_v<std::decay_t<decltype(*R.begin())>, NodePtr>)
       return R;
     else
@@ -293,15 +294,17 @@ template <typename DomTreeT> struct SemiNCAInfo {
   // This function requires DFS to be run before calling it.
   void runSemiNCA() {
     const unsigned NextDFSNum(NumToNode.size());
-    SmallVector<InfoRec *, 32> NumToInfo = {nullptr};
-    NumToInfo.reserve(NextDFSNum);
-    // Immediate dominators in DFS-number space, initialized to spanning tree
-    // parents.
-    SmallVector<unsigned, 32> IDoms(NextDFSNum);
+    // 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.
+    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) {
       auto &VInfo = getNodeInfo(NumToNode[i]);
       IDoms[i] = VInfo.Parent;
-      NumToInfo.push_back(&VInfo);
+      NumToInfo[i] = &VInfo;
     }
 
     // Step #1: Calculate the semidominators of all vertices.



More information about the llvm-commits mailing list