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

Andrei Elovikov via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 3 07:13:59 PST 2026


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

>From cdbcde8d7fed719d3eb958756183a827f5572193 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Thu, 29 Jan 2026 10:45:12 -0800
Subject: [PATCH 1/9] [VPlan] Generalize `VPAllSuccessorsIterator` to support
 predecessors

To be used in Mel's https://github.com/llvm/llvm-project/pull/173265.
---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h      | 162 +++++++++++-------
 .../Transforms/Vectorize/VPlanTest.cpp        |  36 +++-
 2 files changed, 136 insertions(+), 62 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index c79485c4cea71..c96a8cf687531 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -25,90 +25,132 @@ 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, such
+/// that:
+///
+///     A
+///     |
+///  +-----+ <- Region R
+///  |  b  |
+///  |     |
+///  | ... |
+///  |     |
+///  |  e  |
+///  +-----+
+///     |
+///     B
+///
+///  children(A) == {R} ; Forward == true
+///  children(R) == {b} ; Forward == true
+///  children(e) == {B} ; Forward == true
+///
+///  children(B) == {R} ; Forward == false
+///  children(R) == {e} ; Forward == false
+///  children(b) == {A} ; Forward == false
+///
+/// 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 auto getNumOutgoingEdges(BlockPtrTy Current) {
+    if constexpr (Forward)
+      return Current->getNumSuccessors();
+    else
+      return Current->getNumPredecessors();
+  }
+
+  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 +171,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 +190,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 +301,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 +317,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 +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 =
+      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/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 9b1e7c0af7c8c..a93ca45772460 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -388,11 +388,28 @@ TEST_F(VPBasicBlockTest, TraversingIteratorTest) {
 
     // Successors of R1.
     SmallVector<const VPBlockBase *> FromIterator(
-        VPAllSuccessorsIterator<VPBlockBase *>(R1),
-        VPAllSuccessorsIterator<VPBlockBase *>::end(R1));
+        VPImmediateHierarchicalChildrenIterator<VPBlockBase *>(R1),
+        VPImmediateHierarchicalChildrenIterator<VPBlockBase *>::end(R1));
     EXPECT_EQ(1u, FromIterator.size());
     EXPECT_EQ(R1BB1, FromIterator[0]);
 
+    // Predecessors of R1.
+    FromIterator.clear();
+    copy(VPImmediateHierarchicalChildrenIterator<VPBlockBase *, false>(R1),
+         VPImmediateHierarchicalChildrenIterator<VPBlockBase *, false>::end(R1),
+         std::back_inserter(FromIterator));
+    EXPECT_EQ(1u, FromIterator.size());
+    EXPECT_EQ(R1BB4, FromIterator[0]);
+
+    // Predecessors of R1BB1.
+    FromIterator.clear();
+    copy(VPImmediateHierarchicalChildrenIterator<VPBlockBase *, false>(R1BB1),
+         VPImmediateHierarchicalChildrenIterator<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();
@@ -407,6 +424,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)),

>From b14e7a4728ebf4075b7f263a9dc55b3067e5ff37 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <a.elovikov at gmail.com>
Date: Mon, 2 Feb 2026 08:08:49 -0800
Subject: [PATCH 2/9] Apply suggestions from code review

