[llvm] [SelectionDAG] Add support to dump DAGs with sorted nodes (PR #161097)
Min-Yih Hsu via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 1 14:19:46 PDT 2025
https://github.com/mshockwave updated https://github.com/llvm/llvm-project/pull/161097
>From 4f59ec00e1898be0b60a4c91c279033a213ac169 Mon Sep 17 00:00:00 2001
From: Min Hsu <min at myhsu.dev>
Date: Sun, 28 Sep 2025 13:41:56 -0700
Subject: [PATCH 1/3] [SelectionDAG] Add support to dump DAGs with sorted nodes
---
llvm/include/llvm/CodeGen/SelectionDAG.h | 8 +-
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 129 +++++++-----------
.../SelectionDAG/SelectionDAGDumper.cpp | 14 +-
.../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 25 ++--
4 files changed, 83 insertions(+), 93 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index d9d6f0bcdcb84..446239a007186 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -1959,6 +1959,10 @@ class SelectionDAG {
LLVM_ABI SDValue makeEquivalentMemoryOrdering(LoadSDNode *OldLoad,
SDValue NewMemOp);
+ /// Get all the nodes in their topological order without modifying any states.
+ LLVM_ABI void getTopologicallyOrderedNodes(
+ SmallVectorImpl<const SDNode *> &SortedNodes) const;
+
/// Topological-sort the AllNodes list and a
/// assign a unique node id for each node in the DAG based on their
/// topological order. Returns the number of nodes.
@@ -2009,7 +2013,9 @@ class SelectionDAG {
/// function mirrors \c llvm::salvageDebugInfo.
LLVM_ABI void salvageDebugInfo(SDNode &N);
- LLVM_ABI void dump() const;
+ /// Dump the textual format of this DAG. Print nodes in sorted orders is \p
+ /// Sorted is true.
+ LLVM_ABI void dump(bool Sorted = false) const;
/// In most cases this function returns the ABI alignment for a given type,
/// except for illegal vector types where the alignment exceeds that of the
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 8fc7eabf90ea8..f1999fe34e88a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -12610,87 +12610,56 @@ void SelectionDAG::ReplaceAllUsesOfValuesWith(const SDValue *From,
/// based on their topological order. It returns the maximum id and a vector
/// of the SDNodes* in assigned order by reference.
unsigned SelectionDAG::AssignTopologicalOrder() {
- unsigned DAGSize = 0;
-
- // SortedPos tracks the progress of the algorithm. Nodes before it are
- // sorted, nodes after it are unsorted. When the algorithm completes
- // it is at the end of the list.
- allnodes_iterator SortedPos = allnodes_begin();
-
- // Visit all the nodes. Move nodes with no operands to the front of
- // the list immediately. Annotate nodes that do have operands with their
- // operand count. Before we do this, the Node Id fields of the nodes
- // may contain arbitrary values. After, the Node Id fields for nodes
- // before SortedPos will contain the topological sort index, and the
- // Node Id fields for nodes At SortedPos and after will contain the
- // count of outstanding operands.
- for (SDNode &N : llvm::make_early_inc_range(allnodes())) {
+ SmallVector<const SDNode *> SortedNodes(AllNodes.size());
+ getTopologicallyOrderedNodes(SortedNodes);
+
+ for (auto [Idx, ConstN] : enumerate(SortedNodes)) {
+ auto *N = const_cast<SDNode *>(ConstN);
+ N->setNodeId(Idx);
+ if (N->getIterator() != std::prev(allnodes_end()))
+ AllNodes.push_back(AllNodes.remove(N));
+ }
+
+ return SortedNodes.size();
+}
+
+void SelectionDAG::getTopologicallyOrderedNodes(
+ SmallVectorImpl<const SDNode *> &SortedNodes) const {
+ SortedNodes.clear();
+ // Node -> remaining number of outstanding operands.
+ DenseMap<const SDNode *, unsigned> RemainingOperands;
+
+ // Put nodes without any operands into SortedNodes first.
+ for (const SDNode &N : allnodes()) {
checkForCycles(&N, this);
- unsigned Degree = N.getNumOperands();
- if (Degree == 0) {
- // A node with no uses, add it to the result array immediately.
- N.setNodeId(DAGSize++);
- allnodes_iterator Q(&N);
- if (Q != SortedPos)
- SortedPos = AllNodes.insert(SortedPos, AllNodes.remove(Q));
- assert(SortedPos != AllNodes.end() && "Overran node list");
- ++SortedPos;
- } else {
- // Temporarily use the Node Id as scratch space for the degree count.
- N.setNodeId(Degree);
- }
- }
-
- // Visit all the nodes. As we iterate, move nodes into sorted order,
- // such that by the time the end is reached all nodes will be sorted.
- for (SDNode &Node : allnodes()) {
- SDNode *N = &Node;
- checkForCycles(N, this);
- // N is in sorted position, so all its uses have one less operand
- // that needs to be sorted.
- for (SDNode *P : N->users()) {
- unsigned Degree = P->getNodeId();
- assert(Degree != 0 && "Invalid node degree");
- --Degree;
- if (Degree == 0) {
- // All of P's operands are sorted, so P may sorted now.
- P->setNodeId(DAGSize++);
- if (P->getIterator() != SortedPos)
- SortedPos = AllNodes.insert(SortedPos, AllNodes.remove(P));
- assert(SortedPos != AllNodes.end() && "Overran node list");
- ++SortedPos;
- } else {
- // Update P's outstanding operand count.
- P->setNodeId(Degree);
- }
- }
- if (Node.getIterator() == SortedPos) {
-#ifndef NDEBUG
- allnodes_iterator I(N);
- SDNode *S = &*++I;
- dbgs() << "Overran sorted position:\n";
- S->dumprFull(this); dbgs() << "\n";
- dbgs() << "Checking if this is due to cycles\n";
- checkForCycles(this, true);
-#endif
- llvm_unreachable(nullptr);
- }
- }
-
- assert(SortedPos == AllNodes.end() &&
- "Topological sort incomplete!");
- assert(AllNodes.front().getOpcode() == ISD::EntryToken &&
- "First node in topological sort is not the entry token!");
- assert(AllNodes.front().getNodeId() == 0 &&
- "First node in topological sort has non-zero id!");
- assert(AllNodes.front().getNumOperands() == 0 &&
- "First node in topological sort has operands!");
- assert(AllNodes.back().getNodeId() == (int)DAGSize-1 &&
- "Last node in topologic sort has unexpected id!");
- assert(AllNodes.back().use_empty() &&
- "Last node in topologic sort has users!");
- assert(DAGSize == allnodes_size() && "Node count mismatch!");
- return DAGSize;
+ unsigned NumOperands = N.getNumOperands();
+ if (NumOperands == 0)
+ SortedNodes.push_back(&N);
+ else
+ // Record their total number of outstanding operands.
+ RemainingOperands[&N] = NumOperands;
+ }
+
+ // A node is pushed into SortedNodes when all of its operands (predecessors in
+ // the graph) are also in SortedNodes.
+ for (unsigned i = 0U; i < SortedNodes.size(); ++i) {
+ const SDNode *N = SortedNodes[i];
+ for (const SDNode *U : N->users()) {
+ unsigned &NumRemOperands = RemainingOperands[U];
+ assert(NumRemOperands && "Invalid number of remaining operands");
+ --NumRemOperands;
+ if (!NumRemOperands)
+ SortedNodes.push_back(U);
+ }
+ }
+
+ assert(SortedNodes.size() == AllNodes.size() && "Node count mismatch");
+ assert(SortedNodes.front()->getOpcode() == ISD::EntryToken &&
+ "First node in topological sort is not the entry token");
+ assert(SortedNodes.front()->getNumOperands() == 0 &&
+ "First node in topological sort has operands");
+ assert(SortedNodes.back()->use_empty() &&
+ "Last node in topologic sort has users");
}
/// AddDbgValue - Add a dbg_value SDNode. If SD is non-null that means the
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 4b2a00c2e2cfa..46dde33acb2da 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -1061,13 +1061,23 @@ static void DumpNodes(const SDNode *N, unsigned indent, const SelectionDAG *G) {
N->dump(G);
}
-LLVM_DUMP_METHOD void SelectionDAG::dump() const {
+LLVM_DUMP_METHOD void SelectionDAG::dump(bool Sorted) const {
dbgs() << "SelectionDAG has " << AllNodes.size() << " nodes:\n";
- for (const SDNode &N : allnodes()) {
+ auto dumpEachNode = [this](const SDNode &N) {
if (!N.hasOneUse() && &N != getRoot().getNode() &&
(!shouldPrintInline(N, this) || N.use_empty()))
DumpNodes(&N, 2, this);
+ };
+
+ if (Sorted) {
+ SmallVector<const SDNode *> SortedNodes(AllNodes.size());
+ getTopologicallyOrderedNodes(SortedNodes);
+ for (const SDNode *N : SortedNodes)
+ dumpEachNode(*N);
+ } else {
+ for (const SDNode &N : allnodes())
+ dumpEachNode(N);
}
if (getRoot().getNode()) DumpNodes(getRoot().getNode(), 2, this);
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index e61558c59bf0d..0604bd4ed8889 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -144,6 +144,11 @@ UseMBPI("use-mbpi",
cl::init(true), cl::Hidden);
#ifndef NDEBUG
+static cl::opt<bool>
+ DumpSortedDAG("dump-sorted-dags", cl::Hidden,
+ cl::desc("Print the DAG with sorted nodes in debug dump"),
+ cl::init(false));
+
static cl::opt<std::string>
FilterDAGBasicBlockName("filter-view-dags", cl::Hidden,
cl::desc("Only display the basic block whose name "
@@ -932,7 +937,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nInitial selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -952,7 +957,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nOptimized lowered selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -974,7 +979,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nType-legalized selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -998,7 +1003,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nOptimized type-legalized selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -1016,7 +1021,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nVector-legalized selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -1032,7 +1037,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nVector/type-legalized selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -1052,7 +1057,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nOptimized vector-legalized selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -1072,7 +1077,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nLegalized selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -1092,7 +1097,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nOptimized legalized selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
#if !defined(NDEBUG) && LLVM_ENABLE_ABI_BREAKING_CHECKS
if (TTI->hasBranchDivergence())
@@ -1116,7 +1121,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
ISEL_DUMP(dbgs() << "\nSelected selection DAG: "
<< printMBBReference(*FuncInfo->MBB) << " '" << BlockName
<< "'\n";
- CurDAG->dump());
+ CurDAG->dump(DumpSortedDAG));
if (ViewSchedDAGs && MatchFilterBB)
CurDAG->viewGraph("scheduler input for " + BlockName);
>From a61f8d708be6fd7c64963ccc713dff455377cc02 Mon Sep 17 00:00:00 2001
From: Min Hsu <min at myhsu.dev>
Date: Sun, 28 Sep 2025 14:08:06 -0700
Subject: [PATCH 2/3] fixup! [SelectionDAG] Add support to dump DAGs with
sorted nodes
---
llvm/include/llvm/CodeGen/SelectionDAG.h | 2 +-
llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index 446239a007186..62c0806dada2e 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -2013,7 +2013,7 @@ class SelectionDAG {
/// function mirrors \c llvm::salvageDebugInfo.
LLVM_ABI void salvageDebugInfo(SDNode &N);
- /// Dump the textual format of this DAG. Print nodes in sorted orders is \p
+ /// Dump the textual format of this DAG. Print nodes in sorted orders if \p
/// Sorted is true.
LLVM_ABI void dump(bool Sorted = false) const;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index 0604bd4ed8889..c35f29dc2548c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -146,7 +146,7 @@ UseMBPI("use-mbpi",
#ifndef NDEBUG
static cl::opt<bool>
DumpSortedDAG("dump-sorted-dags", cl::Hidden,
- cl::desc("Print the DAG with sorted nodes in debug dump"),
+ cl::desc("Print DAGs with sorted nodes in debug dump"),
cl::init(false));
static cl::opt<std::string>
>From 9e0da5004c67b3bd0fd923c073640cb1fe896ac5 Mon Sep 17 00:00:00 2001
From: Min-Yih Hsu <min.hsu at sifive.com>
Date: Wed, 1 Oct 2025 14:19:28 -0700
Subject: [PATCH 3/3] fixup! Do not use getTopologicallySortedNodes in
AssignTopologicalOrder
---
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 92 ++++++++++++++++---
1 file changed, 81 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index f1999fe34e88a..b07ace51dfb50 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -12610,17 +12610,87 @@ void SelectionDAG::ReplaceAllUsesOfValuesWith(const SDValue *From,
/// based on their topological order. It returns the maximum id and a vector
/// of the SDNodes* in assigned order by reference.
unsigned SelectionDAG::AssignTopologicalOrder() {
- SmallVector<const SDNode *> SortedNodes(AllNodes.size());
- getTopologicallyOrderedNodes(SortedNodes);
-
- for (auto [Idx, ConstN] : enumerate(SortedNodes)) {
- auto *N = const_cast<SDNode *>(ConstN);
- N->setNodeId(Idx);
- if (N->getIterator() != std::prev(allnodes_end()))
- AllNodes.push_back(AllNodes.remove(N));
- }
-
- return SortedNodes.size();
+ unsigned DAGSize = 0;
+
+ // SortedPos tracks the progress of the algorithm. Nodes before it are
+ // sorted, nodes after it are unsorted. When the algorithm completes
+ // it is at the end of the list.
+ allnodes_iterator SortedPos = allnodes_begin();
+
+ // Visit all the nodes. Move nodes with no operands to the front of
+ // the list immediately. Annotate nodes that do have operands with their
+ // operand count. Before we do this, the Node Id fields of the nodes
+ // may contain arbitrary values. After, the Node Id fields for nodes
+ // before SortedPos will contain the topological sort index, and the
+ // Node Id fields for nodes At SortedPos and after will contain the
+ // count of outstanding operands.
+ for (SDNode &N : llvm::make_early_inc_range(allnodes())) {
+ checkForCycles(&N, this);
+ unsigned Degree = N.getNumOperands();
+ if (Degree == 0) {
+ // A node with no uses, add it to the result array immediately.
+ N.setNodeId(DAGSize++);
+ allnodes_iterator Q(&N);
+ if (Q != SortedPos)
+ SortedPos = AllNodes.insert(SortedPos, AllNodes.remove(Q));
+ assert(SortedPos != AllNodes.end() && "Overran node list");
+ ++SortedPos;
+ } else {
+ // Temporarily use the Node Id as scratch space for the degree count.
+ N.setNodeId(Degree);
+ }
+ }
+
+ // Visit all the nodes. As we iterate, move nodes into sorted order,
+ // such that by the time the end is reached all nodes will be sorted.
+ for (SDNode &Node : allnodes()) {
+ SDNode *N = &Node;
+ checkForCycles(N, this);
+ // N is in sorted position, so all its uses have one less operand
+ // that needs to be sorted.
+ for (SDNode *P : N->users()) {
+ unsigned Degree = P->getNodeId();
+ assert(Degree != 0 && "Invalid node degree");
+ --Degree;
+ if (Degree == 0) {
+ // All of P's operands are sorted, so P may sorted now.
+ P->setNodeId(DAGSize++);
+ if (P->getIterator() != SortedPos)
+ SortedPos = AllNodes.insert(SortedPos, AllNodes.remove(P));
+ assert(SortedPos != AllNodes.end() && "Overran node list");
+ ++SortedPos;
+ } else {
+ // Update P's outstanding operand count.
+ P->setNodeId(Degree);
+ }
+ }
+ if (Node.getIterator() == SortedPos) {
+#ifndef NDEBUG
+ allnodes_iterator I(N);
+ SDNode *S = &*++I;
+ dbgs() << "Overran sorted position:\n";
+ S->dumprFull(this); dbgs() << "\n";
+ dbgs() << "Checking if this is due to cycles\n";
+ checkForCycles(this, true);
+#endif
+ llvm_unreachable(nullptr);
+ }
+ }
+
+ assert(SortedPos == AllNodes.end() &&
+ "Topological sort incomplete!");
+ assert(AllNodes.front().getOpcode() == ISD::EntryToken &&
+ "First node in topological sort is not the entry token!");
+ assert(AllNodes.front().getNodeId() == 0 &&
+ "First node in topological sort has non-zero id!");
+ assert(AllNodes.front().getNumOperands() == 0 &&
+ "First node in topological sort has operands!");
+ assert(AllNodes.back().getNodeId() == (int)DAGSize-1 &&
+ "Last node in topologic sort has unexpected id!");
+ assert(AllNodes.back().use_empty() &&
+ "Last node in topologic sort has users!");
+ assert(DAGSize == allnodes_size() && "Node count mismatch!");
+ return DAGSize;
}
void SelectionDAG::getTopologicallyOrderedNodes(
More information about the llvm-commits
mailing list