[Mlir-commits] [clang] [llvm] [mlir] [ADT] Refactor post order traversal (PR #191047)
Alexis Engelke
llvmlistbot at llvm.org
Thu Apr 9 12:28:29 PDT 2026
https://github.com/aengelke updated https://github.com/llvm/llvm-project/pull/191047
>From aa004c0eed82a0651178f73d2c7bbeb7dc359462 Mon Sep 17 00:00:00 2001
From: Alexis Engelke <engelke at in.tum.de>
Date: Wed, 8 Apr 2026 20:08:51 +0000
Subject: [PATCH 1/5] [spr] initial version
Created using spr 1.3.8-wip
---
llvm/include/llvm/ADT/PostOrderIterator.h | 218 ++++++++++++------
.../llvm/Analysis/BlockFrequencyInfoImpl.h | 3 +-
llvm/include/llvm/Analysis/LoopIterator.h | 46 +---
llvm/lib/Analysis/BranchProbabilityInfo.cpp | 2 +-
llvm/lib/Analysis/CFGPrinter.cpp | 2 +-
llvm/lib/Analysis/LoopInfo.cpp | 4 +-
llvm/lib/CodeGen/MachineTraceMetrics.cpp | 19 +-
.../Transforms/Vectorize/SLPVectorizer.cpp | 2 +-
llvm/lib/Transforms/Vectorize/VPlanCFG.h | 5 +-
.../Transforms/Vectorize/VPlanTransforms.cpp | 10 +-
llvm/lib/Transforms/Vectorize/VPlanUtils.h | 3 +-
llvm/unittests/ADT/PostOrderIteratorTest.cpp | 34 +--
.../Transforms/Vectorize/VPlanTest.cpp | 9 +-
mlir/include/mlir/IR/Iterators.h | 4 +-
14 files changed, 190 insertions(+), 171 deletions(-)
diff --git a/llvm/include/llvm/ADT/PostOrderIterator.h b/llvm/include/llvm/ADT/PostOrderIterator.h
index 1dfd259e58897..cb777d6c3055b 100644
--- a/llvm/include/llvm/ADT/PostOrderIterator.h
+++ b/llvm/include/llvm/ADT/PostOrderIterator.h
@@ -120,6 +120,142 @@ using DefaultSet =
} // namespace po_detail
+template <typename DerivedT, typename GraphT> class PostOrderTraversalBase {
+ using GT = GraphTraits<GraphT>;
+ using NodeRef = typename GT::NodeRef;
+ using ChildItTy = typename GT::ChildIteratorType;
+
+ /// Used to maintain the ordering.
+ /// First element is basic block pointer, second is iterator for the next
+ /// child to visit, third is the end iterator.
+ SmallVector<std::tuple<NodeRef, ChildItTy, ChildItTy>, 8> VisitStack;
+
+public:
+ class iterator {
+ friend class PostOrderTraversalBase;
+
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = NodeRef;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type *;
+ using reference = NodeRef;
+
+ private:
+ DerivedT *POT = nullptr;
+ NodeRef V = nullptr;
+
+ public:
+ iterator() = default;
+
+ private:
+ iterator(DerivedT &POT, value_type V) : POT(&POT), V(V) {}
+
+ public:
+ bool operator==(const iterator &X) const { return V == X.V; }
+ bool operator!=(const iterator &X) const { return !(*this == X); }
+
+ NodeRef operator*() const { return V; }
+
+ // This is a nonstandard operator-> that dereferences the pointer an extra
+ // time... so that you can actually call methods ON the BasicBlock, because
+ // the contained type is a pointer. This allows BBIt->getTerminator() f.e.
+ //
+ NodeRef operator->() const { return **this; }
+
+ iterator &operator++() { // Preincrement
+ V = POT->next();
+ return *this;
+ }
+
+ iterator operator++(int) { // Postincrement
+ iterator tmp = *this;
+ ++*this;
+ return tmp;
+ }
+ };
+
+protected:
+ PostOrderTraversalBase() = default;
+
+ DerivedT *derived() { return static_cast<DerivedT *>(this); }
+
+ void init(NodeRef Start) {
+ if (derived()->insertEdge(std::optional<NodeRef>(), Start)) {
+ VisitStack.emplace_back(Start, GT::child_begin(Start),
+ GT::child_end(Start));
+ traverseChild();
+ }
+ }
+
+private:
+ void traverseChild() {
+ while (true) {
+ auto &Entry = VisitStack.back();
+ if (std::get<1>(Entry) == std::get<2>(Entry))
+ break;
+ NodeRef BB = *std::get<1>(Entry)++;
+ if (derived()->insertEdge(std::optional<NodeRef>(std::get<0>(Entry)),
+ BB)) {
+ // If the block is not visited...
+ VisitStack.emplace_back(BB, GT::child_begin(BB), GT::child_end(BB));
+ }
+ }
+ }
+
+ NodeRef next() {
+ derived()->finishPostorder(std::get<0>(VisitStack.back()));
+ VisitStack.pop_back();
+ if (!VisitStack.empty())
+ traverseChild();
+ return !VisitStack.empty() ? std::get<0>(VisitStack.back()) : nullptr;
+ }
+
+public:
+ iterator begin() {
+ if (VisitStack.empty())
+ return iterator(); // We don't even want to see the start node.
+ return iterator(*derived(), std::get<0>(VisitStack.back()));
+ }
+ iterator end() { return iterator(); }
+
+ // Methods that are intended to be overridden by sub-classes.
+
+ /// Add edge and return whether To should be visited. From is nullopt for the
+ /// root node.
+ bool insertEdge(std::optional<NodeRef> From, NodeRef To);
+
+ /// Callback just before the iterator moves to the next block.
+ void finishPostorder(NodeRef) {}
+};
+
+/// Post-order traversal of a graph.
+template <typename GraphT, typename SetType = po_detail::DefaultSet<GraphT>>
+class PostOrderTraversal
+ : public PostOrderTraversalBase<PostOrderTraversal<GraphT, SetType>,
+ GraphT> {
+ using NodeRef = typename GraphTraits<GraphT>::NodeRef;
+
+ SetType Visited;
+
+public:
+ PostOrderTraversal(const GraphT &G) {
+ this->init(GraphTraits<GraphT>::getEntryNode(G));
+#if 0
+ if constexpr (GraphHasNodeNumbers<GraphT>)
+ Visited.reserve(GraphTraits<GraphT>::getMaxNumber(G));
+#endif
+ }
+
+ PostOrderTraversal(const GraphT &G, SetType &S) : Visited(S) {
+ this->init(GraphTraits<GraphT>::getEntryNode(G));
+ }
+
+ bool insertEdge(std::optional<NodeRef> From, NodeRef To) {
+ return Visited.insert(To).second;
+ }
+};
+
template <class GraphT, class SetType = po_detail::DefaultSet<GraphT>,
bool ExtStorage = false, class GT = GraphTraits<GraphT>>
class po_iterator : public po_iterator_storage<SetType, ExtStorage> {
@@ -217,83 +353,15 @@ class po_iterator : public po_iterator_storage<SetType, ExtStorage> {
// Provide global constructors that automatically figure out correct types...
//
-template <class T>
-po_iterator<T> po_begin(const T &G) { return po_iterator<T>::begin(G); }
-template <class T>
-po_iterator<T> po_end (const T &G) { return po_iterator<T>::end(G); }
-
-template <class T> iterator_range<po_iterator<T>> post_order(const T &G) {
- return make_range(po_begin(G), po_end(G));
-}
-
-// Provide global definitions of external postorder iterators...
-template <class T, class SetType = std::set<typename GraphTraits<T>::NodeRef>>
-struct po_ext_iterator : po_iterator<T, SetType, true> {
- po_ext_iterator(const po_iterator<T, SetType, true> &V) :
- po_iterator<T, SetType, true>(V) {}
-};
-
-template <class T, class SetType>
-po_ext_iterator<T, SetType> po_ext_begin(const T &G, SetType &S) {
- return po_ext_iterator<T, SetType>::begin(G, S);
-}
-
-template <class T, class SetType>
-po_ext_iterator<T, SetType> po_ext_end(const T &G, SetType &S) {
- return po_ext_iterator<T, SetType>::end(G, S);
-}
-
-template <class T, class SetType>
-iterator_range<po_ext_iterator<T, SetType>> post_order_ext(const T &G, SetType &S) {
- return make_range(po_ext_begin(G, S), po_ext_end(G, S));
-}
-
-// Provide global definitions of inverse post order iterators...
-template <class T, class SetType = std::set<typename GraphTraits<T>::NodeRef>,
- bool External = false>
-struct ipo_iterator : po_iterator<Inverse<T>, SetType, External> {
- ipo_iterator(const po_iterator<Inverse<T>, SetType, External> &V) :
- po_iterator<Inverse<T>, SetType, External> (V) {}
-};
-
-template <class T>
-ipo_iterator<T> ipo_begin(const T &G) {
- return ipo_iterator<T>::begin(G);
+template <class T> auto post_order(const T &G) {
+ return PostOrderTraversal<T>(G);
}
-
-template <class T>
-ipo_iterator<T> ipo_end(const T &G){
- return ipo_iterator<T>::end(G);
+template <class T, class SetType> auto post_order_ext(const T &G, SetType &S) {
+ return PostOrderTraversal<T, SetType &>(G, S);
}
-
-template <class T>
-iterator_range<ipo_iterator<T>> inverse_post_order(const T &G) {
- return make_range(ipo_begin(G), ipo_end(G));
-}
-
-// Provide global definitions of external inverse postorder iterators...
-template <class T, class SetType = std::set<typename GraphTraits<T>::NodeRef>>
-struct ipo_ext_iterator : ipo_iterator<T, SetType, true> {
- ipo_ext_iterator(const ipo_iterator<T, SetType, true> &V) :
- ipo_iterator<T, SetType, true>(V) {}
- ipo_ext_iterator(const po_iterator<Inverse<T>, SetType, true> &V) :
- ipo_iterator<T, SetType, true>(V) {}
-};
-
-template <class T, class SetType>
-ipo_ext_iterator<T, SetType> ipo_ext_begin(const T &G, SetType &S) {
- return ipo_ext_iterator<T, SetType>::begin(G, S);
-}
-
-template <class T, class SetType>
-ipo_ext_iterator<T, SetType> ipo_ext_end(const T &G, SetType &S) {
- return ipo_ext_iterator<T, SetType>::end(G, S);
-}
-
template <class T, class SetType>
-iterator_range<ipo_ext_iterator<T, SetType>>
-inverse_post_order_ext(const T &G, SetType &S) {
- return make_range(ipo_ext_begin(G, S), ipo_ext_end(G, S));
+auto inverse_post_order_ext(const T &G, SetType &S) {
+ return PostOrderTraversal<Inverse<T>, SetType &>(G, S);
}
//===--------------------------------------------------------------------===//
@@ -331,7 +399,7 @@ class ReversePostOrderTraversal {
VecTy Blocks; // Block list in normal PO order
void Initialize(const GraphT &G) {
- std::copy(po_begin(G), po_end(G), std::back_inserter(Blocks));
+ llvm::copy(post_order(G), std::back_inserter(Blocks));
}
public:
diff --git a/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h b/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h
index cc2404a0249e7..7cc40084b9247 100644
--- a/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h
+++ b/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h
@@ -1106,9 +1106,8 @@ void BlockFrequencyInfoImpl<BT>::setBlockFreq(const BlockT *BB,
}
template <class BT> void BlockFrequencyInfoImpl<BT>::initializeRPOT() {
- const BlockT *Entry = &F->front();
RPOT.reserve(F->size());
- for (const BlockT *BB : post_order(Entry))
+ for (const BlockT *BB : post_order(F))
RPOT.emplace_back(BB);
std::reverse(RPOT.begin(), RPOT.end());
diff --git a/llvm/include/llvm/Analysis/LoopIterator.h b/llvm/include/llvm/Analysis/LoopIterator.h
index 1ac8e68bfa2f1..b359812d396cd 100644
--- a/llvm/include/llvm/Analysis/LoopIterator.h
+++ b/llvm/include/llvm/Analysis/LoopIterator.h
@@ -186,49 +186,33 @@ class LoopBlocksRPO {
LoopBlocksDFS::RPOIterator end() const { return DFS.endRPO(); }
};
-/// Specialize po_iterator_storage to record postorder numbers.
-template<> class po_iterator_storage<LoopBlocksTraversal, true> {
- LoopBlocksTraversal &LBT;
-public:
- po_iterator_storage(LoopBlocksTraversal &lbs) : LBT(lbs) {}
- // These functions are defined below.
- bool insertEdge(std::optional<BasicBlock *> From, BasicBlock *To);
- void finishPostorder(BasicBlock *BB);
-};
-
/// Traverse the blocks in a loop using a depth-first search.
-class LoopBlocksTraversal {
-public:
- /// Graph traversal iterator.
- typedef po_iterator<BasicBlock*, LoopBlocksTraversal, true> POTIterator;
-
-private:
+class LoopBlocksTraversal
+ : public PostOrderTraversalBase<LoopBlocksTraversal, Function *> {
LoopBlocksDFS &DFS;
const LoopInfo *LI;
public:
- LoopBlocksTraversal(LoopBlocksDFS &Storage, const LoopInfo *LInfo) :
- DFS(Storage), LI(LInfo) {}
+ LoopBlocksTraversal(LoopBlocksDFS &Storage, const LoopInfo *LInfo)
+ : DFS(Storage), LI(LInfo) {}
/// Postorder traversal over the graph. This only needs to be done once.
/// po_iterator "automatically" calls back to visitPreorder and
/// finishPostorder to record the DFS result.
- POTIterator begin() {
+ iterator begin() {
assert(DFS.PostBlocks.empty() && "Need clear DFS result before traversing");
- assert(DFS.L->getNumBlocks() && "po_iterator cannot handle an empty graph");
- return po_ext_begin(DFS.L->getHeader(), *this);
- }
- POTIterator end() {
- // po_ext_end interface requires a basic block, but ignores its value.
- return po_ext_end(DFS.L->getHeader(), *this);
+ assert(DFS.L->getNumBlocks() && "cannot handle an empty graph");
+ init(DFS.L->getHeader());
+ return PostOrderTraversalBase::begin();
}
+ iterator end() { return PostOrderTraversalBase::end(); }
/// Called by po_iterator upon reaching a block via a CFG edge. If this block
/// is contained in the loop and has not been visited, then mark it preorder
/// visited and return true.
///
/// TODO: If anyone is interested, we could record preorder numbers here.
- bool visitPreorder(BasicBlock *BB) {
+ bool insertEdge(std::optional<BasicBlock *> /*From*/, BasicBlock *BB) {
if (!DFS.L->contains(LI->getLoopFor(BB)))
return false;
@@ -244,16 +228,6 @@ class LoopBlocksTraversal {
}
};
-inline bool po_iterator_storage<LoopBlocksTraversal, true>::insertEdge(
- std::optional<BasicBlock *> From, BasicBlock *To) {
- return LBT.visitPreorder(To);
-}
-
-inline void po_iterator_storage<LoopBlocksTraversal, true>::
-finishPostorder(BasicBlock *BB) {
- LBT.finishPostorder(BB);
-}
-
} // End namespace llvm
#endif
diff --git a/llvm/lib/Analysis/BranchProbabilityInfo.cpp b/llvm/lib/Analysis/BranchProbabilityInfo.cpp
index 490bfbc0fb7ca..fdb539d91313c 100644
--- a/llvm/lib/Analysis/BranchProbabilityInfo.cpp
+++ b/llvm/lib/Analysis/BranchProbabilityInfo.cpp
@@ -1263,7 +1263,7 @@ void BPIConstruction::calculate(const Function &F, const LoopInfo &LoopI,
// Walk the basic blocks in post-order so that we can build up state about
// the successors of a block iteratively.
- for (const auto *BB : post_order(&F.getEntryBlock())) {
+ for (const auto *BB : post_order(&F)) {
LLVM_DEBUG(dbgs() << "Computing probabilities for " << BB->getName()
<< "\n");
// If there is no at least two successors, no sense to set probability.
diff --git a/llvm/lib/Analysis/CFGPrinter.cpp b/llvm/lib/Analysis/CFGPrinter.cpp
index 39108a906f081..18776bc539b32 100644
--- a/llvm/lib/Analysis/CFGPrinter.cpp
+++ b/llvm/lib/Analysis/CFGPrinter.cpp
@@ -206,7 +206,7 @@ void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(
};
/// The post order traversal iteration is done to know the status of
/// isOnDeoptOrUnreachablePath for all the successors on the current BB.
- llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);
+ llvm::for_each(post_order(F), evaluateBB);
}
bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,
diff --git a/llvm/lib/Analysis/LoopInfo.cpp b/llvm/lib/Analysis/LoopInfo.cpp
index 8e08a70e69cdd..b459b12d47e8e 100644
--- a/llvm/lib/Analysis/LoopInfo.cpp
+++ b/llvm/lib/Analysis/LoopInfo.cpp
@@ -1284,8 +1284,6 @@ PreservedAnalyses LoopVerifierPass::run(Function &F,
/// visit blocks during the initial traversal.
void LoopBlocksDFS::perform(const LoopInfo *LI) {
LoopBlocksTraversal Traversal(*this, LI);
- for (LoopBlocksTraversal::POTIterator POI = Traversal.begin(),
- POE = Traversal.end();
- POI != POE; ++POI)
+ for ([[maybe_unused]] BasicBlock *BB : Traversal)
;
}
diff --git a/llvm/lib/CodeGen/MachineTraceMetrics.cpp b/llvm/lib/CodeGen/MachineTraceMetrics.cpp
index 81dd68a519e76..9b7bf6ea3cc82 100644
--- a/llvm/lib/CodeGen/MachineTraceMetrics.cpp
+++ b/llvm/lib/CodeGen/MachineTraceMetrics.cpp
@@ -484,13 +484,17 @@ struct LoopBounds {
// Specialize po_iterator_storage in order to prune the post-order traversal so
// it is limited to the current loop and doesn't traverse the loop back edges.
-template <> class llvm::po_iterator_storage<LoopBounds, true> {
+template <typename GraphT>
+class LoopBoundsPostOrderTraversal
+ : public PostOrderTraversalBase<LoopBoundsPostOrderTraversal<GraphT>,
+ GraphT> {
LoopBounds &LB;
public:
- po_iterator_storage(LoopBounds &lb) : LB(lb) {}
-
- void finishPostorder(const MachineBasicBlock*) {}
+ LoopBoundsPostOrderTraversal(const MachineBasicBlock *Start, LoopBounds &LB)
+ : LB(LB) {
+ this->init(Start);
+ }
bool insertEdge(std::optional<const MachineBasicBlock *> From,
const MachineBasicBlock *To) {
@@ -525,7 +529,9 @@ void MachineTraceMetrics::Ensemble::computeTrace(const MachineBasicBlock *MBB) {
// Run an upwards post-order search for the trace start.
Bounds.Downward = false;
Bounds.Visited.clear();
- for (const auto *I : inverse_post_order_ext(MBB, Bounds)) {
+ for (const auto *I :
+ LoopBoundsPostOrderTraversal<Inverse<const MachineBasicBlock *>>(
+ MBB, Bounds)) {
LLVM_DEBUG(dbgs() << " pred for " << printMBBReference(*I) << ": ");
TraceBlockInfo &TBI = BlockInfo[I->getNumber()];
// All the predecessors have been visited, pick the preferred one.
@@ -543,7 +549,8 @@ void MachineTraceMetrics::Ensemble::computeTrace(const MachineBasicBlock *MBB) {
// Run a downwards post-order search for the trace end.
Bounds.Downward = true;
Bounds.Visited.clear();
- for (const auto *I : post_order_ext(MBB, Bounds)) {
+ for (const auto *I :
+ LoopBoundsPostOrderTraversal<const MachineBasicBlock *>(MBB, Bounds)) {
LLVM_DEBUG(dbgs() << " succ for " << printMBBReference(*I) << ": ");
TraceBlockInfo &TBI = BlockInfo[I->getNumber()];
// All the successors have been visited, pick the preferred one.
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index f2ccf198c4c81..bc26d74ba3e67 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -25250,7 +25250,7 @@ bool SLPVectorizerPass::runImpl(Function &F, ScalarEvolution *SE_,
DT->updateDFSNumbers();
// Scan the blocks in the function in post order.
- for (auto *BB : post_order(&F.getEntryBlock())) {
+ for (auto *BB : post_order(&F)) {
if (BB->isEHPad() || isa_and_nonnull<UnreachableInst>(BB->getTerminator()))
continue;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanCFG.h b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
index 963d84675693a..58e43a9d81809 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanCFG.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanCFG.h
@@ -261,15 +261,14 @@ vp_depth_first_shallow(const VPBlockBase *G) {
/// Returns an iterator range to traverse the graph starting at \p G in
/// post order. The iterator won't traverse through region blocks.
-inline iterator_range<
- po_iterator<VPBlockShallowTraversalWrapper<VPBlockBase *>>>
+inline PostOrderTraversal<VPBlockShallowTraversalWrapper<VPBlockBase *>>
vp_post_order_shallow(VPBlockBase *G) {
return post_order(VPBlockShallowTraversalWrapper<VPBlockBase *>(G));
}
/// Returns an iterator range to traverse the graph starting at \p G in
/// post order while traversing through region blocks.
-inline iterator_range<po_iterator<VPBlockDeepTraversalWrapper<VPBlockBase *>>>
+inline PostOrderTraversal<VPBlockDeepTraversalWrapper<VPBlockBase *>>
vp_post_order_deep(VPBlockBase *G) {
return post_order(VPBlockDeepTraversalWrapper<VPBlockBase *>(G));
}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 5fbdb2aa98d9f..8ce2d682a123f 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -734,8 +734,9 @@ static bool isDeadRecipe(VPRecipeBase &R) {
}
void VPlanTransforms::removeDeadRecipes(VPlan &Plan) {
- for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
- vp_post_order_deep(Plan.getEntry()))) {
+ PostOrderTraversal<VPBlockDeepTraversalWrapper<VPBlockBase *>> POT(
+ Plan.getEntry());
+ for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(POT)) {
// The recipes in the block are processed in reverse order, to catch chains
// of dead recipes.
for (VPRecipeBase &R : make_early_inc_range(reverse(*VPBB))) {
@@ -2711,8 +2712,9 @@ static void licm(VPlan &Plan) {
// Sink recipes with no users inside the vector loop region if all users are
// in the same exit block of the region.
// TODO: Extend to sink recipes from inner loops.
- for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
- vp_post_order_shallow(LoopRegion->getEntry()))) {
+ PostOrderTraversal<VPBlockShallowTraversalWrapper<VPBlockBase *>> POT(
+ LoopRegion->getEntry());
+ for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(POT)) {
for (VPRecipeBase &R : make_early_inc_range(reverse(*VPBB))) {
if (cannotHoistOrSinkRecipe(R))
continue;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 5fdd5ea4204e0..2cab5967b42f7 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -269,8 +269,7 @@ class VPBlockUtils {
/// Return an iterator range over \p Range which only includes \p BlockTy
/// blocks. The accesses are casted to \p BlockTy.
- template <typename BlockTy, typename T>
- static auto blocksOnly(const T &Range) {
+ template <typename BlockTy, typename T> static auto blocksOnly(T &&Range) {
// Create BaseTy with correct const-ness based on BlockTy.
using BaseTy = std::conditional_t<std::is_const<BlockTy>::value,
const VPBlockBase, VPBlockBase>;
diff --git a/llvm/unittests/ADT/PostOrderIteratorTest.cpp b/llvm/unittests/ADT/PostOrderIteratorTest.cpp
index 11da6925bb1fb..f66d9b6bc8b83 100644
--- a/llvm/unittests/ADT/PostOrderIteratorTest.cpp
+++ b/llvm/unittests/ADT/PostOrderIteratorTest.cpp
@@ -38,9 +38,9 @@ TEST(PostOrderIteratorTest, Compiles) {
Graph<6> G;
using NodeType = Graph<6>::NodeType;
NodeType *NullNode = nullptr;
- auto PI = po_end(G);
+ auto PI = post_order(G);
PI.insertEdge(std::optional<NodeType *>(), NullNode);
- auto PIExt = po_ext_end(G, Ext);
+ auto PIExt = post_order_ext(G, Ext);
PIExt.insertEdge(std::optional<NodeType *>(), NullNode);
}
@@ -83,34 +83,4 @@ TEST(PostOrderIteratorTest, PostOrderAndReversePostOrderTraverrsal) {
EXPECT_EQ(1, FromIterator[4]);
EXPECT_EQ(4, FromIterator[5]);
}
-
-// po_iterator should be (at-least) a forward-iterator
-static_assert(std::is_base_of_v<std::forward_iterator_tag,
- po_iterator<Graph<4>>::iterator_category>);
-
-// po_ext_iterator cannot provide multi-pass guarantee, therefore its only
-// an input-iterator
-static_assert(std::is_same_v<po_ext_iterator<Graph<4>>::iterator_category,
- std::input_iterator_tag>);
-
-TEST(PostOrderIteratorTest, MultiPassSafeWithInternalSet) {
- Graph<4> G;
- G.AddEdge(0, 1);
- G.AddEdge(1, 2);
- G.AddEdge(1, 3);
-
- std::array<decltype(G)::NodeType *, 4> NodesFirstPass, NodesSecondPass;
-
- auto B = po_begin(G), E = po_end(G);
-
- std::size_t I = 0;
- for (auto It = B; It != E; ++It)
- NodesFirstPass[I++] = *It;
-
- I = 0;
- for (auto It = B; It != E; ++It)
- NodesSecondPass[I++] = *It;
-
- EXPECT_EQ(NodesFirstPass, NodesSecondPass);
-}
}
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 017aa6dab705e..b378b74618258 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -546,7 +546,7 @@ TEST_F(VPBasicBlockTest, TraversingIteratorTest) {
// Post-order.
FromIterator.clear();
- FromIterator.append(po_begin(Start), po_end(Start));
+ copy(post_order(Start), std::back_inserter(FromIterator));
EXPECT_EQ(10u, FromIterator.size());
EXPECT_EQ(VPBB2, FromIterator[0]);
EXPECT_EQ(R1BB3, FromIterator[1]);
@@ -597,7 +597,7 @@ TEST_F(VPBasicBlockTest, TraversingIteratorTest) {
// Post-order.
FromIterator.clear();
- FromIterator.append(po_begin(Start), po_end(Start));
+ copy(post_order(Start), std::back_inserter(FromIterator));
EXPECT_EQ(5u, FromIterator.size());
EXPECT_EQ(R2BB2, FromIterator[0]);
EXPECT_EQ(R2BB1, FromIterator[1]);
@@ -678,8 +678,9 @@ TEST_F(VPBasicBlockTest, TraversingIteratorTest) {
// Post-order, const VPRegionBlocks only.
VPBlockDeepTraversalWrapper<const VPBlockBase *> StartConst(VPBB1);
- SmallVector<const VPRegionBlock *> FromIteratorVPRegion(
- VPBlockUtils::blocksOnly<const VPRegionBlock>(post_order(StartConst)));
+ SmallVector<const VPRegionBlock *> FromIteratorVPRegion;
+ copy(VPBlockUtils::blocksOnly<const VPRegionBlock>(post_order(StartConst)),
+ std::back_inserter(FromIteratorVPRegion));
EXPECT_EQ(3u, FromIteratorVPRegion.size());
EXPECT_EQ(R3, FromIteratorVPRegion[0]);
EXPECT_EQ(R2, FromIteratorVPRegion[1]);
diff --git a/mlir/include/mlir/IR/Iterators.h b/mlir/include/mlir/IR/Iterators.h
index dcb738c549438..4754d3beb2c6e 100644
--- a/mlir/include/mlir/IR/Iterators.h
+++ b/mlir/include/mlir/IR/Iterators.h
@@ -100,9 +100,10 @@ struct ReverseDominanceIterator {
static constexpr auto makeIterable(Operation &range) {
return llvm::reverse(ForwardIterator::makeIterable(range));
}
-
+#if 0
static auto makeIterable(Region ®ion) {
Block *null = nullptr;
+ llvm::PostOrderTraversal<Block *>::iterator sentinel;
if (SkipGraphRegion && !mayHaveSSADominance(region)) {
// Skip graph regions.
return llvm::make_pointee_range(
@@ -118,6 +119,7 @@ struct ReverseDominanceIterator {
// Walk API expects Block references instead of pointers.
return llvm::make_pointee_range(it);
}
+#endif
};
} // namespace mlir
>From d2b122989fc605b6aaba551c2faf6618a6cfa1c8 Mon Sep 17 00:00:00 2001
From: Alexis Engelke <engelke at in.tum.de>
Date: Thu, 9 Apr 2026 07:45:17 +0000
Subject: [PATCH 2/5] Fix Clang+MLIR
Created using spr 1.3.8-wip
---
.../Analysis/Analyses/PostOrderCFGView.h | 51 -----
clang/lib/Analysis/PostOrderCFGView.cpp | 38 +++-
llvm/include/llvm/ADT/PostOrderIterator.h | 191 +-----------------
.../llvm/Analysis/BlockFrequencyInfoImpl.h | 3 +-
llvm/include/llvm/Analysis/LoopIterator.h | 3 +-
llvm/lib/Analysis/BranchProbabilityInfo.cpp | 2 +-
llvm/lib/Analysis/CFGPrinter.cpp | 2 +-
llvm/lib/CodeGen/MachineTraceMetrics.cpp | 2 +-
.../Transforms/Vectorize/SLPVectorizer.cpp | 2 +-
mlir/include/mlir/IR/Iterators.h | 41 ++--
10 files changed, 78 insertions(+), 257 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h b/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h
index c4998bb2285f7..0a37fdea63fac 100644
--- a/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h
+++ b/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h
@@ -29,32 +29,16 @@ class PostOrderCFGView : public ManagedAnalysis {
public:
/// Implements a set of CFGBlocks using a BitVector.
- ///
- /// This class contains a minimal interface, primarily dictated by the SetType
- /// template parameter of the llvm::po_iterator template, as used with
- /// external storage. We also use this set to keep track of which CFGBlocks we
- /// visit during the analysis.
class CFGBlockSet {
llvm::BitVector VisitedBlockIDs;
public:
- // po_iterator requires this iterator, but the only interface needed is the
- // value_type type.
- struct iterator { using value_type = const CFGBlock *; };
-
CFGBlockSet() = default;
CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false) {}
/// Set the bit associated with a particular CFGBlock.
/// This is the important method for the SetType template parameter.
std::pair<std::nullopt_t, bool> insert(const CFGBlock *Block) {
- // Note that insert() is called by po_iterator, which doesn't check to
- // make sure that Block is non-null. Moreover, the CFGBlock iterator will
- // occasionally hand out null pointers for pruned edges, so we catch those
- // here.
- if (!Block)
- return std::make_pair(std::nullopt,
- false); // if an edge is trivially false.
if (VisitedBlockIDs.test(Block->getBlockID()))
return std::make_pair(std::nullopt, false);
VisitedBlockIDs.set(Block->getBlockID());
@@ -70,41 +54,6 @@ class PostOrderCFGView : public ManagedAnalysis {
};
private:
- // The CFG orders the blocks of loop bodies before those of loop successors
- // (both numerically, and in the successor order of the loop condition
- // block). So, RPO necessarily reverses that order, placing the loop successor
- // *before* the loop body. For many analyses, particularly those that converge
- // to a fixpoint, this results in potentially significant extra work because
- // loop successors will necessarily need to be reconsidered once the algorithm
- // has reached a fixpoint on the loop body.
- //
- // This definition of CFG graph traits reverses the order of children, so that
- // loop bodies will come first in an RPO.
- struct CFGLoopBodyFirstTraits {
- using NodeRef = const ::clang::CFGBlock *;
- using ChildIteratorType = ::clang::CFGBlock::const_succ_reverse_iterator;
-
- static ChildIteratorType child_begin(NodeRef N) { return N->succ_rbegin(); }
- static ChildIteratorType child_end(NodeRef N) { return N->succ_rend(); }
-
- using nodes_iterator = ::clang::CFG::const_iterator;
-
- static NodeRef getEntryNode(const ::clang::CFG *F) {
- return &F->getEntry();
- }
-
- static nodes_iterator nodes_begin(const ::clang::CFG *F) {
- return F->nodes_begin();
- }
-
- static nodes_iterator nodes_end(const ::clang::CFG *F) {
- return F->nodes_end();
- }
-
- static unsigned size(const ::clang::CFG *F) { return F->size(); }
- };
- using po_iterator =
- llvm::po_iterator<const CFG *, CFGBlockSet, true, CFGLoopBodyFirstTraits>;
std::vector<const CFGBlock *> Blocks;
using BlockOrderTy = llvm::DenseMap<const CFGBlock *, unsigned>;
diff --git a/clang/lib/Analysis/PostOrderCFGView.cpp b/clang/lib/Analysis/PostOrderCFGView.cpp
index 0c09c0f97ff68..324d64c25e090 100644
--- a/clang/lib/Analysis/PostOrderCFGView.cpp
+++ b/clang/lib/Analysis/PostOrderCFGView.cpp
@@ -20,12 +20,40 @@ void PostOrderCFGView::anchor() {}
PostOrderCFGView::PostOrderCFGView(const CFG *cfg) {
Blocks.reserve(cfg->getNumBlockIDs());
- CFGBlockSet BSet(cfg);
- for (po_iterator I = po_iterator::begin(cfg, BSet),
- E = po_iterator::end(cfg, BSet); I != E; ++I) {
- BlockOrder[*I] = Blocks.size() + 1;
- Blocks.push_back(*I);
+ // The CFG orders the blocks of loop bodies before those of loop successors
+ // (both numerically, and in the successor order of the loop condition
+ // block). So, RPO necessarily reverses that order, placing the loop successor
+ // *before* the loop body. For many analyses, particularly those that converge
+ // to a fixpoint, this results in potentially significant extra work because
+ // loop successors will necessarily need to be reconsidered once the algorithm
+ // has reached a fixpoint on the loop body.
+ //
+ // This definition of CFG graph traits reverses the order of children, so that
+ // loop bodies will come first in an RPO.
+ struct CFGLoopBodyFirstTraits {
+ using NodeRef = const ::clang::CFGBlock *;
+ using ChildIteratorType = ::clang::CFGBlock::const_succ_reverse_iterator;
+
+ static ChildIteratorType child_begin(NodeRef N) { return N->succ_rbegin(); }
+ static ChildIteratorType child_end(NodeRef N) { return N->succ_rend(); }
+ };
+
+ struct POTraversal
+ : llvm::PostOrderTraversalBase<POTraversal, CFGLoopBodyFirstTraits> {
+ CFGBlockSet BSet;
+
+ POTraversal(const CFG *cfg) : BSet(cfg) { this->init(&cfg->getEntry()); }
+ bool insertEdge(std::optional<const CFGBlock *>, const CFGBlock *To) {
+ if (!To)
+ return false;
+ return BSet.insert(To).second;
+ }
+ };
+
+ for (const CFGBlock *Block : POTraversal(cfg)) {
+ BlockOrder[Block] = Blocks.size() + 1;
+ Blocks.push_back(Block);
}
}
diff --git a/llvm/include/llvm/ADT/PostOrderIterator.h b/llvm/include/llvm/ADT/PostOrderIterator.h
index cb777d6c3055b..77ce6cc6c6ff2 100644
--- a/llvm/include/llvm/ADT/PostOrderIterator.h
+++ b/llvm/include/llvm/ADT/PostOrderIterator.h
@@ -19,78 +19,12 @@
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/iterator_range.h"
#include <iterator>
#include <optional>
-#include <set>
#include <type_traits>
#include <utility>
namespace llvm {
-
-// The po_iterator_storage template provides access to the set of already
-// visited nodes during the po_iterator's depth-first traversal.
-//
-// The default implementation simply contains a set of visited nodes, while
-// the External=true version uses a reference to an external set.
-//
-// It is possible to prune the depth-first traversal in several ways:
-//
-// - When providing an external set that already contains some graph nodes,
-// those nodes won't be visited again. This is useful for restarting a
-// post-order traversal on a graph with nodes that aren't dominated by a
-// single node.
-//
-// - By providing a custom SetType class, unwanted graph nodes can be excluded
-// by having the insert() function return false. This could for example
-// confine a CFG traversal to blocks in a specific loop.
-//
-// - Finally, by specializing the po_iterator_storage template itself, graph
-// edges can be pruned by returning false in the insertEdge() function. This
-// could be used to remove loop back-edges from the CFG seen by po_iterator.
-//
-// A specialized po_iterator_storage class can observe both the pre-order and
-// the post-order. The insertEdge() function is called in a pre-order, while
-// the finishPostorder() function is called just before the po_iterator moves
-// on to the next node.
-
-/// Default po_iterator_storage implementation with an internal set object.
-template<class SetType, bool External>
-class po_iterator_storage {
- SetType Visited;
-
-public:
- // Return true if edge destination should be visited.
- template <typename NodeRef>
- bool insertEdge(std::optional<NodeRef> From, NodeRef To) {
- return Visited.insert(To).second;
- }
-
- // Called after all children of BB have been visited.
- template <typename NodeRef> void finishPostorder(NodeRef BB) {}
-};
-
-/// Specialization of po_iterator_storage that references an external set.
-template<class SetType>
-class po_iterator_storage<SetType, true> {
- SetType &Visited;
-
-public:
- po_iterator_storage(SetType &VSet) : Visited(VSet) {}
- po_iterator_storage(const po_iterator_storage &S) : Visited(S.Visited) {}
-
- // Return true if edge destination should be visited, called with From = 0 for
- // the root node.
- // Graph edges can be pruned by specializing this function.
- template <class NodeRef>
- bool insertEdge(std::optional<NodeRef> From, NodeRef To) {
- return Visited.insert(To).second;
- }
-
- // Called after all children of BB have been visited.
- template <class NodeRef> void finishPostorder(NodeRef BB) {}
-};
-
namespace po_detail {
template <typename NodeRef> class NumberSet {
@@ -120,10 +54,10 @@ using DefaultSet =
} // namespace po_detail
-template <typename DerivedT, typename GraphT> class PostOrderTraversalBase {
- using GT = GraphTraits<GraphT>;
- using NodeRef = typename GT::NodeRef;
- using ChildItTy = typename GT::ChildIteratorType;
+template <typename DerivedT, typename GraphTraits>
+class PostOrderTraversalBase {
+ using NodeRef = typename GraphTraits::NodeRef;
+ using ChildItTy = typename GraphTraits::ChildIteratorType;
/// Used to maintain the ordering.
/// First element is basic block pointer, second is iterator for the next
@@ -155,13 +89,8 @@ template <typename DerivedT, typename GraphT> class PostOrderTraversalBase {
bool operator==(const iterator &X) const { return V == X.V; }
bool operator!=(const iterator &X) const { return !(*this == X); }
- NodeRef operator*() const { return V; }
-
- // This is a nonstandard operator-> that dereferences the pointer an extra
- // time... so that you can actually call methods ON the BasicBlock, because
- // the contained type is a pointer. This allows BBIt->getTerminator() f.e.
- //
- NodeRef operator->() const { return **this; }
+ reference operator*() const { return V; }
+ pointer operator->() const { return &V; }
iterator &operator++() { // Preincrement
V = POT->next();
@@ -182,8 +111,8 @@ template <typename DerivedT, typename GraphT> class PostOrderTraversalBase {
void init(NodeRef Start) {
if (derived()->insertEdge(std::optional<NodeRef>(), Start)) {
- VisitStack.emplace_back(Start, GT::child_begin(Start),
- GT::child_end(Start));
+ VisitStack.emplace_back(Start, GraphTraits::child_begin(Start),
+ GraphTraits::child_end(Start));
traverseChild();
}
}
@@ -198,7 +127,8 @@ template <typename DerivedT, typename GraphT> class PostOrderTraversalBase {
if (derived()->insertEdge(std::optional<NodeRef>(std::get<0>(Entry)),
BB)) {
// If the block is not visited...
- VisitStack.emplace_back(BB, GT::child_begin(BB), GT::child_end(BB));
+ VisitStack.emplace_back(BB, GraphTraits::child_begin(BB),
+ GraphTraits::child_end(BB));
}
}
}
@@ -233,7 +163,7 @@ template <typename DerivedT, typename GraphT> class PostOrderTraversalBase {
template <typename GraphT, typename SetType = po_detail::DefaultSet<GraphT>>
class PostOrderTraversal
: public PostOrderTraversalBase<PostOrderTraversal<GraphT, SetType>,
- GraphT> {
+ GraphTraits<GraphT>> {
using NodeRef = typename GraphTraits<GraphT>::NodeRef;
SetType Visited;
@@ -241,10 +171,6 @@ class PostOrderTraversal
public:
PostOrderTraversal(const GraphT &G) {
this->init(GraphTraits<GraphT>::getEntryNode(G));
-#if 0
- if constexpr (GraphHasNodeNumbers<GraphT>)
- Visited.reserve(GraphTraits<GraphT>::getMaxNumber(G));
-#endif
}
PostOrderTraversal(const GraphT &G, SetType &S) : Visited(S) {
@@ -256,101 +182,6 @@ class PostOrderTraversal
}
};
-template <class GraphT, class SetType = po_detail::DefaultSet<GraphT>,
- bool ExtStorage = false, class GT = GraphTraits<GraphT>>
-class po_iterator : public po_iterator_storage<SetType, ExtStorage> {
-public:
- // When External storage is used we are not multi-pass safe.
- using iterator_category =
- std::conditional_t<ExtStorage, std::input_iterator_tag,
- std::forward_iterator_tag>;
- using value_type = typename GT::NodeRef;
- using difference_type = std::ptrdiff_t;
- using pointer = value_type *;
- using reference = const value_type &;
-
-private:
- using NodeRef = typename GT::NodeRef;
- using ChildItTy = typename GT::ChildIteratorType;
-
- /// Used to maintain the ordering.
- /// First element is basic block pointer, second is iterator for the next
- /// child to visit, third is the end iterator.
- SmallVector<std::tuple<NodeRef, ChildItTy, ChildItTy>, 8> VisitStack;
-
- po_iterator(NodeRef BB) {
- this->insertEdge(std::optional<NodeRef>(), BB);
- VisitStack.emplace_back(BB, GT::child_begin(BB), GT::child_end(BB));
- traverseChild();
- }
-
- po_iterator() = default; // End is when stack is empty.
-
- po_iterator(NodeRef BB, SetType &S)
- : po_iterator_storage<SetType, ExtStorage>(S) {
- if (this->insertEdge(std::optional<NodeRef>(), BB)) {
- VisitStack.emplace_back(BB, GT::child_begin(BB), GT::child_end(BB));
- traverseChild();
- }
- }
-
- po_iterator(SetType &S)
- : po_iterator_storage<SetType, ExtStorage>(S) {
- } // End is when stack is empty.
-
- void traverseChild() {
- while (true) {
- auto &Entry = VisitStack.back();
- if (std::get<1>(Entry) == std::get<2>(Entry))
- break;
- NodeRef BB = *std::get<1>(Entry)++;
- if (this->insertEdge(std::optional<NodeRef>(std::get<0>(Entry)), BB)) {
- // If the block is not visited...
- VisitStack.emplace_back(BB, GT::child_begin(BB), GT::child_end(BB));
- }
- }
- }
-
-public:
- // Provide static "constructors"...
- static po_iterator begin(const GraphT &G) {
- return po_iterator(GT::getEntryNode(G));
- }
- static po_iterator end(const GraphT &G) { return po_iterator(); }
-
- static po_iterator begin(const GraphT &G, SetType &S) {
- return po_iterator(GT::getEntryNode(G), S);
- }
- static po_iterator end(const GraphT &G, SetType &S) { return po_iterator(S); }
-
- bool operator==(const po_iterator &x) const {
- return VisitStack == x.VisitStack;
- }
- bool operator!=(const po_iterator &x) const { return !(*this == x); }
-
- reference operator*() const { return std::get<0>(VisitStack.back()); }
-
- // This is a nonstandard operator-> that dereferences the pointer an extra
- // time... so that you can actually call methods ON the BasicBlock, because
- // the contained type is a pointer. This allows BBIt->getTerminator() f.e.
- //
- NodeRef operator->() const { return **this; }
-
- po_iterator &operator++() { // Preincrement
- this->finishPostorder(std::get<0>(VisitStack.back()));
- VisitStack.pop_back();
- if (!VisitStack.empty())
- traverseChild();
- return *this;
- }
-
- po_iterator operator++(int) { // Postincrement
- po_iterator tmp = *this;
- ++*this;
- return tmp;
- }
-};
-
// Provide global constructors that automatically figure out correct types...
//
template <class T> auto post_order(const T &G) {
diff --git a/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h b/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h
index 7cc40084b9247..cc2404a0249e7 100644
--- a/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h
+++ b/llvm/include/llvm/Analysis/BlockFrequencyInfoImpl.h
@@ -1106,8 +1106,9 @@ void BlockFrequencyInfoImpl<BT>::setBlockFreq(const BlockT *BB,
}
template <class BT> void BlockFrequencyInfoImpl<BT>::initializeRPOT() {
+ const BlockT *Entry = &F->front();
RPOT.reserve(F->size());
- for (const BlockT *BB : post_order(F))
+ for (const BlockT *BB : post_order(Entry))
RPOT.emplace_back(BB);
std::reverse(RPOT.begin(), RPOT.end());
diff --git a/llvm/include/llvm/Analysis/LoopIterator.h b/llvm/include/llvm/Analysis/LoopIterator.h
index b359812d396cd..1205545fe863f 100644
--- a/llvm/include/llvm/Analysis/LoopIterator.h
+++ b/llvm/include/llvm/Analysis/LoopIterator.h
@@ -188,7 +188,8 @@ class LoopBlocksRPO {
/// Traverse the blocks in a loop using a depth-first search.
class LoopBlocksTraversal
- : public PostOrderTraversalBase<LoopBlocksTraversal, Function *> {
+ : public PostOrderTraversalBase<LoopBlocksTraversal,
+ GraphTraits<Function *>> {
LoopBlocksDFS &DFS;
const LoopInfo *LI;
diff --git a/llvm/lib/Analysis/BranchProbabilityInfo.cpp b/llvm/lib/Analysis/BranchProbabilityInfo.cpp
index fdb539d91313c..490bfbc0fb7ca 100644
--- a/llvm/lib/Analysis/BranchProbabilityInfo.cpp
+++ b/llvm/lib/Analysis/BranchProbabilityInfo.cpp
@@ -1263,7 +1263,7 @@ void BPIConstruction::calculate(const Function &F, const LoopInfo &LoopI,
// Walk the basic blocks in post-order so that we can build up state about
// the successors of a block iteratively.
- for (const auto *BB : post_order(&F)) {
+ for (const auto *BB : post_order(&F.getEntryBlock())) {
LLVM_DEBUG(dbgs() << "Computing probabilities for " << BB->getName()
<< "\n");
// If there is no at least two successors, no sense to set probability.
diff --git a/llvm/lib/Analysis/CFGPrinter.cpp b/llvm/lib/Analysis/CFGPrinter.cpp
index 18776bc539b32..39108a906f081 100644
--- a/llvm/lib/Analysis/CFGPrinter.cpp
+++ b/llvm/lib/Analysis/CFGPrinter.cpp
@@ -206,7 +206,7 @@ void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(
};
/// The post order traversal iteration is done to know the status of
/// isOnDeoptOrUnreachablePath for all the successors on the current BB.
- llvm::for_each(post_order(F), evaluateBB);
+ llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);
}
bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,
diff --git a/llvm/lib/CodeGen/MachineTraceMetrics.cpp b/llvm/lib/CodeGen/MachineTraceMetrics.cpp
index 9b7bf6ea3cc82..3525ed7e7d657 100644
--- a/llvm/lib/CodeGen/MachineTraceMetrics.cpp
+++ b/llvm/lib/CodeGen/MachineTraceMetrics.cpp
@@ -487,7 +487,7 @@ struct LoopBounds {
template <typename GraphT>
class LoopBoundsPostOrderTraversal
: public PostOrderTraversalBase<LoopBoundsPostOrderTraversal<GraphT>,
- GraphT> {
+ GraphTraits<GraphT>> {
LoopBounds &LB;
public:
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index bc26d74ba3e67..f2ccf198c4c81 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -25250,7 +25250,7 @@ bool SLPVectorizerPass::runImpl(Function &F, ScalarEvolution *SE_,
DT->updateDFSNumbers();
// Scan the blocks in the function in post order.
- for (auto *BB : post_order(&F)) {
+ for (auto *BB : post_order(&F.getEntryBlock())) {
if (BB->isEHPad() || isa_and_nonnull<UnreachableInst>(BB->getTerminator()))
continue;
diff --git a/mlir/include/mlir/IR/Iterators.h b/mlir/include/mlir/IR/Iterators.h
index 4754d3beb2c6e..311861a4e1fa5 100644
--- a/mlir/include/mlir/IR/Iterators.h
+++ b/mlir/include/mlir/IR/Iterators.h
@@ -100,26 +100,37 @@ struct ReverseDominanceIterator {
static constexpr auto makeIterable(Operation &range) {
return llvm::reverse(ForwardIterator::makeIterable(range));
}
-#if 0
+
static auto makeIterable(Region ®ion) {
- Block *null = nullptr;
- llvm::PostOrderTraversal<Block *>::iterator sentinel;
- if (SkipGraphRegion && !mayHaveSSADominance(region)) {
- // Skip graph regions.
- return llvm::make_pointee_range(
- llvm::make_range(llvm::po_end(null), llvm::po_end(null)));
- }
+ struct Traversal
+ : llvm::PostOrderTraversalBase<Traversal, llvm::GraphTraits<Block *>> {
+ SmallPtrSet<Block *, 16> Visited;
+
+ Traversal(Region *region) {
+ if (region)
+ this->init(®ion->front());
+ }
+ bool insertEdge(std::optional<Block *>, Block *To) {
+ return Visited.insert(To).second;
+ }
+
+ using BaseT =
+ typename llvm::PostOrderTraversalBase<Traversal,
+ llvm::GraphTraits<Block *>>;
+ // Walk API expects Block references instead of pointers.
+ using iterator = llvm::pointee_iterator<typename BaseT::iterator>;
+ iterator begin() { return iterator(BaseT::begin()); }
+ iterator end() { return iterator(BaseT::end()); }
+ };
+
+ // Skip graph regions.
+ if ((SkipGraphRegion && !mayHaveSSADominance(region)) || region.empty())
+ return Traversal(nullptr);
// Create post-order iterator. Blocks are enumerated according to their
// successor relationship.
- auto it = region.empty()
- ? llvm::make_range(llvm::po_end(null), llvm::po_end(null))
- : llvm::post_order(®ion.front());
-
- // Walk API expects Block references instead of pointers.
- return llvm::make_pointee_range(it);
+ return Traversal(®ion);
}
-#endif
};
} // namespace mlir
>From a2d3d35efcf550ccbcd058dc8a04fca88bcd3032 Mon Sep 17 00:00:00 2001
From: Alexis Engelke <engelke at in.tum.de>
Date: Thu, 9 Apr 2026 09:24:03 +0000
Subject: [PATCH 3/5] fix + better MLIR use
Created using spr 1.3.8-wip
---
llvm/include/llvm/ADT/PostOrderIterator.h | 3 +++
llvm/lib/Target/SPIRV/SPIRVUtils.h | 1 +
llvm/unittests/ADT/PostOrderIteratorTest.cpp | 22 +++++++-------------
mlir/include/mlir/IR/Iterators.h | 21 +++++--------------
4 files changed, 16 insertions(+), 31 deletions(-)
diff --git a/llvm/include/llvm/ADT/PostOrderIterator.h b/llvm/include/llvm/ADT/PostOrderIterator.h
index 77ce6cc6c6ff2..0e129f2df88f2 100644
--- a/llvm/include/llvm/ADT/PostOrderIterator.h
+++ b/llvm/include/llvm/ADT/PostOrderIterator.h
@@ -169,6 +169,9 @@ class PostOrderTraversal
SetType Visited;
public:
+ /// Default constructor for an empty traversal.
+ PostOrderTraversal() = default;
+
PostOrderTraversal(const GraphT &G) {
this->init(GraphTraits<GraphT>::getEntryNode(G));
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index d541ead5ac22c..f47c5ed9ba5f3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -21,6 +21,7 @@
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/TypedPointerType.h"
#include <queue>
+#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
diff --git a/llvm/unittests/ADT/PostOrderIteratorTest.cpp b/llvm/unittests/ADT/PostOrderIteratorTest.cpp
index f66d9b6bc8b83..f7f33809865c1 100644
--- a/llvm/unittests/ADT/PostOrderIteratorTest.cpp
+++ b/llvm/unittests/ADT/PostOrderIteratorTest.cpp
@@ -23,30 +23,22 @@ namespace {
// Whether we're able to compile
TEST(PostOrderIteratorTest, Compiles) {
- using ExtSetTy = SmallPtrSet<void *, 4>;
-
- // Tests that template specializations are kept up to date
- void *Null = nullptr;
- po_iterator_storage<std::set<void *>, false> PIS;
- PIS.insertEdge(std::optional<void *>(), Null);
- ExtSetTy Ext;
- po_iterator_storage<ExtSetTy, true> PISExt(Ext);
- PIS.insertEdge(std::optional<void *>(), Null);
-
- // Test above, but going through po_iterator (which inherits from template
- // base)
Graph<6> G;
using NodeType = Graph<6>::NodeType;
NodeType *NullNode = nullptr;
+
auto PI = post_order(G);
PI.insertEdge(std::optional<NodeType *>(), NullNode);
+
+ using ExtSetTy = SmallPtrSet<void *, 4>;
+ ExtSetTy Ext;
auto PIExt = post_order_ext(G, Ext);
PIExt.insertEdge(std::optional<NodeType *>(), NullNode);
}
-static_assert(
- std::is_convertible_v<decltype(*std::declval<po_iterator<Graph<3>>>()),
- po_iterator<Graph<3>>::reference>);
+static_assert(std::is_convertible_v<
+ decltype(*post_order(std::declval<Graph<3>>()).begin()),
+ PostOrderTraversal<Graph<3>>::iterator::reference>);
// Test post-order and reverse post-order traversals for simple graph type.
TEST(PostOrderIteratorTest, PostOrderAndReversePostOrderTraverrsal) {
diff --git a/mlir/include/mlir/IR/Iterators.h b/mlir/include/mlir/IR/Iterators.h
index 311861a4e1fa5..12e74d1770a43 100644
--- a/mlir/include/mlir/IR/Iterators.h
+++ b/mlir/include/mlir/IR/Iterators.h
@@ -102,21 +102,10 @@ struct ReverseDominanceIterator {
}
static auto makeIterable(Region ®ion) {
- struct Traversal
- : llvm::PostOrderTraversalBase<Traversal, llvm::GraphTraits<Block *>> {
- SmallPtrSet<Block *, 16> Visited;
-
- Traversal(Region *region) {
- if (region)
- this->init(®ion->front());
- }
- bool insertEdge(std::optional<Block *>, Block *To) {
- return Visited.insert(To).second;
- }
-
- using BaseT =
- typename llvm::PostOrderTraversalBase<Traversal,
- llvm::GraphTraits<Block *>>;
+ struct Traversal : llvm::PostOrderTraversal<Region *> {
+ using BaseT = llvm::PostOrderTraversal<Region *>;
+ using BaseT::PostOrderTraversal; // Re-use constructors.
+
// Walk API expects Block references instead of pointers.
using iterator = llvm::pointee_iterator<typename BaseT::iterator>;
iterator begin() { return iterator(BaseT::begin()); }
@@ -125,7 +114,7 @@ struct ReverseDominanceIterator {
// Skip graph regions.
if ((SkipGraphRegion && !mayHaveSSADominance(region)) || region.empty())
- return Traversal(nullptr);
+ return Traversal();
// Create post-order iterator. Blocks are enumerated according to their
// successor relationship.
>From 22a250d5d1cd52e321e3045be8d9cfd4a33d9cd9 Mon Sep 17 00:00:00 2001
From: Alexis Engelke <engelke at in.tum.de>
Date: Thu, 9 Apr 2026 16:27:45 +0000
Subject: [PATCH 4/5] docs
Created using spr 1.3.8-wip
---
llvm/include/llvm/ADT/PostOrderIterator.h | 39 ++++++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/PostOrderIterator.h b/llvm/include/llvm/ADT/PostOrderIterator.h
index 0e129f2df88f2..4e625488ab22e 100644
--- a/llvm/include/llvm/ADT/PostOrderIterator.h
+++ b/llvm/include/llvm/ADT/PostOrderIterator.h
@@ -54,6 +54,19 @@ using DefaultSet =
} // namespace po_detail
+/// CRTP base class for post-order traversal. Storage for visited nodes must be
+/// provided by the sub-class, which must implement insertEdge(). Due to CRTP
+/// limitations, the sub-class must call init() with the start node before
+/// traversing; not calling init results in an empty iterator.
+///
+/// Sub-classes can observe the post-order traversal with finishPostorder(),
+/// which is called before the iterator moves to the next node, and also the
+/// pre-order traversal with insertEdge().
+///
+/// Unwanted graph nodes (e.g. from a previous traversal) can be skipped by
+/// returning false from insertEdge().
+///
+/// This class only supports a single traversal of the graph.
template <typename DerivedT, typename GraphTraits>
class PostOrderTraversalBase {
using NodeRef = typename GraphTraits::NodeRef;
@@ -109,6 +122,7 @@ class PostOrderTraversalBase {
DerivedT *derived() { return static_cast<DerivedT *>(this); }
+ /// Initialize post-order traversal at given start node.
void init(NodeRef Start) {
if (derived()->insertEdge(std::optional<NodeRef>(), Start)) {
VisitStack.emplace_back(Start, GraphTraits::child_begin(Start),
@@ -159,7 +173,23 @@ class PostOrderTraversalBase {
void finishPostorder(NodeRef) {}
};
-/// Post-order traversal of a graph.
+/// Post-order traversal of a graph. Note: the traversal state is stored in this
+/// class, not in the iterators -- the lifetime of PostOrderTraversal must
+/// exceed the lifetime of the iterators. Special care must be taken with
+/// range-based for-loops in combination with LLVM ranges:
+///
+/// // Fine:
+/// for (BasicBlock *BB : post_order(F)) { ... }
+///
+/// // Problematic! Lifetime of PostOrderTraversal ends before the loop is
+/// // entered, because make_filter_range only stores the iterators but not
+/// // the range object itself.
+/// for (BasicBlock *BB : make_filter_range(post_order(F), ...)) { ... }
+/// // Fixed:
+/// auto POT = post_order(F);
+/// for (BasicBlock *BB : make_filter_range(POT, ...)) { ... }
+///
+/// This class only supports a single traversal of the graph.
template <typename GraphT, typename SetType = po_detail::DefaultSet<GraphT>>
class PostOrderTraversal
: public PostOrderTraversalBase<PostOrderTraversal<GraphT, SetType>,
@@ -172,10 +202,15 @@ class PostOrderTraversal
/// Default constructor for an empty traversal.
PostOrderTraversal() = default;
+ /// Post-order traversal of the graph starting at the root node using an
+ /// internal storage.
PostOrderTraversal(const GraphT &G) {
this->init(GraphTraits<GraphT>::getEntryNode(G));
}
+ /// Post-order traversal of the graph starting at the root node using an
+ /// external storage. This can be used to keep track of visited nodes after
+ /// the traversal and to skip nodes that are already contained in the set.
PostOrderTraversal(const GraphT &G, SetType &S) : Visited(S) {
this->init(GraphTraits<GraphT>::getEntryNode(G));
}
@@ -187,6 +222,8 @@ class PostOrderTraversal
// Provide global constructors that automatically figure out correct types...
//
+/// Post-order traversal of a graph. Note: this returns a PostOrderTraversal,
+/// not an iterator range; \see PostOrderTraversal.
template <class T> auto post_order(const T &G) {
return PostOrderTraversal<T>(G);
}
>From 1dcd135d79be7b46e4cc3a1b7e9e4bfd05158c1d Mon Sep 17 00:00:00 2001
From: Alexis Engelke <engelke at in.tum.de>
Date: Thu, 9 Apr 2026 19:27:48 +0000
Subject: [PATCH 5/5] update outdated comments
Created using spr 1.3.8-wip
---
.../clang/Analysis/Analyses/PostOrderCFGView.h | 1 -
llvm/include/llvm/Analysis/LoopIterator.h | 11 +++++------
llvm/lib/CodeGen/MachineTraceMetrics.cpp | 4 ++--
3 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h b/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h
index 0a37fdea63fac..f4a4e5237995a 100644
--- a/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h
+++ b/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h
@@ -37,7 +37,6 @@ class PostOrderCFGView : public ManagedAnalysis {
CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false) {}
/// Set the bit associated with a particular CFGBlock.
- /// This is the important method for the SetType template parameter.
std::pair<std::nullopt_t, bool> insert(const CFGBlock *Block) {
if (VisitedBlockIDs.test(Block->getBlockID()))
return std::make_pair(std::nullopt, false);
diff --git a/llvm/include/llvm/Analysis/LoopIterator.h b/llvm/include/llvm/Analysis/LoopIterator.h
index 1205545fe863f..6d25b2fd8923c 100644
--- a/llvm/include/llvm/Analysis/LoopIterator.h
+++ b/llvm/include/llvm/Analysis/LoopIterator.h
@@ -198,7 +198,7 @@ class LoopBlocksTraversal
: DFS(Storage), LI(LInfo) {}
/// Postorder traversal over the graph. This only needs to be done once.
- /// po_iterator "automatically" calls back to visitPreorder and
+ /// PostOrderTraversalBase "automatically" calls back to insertEdge and
/// finishPostorder to record the DFS result.
iterator begin() {
assert(DFS.PostBlocks.empty() && "Need clear DFS result before traversing");
@@ -208,9 +208,9 @@ class LoopBlocksTraversal
}
iterator end() { return PostOrderTraversalBase::end(); }
- /// Called by po_iterator upon reaching a block via a CFG edge. If this block
- /// is contained in the loop and has not been visited, then mark it preorder
- /// visited and return true.
+ /// Called upon reaching a block via a CFG edge. If this block is contained
+ /// in the loop and has not been visited, then mark it preorder visited and
+ /// return true (i.e., traverse the edge).
///
/// TODO: If anyone is interested, we could record preorder numbers here.
bool insertEdge(std::optional<BasicBlock *> /*From*/, BasicBlock *BB) {
@@ -220,8 +220,7 @@ class LoopBlocksTraversal
return DFS.PostNumbers.insert(std::make_pair(BB, 0)).second;
}
- /// Called by po_iterator each time it advances, indicating a block's
- /// postorder.
+ /// Called each time the iterator advances, indicating a block's postorder.
void finishPostorder(BasicBlock *BB) {
assert(DFS.PostNumbers.count(BB) && "Loop DFS skipped preorder");
DFS.PostBlocks.push_back(BB);
diff --git a/llvm/lib/CodeGen/MachineTraceMetrics.cpp b/llvm/lib/CodeGen/MachineTraceMetrics.cpp
index 3525ed7e7d657..a477fa23406e3 100644
--- a/llvm/lib/CodeGen/MachineTraceMetrics.cpp
+++ b/llvm/lib/CodeGen/MachineTraceMetrics.cpp
@@ -482,8 +482,8 @@ struct LoopBounds {
} // end anonymous namespace
-// Specialize po_iterator_storage in order to prune the post-order traversal so
-// it is limited to the current loop and doesn't traverse the loop back edges.
+// Restrict the post-order traversal to the current loop and don't traverse the
+// loop back edges.
template <typename GraphT>
class LoopBoundsPostOrderTraversal
: public PostOrderTraversalBase<LoopBoundsPostOrderTraversal<GraphT>,
More information about the Mlir-commits
mailing list