[llvm] d510c4c - [VPlan] Generalize `VPAllSuccessorsIterator` to support predecessors (#178724)

via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 3 13:03:14 PST 2026


Author: Andrei Elovikov
Date: 2026-02-03T21:03:09Z
New Revision: d510c4c3f3ca26ef3b73a4b5205007471e412f5a

URL: https://github.com/llvm/llvm-project/commit/d510c4c3f3ca26ef3b73a4b5205007471e412f5a
DIFF: https://github.com/llvm/llvm-project/commit/d510c4c3f3ca26ef3b73a4b5205007471e412f5a.diff

LOG: [VPlan] Generalize `VPAllSuccessorsIterator` to support predecessors (#178724)

To be used in Mel's https://github.com/llvm/llvm-project/pull/173265.

---------

Co-authored-by: Florian Hahn <flo at fhahn.com>
Co-authored-by: Luke Lau <luke_lau at icloud.com>

Added: 
    

Modified: 
    llvm/lib/Transforms/Vectorize/VPlanCFG.h
    llvm/unittests/Transforms/Vectorize/VPlanTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index c79485c4cea71..ec3016f700fc2 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -25,90 +25,136 @@ 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 implictly 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 implicitly traversed when processing its entry
+///    block.
+///    children(B) == {R}
+///    children(R) == {e}
+///    children(b) == {A}
+///
+/// The scheme described above 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 VPHierarchicalChildrenIterator
+    : public iterator_facade_base<
+          VPHierarchicalChildrenIterator<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();
+  }
+
+  static ArrayRef<BlockPtrTy> 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) {
+  /// Templated helper to dereference successor/predecessor \p EdgeIdx of \p
+  /// Block. Used by both the const and non-const operator* implementations.
+  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) {}
+  VPHierarchicalChildrenIterator(BlockPtrTy Block, size_t Idx = 0)
+      : Block(Block), EdgeIdx(Idx) {}
+  VPHierarchicalChildrenIterator(const VPHierarchicalChildrenIterator &Other)
+      : Block(Other.Block), EdgeIdx(Other.EdgeIdx) {}
 
