Files
llvm-project/llvm/unittests/ADT/PostOrderIteratorTest.cpp
Alexis Engelke 691a130e0f [ADT] Refactor post order traversal (#191047)
Currently, po_iterator holds the traversal state. This makes copying
and moving po_iterator fairly expensive and the code cannot be optimized
away in several cases (most of it isn't even inlined in a default
build).

Therefore, refactor post-order traversal to hold the state in a wrapper
class with cheap iterators. Additionally, replace po_storage base class
with a CRTP implementation where users can provide their own storage.

Benefits:

- Performance in stage2-O3 improves by 0.19% instructions:u and even
  more substantially in cycles/wall-time.

- Users that use a custom storage/iteration limitation can do so in a
  more clean way by subclassing PostIteratorTraversalBase. See e.g.
  LoopBlocksTraversal.

- For graphs with block numbers, reserving can now be implemented
  reasonably easy (not done yet).

Implications:

- PostOrderTraversal::iterator is no longer a forward iterator. This
  property was never really used, though.

- PostOrderTraversal must be live while iterators are live. For typical
  uses (for (X x : post_order(...))), this is no problem, but could end
  up being problematic if the iterator is wrapped (e.g.
  for (X x : make_filter_range(post_order(...), ...)) -- problematic,
  because make_filter_range doesn't preserve the range but only the two
  iterators, which become invalid as the for loop is entered). This is a
  limitation of the way LLVM implements ranges.
2026-04-09 20:31:15 +00:00

79 lines
2.2 KiB
C++

//===- PostOrderIteratorTest.cpp - PostOrderIterator unit tests -----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "gtest/gtest.h"
#include "TestGraph.h"
#include <array>
#include <iterator>
#include <type_traits>
#include <cstddef>
using namespace llvm;
namespace {
// Whether we're able to compile
TEST(PostOrderIteratorTest, Compiles) {
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(*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) {
Graph<6> G;
G.AddEdge(0, 1);
G.AddEdge(0, 2);
G.AddEdge(0, 3);
G.AddEdge(1, 4);
G.AddEdge(2, 5);
G.AddEdge(5, 2);
G.AddEdge(2, 4);
G.AddEdge(1, 4);
SmallVector<int> FromIterator;
for (auto N : post_order(G))
FromIterator.push_back(N->first);
EXPECT_EQ(6u, FromIterator.size());
EXPECT_EQ(4, FromIterator[0]);
EXPECT_EQ(1, FromIterator[1]);
EXPECT_EQ(5, FromIterator[2]);
EXPECT_EQ(2, FromIterator[3]);
EXPECT_EQ(3, FromIterator[4]);
EXPECT_EQ(0, FromIterator[5]);
FromIterator.clear();
ReversePostOrderTraversal<Graph<6>> RPOT(G);
for (auto N : RPOT)
FromIterator.push_back(N->first);
EXPECT_EQ(6u, FromIterator.size());
EXPECT_EQ(0, FromIterator[0]);
EXPECT_EQ(3, FromIterator[1]);
EXPECT_EQ(2, FromIterator[2]);
EXPECT_EQ(5, FromIterator[3]);
EXPECT_EQ(1, FromIterator[4]);
EXPECT_EQ(4, FromIterator[5]);
}
}