[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