[llvm] [DominanceFrontier] Support post-dominators (PR #179336)

Andrei Elovikov via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 4 10:16:31 PST 2026


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

>From 6064a620fe281aa03ebb3260bfb00c25b0f58c93 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Wed, 28 Jan 2026 13:54:12 -0800
Subject: [PATCH 1/2] [DominanceFrontier] Support post-dominators

I plan to use that to optimize mask creation in VPlan predicator by
`or`ing edge masks from the post-dominance frontier instead of all
predecessors in a subsequent patch. Note that it would require to use
the same unmodified post-dom tree for *all* the basic blocks in a VPlan
that is already limited to a particular loopnest so the algorithmic
complexity concerns behind the "deprecation" notice in the beggining of
`DominanceFrontier.h` (and also discussion in the
https://discourse.llvm.org/t/dominance-frontiers/21755 thread) don't
apply for my use case (at least to the best of my understanding).

The change here can be summarized to properly handling virtual root node
from `DominatorTreeBase::getRootNode()` and using that to start worklist
processing in the pre-existing dominance frontier calculation.

Since the only planned use-case is in the vectorizer, I'm adding a
VPlan-base unittest along with this change.

Based on top of https://github.com/llvm/llvm-project/pull/178724.
---
 .../include/llvm/Analysis/DominanceFrontier.h | 34 +++-----
 .../llvm/Analysis/DominanceFrontierImpl.h     | 42 ++++++---
 .../llvm/CodeGen/MachineDominanceFrontier.h   |  2 +-
 llvm/lib/Analysis/DominanceFrontier.cpp       |  1 -
 llvm/lib/CodeGen/MachineDominanceFrontier.cpp |  2 -
 .../Transforms/Vectorize/CMakeLists.txt       |  1 +
 .../Vectorize/VPPostDomFrontierTest.cpp       | 86 +++++++++++++++++++
 7 files changed, 126 insertions(+), 42 deletions(-)
 create mode 100644 llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp

diff --git a/llvm/include/llvm/Analysis/DominanceFrontier.h b/llvm/include/llvm/Analysis/DominanceFrontier.h
index ec49b36d64cfd..05868678d2b15 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontier.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontier.h
@@ -42,9 +42,12 @@ class DominanceFrontierBase {
   // deterministic.
   using DomSetType = SetVector<BlockT *>;
   using DomSetMapType = DenseMap<BlockT *, DomSetType>; // Dom set map
+  using DomTreeT = DominatorTreeBase<BlockT, IsPostDom>;
+  using DomTreeNodeT = DomTreeNodeBase<BlockT>;
 
 protected:
-  using BlockTraits = GraphTraits<BlockT *>;
+  using GraphTy = std::conditional_t<IsPostDom, Inverse<BlockT *>, BlockT *>;
+  using BlockTraits = GraphTraits<GraphTy>;
 
   DomSetMapType Frontiers;
   // Postdominators can have multiple roots.
@@ -92,34 +95,18 @@ class DominanceFrontierBase {
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
   void dump() const;
 #endif
-};
-
-//===-------------------------------------
-/// DominanceFrontier Class - Concrete subclass of DominanceFrontierBase that is
-/// used to compute a forward dominator frontiers.
-///
-template <class BlockT>
-class ForwardDominanceFrontierBase
-    : public DominanceFrontierBase<BlockT, false> {
-private:
-  using BlockTraits = GraphTraits<BlockT *>;
-
-public:
-  using DomTreeT = DomTreeBase<BlockT>;
-  using DomTreeNodeT = DomTreeNodeBase<BlockT>;
-  using DomSetType = typename DominanceFrontierBase<BlockT, false>::DomSetType;
 
   void analyze(DomTreeT &DT) {
-    assert(DT.root_size() == 1 &&
-           "Only one entry block for forward domfronts!");
-    this->Roots = {DT.getRoot()};
-    calculate(DT, DT[this->Roots[0]]);
+    for (auto *R : DT.roots()) {
+      Roots.push_back(R);
+    }
+    calculate(DT);
   }
 
-  void calculate(const DomTreeT &DT, const DomTreeNodeT *Node);
+  void calculate(const DomTreeT &DT);
 };
 
-class DominanceFrontier : public ForwardDominanceFrontierBase<BasicBlock> {
+class DominanceFrontier : public DominanceFrontierBase<BasicBlock, false> {
 public:
   using DomTreeT = DomTreeBase<BasicBlock>;
   using DomTreeNodeT = DomTreeNodeBase<BasicBlock>;
@@ -157,7 +144,6 @@ class DominanceFrontierWrapperPass : public FunctionPass {
 
 extern template class DominanceFrontierBase<BasicBlock, false>;
 extern template class DominanceFrontierBase<BasicBlock, true>;
-extern template class ForwardDominanceFrontierBase<BasicBlock>;
 
 /// Analysis pass which computes a \c DominanceFrontier.
 class DominanceFrontierAnalysis
diff --git a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
index c7fc463fd3af5..e4a2ddd60211e 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
@@ -73,15 +73,17 @@ void DominanceFrontierBase<BlockT, IsPostDom>::dump() const {
 }
 #endif
 
-template <class BlockT>
-void ForwardDominanceFrontierBase<BlockT>::calculate(const DomTreeT &DT,
-                                                     const DomTreeNodeT *Node) {
-  BlockT *BB = Node->getBlock();
+template <class BlockT, bool IsPostDom>
+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 ForwardDominanceFrontierBase<BlockT>::calculate(const DomTreeT &DT,
     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 Succ : children<BlockT *>(currentBB)) {
-        // Does Node immediately dominate this successor?
-        if (DT[Succ]->getIDom() != currentNode)
-          S.insert(Succ);
+      // 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 Succ : children<GraphTy>(currentBB)) {
+          // Does Node immediately dominate this successor?
+          if (DT[Succ]->getIDom() != currentNode)
+            S.insert(Succ);
+        }
       }
     }
 
@@ -123,17 +130,24 @@ void ForwardDominanceFrontierBase<BlockT>::calculate(const DomTreeT &DT,
     // 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) {
+        // IsPostDom == true with multiple roots, processing below isn't
+        // necessary for the virtual root node.
+        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/include/llvm/CodeGen/MachineDominanceFrontier.h b/llvm/include/llvm/CodeGen/MachineDominanceFrontier.h
index bbbd1a546a573..69cfc4a508c80 100644
--- a/llvm/include/llvm/CodeGen/MachineDominanceFrontier.h
+++ b/llvm/include/llvm/CodeGen/MachineDominanceFrontier.h
@@ -20,7 +20,7 @@
 namespace llvm {
 
 class MachineDominanceFrontier
-    : public ForwardDominanceFrontierBase<MachineBasicBlock> {
+    : public DominanceFrontierBase<MachineBasicBlock, false> {
 public:
  using DomTreeT = DomTreeBase<MachineBasicBlock>;
  using DomTreeNodeT = DomTreeNodeBase<MachineBasicBlock>;
diff --git a/llvm/lib/Analysis/DominanceFrontier.cpp b/llvm/lib/Analysis/DominanceFrontier.cpp
index b7396a17f35d2..0e5a56e09d1b9 100644
--- a/llvm/lib/Analysis/DominanceFrontier.cpp
+++ b/llvm/lib/Analysis/DominanceFrontier.cpp
@@ -23,7 +23,6 @@ namespace llvm {
 
 template class DominanceFrontierBase<BasicBlock, false>;
 template class DominanceFrontierBase<BasicBlock, true>;
-template class ForwardDominanceFrontierBase<BasicBlock>;
 
 } // end namespace llvm
 
diff --git a/llvm/lib/CodeGen/MachineDominanceFrontier.cpp b/llvm/lib/CodeGen/MachineDominanceFrontier.cpp
index 356123776bf2a..e7eb07392f742 100644
--- a/llvm/lib/CodeGen/MachineDominanceFrontier.cpp
+++ b/llvm/lib/CodeGen/MachineDominanceFrontier.cpp
@@ -17,8 +17,6 @@ using namespace llvm;
 
 namespace llvm {
 template class DominanceFrontierBase<MachineBasicBlock, false>;
-template class DominanceFrontierBase<MachineBasicBlock, true>;
-template class ForwardDominanceFrontierBase<MachineBasicBlock>;
 }
 
 char MachineDominanceFrontierWrapperPass::ID = 0;
diff --git a/llvm/unittests/Transforms/Vectorize/CMakeLists.txt b/llvm/unittests/Transforms/Vectorize/CMakeLists.txt
index af111a29b90e5..08f7bbfe448ac 100644
--- a/llvm/unittests/Transforms/Vectorize/CMakeLists.txt
+++ b/llvm/unittests/Transforms/Vectorize/CMakeLists.txt
@@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS
 add_llvm_unittest(VectorizeTests
   VPlanTest.cpp
   VPDomTreeTest.cpp
+  VPPostDomFrontierTest.cpp
   VPlanHCFGTest.cpp
   VPlanPatternMatchTest.cpp
   VPlanSlpTest.cpp
diff --git a/llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp b/llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp
new file mode 100644
index 0000000000000..b1c07e38c86cb
--- /dev/null
+++ b/llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../lib/Transforms/Vectorize/VPlan.h"
+
+#include "VPlanTestBase.h"
+#include "llvm/Analysis/DominanceFrontier.h"
+#include "llvm/Analysis/DominanceFrontierImpl.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace {
+
+using VPPostDomFrontierTest = VPlanTestBase;
+
+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(), 0);
+  EXPECT_EQ(F1.size(), 1);
+  EXPECT_TRUE(is_contained(F1, VPBB0));
+  EXPECT_EQ(F2.size(), 1);
+  EXPECT_TRUE(is_contained(F2, VPBB0));
+  EXPECT_EQ(F3.size(), 1);
+  EXPECT_TRUE(is_contained(F3, VPBB2));
+  EXPECT_EQ(F4.size(), 1);
+  EXPECT_TRUE(is_contained(F4, VPBB1));
+  EXPECT_EQ(F5.size(), 2);
+  EXPECT_TRUE(is_contained(F5, VPBB1));
+  EXPECT_TRUE(is_contained(F5, VPBB2));
+  EXPECT_EQ(F6.size(), 2);
+  EXPECT_TRUE(is_contained(F6, VPBB0));
+  EXPECT_TRUE(is_contained(F6, VPBB2));
+}
+
+} // namespace
+} // namespace llvm

>From 23f98d17f14204a0d8ea67b430acc2553b6f986a Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Wed, 4 Feb 2026 08:29:26 -0800
Subject: [PATCH 2/2] Rename Succ->Child

---
 llvm/include/llvm/Analysis/DominanceFrontierImpl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
index e4a2ddd60211e..19b815893c5fe 100644
--- a/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
+++ b/llvm/include/llvm/Analysis/DominanceFrontierImpl.h
@@ -103,10 +103,10 @@ void DominanceFrontierBase<BlockT, IsPostDom>::calculate(const DomTreeT &DT) {
       // virtual node's CFG successors).
       if (currentBB) {
         DomSetType &S = this->Frontiers[currentBB];
-        for (const auto Succ : children<GraphTy>(currentBB)) {
+        for (const auto Child : children<GraphTy>(currentBB)) {
           // Does Node immediately dominate this successor?
-          if (DT[Succ]->getIDom() != currentNode)
-            S.insert(Succ);
+          if (DT[Child]->getIDom() != currentNode)
+            S.insert(Child);
         }
       }
     }



More information about the llvm-commits mailing list