[llvm] [LoopInfo] Reuse DomTree's own DFS numbering in analyze() (PR #207650)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 5 21:28:37 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-support
Author: Fangrui Song (MaskRay)
<details>
<summary>Changes</summary>
analyze() discovers loop headers with a post_order(DomRoot) walk, then
tests each back-edge candidate with DomTree.dominates(). Initial queries
use `dominatedBySlowTreeWalk` and then switch to `updateDFSNumbers`
(Euler tour technique), requiring two tree walks in total.
Optimize this with an upfront updateDFSNumbers to avoid the post-order
walk. This works because natural loop identification just requires
sub-loops to be identified before super-loops. Both post-order and
reverse pre-order walk.
DFS numbers, of [0, 2*N), have many nullptr gaps. Simplify
updateDFSNumbers() so DFSNumOut no longer consumes its own counter tick:
DFSNumIn becomes a dense [0, N) preorder rank.
---
Full diff: https://github.com/llvm/llvm-project/pull/207650.diff
4 Files Affected:
- (modified) llvm/include/llvm/Support/GenericDomTree.h (+12-2)
- (modified) llvm/include/llvm/Support/GenericDomTreeConstruction.h (+2-2)
- (modified) llvm/include/llvm/Support/GenericLoopInfoImpl.h (+11-4)
- (modified) llvm/unittests/IR/DominatorTreeTest.cpp (+11-11)
``````````diff
diff --git a/llvm/include/llvm/Support/GenericDomTree.h b/llvm/include/llvm/Support/GenericDomTree.h
index 920983e7bd800..45bc6269177c7 100644
--- a/llvm/include/llvm/Support/GenericDomTree.h
+++ b/llvm/include/llvm/Support/GenericDomTree.h
@@ -46,6 +46,8 @@ namespace llvm {
template <typename NodeT, bool IsPostDom>
class DominatorTreeBase;
+template <class BlockT, class LoopT> class LoopInfoBase;
+
namespace DomTreeBuilder {
template <typename DomTreeT>
struct SemiNCAInfo;
@@ -316,8 +318,9 @@ class DominatorTreeBase {
unsigned BlockNumberEpoch = 0;
friend struct DomTreeBuilder::SemiNCAInfo<DominatorTreeBase>;
+ template <class BlockT, class LoopT> friend class LoopInfoBase;
- public:
+public:
DominatorTreeBase() = default;
DominatorTreeBase(const DominatorTreeBase &) = delete;
@@ -382,6 +385,13 @@ class DominatorTreeBase {
}
private:
+ // For LoopInfoBase's use in deriving a reverse-preorder traversal.
+ auto nodes() const {
+ return make_filter_range(DomTreeNodes, [](const DomTreeNodeBase<NodeT> *N) {
+ return N != nullptr;
+ });
+ }
+
std::optional<unsigned> getNodeIndex(const NodeT *BB) const {
if constexpr (GraphHasNodeNumbers<NodeT *>) {
assert(BlockNumberEpoch ==
@@ -850,7 +860,7 @@ class DominatorTreeBase {
// If we visited all of the children of this node, "recurse" back up the
// stack setting the DFOutNum.
if (ChildIt == Node->end()) {
- Node->DFSNumOut = DFSNum++;
+ Node->DFSNumOut = DFSNum;
WorkStack.pop_back();
} else {
// Otherwise, recursively visit this child.
diff --git a/llvm/include/llvm/Support/GenericDomTreeConstruction.h b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
index 9ac5c5444c88a..80365e72e8d07 100644
--- a/llvm/include/llvm/Support/GenericDomTreeConstruction.h
+++ b/llvm/include/llvm/Support/GenericDomTreeConstruction.h
@@ -1444,13 +1444,13 @@ template <typename DomTreeT> struct SemiNCAInfo {
return false;
}
- if (Children.back()->getDFSNumOut() + 1 != Node->getDFSNumOut()) {
+ if (Children.back()->getDFSNumOut() != Node->getDFSNumOut()) {
PrintChildrenError(Children.back(), nullptr);
return false;
}
for (size_t i = 0, e = Children.size() - 1; i != e; ++i) {
- if (Children[i]->getDFSNumOut() + 1 != Children[i + 1]->getDFSNumIn()) {
+ if (Children[i]->getDFSNumOut() != Children[i + 1]->getDFSNumIn()) {
PrintChildrenError(Children[i], Children[i + 1]);
return false;
}
diff --git a/llvm/include/llvm/Support/GenericLoopInfoImpl.h b/llvm/include/llvm/Support/GenericLoopInfoImpl.h
index 4666f4dac9cb6..9579a236b4da6 100644
--- a/llvm/include/llvm/Support/GenericLoopInfoImpl.h
+++ b/llvm/include/llvm/Support/GenericLoopInfoImpl.h
@@ -560,8 +560,8 @@ void PopulateLoopsDFS<BlockT, LoopT>::insertIntoLoop(BlockT *Block) {
Subloop->addBlockEntry(Block);
}
-/// Analyze LoopInfo discovers loops during a postorder DominatorTree traversal
-/// interleaved with backward CFG traversals within each subloop
+/// Analyze LoopInfo discovers loops during a reverse preorder DominatorTree
+/// traversal interleaved with backward CFG traversals within each subloop
/// (discoverAndMapSubloop). The backward traversal skips inner subloops, so
/// this part of the algorithm is linear in the number of CFG edges. Subloop and
/// Block vectors are then populated during a single forward CFG traversal
@@ -576,7 +576,6 @@ void PopulateLoopsDFS<BlockT, LoopT>::insertIntoLoop(BlockT *Block) {
/// insertions per block.
template <class BlockT, class LoopT>
void LoopInfoBase<BlockT, LoopT>::analyze(const DomTreeBase<BlockT> &DomTree) {
- // Postorder traversal of the dominator tree.
const DomTreeNodeBase<BlockT> *DomRoot = DomTree.getRootNode();
if constexpr (GraphHasNodeNumbers<const BlockT *>) {
ParentPtr = DomRoot->getBlock()->getParent();
@@ -584,8 +583,16 @@ void LoopInfoBase<BlockT, LoopT>::analyze(const DomTreeBase<BlockT> &DomTree) {
unsigned Max = GraphTraits<ParentT>::getMaxNumber(ParentPtr);
BBMap.resize(Max);
}
- for (auto DomNode : post_order(DomRoot)) {
+ // Visit dominator tree nodes in reverse preorder: like postorder, this
+ // guarantees a subloop is discovered before the super loop.
+ DomTree.updateDFSNumbers();
+ SmallVector<const DomTreeNodeBase<BlockT> *, 32> PreorderNodes(
+ DomRoot->getDFSNumOut());
+ for (const DomTreeNodeBase<BlockT> *Node : DomTree.nodes())
+ PreorderNodes[Node->getDFSNumIn()] = Node;
+
+ for (const DomTreeNodeBase<BlockT> *DomNode : llvm::reverse(PreorderNodes)) {
BlockT *Header = DomNode->getBlock();
SmallVector<BlockT *, 4> Backedges;
diff --git a/llvm/unittests/IR/DominatorTreeTest.cpp b/llvm/unittests/IR/DominatorTreeTest.cpp
index 042b7f449ba89..070a7325bf28e 100644
--- a/llvm/unittests/IR/DominatorTreeTest.cpp
+++ b/llvm/unittests/IR/DominatorTreeTest.cpp
@@ -253,13 +253,13 @@ TEST(DominatorTree, Unreachable) {
// Check DFS Numbers before
DT->updateDFSNumbers();
EXPECT_EQ(DT->getNode(BB0)->getDFSNumIn(), 0UL);
- EXPECT_EQ(DT->getNode(BB0)->getDFSNumOut(), 7UL);
+ EXPECT_EQ(DT->getNode(BB0)->getDFSNumOut(), 4UL);
EXPECT_EQ(DT->getNode(BB1)->getDFSNumIn(), 1UL);
EXPECT_EQ(DT->getNode(BB1)->getDFSNumOut(), 2UL);
- EXPECT_EQ(DT->getNode(BB2)->getDFSNumIn(), 5UL);
- EXPECT_EQ(DT->getNode(BB2)->getDFSNumOut(), 6UL);
- EXPECT_EQ(DT->getNode(BB4)->getDFSNumIn(), 3UL);
- EXPECT_EQ(DT->getNode(BB4)->getDFSNumOut(), 4UL);
+ EXPECT_EQ(DT->getNode(BB2)->getDFSNumIn(), 3UL);
+ EXPECT_EQ(DT->getNode(BB2)->getDFSNumOut(), 4UL);
+ EXPECT_EQ(DT->getNode(BB4)->getDFSNumIn(), 2UL);
+ EXPECT_EQ(DT->getNode(BB4)->getDFSNumOut(), 3UL);
// Check levels before
EXPECT_EQ(DT->getNode(BB0)->getLevel(), 0U);
@@ -275,15 +275,15 @@ TEST(DominatorTree, Unreachable) {
// Check DFS Numbers after
DT->updateDFSNumbers();
EXPECT_EQ(DT->getNode(BB0)->getDFSNumIn(), 0UL);
- EXPECT_EQ(DT->getNode(BB0)->getDFSNumOut(), 9UL);
+ EXPECT_EQ(DT->getNode(BB0)->getDFSNumOut(), 5UL);
EXPECT_EQ(DT->getNode(BB1)->getDFSNumIn(), 1UL);
- EXPECT_EQ(DT->getNode(BB1)->getDFSNumOut(), 4UL);
- EXPECT_EQ(DT->getNode(BB2)->getDFSNumIn(), 7UL);
- EXPECT_EQ(DT->getNode(BB2)->getDFSNumOut(), 8UL);
+ EXPECT_EQ(DT->getNode(BB1)->getDFSNumOut(), 3UL);
+ EXPECT_EQ(DT->getNode(BB2)->getDFSNumIn(), 4UL);
+ EXPECT_EQ(DT->getNode(BB2)->getDFSNumOut(), 5UL);
EXPECT_EQ(DT->getNode(BB3)->getDFSNumIn(), 2UL);
EXPECT_EQ(DT->getNode(BB3)->getDFSNumOut(), 3UL);
- EXPECT_EQ(DT->getNode(BB4)->getDFSNumIn(), 5UL);
- EXPECT_EQ(DT->getNode(BB4)->getDFSNumOut(), 6UL);
+ EXPECT_EQ(DT->getNode(BB4)->getDFSNumIn(), 3UL);
+ EXPECT_EQ(DT->getNode(BB4)->getDFSNumOut(), 4UL);
// Check levels after
EXPECT_EQ(DT->getNode(BB0)->getLevel(), 0U);
``````````
</details>
https://github.com/llvm/llvm-project/pull/207650
More information about the llvm-commits
mailing list