Co-authored-by: Florian Hahn <flo at fhahn.com>
Co-authored-by: Luke Lau <luke_lau at icloud.com>
---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index c96a8cf687531..a48ae48416be9 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -25,7 +25,7 @@ namespace llvm {
 // GraphTraits specializations for VPlan Hierarchical Control-Flow Graphs     //
 //===----------------------------------------------------------------------===//
 
-/// Iterator to traverse all successors/predecessors of a VPBlockBase node, such
+/// Iterator to traverse all successors/predecessors of a VPBlockBase node, including its hierarchical successors/predecessors. For example:
 /// that:
 ///
 ///     A
@@ -66,14 +66,14 @@ class VPImmediateHierarchicalChildrenIterator
   /// successors/predecessors array.
   size_t EdgeIdx;
 
-  static auto getNumOutgoingEdges(BlockPtrTy Current) {
+  static size_t getNumOutgoingEdges(BlockPtrTy Current) {
     if constexpr (Forward)
       return Current->getNumSuccessors();
     else
       return Current->getNumPredecessors();
   }
 
-  static decltype(auto) getOutgoingEdges(BlockPtrTy Current) {
+  static SmallVectorImpl<BlockPtrTy> &getOutgoingEdges(BlockPtrTy Current) {
     if constexpr (Forward)
       return Current->getSuccessors();
     else
@@ -339,7 +339,7 @@ template <> struct GraphTraits<Inverse<VPBlockBase *>> {
   static NodeRef getEntryNode(Inverse<NodeRef> B) { return B.Graph; }
 
   static inline ChildIteratorType child_begin(NodeRef N) {
-    return ChildIteratorType{N};
+    return ChildIteratorType(N);
   }
 
   static inline ChildIteratorType child_end(NodeRef N) {

>From 319c6ed6c6f03b8190de42406e7debe47bb9dc67 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Mon, 2 Feb 2026 08:25:17 -0800
Subject: [PATCH 3/9] Update comments per code-review suggestions

---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index a48ae48416be9..124c5f3f50754 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -25,8 +25,8 @@ namespace llvm {
 // GraphTraits specializations for VPlan Hierarchical Control-Flow Graphs     //
 //===----------------------------------------------------------------------===//
 
-/// Iterator to traverse all successors/predecessors of a VPBlockBase node, including its hierarchical successors/predecessors. For example:
-/// that:
+/// Iterator to traverse all successors/predecessors of a VPBlockBase node,
+/// including its hierarchical successors/predecessors. For example: that:
 ///
 ///     A
 ///     |
@@ -40,18 +40,23 @@ namespace llvm {
 ///     |
 ///     B
 ///
-///  children(A) == {R} ; Forward == true
-///  children(R) == {b} ; Forward == true
-///  children(e) == {B} ; Forward == true
+///  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}
 ///
-///  children(B) == {R} ; Forward == false
-///  children(R) == {e} ; Forward == false
-///  children(b) == {A} ; Forward == false
+///  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<

>From 631b7501ecc2b232e6a435e3bcd82b4489ad0bf2 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Mon, 2 Feb 2026 08:31:15 -0800
Subject: [PATCH 4/9] Drop "For example: that:"

---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index 124c5f3f50754..e6e2f9fd23953 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -26,7 +26,7 @@ namespace llvm {
 //===----------------------------------------------------------------------===//
 
 /// Iterator to traverse all successors/predecessors of a VPBlockBase node,
-/// including its hierarchical successors/predecessors. For example: that:
+/// including its hierarchical successors/predecessors:
 ///
 ///     A
 ///     |

>From a857ea7737eb436c3ec9a3866e95286baccb8991 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Mon, 2 Feb 2026 08:45:46 -0800
Subject: [PATCH 5/9] Restore `decltype(auto)` return type to fix the build

---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index e6e2f9fd23953..9c9ed6666f011 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -78,7 +78,10 @@ class VPImmediateHierarchicalChildrenIterator
       return Current->getNumPredecessors();
   }
 
-  static SmallVectorImpl<BlockPtrTy> &getOutgoingEdges(BlockPtrTy Current) {
+  /// 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

>From b8f912c9bd0a25a97a2ebfbeca85d4bac99c1a41 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Mon, 2 Feb 2026 15:08:22 -0800
Subject: [PATCH 6/9] Rename
 VPImmediateHierarchicalChildrenIterator->VPHierarchicalChildrenIterator

---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h      | 39 ++++++++-----------
 .../Transforms/Vectorize/VPlanTest.cpp        | 13 +++----
 2 files changed, 23 insertions(+), 29 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index 9c9ed6666f011..c40faa7bebde9 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -58,9 +58,9 @@ namespace llvm {
 /// traversal outside the region when doing a reverse post-order traversal of
 /// the VPlan.
 template <typename BlockPtrTy, bool Forward = true>
-class VPImmediateHierarchicalChildrenIterator
+class VPHierarchicalChildrenIterator
     : public iterator_facade_base<
-          VPImmediateHierarchicalChildrenIterator<BlockPtrTy, Forward>,
+          VPHierarchicalChildrenIterator<BlockPtrTy, Forward>,
           std::bidirectional_iterator_tag, VPBlockBase> {
   BlockPtrTy Block;
   /// Index of the current successor/predecessor.
@@ -113,20 +113,19 @@ class VPImmediateHierarchicalChildrenIterator
   /// Used by iterator_facade_base with bidirectional_iterator_tag.
   using reference = BlockPtrTy;
 
-  VPImmediateHierarchicalChildrenIterator(BlockPtrTy Block, size_t Idx = 0)
+  VPHierarchicalChildrenIterator(BlockPtrTy Block, size_t Idx = 0)
       : Block(Block), EdgeIdx(Idx) {}
-  VPImmediateHierarchicalChildrenIterator(
-      const VPImmediateHierarchicalChildrenIterator &Other)
+  VPHierarchicalChildrenIterator(const VPHierarchicalChildrenIterator &Other)
       : Block(Other.Block), EdgeIdx(Other.EdgeIdx) {}
 
-  VPImmediateHierarchicalChildrenIterator &
-  operator=(const VPImmediateHierarchicalChildrenIterator &R) {
+  VPHierarchicalChildrenIterator &
+  operator=(const VPHierarchicalChildrenIterator &R) {
     Block = R.Block;
     EdgeIdx = R.EdgeIdx;
     return *this;
   }
 
-  static VPImmediateHierarchicalChildrenIterator end(BlockPtrTy Block) {
+  static VPHierarchicalChildrenIterator end(BlockPtrTy Block) {
     if (auto *R = dyn_cast<VPRegionBlock>(Block)) {
       // Traverse through the region's entry/exiting (based on Forward) node.
       return {R, 1};
@@ -138,7 +137,7 @@ class VPImmediateHierarchicalChildrenIterator
     return {Block, NumOutgoingEdges};
   }
 
-  bool operator==(const VPImmediateHierarchicalChildrenIterator &R) const {
+  bool operator==(const VPHierarchicalChildrenIterator &R) const {
     return Block == R.Block && EdgeIdx == R.EdgeIdx;
   }
 
@@ -146,18 +145,18 @@ class VPImmediateHierarchicalChildrenIterator
 
   BlockPtrTy operator*() { return deref(Block, EdgeIdx); }
 
-  VPImmediateHierarchicalChildrenIterator &operator++() {
+  VPHierarchicalChildrenIterator &operator++() {
     EdgeIdx++;
     return *this;
   }
 
-  VPImmediateHierarchicalChildrenIterator &operator--() {
+  VPHierarchicalChildrenIterator &operator--() {
     EdgeIdx--;
     return *this;
   }
 
-  VPImmediateHierarchicalChildrenIterator operator++(int X) {
-    VPImmediateHierarchicalChildrenIterator Orig = *this;
+  VPHierarchicalChildrenIterator operator++(int X) {
+    VPHierarchicalChildrenIterator Orig = *this;
     EdgeIdx++;
     return Orig;
   }
@@ -179,8 +178,7 @@ template <typename BlockTy> class VPBlockDeepTraversalWrapper {
 /// reverse post-order traversal of the graph.
 template <> struct GraphTraits<VPBlockDeepTraversalWrapper<VPBlockBase *>> {
   using NodeRef = VPBlockBase *;
-  using ChildIteratorType =
-      VPImmediateHierarchicalChildrenIterator<VPBlockBase *>;
+  using ChildIteratorType = VPHierarchicalChildrenIterator<VPBlockBase *>;
 
   static NodeRef getEntryNode(VPBlockDeepTraversalWrapper<VPBlockBase *> N) {
     return N.getEntry();
@@ -198,8 +196,7 @@ template <> struct GraphTraits<VPBlockDeepTraversalWrapper<VPBlockBase *>> {
 template <>
 struct GraphTraits<VPBlockDeepTraversalWrapper<const VPBlockBase *>> {
   using NodeRef = const VPBlockBase *;
-  using ChildIteratorType =
-      VPImmediateHierarchicalChildrenIterator<const VPBlockBase *>;
+  using ChildIteratorType = VPHierarchicalChildrenIterator<const VPBlockBase *>;
 
   static NodeRef
   getEntryNode(VPBlockDeepTraversalWrapper<const VPBlockBase *> N) {
@@ -309,8 +306,7 @@ vp_depth_first_deep(const VPBlockBase *G) {
 
 template <> struct GraphTraits<VPBlockBase *> {
   using NodeRef = VPBlockBase *;
-  using ChildIteratorType =
-      VPImmediateHierarchicalChildrenIterator<VPBlockBase *>;
+  using ChildIteratorType = VPHierarchicalChildrenIterator<VPBlockBase *>;
 
   static NodeRef getEntryNode(NodeRef N) { return N; }
 
@@ -325,8 +321,7 @@ template <> struct GraphTraits<VPBlockBase *> {
 
 template <> struct GraphTraits<const VPBlockBase *> {
   using NodeRef = const VPBlockBase *;
-  using ChildIteratorType =
-      VPImmediateHierarchicalChildrenIterator<const VPBlockBase *>;
+  using ChildIteratorType = VPHierarchicalChildrenIterator<const VPBlockBase *>;
 
   static NodeRef getEntryNode(NodeRef N) { return N; }
 
@@ -342,7 +337,7 @@ template <> struct GraphTraits<const VPBlockBase *> {
 template <> struct GraphTraits<Inverse<VPBlockBase *>> {
   using NodeRef = VPBlockBase *;
   using ChildIteratorType =
-      VPImmediateHierarchicalChildrenIterator<VPBlockBase *, /*Forward=*/false>;
+      VPHierarchicalChildrenIterator<VPBlockBase *, /*Forward=*/false>;
 
   static NodeRef getEntryNode(Inverse<NodeRef> B) { return B.Graph; }
 
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index a93ca45772460..12ab48e598269 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -388,24 +388,23 @@ TEST_F(VPBasicBlockTest, TraversingIteratorTest) {
 
     // Successors of R1.
     SmallVector<const VPBlockBase *> FromIterator(
-        VPImmediateHierarchicalChildrenIterator<VPBlockBase *>(R1),
-        VPImmediateHierarchicalChildrenIterator<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(VPImmediateHierarchicalChildrenIterator<VPBlockBase *, false>(R1),
-         VPImmediateHierarchicalChildrenIterator<VPBlockBase *, false>::end(R1),
+    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(VPImmediateHierarchicalChildrenIterator<VPBlockBase *, false>(R1BB1),
-         VPImmediateHierarchicalChildrenIterator<VPBlockBase *, false>::end(
-             R1BB1),
+    copy(VPHierarchicalChildrenIterator<VPBlockBase *, false>(R1BB1),
+         VPHierarchicalChildrenIterator<VPBlockBase *, false>::end(R1BB1),
          std::back_inserter(FromIterator));
     EXPECT_EQ(1u, FromIterator.size());
     EXPECT_EQ(VPBB0, FromIterator[0]);

>From ab6810035f771e9ba7edc754c9b02fad9a48dfc9 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Mon, 2 Feb 2026 15:14:56 -0800
Subject: [PATCH 7/9] Some changes to the comments, hopefully to address CR
 feedback

---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h | 26 ++++++++++++------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index c40faa7bebde9..becb6804e85cd 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -41,34 +41,34 @@ namespace llvm {
 ///     B
 ///
 ///  Forward == true:
-///    Region blocks themselves traverse only their entries directly:
-///    Region's successor is traversed when processing its exiting block:
+///    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 traversed when processing its entry block:
+///    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}
 ///
-/// 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.
+/// 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/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.
+  /// 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) {

>From 428c2987c98a8762c177b3841ee99d3e3f56c502 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Mon, 2 Feb 2026 15:16:49 -0800
Subject: [PATCH 8/9] ArrayRef instead of decltype(auto) return type

---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index becb6804e85cd..cc497e1a6034f 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -78,10 +78,7 @@ class VPHierarchicalChildrenIterator
       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) {
+  static ArrayRef<BlockPtrTy> getOutgoingEdges(BlockPtrTy Current) {
     if constexpr (Forward)
       return Current->getSuccessors();
     else

>From 9294b97e84f347b1d79c068c68a8c0534b892484 Mon Sep 17 00:00:00 2001
From: Andrei Elovikov <andrei.elovikov at sifive.com>
Date: Tue, 3 Feb 2026 07:13:34 -0800
Subject: [PATCH 9/9] Update comment for `deref`

---
 llvm/lib/Transforms/Vectorize/VPlanCFG.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index cc497e1a6034f..ec3016f700fc2 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -91,8 +91,8 @@ class VPHierarchicalChildrenIterator
     return Current;
   }
 
-  /// Templated helper to dereference successor \p SuccIdx of \p Block. Used by
-  /// both the const and non-const operator* implementations.
+  /// 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(EdgeIdx == 0);



More information about the llvm-commits mailing list