[llvm] [DominanceFrontier] Support multiple root nodes for post-dom (PR #181257)

Andrei Elovikov via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 2 08:51:33 PST 2026


https://github.com/eas updated https://github.com/llvm/llvm-project/pull/181257

>From 8053d517551ffbc7b15d97421f8a0b7a9e2f2a10 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Thu, 12 Feb 2026 14:15:41 -0800
Subject: [PATCH 1/4] [DominanceFrontier] Support multiple root nodes for
 post-dom

Post-dominator tree has a notion of a single virtual root node, use that
in the dominance-frontier implementation to support multiple root nodes.

Originally part of https://github.com/llvm/llvm-project/pull/179336 but
split up into a separate later PR to ease review.
---
 .../include/llvm/Analysis/DominanceFrontier.h | 11 +---
 .../llvm/Analysis/DominanceFrontierImpl.h     | 42 ++++++++----
 .../Vectorize/VPPostDomFrontierTest.cpp       | 64 +++++++++++++++++++
 3 files changed, 96 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/Analysis/DominanceFrontier.h b/llvm/include/llvm/Analysis/DominanceFrontier.h
index 829afefa43091..038731f2ca94e 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontier.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontier.h
@@ -101,16 +101,11 @@ class DominanceFrontierBase {
 #endif
 
   void analyze(DomTreeT &DT) {
-    assert((IsPostDom || DT.root_size() == 1) &&
-           "Only one entry block for forward domfronts!");
-    assert(DT.root_size() == 1 &&
-           "Support for post-dom frontiers with multiple roots hasn't been "
-           "implemented yet!");
-    this->Roots = {DT.getRoot()};
-    calculate(DT, DT[this->Roots[0]]);
+    copy(DT.roots(), std::back_inserter(Roots));
+    calculate(DT);
   }
 
-  void calculate(const DomTreeT &DT, const DomTreeNodeT *Node);
+  void calculate(const DomTreeT &DT);
 };
 
 class DominanceFrontier : public DominanceFrontierBase<BasicBlock, false> {
diff --git a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
index 9919f5432fb16..1973adc36cfc5 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
@@ -74,14 +74,16 @@ void DominanceFrontierBase<BlockT, IsPostDom>::dump() const {
 #endif
 
 template <class BlockT, bool IsPostDom>
-void DominanceFrontierBase<BlockT, IsPostDom>::calculate(
-    const DomTreeT &DT, const DomTreeNodeT *Node) {
-  BlockT *BB = Node->getBlock();
+void DominanceFrontierBase<BlockT, IsPostDom>::calculate(const DomTreeT &DT) {
+  // NOTE: RootNode might be virtual for `IsPostDom == true`.
+  const DomTreeNodeT *RootNode = DT.getRootNode();
+  BlockT *BB = RootNode->getBlock();
 
   std::vector<DFCalculateWorkObject<BlockT>> workList;
   SmallPtrSet<BlockT *, 32> visited;
 
-  workList.push_back(DFCalculateWorkObject<BlockT>(BB, nullptr, Node, nullptr));
+  workList.push_back(
+      DFCalculateWorkObject<BlockT>(BB, nullptr, RootNode, nullptr));
   do {
     DFCalculateWorkObject<BlockT> *currentW = &workList.back();
     assert(currentW && "Missing work object.");
@@ -90,17 +92,22 @@ void DominanceFrontierBase<BlockT, IsPostDom>::calculate(
     BlockT *parentBB = currentW->parentBB;
     const DomTreeNodeT *currentNode = currentW->Node;
     const DomTreeNodeT *parentNode = currentW->parentNode;
-    assert(currentBB && "Invalid work object. Missing current Basic Block");
     assert(currentNode && "Invalid work object. Missing current Node");
-    DomSetType &S = this->Frontiers[currentBB];
 
     // Visit each block only once.
     if (visited.insert(currentBB).second) {
-      // Loop over CFG successors to calculate DFlocal[currentNode]
-      for (const auto Child : children<GraphTy>(currentBB)) {
-        // Does Node immediately dominate this successor?
-        if (DT[Child]->getIDom() != currentNode)
-          S.insert(Child);
+      // Loop over CFG successors to calculate DFlocal[currentNode].
+      //
+      // Note that for `IsPostDom == true`, virtual root node (empty currentBB)
+      // is an immediate post-dominator for all the exit nodes (which are
+      // virtual node's CFG successors).
+      if (currentBB) {
+        DomSetType &S = this->Frontiers[currentBB];
+        for (const auto Child : children<GraphTy>(currentBB)) {
+          // Does Node immediately dominate this successor?
+          if (DT[Child]->getIDom() != currentNode)
+            S.insert(Child);
+        }
       }
     }
 
@@ -123,17 +130,26 @@ void DominanceFrontierBase<BlockT, IsPostDom>::calculate(
     // If all children are visited or there is any child then pop this block
     // from the workList.
     if (!visitChild) {
-      if (!parentBB) {
+      if (RootNode == currentNode) {
         break;
       }
 
+      workList.pop_back();
+      if (!parentBB) {
+        assert(IsPostDom && "For forward frontiers only root node (processed "
+                            "above) might not have a parent.");
+        // Processing below isn't necessary for the virtual root node in case
+        // of post-dominance frontier.
+        continue;
+      }
+
+      DomSetType &S = this->Frontiers[currentBB];
       typename DomSetType::const_iterator CDFI = S.begin(), CDFE = S.end();
       DomSetType &parentSet = this->Frontiers[parentBB];
       for (; CDFI != CDFE; ++CDFI) {
         if (!DT.properlyDominates(parentNode, DT[*CDFI]))
           parentSet.insert(*CDFI);
       }
-      workList.pop_back();
     }
 
   } while (!workList.empty());
diff --git a/llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp b/llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp
index a8097a4e1d5ca..f15f078c3a419 100644
--- a/llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp
@@ -90,5 +90,69 @@ TEST_F(VPPostDomFrontierTest, SingleExitTest) {
   EXPECT_EQ(F7.size(), 0u);
 }
 
+TEST_F(VPPostDomFrontierTest, MultipleExitsTest) {
+  //   VPBB0
+  //  /    \
+  // VBBB1 VBB2->VPBB3
+  //   /  \ |
+  // VPBB4 VPBB5
+  //    \  /
+  //    VPBB6
+  VPlan &Plan = getPlan();
+  VPBasicBlock *VPBB0 = Plan.getEntry();
+  VPBasicBlock *VPBB1 = Plan.createVPBasicBlock("VPBB1");
+  VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("VPBB2");
+  VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("VPBB3");
+  VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("VPBB4");
+  VPBasicBlock *VPBB5 = Plan.createVPBasicBlock("VPBB5");
+  VPBasicBlock *VPBB6 = Plan.createVPBasicBlock("VPBB6");
+
+  VPBlockUtils::connectBlocks(VPBB0, VPBB1);
+  VPBlockUtils::connectBlocks(VPBB0, VPBB2);
+  VPBlockUtils::connectBlocks(VPBB1, VPBB4);
+  VPBlockUtils::connectBlocks(VPBB1, VPBB5);
+  VPBlockUtils::connectBlocks(VPBB2, VPBB5);
+  VPBlockUtils::connectBlocks(VPBB2, VPBB3);
+  VPBlockUtils::connectBlocks(VPBB4, VPBB6);
+  VPBlockUtils::connectBlocks(VPBB5, VPBB6);
+
+  PostDomTreeBase<VPBlockBase> VPPDT;
+  VPPDT.recalculate(Plan);
+  DominanceFrontierBase<VPBlockBase, true> VPPDF;
+  VPPDF.analyze(VPPDT);
+
+  EXPECT_TRUE(VPPDF.find(VPBB0) != VPPDF.end());
+  EXPECT_TRUE(VPPDF.find(VPBB1) != VPPDF.end());
+  EXPECT_TRUE(VPPDF.find(VPBB2) != VPPDF.end());
+  EXPECT_TRUE(VPPDF.find(VPBB3) != VPPDF.end());
+  EXPECT_TRUE(VPPDF.find(VPBB4) != VPPDF.end());
+  EXPECT_TRUE(VPPDF.find(VPBB5) != VPPDF.end());
+  EXPECT_TRUE(VPPDF.find(VPBB6) != VPPDF.end());
+
+  auto F0 = VPPDF.find(VPBB0)->second;
+  auto F1 = VPPDF.find(VPBB1)->second;
+  auto F2 = VPPDF.find(VPBB2)->second;
+  auto F3 = VPPDF.find(VPBB3)->second;
+  auto F4 = VPPDF.find(VPBB4)->second;
+  auto F5 = VPPDF.find(VPBB5)->second;
+  auto F6 = VPPDF.find(VPBB6)->second;
+
+  EXPECT_EQ(F0.size(), 0u);
+  EXPECT_EQ(F1.size(), 1u);
+  EXPECT_TRUE(is_contained(F1, VPBB0));
+  EXPECT_EQ(F2.size(), 1u);
+  EXPECT_TRUE(is_contained(F2, VPBB0));
+  EXPECT_EQ(F3.size(), 1u);
+  EXPECT_TRUE(is_contained(F3, VPBB2));
+  EXPECT_EQ(F4.size(), 1u);
+  EXPECT_TRUE(is_contained(F4, VPBB1));
+  EXPECT_EQ(F5.size(), 2u);
+  EXPECT_TRUE(is_contained(F5, VPBB1));
+  EXPECT_TRUE(is_contained(F5, VPBB2));
+  EXPECT_EQ(F6.size(), 2u);
+  EXPECT_TRUE(is_contained(F6, VPBB0));
+  EXPECT_TRUE(is_contained(F6, VPBB2));
+}
+
 } // namespace
 } // namespace llvm

>From b0caaef4094a82a696bf24727afad30a6a30eadb Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Mon, 23 Feb 2026 10:57:04 -0800
Subject: [PATCH 2/4] Drop this->Roots, merge analyze/calculate

---
 .../include/llvm/Analysis/DominanceFrontier.h | 19 +------------------
 .../llvm/Analysis/DominanceFrontierImpl.h     |  5 ++++-
 2 files changed, 5 insertions(+), 19 deletions(-)

diff --git a/llvm/include/llvm/Analysis/DominanceFrontier.h b/llvm/include/llvm/Analysis/DominanceFrontier.h
index 038731f2ca94e..b76e6efd812fd 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontier.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontier.h
@@ -54,23 +54,11 @@ class DominanceFrontierBase {
   using BlockTraits = GraphTraits<GraphTy>;
 
   DomSetMapType Frontiers;
-  // Postdominators can have multiple roots.
-  SmallVector<BlockT *, IsPostDom ? 4 : 1> Roots;
   static constexpr bool IsPostDominators = IsPostDom;
 
 public:
   DominanceFrontierBase() = default;
 
-  /// getRoots - Return the root blocks of the current CFG.  This may include
-  /// multiple blocks if we are computing post dominators.  For forward
-  /// dominators, this will always be a single block (the entry node).
-  const SmallVectorImpl<BlockT *> &getRoots() const { return Roots; }
-
-  BlockT *getRoot() const {
-    assert(Roots.size() == 1 && "Should always have entry node!");
-    return Roots[0];
-  }
-
   /// isPostDominator - Returns true if analysis based of postdoms
   bool isPostDominator() const {
     return IsPostDominators;
@@ -100,12 +88,7 @@ class DominanceFrontierBase {
   void dump() const;
 #endif
 
-  void analyze(DomTreeT &DT) {
-    copy(DT.roots(), std::back_inserter(Roots));
-    calculate(DT);
-  }
-
-  void calculate(const DomTreeT &DT);
+  void analyze(const DomTreeT &DT);
 };
 
 class DominanceFrontier : public DominanceFrontierBase<BasicBlock, false> {
diff --git a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
index 1973adc36cfc5..983583bc61fa0 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
@@ -74,9 +74,12 @@ void DominanceFrontierBase<BlockT, IsPostDom>::dump() const {
 #endif
 
 template <class BlockT, bool IsPostDom>
-void DominanceFrontierBase<BlockT, IsPostDom>::calculate(const DomTreeT &DT) {
+void DominanceFrontierBase<BlockT, IsPostDom>::analyze(const DomTreeT &DT) {
   // NOTE: RootNode might be virtual for `IsPostDom == true`.
   const DomTreeNodeT *RootNode = DT.getRootNode();
+  assert(IsPostDom ||
+         (DT.root_size() == 1 && RootNode->getBlock() == *DT.root_begin()) &&
+             "Multiple roots for forward dominators?");
   BlockT *BB = RootNode->getBlock();
 
   std::vector<DFCalculateWorkObject<BlockT>> workList;

>From 2e6dee8411124732a737dfd062f32e6206694ad9 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Wed, 25 Feb 2026 14:34:28 -0800
Subject: [PATCH 3/4] Avoid `nullptr` insertion by checking `currentBB` first

---
 .../llvm/Analysis/DominanceFrontierImpl.h     | 22 +++++++++----------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
index 983583bc61fa0..d1d50344a88e1 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
@@ -97,20 +97,18 @@ void DominanceFrontierBase<BlockT, IsPostDom>::analyze(const DomTreeT &DT) {
     const DomTreeNodeT *parentNode = currentW->parentNode;
     assert(currentNode && "Invalid work object. Missing current Node");
 
+    // Note that for `IsPostDom == true`, virtual root node (empty currentBB)
+    // is an immediate post-dominator for all the exit nodes (which are
+    // virtual node's CFG successors).
+
     // Visit each block only once.
-    if (visited.insert(currentBB).second) {
+    if (currentBB && visited.insert(currentBB).second) {
       // Loop over CFG successors to calculate DFlocal[currentNode].
-      //
-      // Note that for `IsPostDom == true`, virtual root node (empty currentBB)
-      // is an immediate post-dominator for all the exit nodes (which are
-      // virtual node's CFG successors).
-      if (currentBB) {
-        DomSetType &S = this->Frontiers[currentBB];
-        for (const auto Child : children<GraphTy>(currentBB)) {
-          // Does Node immediately dominate this successor?
-          if (DT[Child]->getIDom() != currentNode)
-            S.insert(Child);
-        }
+      DomSetType &S = this->Frontiers[currentBB];
+      for (const auto Child : children<GraphTy>(currentBB)) {
+        // Does Node immediately dominate this successor?
+        if (DT[Child]->getIDom() != currentNode)
+          S.insert(Child);
       }
     }
 

>From a01e687467457ebfd033b0391d43630ca3288651 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <a.elovikov at gmail.com>
Date: Mon, 2 Mar 2026 08:51:23 -0800
Subject: [PATCH 4/4] Apply suggestions from code review

Co-authored-by: Luke Lau <luke_lau at icloud.com>
---
 llvm/include/llvm/Analysis/DominanceFrontierImpl.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
index d1d50344a88e1..5e4c88e525b2f 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
@@ -96,8 +96,9 @@ void DominanceFrontierBase<BlockT, IsPostDom>::analyze(const DomTreeT &DT) {
     const DomTreeNodeT *currentNode = currentW->Node;
     const DomTreeNodeT *parentNode = currentW->parentNode;
     assert(currentNode && "Invalid work object. Missing current Node");
+    assert((currentBB || DT.isVirtualRoot(CurrentNode)) && "Invalid work object. Missing current Basic Block");
 
-    // Note that for `IsPostDom == true`, virtual root node (empty currentBB)
+    // Note that for `IsPostDom == true`, the virtual root node (null currentBB)
     // is an immediate post-dominator for all the exit nodes (which are
     // virtual node's CFG successors).
 



More information about the llvm-commits mailing list