-  VPAllSuccessorsIterator &operator=(const VPAllSuccessorsIterator &R) {
+  VPHierarchicalChildrenIterator &
+  operator=(const VPHierarchicalChildrenIterator &R) {
     Block = R.Block;
-    SuccessorIdx = R.SuccessorIdx;
+    EdgeIdx = R.EdgeIdx;
     return *this;
   }
 
-  static VPAllSuccessorsIterator end(BlockPtrTy Block) {
+  static VPHierarchicalChildrenIterator 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 VPHierarchicalChildrenIterator &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++;
+  VPHierarchicalChildrenIterator &operator++() {
+    EdgeIdx++;
     return *this;
   }
 
-  VPAllSuccessorsIterator &operator--() {
-    SuccessorIdx--;
+  VPHierarchicalChildrenIterator &operator--() {
+    EdgeIdx--;
     return *this;
   }
 
-  VPAllSuccessorsIterator operator++(int X) {
-    VPAllSuccessorsIterator Orig = *this;
-    SuccessorIdx++;
+  VPHierarchicalChildrenIterator operator++(int X) {
+    VPHierarchicalChildrenIterator Orig = *this;
+    EdgeIdx++;
     return Orig;
   }
 };
@@ -129,7 +175,7 @@ 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 = VPHierarchicalChildrenIterator<VPBlockBase *>;
 
   static NodeRef getEntryNode(VPBlockDeepTraversalWrapper<VPBlockBase *> N) {
     return N.getEntry();
@@ -147,7 +193,7 @@ template <> struct GraphTraits<VPBlockDeepTraversalWrapper<VPBlockBase *>> {
 template <>
 struct GraphTraits<VPBlockDeepTraversalWrapper<const VPBlockBase *>> {
   using NodeRef = const VPBlockBase *;
-  using ChildIteratorType = VPAllSuccessorsIterator<const VPBlockBase *>;
+  using ChildIteratorType = VPHierarchicalChildrenIterator<const VPBlockBase *>;
 
   static NodeRef
   getEntryNode(VPBlockDeepTraversalWrapper<const VPBlockBase *> N) {
@@ -257,7 +303,7 @@ vp_depth_first_deep(const VPBlockBase *G) {
 
 template <> struct GraphTraits<VPBlockBase *> {
   using NodeRef = VPBlockBase *;
-  using ChildIteratorType = VPAllSuccessorsIterator<VPBlockBase *>;
+  using ChildIteratorType = VPHierarchicalChildrenIterator<VPBlockBase *>;
 
   static NodeRef getEntryNode(NodeRef N) { return N; }
 
@@ -272,7 +318,7 @@ template <> struct GraphTraits<VPBlockBase *> {
 
 template <> struct GraphTraits<const VPBlockBase *> {
   using NodeRef = const VPBlockBase *;
-  using ChildIteratorType = VPAllSuccessorsIterator<const VPBlockBase *>;
+  using ChildIteratorType = VPHierarchicalChildrenIterator<const VPBlockBase *>;
 
   static NodeRef getEntryNode(NodeRef N) { return N; }
 
@@ -285,23 +331,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 =
+      VPHierarchicalChildrenIterator<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/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 13013794f3560..52634abcfb670 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -396,11 +396,27 @@ TEST_F(VPBasicBlockTest, TraversingIteratorTest) {
 
     // Successors of R1.
     SmallVector<const VPBlockBase *> FromIterator(
-        VPAllSuccessorsIterator<VPBlockBase *>(R1),
-        VPAllSuccessorsIterator<VPBlockBase *>::end(R1));
+        VPHierarchicalChildrenIterator<VPBlockBase *>(R1),
+        VPHierarchicalChildrenIterator<VPBlockBase *>::end(R1));
     EXPECT_EQ(1u, FromIterator.size());
     EXPECT_EQ(R1BB1, FromIterator[0]);
 
+    // Predecessors of R1.
+    FromIterator.clear();
+    copy(VPHierarchicalChildrenIterator<VPBlockBase *, false>(R1),
+         VPHierarchicalChildrenIterator<VPBlockBase *, false>::end(R1),
+         std::back_inserter(FromIterator));
+    EXPECT_EQ(1u, FromIterator.size());
+    EXPECT_EQ(R1BB4, FromIterator[0]);
+
+    // Predecessors of R1BB1.
+    FromIterator.clear();
+    copy(VPHierarchicalChildrenIterator<VPBlockBase *, false>(R1BB1),
+         VPHierarchicalChildrenIterator<VPBlockBase *, false>::end(R1BB1),
+         std::back_inserter(FromIterator));
+    EXPECT_EQ(1u, FromIterator.size());
+    EXPECT_EQ(VPBB0, FromIterator[0]);
+
     // Depth-first.
     VPBlockDeepTraversalWrapper<VPBlockBase *> Start(R1);
     FromIterator.clear();
@@ -415,6 +431,21 @@ TEST_F(VPBasicBlockTest, TraversingIteratorTest) {
     EXPECT_EQ(R2BB2, FromIterator[6]);
     EXPECT_EQ(R1BB3, FromIterator[7]);
 
+    // Depth-first on the inverse graph (traverse predecessors).
+    FromIterator.clear();
+    copy(depth_first<Inverse<VPBlockBase *>>(R2),
+         std::back_inserter(FromIterator));
+    EXPECT_EQ(9u, FromIterator.size());
+    EXPECT_EQ(R2, FromIterator[0]);
+    EXPECT_EQ(R2BB2, FromIterator[1]);
+    EXPECT_EQ(R2BB1, FromIterator[2]);
+    EXPECT_EQ(R1, FromIterator[3]);
+    EXPECT_EQ(R1BB4, FromIterator[4]);
+    EXPECT_EQ(R1BB2, FromIterator[5]);
+    EXPECT_EQ(R1BB1, FromIterator[6]);
+    EXPECT_EQ(VPBB0, FromIterator[7]);
+    EXPECT_EQ(R1BB3, FromIterator[8]);
+
     // const VPBasicBlocks only.
     FromIterator.clear();
     copy(VPBlockUtils::blocksOnly<const VPBasicBlock>(depth_first(Start)),


        


More information about the llvm-commits mailing list