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

via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 4 08:30:36 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

Author: Andrei Elovikov (eas)

<details>
<summary>Changes</summary>

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 handle 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.


---

Patch is 24.04 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/179336.diff


9 Files Affected:

- (modified) llvm/include/llvm/Analysis/DominanceFrontier.h (+10-24) 
- (modified) llvm/include/llvm/Analysis/DominanceFrontierImpl.h (+28-14) 
- (modified) llvm/include/llvm/CodeGen/MachineDominanceFrontier.h (+2-2) 
- (modified) llvm/lib/Analysis/DominanceFrontier.cpp (-1) 
- (modified) llvm/lib/CodeGen/MachineDominanceFrontier.cpp (-2) 
- (modified) llvm/lib/Transforms/Vectorize/VPlanCFG.h (+110-60) 
- (modified) llvm/unittests/Transforms/Vectorize/CMakeLists.txt (+1) 
- (added) llvm/unittests/Transforms/Vectorize/VPPostDomFrontierTest.cpp (+86) 
- (modified) llvm/unittests/Transforms/Vectorize/VPlanTest.cpp (+34-2) 


``````````diff
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..19b815893c5fe 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 Child : children<GraphTy>(currentBB)) {
+          // Does Node immediately dominate this successor?
+          if (DT[Child]->getIDom() != currentNode)
+            S.insert(Child);
+        }
       }
     }
 
