[llvm] [ADT] SCC iterator for general graph (PR #84268)
Yida Zhang via llvm-commits
llvm-commits at lists.llvm.org
Sat Apr 6 22:33:41 PDT 2024
https://github.com/zyd2001 updated https://github.com/llvm/llvm-project/pull/84268
>From 11489913facb5c8362c16bf78766ce33db65fcfa Mon Sep 17 00:00:00 2001
From: Yida Zhang <zyd200101 at outlook.com>
Date: Wed, 6 Mar 2024 20:32:15 -0500
Subject: [PATCH 1/5] SCC iterator for general graph
---
llvm/include/llvm/ADT/SCCIterator.h | 135 ++++++++++++++++++++++++++++
1 file changed, 135 insertions(+)
diff --git a/llvm/include/llvm/ADT/SCCIterator.h b/llvm/include/llvm/ADT/SCCIterator.h
index e743ae7c11edbc..0c25323f32f2b3 100644
--- a/llvm/include/llvm/ADT/SCCIterator.h
+++ b/llvm/include/llvm/ADT/SCCIterator.h
@@ -377,6 +377,141 @@ scc_member_iterator<GraphT, GT>::scc_member_iterator(
assert(InputNodes.size() == Nodes.size() && "missing nodes in MST");
std::reverse(Nodes.begin(), Nodes.end());
}
+
+template <class GraphT, class GT = GraphTraits<GraphT>>
+class graph_scc_iterator : public iterator_facade_base<
+ graph_scc_iterator<GraphT, GT>, std::forward_iterator_tag,
+ const std::vector<typename GT::NodeRef>, ptrdiff_t> {
+ using NodeRef = typename GT::NodeRef;
+ using NodesIter = typename GT::nodes_iterator;
+ using ChildIter = typename GT::ChildIteratorType;
+ using reference = typename graph_scc_iterator::reference;
+
+ struct NodeEntry {
+ int index = 0;
+ int lowlink;
+ bool onStack = false;
+ };
+
+ struct StackEntry {
+ NodeRef n;
+ ChildIter currentChild;
+ NodeRef lastChild;
+ bool visited;
+ };
+
+ std::vector<NodeRef> currentSCC;
+ std::stack<StackEntry> recursionStack;
+ std::stack<NodeRef> S;
+ DenseMap<NodeRef, NodeEntry> nodeInfo;
+ int index = 1;
+
+ // current node in the outer loop for all nodes
+ NodesIter currentNode;
+ NodesIter nodeEndIter;
+
+ graph_scc_iterator(const GraphT &g, bool e = false)
+ : currentNode(GT::nodes_begin(&g)), nodeEndIter(GT::nodes_end(&g)) {
+ if (e) {
+ currentNode = nodeEndIter;
+ return;
+ }
+ computeNext();
+ }
+
+ void computeNext() {
+ currentSCC.clear();
+ if (recursionStack.empty()) {
+ // not in DFS process
+ for (; currentNode != nodeEndIter; currentNode++) {
+ NodeRef v = *currentNode;
+ if (nodeInfo[v].index == 0) {
+ recursionStack.emplace(StackEntry{v, GT::child_begin(v), nullptr, false});
+ currentNode++;
+ break;
+ }
+ }
+ }
+
+ while (!recursionStack.empty()) {
+ StackEntry &stackEntry = recursionStack.top();
+ NodeRef v = stackEntry.n;
+ ChildIter &child = stackEntry.currentChild;
+ NodeEntry &vEntry = nodeInfo[v];
+ if (!stackEntry.visited) {
+ // first time
+ vEntry.index = index;
+ vEntry.lowlink = index;
+ index++;
+ S.push(v);
+ vEntry.onStack = true;
+ stackEntry.visited = true;
+ } else {
+ assert(nodeInfo.count(stackEntry.lastChild));
+ vEntry.lowlink = std::min(vEntry.lowlink, nodeInfo[stackEntry.lastChild].lowlink);
+ }
+
+ bool inserted = false;
+ for (; child != GT::child_end(v); child++) {
+ NodeRef w = *child;
+ NodeEntry &wEntry = nodeInfo[w];
+ if (wEntry.index == 0) {
+ inserted = true;
+ recursionStack.emplace(StackEntry{w, GT::child_begin(w), nullptr, false});
+ stackEntry.lastChild = w;
+ child++;
+ break;
+ } else if (wEntry.onStack) {
+ vEntry.lowlink = std::min(vEntry.lowlink, wEntry.index);
+ }
+ }
+
+ if (inserted)
+ continue;
+
+ recursionStack.pop();
+ if (vEntry.lowlink == vEntry.index) {
+ NodeRef w;
+ do {
+ w = S.top();
+ S.pop();
+ nodeInfo[w].onStack = false;
+ currentSCC.push_back(w);
+ } while (w != v);
+ return;
+ }
+ }
+ }
+
+public:
+ static graph_scc_iterator begin(const GraphT &g) {
+ return graph_scc_iterator(g);
+ }
+ static graph_scc_iterator end(const GraphT &g) {
+ return graph_scc_iterator(g, true);
+ }
+
+ bool operator!=(const graph_scc_iterator &x) const {
+ return currentNode != x.currentNode || currentSCC != x.currentSCC;
+ }
+
+ graph_scc_iterator &operator++() {
+ computeNext();
+ return *this;
+ }
+
+ reference operator*() const { return currentSCC; }
+};
+/// Construct the begin iterator for a deduced graph type T.
+template <class T> graph_scc_iterator<T> graph_scc_begin(const T &G) {
+ return graph_scc_iterator<T>::begin(G);
+}
+
+/// Construct the end iterator for a deduced graph type T.
+template <class T> graph_scc_iterator<T> graph_scc_end(const T &G) {
+ return graph_scc_iterator<T>::end(G);
+}
+
} // end namespace llvm
#endif // LLVM_ADT_SCCITERATOR_H
>From 601ad3f8ccbb61fe5f930ddcf584a51fad08be3f Mon Sep 17 00:00:00 2001
From: Yida Zhang <zyd200101 at outlook.com>
Date: Wed, 6 Mar 2024 22:28:02 -0500
Subject: [PATCH 2/5] add header and format
---
llvm/include/llvm/ADT/SCCIterator.h | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/ADT/SCCIterator.h b/llvm/include/llvm/ADT/SCCIterator.h
index 0c25323f32f2b3..57722789492726 100644
--- a/llvm/include/llvm/ADT/SCCIterator.h
+++ b/llvm/include/llvm/ADT/SCCIterator.h
@@ -31,6 +31,7 @@
#include <iterator>
#include <queue>
#include <set>
+#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -379,9 +380,10 @@ scc_member_iterator<GraphT, GT>::scc_member_iterator(
}
template <class GraphT, class GT = GraphTraits<GraphT>>
-class graph_scc_iterator : public iterator_facade_base<
- graph_scc_iterator<GraphT, GT>, std::forward_iterator_tag,
- const std::vector<typename GT::NodeRef>, ptrdiff_t> {
+class graph_scc_iterator
+ : public iterator_facade_base<
+ graph_scc_iterator<GraphT, GT>, std::forward_iterator_tag,
+ const std::vector<typename GT::NodeRef>, ptrdiff_t> {
using NodeRef = typename GT::NodeRef;
using NodesIter = typename GT::nodes_iterator;
using ChildIter = typename GT::ChildIteratorType;
@@ -426,7 +428,8 @@ class graph_scc_iterator : public iterator_facade_base<
for (; currentNode != nodeEndIter; currentNode++) {
NodeRef v = *currentNode;
if (nodeInfo[v].index == 0) {
- recursionStack.emplace(StackEntry{v, GT::child_begin(v), nullptr, false});
+ recursionStack.emplace(
+ StackEntry{v, GT::child_begin(v), nullptr, false});
currentNode++;
break;
}
@@ -448,7 +451,8 @@ class graph_scc_iterator : public iterator_facade_base<
stackEntry.visited = true;
} else {
assert(nodeInfo.count(stackEntry.lastChild));
- vEntry.lowlink = std::min(vEntry.lowlink, nodeInfo[stackEntry.lastChild].lowlink);
+ vEntry.lowlink =
+ std::min(vEntry.lowlink, nodeInfo[stackEntry.lastChild].lowlink);
}
bool inserted = false;
@@ -457,7 +461,8 @@ class graph_scc_iterator : public iterator_facade_base<
NodeEntry &wEntry = nodeInfo[w];
if (wEntry.index == 0) {
inserted = true;
- recursionStack.emplace(StackEntry{w, GT::child_begin(w), nullptr, false});
+ recursionStack.emplace(
+ StackEntry{w, GT::child_begin(w), nullptr, false});
stackEntry.lastChild = w;
child++;
break;
>From bb8ae393c0606032bc2c04568e2dc7faa1b7e1d6 Mon Sep 17 00:00:00 2001
From: Yida Zhang <zyd200101 at outlook.com>
Date: Fri, 8 Mar 2024 19:36:09 -0500
Subject: [PATCH 3/5] Use non-member operator
---
llvm/include/llvm/ADT/SCCIterator.h | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/ADT/SCCIterator.h b/llvm/include/llvm/ADT/SCCIterator.h
index 57722789492726..8d3307b872eb16 100644
--- a/llvm/include/llvm/ADT/SCCIterator.h
+++ b/llvm/include/llvm/ADT/SCCIterator.h
@@ -413,7 +413,7 @@ class graph_scc_iterator
NodesIter nodeEndIter;
graph_scc_iterator(const GraphT &g, bool e = false)
- : currentNode(GT::nodes_begin(&g)), nodeEndIter(GT::nodes_end(&g)) {
+ : currentNode(GT::nodes_begin(g)), nodeEndIter(GT::nodes_end(g)) {
if (e) {
currentNode = nodeEndIter;
return;
@@ -496,9 +496,9 @@ class graph_scc_iterator
return graph_scc_iterator(g, true);
}
- bool operator!=(const graph_scc_iterator &x) const {
- return currentNode != x.currentNode || currentSCC != x.currentSCC;
- }
+ template <class T>
+ friend bool operator!=(const graph_scc_iterator<T> &x,
+ const graph_scc_iterator<T> &y);
graph_scc_iterator &operator++() {
computeNext();
@@ -516,6 +516,11 @@ template <class T> graph_scc_iterator<T> graph_scc_begin(const T &G) {
template <class T> graph_scc_iterator<T> graph_scc_end(const T &G) {
return graph_scc_iterator<T>::end(G);
}
+template <class T>
+bool operator!=(const graph_scc_iterator<T> &x,
+ const graph_scc_iterator<T> &y) {
+ return x.currentNode != y.currentNode || x.currentSCC != y.currentSCC;
+}
} // end namespace llvm
>From 36e1950b5bbd9a77c89948ffbdd95b49b1c72888 Mon Sep 17 00:00:00 2001
From: Yida Zhang <zyd200101 at outlook.com>
Date: Sun, 7 Apr 2024 01:21:22 -0400
Subject: [PATCH 4/5] revert
---
llvm/include/llvm/ADT/SCCIterator.h | 145 ----------------------------
1 file changed, 145 deletions(-)
diff --git a/llvm/include/llvm/ADT/SCCIterator.h b/llvm/include/llvm/ADT/SCCIterator.h
index 8d3307b872eb16..e743ae7c11edbc 100644
--- a/llvm/include/llvm/ADT/SCCIterator.h
+++ b/llvm/include/llvm/ADT/SCCIterator.h
@@ -31,7 +31,6 @@
#include <iterator>
#include <queue>
#include <set>
-#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -378,150 +377,6 @@ scc_member_iterator<GraphT, GT>::scc_member_iterator(
assert(InputNodes.size() == Nodes.size() && "missing nodes in MST");
std::reverse(Nodes.begin(), Nodes.end());
}
-
-template <class GraphT, class GT = GraphTraits<GraphT>>
-class graph_scc_iterator
- : public iterator_facade_base<
- graph_scc_iterator<GraphT, GT>, std::forward_iterator_tag,
- const std::vector<typename GT::NodeRef>, ptrdiff_t> {
- using NodeRef = typename GT::NodeRef;
- using NodesIter = typename GT::nodes_iterator;
- using ChildIter = typename GT::ChildIteratorType;
- using reference = typename graph_scc_iterator::reference;
-
- struct NodeEntry {
- int index = 0;
- int lowlink;
- bool onStack = false;
- };
-
- struct StackEntry {
- NodeRef n;
- ChildIter currentChild;
- NodeRef lastChild;
- bool visited;
- };
-
- std::vector<NodeRef> currentSCC;
- std::stack<StackEntry> recursionStack;
- std::stack<NodeRef> S;
- DenseMap<NodeRef, NodeEntry> nodeInfo;
- int index = 1;
-
- // current node in the outer loop for all nodes
- NodesIter currentNode;
- NodesIter nodeEndIter;
-
- graph_scc_iterator(const GraphT &g, bool e = false)
- : currentNode(GT::nodes_begin(g)), nodeEndIter(GT::nodes_end(g)) {
- if (e) {
- currentNode = nodeEndIter;
- return;
- }
- computeNext();
- }
-
- void computeNext() {
- currentSCC.clear();
- if (recursionStack.empty()) {
- // not in DFS process
- for (; currentNode != nodeEndIter; currentNode++) {
- NodeRef v = *currentNode;
- if (nodeInfo[v].index == 0) {
- recursionStack.emplace(
- StackEntry{v, GT::child_begin(v), nullptr, false});
- currentNode++;
- break;
- }
- }
- }
-
- while (!recursionStack.empty()) {
- StackEntry &stackEntry = recursionStack.top();
- NodeRef v = stackEntry.n;
- ChildIter &child = stackEntry.currentChild;
- NodeEntry &vEntry = nodeInfo[v];
- if (!stackEntry.visited) {
- // first time
- vEntry.index = index;
- vEntry.lowlink = index;
- index++;
- S.push(v);
- vEntry.onStack = true;
- stackEntry.visited = true;
- } else {
- assert(nodeInfo.count(stackEntry.lastChild));
- vEntry.lowlink =
- std::min(vEntry.lowlink, nodeInfo[stackEntry.lastChild].lowlink);
- }
-
- bool inserted = false;
- for (; child != GT::child_end(v); child++) {
- NodeRef w = *child;
- NodeEntry &wEntry = nodeInfo[w];
- if (wEntry.index == 0) {
- inserted = true;
- recursionStack.emplace(
- StackEntry{w, GT::child_begin(w), nullptr, false});
- stackEntry.lastChild = w;
- child++;
- break;
- } else if (wEntry.onStack) {
- vEntry.lowlink = std::min(vEntry.lowlink, wEntry.index);
- }
- }
-
- if (inserted)
- continue;
-
- recursionStack.pop();
- if (vEntry.lowlink == vEntry.index) {
- NodeRef w;
- do {
- w = S.top();
- S.pop();
- nodeInfo[w].onStack = false;
- currentSCC.push_back(w);
- } while (w != v);
- return;
- }
- }
- }
-
-public:
- static graph_scc_iterator begin(const GraphT &g) {
- return graph_scc_iterator(g);
- }
- static graph_scc_iterator end(const GraphT &g) {
- return graph_scc_iterator(g, true);
- }
-
- template <class T>
- friend bool operator!=(const graph_scc_iterator<T> &x,
- const graph_scc_iterator<T> &y);
-
- graph_scc_iterator &operator++() {
- computeNext();
- return *this;
- }
-
- reference operator*() const { return currentSCC; }
-};
-/// Construct the begin iterator for a deduced graph type T.
-template <class T> graph_scc_iterator<T> graph_scc_begin(const T &G) {
- return graph_scc_iterator<T>::begin(G);
-}
-
-/// Construct the end iterator for a deduced graph type T.
-template <class T> graph_scc_iterator<T> graph_scc_end(const T &G) {
- return graph_scc_iterator<T>::end(G);
-}
-template <class T>
-bool operator!=(const graph_scc_iterator<T> &x,
- const graph_scc_iterator<T> &y) {
- return x.currentNode != y.currentNode || x.currentSCC != y.currentSCC;
-}
-
} // end namespace llvm
#endif // LLVM_ADT_SCCITERATOR_H
>From b62461b26b9b3e27831c44953e05a50ed3b9dfab Mon Sep 17 00:00:00 2001
From: Yida Zhang <zyd200101 at outlook.com>
Date: Sun, 7 Apr 2024 01:32:05 -0400
Subject: [PATCH 5/5] update doc
---
llvm/include/llvm/ADT/SCCIterator.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/llvm/include/llvm/ADT/SCCIterator.h b/llvm/include/llvm/ADT/SCCIterator.h
index e743ae7c11edbc..8963443b587c9b 100644
--- a/llvm/include/llvm/ADT/SCCIterator.h
+++ b/llvm/include/llvm/ADT/SCCIterator.h
@@ -43,6 +43,10 @@ namespace llvm {
/// This is implemented using Tarjan's DFS algorithm using an internal stack to
/// build up a vector of nodes in a particular SCC. Note that it is a forward
/// iterator and thus you cannot backtrack or re-visit nodes.
+///
+/// This is designed for CFG-like graphs with single entry node and does not
+/// implement the complete Tarjan's DFS algorithm. It won't produce correct
+/// result for generic graph.
template <class GraphT, class GT = GraphTraits<GraphT>>
class scc_iterator : public iterator_facade_base<
scc_iterator<GraphT, GT>, std::forward_iterator_tag,
More information about the llvm-commits
mailing list