@@ -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 7df060a3f5b1a..4556ff80018b6 100644
--- a/llvm/include/llvm/CodeGen/MachineDominanceFrontier.h
+++ b/llvm/include/llvm/CodeGen/MachineDominanceFrontier.h
@@ -18,7 +18,7 @@
 namespace llvm {
 
 class MachineDominanceFrontier : public MachineFunctionPass {
-  ForwardDominanceFrontierBase<MachineBasicBlock> Base;
+  DominanceFrontierBase<MachineBasicBlock, false> Base;
 
 public:
  using DomTreeT = DomTreeBase<MachineBasicBlock>;
@@ -35,7 +35,7 @@ class MachineDominanceFrontier : public MachineFunctionPass {
 
  MachineDominanceFrontier();
 
- ForwardDominanceFrontierBase<MachineBasicBlock> &getBase() { return Base; }
+ DominanceFrontierBase<MachineBasicBlock, false> &getBase() { return Base; }
 
  const SmallVectorImpl<MachineBasicBlock *> &getRoots() const {
    return Base.getRoots();
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 4d35ba89afe31..e85182574d06e 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>;
 }
 
 
diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index c79485c4cea71..9c9ed6666f011 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -25,90 +25,140 @@ namespace llvm {
 // GraphTraits specializations for VPlan Hierarchical Control-Flow Graphs     //
 //===----------------------------------------------------------------------===//
 
-/// Iterator to traverse all successors of a VPBlockBase node. This includes the
-/// entry node of VPRegionBlocks. Exit blocks of a region implicitly have their
-/// parent region's successors. This ensures all blocks in a region are visited
-/// before any blocks in a successor region when doing a reverse post-order
-// traversal of the graph. Region blocks themselves traverse only their entries
-// directly and not their successors. Those will be traversed when a region's
-// exiting block is traversed
-template <typename BlockPtrTy>
-class VPAllSuccessorsIterator
-    : public iterator_facade_base<VPAllSuccessorsIterator<BlockPtrTy>,
-                                  std::bidirectional_iterator_tag,
-                                  VPBlockBase> {
+/// Iterator to traverse all successors/predecessors of a VPBlockBase node,
+/// including its hierarchical successors/predecessors:
+///
+///     A
+///     |
+///  +-----+ <- Region R
+///  |  b  |
+///  |     |
+///  | ... |
+///  |     |
+///  |  e  |
+///  +-----+
+///     |
+///     B
+///
+///  Forward == true:
+///    Region blocks themselves traverse only their entries directly:
+///    Region's successor is traversed when processing its exiting block:
+///    children(A) == {R}
+///    children(R) == {b}
+///    children(e) == {B}
+///
+///  Forward == false:
+///    Region blocks themselves traverse only their exiting blocks directly:
+///    Region's predecessor is traversed when processing its entry block:
+///    children(B) == {R}
+///    children(R) == {e}
+///    children(b) == {A}
+///
+/// This ensures that all blocks of the region are visited before continuing
+/// traversal outside the region when doing a reverse post-order traversal of
+/// the VPlan.
+template <typename BlockPtrTy, bool Forward = true>
+class VPImmediateHierarchicalChildrenIterator
+    : public iterator_facade_base<
+          VPImmediateHierarchicalChildrenIterator<BlockPtrTy, Forward>,
+          std::bidirectional_iterator_tag, VPBlockBase> {
   BlockPtrTy Block;
-  /// Index of the current successor. For VPBasicBlock nodes, this simply is the
-  /// index for the successor array. For VPRegionBlock, SuccessorIdx == 0 is
-  /// used for the region's entry block, and SuccessorIdx - 1 are the indices
-  /// for the successor array.
-  size_t SuccessorIdx;
-
-  static BlockPtrTy getBlockWithSuccs(BlockPtrTy Current) {
-    while (Current && Current->getNumSuccessors() == 0)
+  /// Index of the current successor/predecessor.
+  ///
+  /// For VPBasicBlock nodes, this simply is the index for the
+  /// successors/predecessors array. For VPRegionBlock, EdgeIdx == 0 is used for
+  /// the region's entry/exiting block, and EdgeIdx - 1 are the indices for the
+  /// successors/predecessors array.
+  size_t EdgeIdx;
+
+  static size_t getNumOutgoingEdges(BlockPtrTy Current) {
+    if constexpr (Forward)
+      return Current->getNumSuccessors();
+    else
+      return Current->getNumPredecessors();
+  }
+
+  /// Auto-deduce resulting type depending on the constness of the template type
+  /// parameter BlockPtrTy. Use `decltype(auto)` instead of just `auto` to
+  /// preserve reference and avoid copies.
+  static decltype(auto) getOutgoingEdges(BlockPtrTy Current) {
+    if constexpr (Forward)
+      return Current->getSuccessors();
+    else
+      return Current->getPredecessors();
+  }
+
+  static BlockPtrTy getBlockWithOutgoingEdges(BlockPtrTy Current) {
+    while (Current && getNumOutgoingEdges(Current) == 0)
       Current = Current->getParent();
     return Current;
   }
 
   /// Templated helper to dereference successor \p SuccIdx of \p Block. Used by
   /// both the const and non-const operator* implementations.
-  template <typename T1> static T1 deref(T1 Block, unsigned SuccIdx) {
+  template <typename T1> static T1 deref(T1 Block, unsigned EdgeIdx) {
     if (auto *R = dyn_cast<VPRegionBlock>(Block)) {
-      assert(SuccIdx == 0);
-      return R->getEntry();
+      assert(EdgeIdx == 0);
+      if constexpr (Forward)
+        return R->getEntry();
+      else
+        return R->getExiting();
     }
 
     // For exit blocks, use the next parent region with successors.
-    return getBlockWithSuccs(Block)->getSuccessors()[SuccIdx];
+    return getOutgoingEdges(getBlockWithOutgoingEdges(Block))[EdgeIdx];
   }
 
 public:
   /// Used by iterator_facade_base with bidirectional_iterator_tag.
   using reference = BlockPtrTy;
 
-  VPAllSuccessorsIterator(BlockPtrTy Block, size_t Idx = 0)
-      : Block(Block), SuccessorIdx(Idx) {}
-  VPAllSuccessorsIterator(const VPAllSuccessorsIterator &Other)
-      : Block(Other.Block), SuccessorIdx(Other.SuccessorIdx) {}
+  VPImmediateHierarchicalChildrenIterator(BlockPtrTy Block, size_t Idx = 0)
+      : Block(Block), EdgeIdx(Idx) {}
+  VPImmediateHierarchicalChildrenIterator(
+      const VPImmediateHierarchicalChildrenIterator &Other)
+      : Block(Other.Block), EdgeIdx(Other.EdgeIdx) {}
 
-  VPAllSuccessorsIterator &operator=(const VPAllSuccessorsIterator &R) {
+  VPImmediateHierarchicalChildrenIterator &
+  operator=(const VPImmediateHierarchicalChildrenIterator &R) {
     Block = R.Block;
-    SuccessorIdx = R.SuccessorIdx;
+    EdgeIdx = R.EdgeIdx;
     return *this;
   }
 
-  static VPAllSuccessorsIterator end(BlockPtrTy Block) {
+  static VPImmediateHierarchicalChildrenIterator end(BlockPtrTy Block) {
     if (auto *R = dyn_cast<VPRegionBlock>(Block)) {
-      // Traverse through the region's entry node.
+      // Traverse through the region's entry/exiting (based on Forward) node.
       return {R, 1};
     }
-    BlockPtrTy ParentWithSuccs = getBlockWithSuccs(Block);
-    unsigned NumSuccessors =
-        ParentWithSuccs ? ParentWithSuccs->getNumSuccessors() : 0;
-    return {Block, NumSuccessors};
+    BlockPtrTy ParentWithOutgoingEdges = getBlockWithOutgoingEdges(Block);
+    unsigned NumOutgoingEdges =
+        ParentWithOutgoingEdges ? getNumOutgoingEdges(ParentWithOutgoingEdges)
+                                : 0;
+    return {Block, NumOutgoingEdges};
   }
 
-  bool operator==(const VPAllSuccessorsIterator &R) const {
-    return Block == R.Block && SuccessorIdx == R.SuccessorIdx;
+  bool operator==(const VPImmediateHierarchicalChildrenIterator &R) const {
+    return Block == R.Block && EdgeIdx == R.EdgeIdx;
   }
 
-  const VPBlockBase *operator*() const { return deref(Block, SuccessorIdx); }
+  const VPBlockBase *operator*() const { return deref(Block, EdgeIdx); }
 
-  BlockPtrTy operator*() { return deref(Block, SuccessorIdx); }
+  BlockPtrTy operator*() { return deref(Block, EdgeIdx); }
 
-  VPAllSuccessorsIterator &operator++() {
-    SuccessorIdx++;
+  VPImmediateHierarchicalChildrenIterator &operator++() {
+    EdgeIdx++;
     return *this;
   }
 
-  VPAllSuccessorsIterator &operator--() {
-    SuccessorIdx--;
+  VPImmediateHierarchicalChildrenIterator &operator--() {
+    EdgeIdx--;
     return *this;
   }
 
-  VPAllSuccessorsIterator operator++(int X) {
-    VPAllSuccessorsIterator Orig = *this;
-    SuccessorIdx++;
+  VPImmediateHierarchicalChildrenIterator operator++(int X) {
+    VPImmediateHierarchicalChildrenIterator Orig = *this;
+    EdgeIdx++;
     return Orig;
   }
 };
@@ -129,7 +179,8 @@ template <typename BlockTy> class VPBlockDeepTraversalWrapper {
 /// reverse post-order traversal of the graph.
 template <> struct GraphTraits<VPBlockDeepTraversalWrapper<VPBlockBase *>> {
   using NodeRef = VPBlockBase *;
-  using ChildIteratorType = VPAllSuccessorsIterator<VPBlockBase *>;
+  using ChildIteratorType =
+      VPImmediateHierarchicalChildrenIterator<VPBlockBase *>;
 
   static NodeRef getEntryNode(VPBlockDeepTraversalWrapper<VPBlockBase *> N) {
     return N.getEntry();
@@ -147,7 +198,8 @@ template <> struct GraphTraits<VPBlockDeepTraversalWrapper<VPBlockBase *>> {
 template <>
 struct GraphTraits<VPBlockDeepTraversalWrapper<const VPBlockBase *>> {
   using NodeRef = const VPBlockBase *;
-  using ChildIteratorType = VPAllSuccessorsIterator<const VPBlockBase *>;
+  using ChildIteratorType =
+      VPImmediateHierarchicalChildrenIterator<const VPBlockBase *>;
 
   static NodeRef
   getEntryNode(VPBlockDeepTraversalWrapper<const VPBlockBase *> N) {
@@ -257,7 +309,8 @@ vp_depth_first_deep(const VPBlockBase *G) {
 
 template <> struct GraphTraits<VPBlockBase *> {
   using NodeRef = VPBlockBase *;
-  using ChildIteratorType = VPAllSuccessorsIterator<VPBlockBase *>;
+  using ChildIteratorType =
+      VPImmediateHierarchicalChildrenIterator<VPBlockBase *>;
 
   static NodeRef getEntryNode(NodeRef N) { return N; }
 
@@ -272,7 +325,8 @@ template <> struct GraphTraits<VPBlockBase *> {
 
 template <> struct GraphTraits<const VPBlockBase *> {
   using NodeRef = const VPBlockBase *;
-  using ChildIteratorType = VPAllSuccessorsIterator<const VPBlockBase *>;
+  using ChildIteratorType =
+      VPImmediateHierarchicalChildrenIterator<const VPBlockBase *>;
 
   static NodeRef getEntryNode(NodeRef N) { return N; }
 
@@ -285,23 +339,19 @@ template <> struct GraphTraits<const VPBlockBase *> {
   }
 };
 
-/// Inverse graph traits are not implemented yet.
-/// TODO: Implement a version of VPBlockNonRecursiveTraversalWrapper to traverse
-/// predecessors recursively through regions.
 template <> struct GraphTraits<Inverse<VPBlockBase *>> {
   using NodeRef = VPBlockBase *;
-  using ChildIteratorType = SmallVectorImpl<VPBlockBase *>::iterator;
+  using ChildIteratorType =
+      VPImmediateHierarchicalChildrenIterator<VPBlockBase *, /*Forward=*/false>;
 
-  static NodeRef getEntryNode(Inverse<NodeRef> B) {
-    llvm_unreachable("not implemented");
-  }
+  static NodeRef getEntryNode(Inverse<NodeRef> B) { return B.Graph; }
 
   static inline ChildIteratorType child_begin(NodeRef N) {
-    llvm_unreachable("not implemented");
+    return ChildIteratorType(N);
   }
 
   static inline ChildIteratorType child_end(NodeRef N) {
-    llvm_unreachable("not implemented");
+    return ChildIteratorType::end(N);
   }
 };
 
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::connectBlock...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list