Bug 1874684 - Part 10: Replace BigInt with Int128 in RoundNumberToIncrement. r=mgaudet
[gecko.git] / js / src / gc / FindSCCs.h
blob97069bcd09f223bfff517acfd4148233425ffd24
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef gc_FindSCCs_h
8 #define gc_FindSCCs_h
10 #include "mozilla/Assertions.h" // MOZ_ASSERT
12 #include <algorithm> // std::min
14 #include "js/AllocPolicy.h" // js::SystemAllocPolicy
15 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
16 #include "js/HashTable.h" // js::HashSet, js::DefaultHasher
18 namespace js {
19 namespace gc {
21 template <typename Node>
22 struct GraphNodeBase {
23 using NodeSet =
24 js::HashSet<Node*, js::DefaultHasher<Node*>, js::SystemAllocPolicy>;
26 NodeSet gcGraphEdges;
27 Node* gcNextGraphNode = nullptr;
28 Node* gcNextGraphComponent = nullptr;
29 unsigned gcDiscoveryTime = 0;
30 unsigned gcLowLink = 0;
32 Node* nextNodeInGroup() const {
33 if (gcNextGraphNode &&
34 gcNextGraphNode->gcNextGraphComponent == gcNextGraphComponent) {
35 return gcNextGraphNode;
37 return nullptr;
40 Node* nextGroup() const { return gcNextGraphComponent; }
44 * Find the strongly connected components of a graph using Tarjan's algorithm,
45 * and return them in topological order.
47 * Nodes derive from GraphNodeBase and add target edge pointers to
48 * sourceNode.gcGraphEdges to describe the graph:
50 * struct MyGraphNode : public GraphNodeBase<MyGraphNode>
51 * {
52 * ...
53 * }
55 * MyGraphNode node1, node2, node3;
56 * node1.gcGraphEdges.put(node2); // Error checking elided.
57 * node2.gcGraphEdges.put(node3);
58 * node3.gcGraphEdges.put(node2);
60 * ComponentFinder<MyGraphNode> finder;
61 * finder.addNode(node1);
62 * finder.addNode(node2);
63 * finder.addNode(node3);
64 * MyGraphNode* result = finder.getResultsList();
67 template <typename Node>
68 class ComponentFinder {
69 public:
70 explicit ComponentFinder(JSContext* cx) : cx(cx) {}
72 ~ComponentFinder() {
73 MOZ_ASSERT(!stack);
74 MOZ_ASSERT(!firstComponent);
77 /* Forces all nodes to be added to a single component. */
78 void useOneComponent() { stackFull = true; }
80 void addNode(Node* v) {
81 if (v->gcDiscoveryTime == Undefined) {
82 MOZ_ASSERT(v->gcLowLink == Undefined);
83 processNode(v);
87 Node* getResultsList() {
88 if (stackFull) {
90 * All nodes after the stack overflow are in |stack|. Put them all in
91 * one big component of their own.
93 Node* firstGoodComponent = firstComponent;
94 for (Node* v = stack; v; v = stack) {
95 stack = v->gcNextGraphNode;
96 v->gcNextGraphComponent = firstGoodComponent;
97 v->gcNextGraphNode = firstComponent;
98 firstComponent = v;
100 stackFull = false;
103 MOZ_ASSERT(!stack);
105 Node* result = firstComponent;
106 firstComponent = nullptr;
108 for (Node* v = result; v; v = v->gcNextGraphNode) {
109 v->gcDiscoveryTime = Undefined;
110 v->gcLowLink = Undefined;
113 return result;
116 static void mergeGroups(Node* first) {
117 for (Node* v = first; v; v = v->gcNextGraphNode) {
118 v->gcNextGraphComponent = nullptr;
122 private:
123 // Constant used to indicate an unprocessed vertex.
124 static const unsigned Undefined = 0;
126 // Constant used to indicate a processed vertex that is no longer on the
127 // stack.
128 static const unsigned Finished = (unsigned)-1;
130 void addEdgeTo(Node* w) {
131 if (w->gcDiscoveryTime == Undefined) {
132 processNode(w);
133 cur->gcLowLink = std::min(cur->gcLowLink, w->gcLowLink);
134 } else if (w->gcDiscoveryTime != Finished) {
135 cur->gcLowLink = std::min(cur->gcLowLink, w->gcDiscoveryTime);
139 void processNode(Node* v) {
140 v->gcDiscoveryTime = clock;
141 v->gcLowLink = clock;
142 ++clock;
144 v->gcNextGraphNode = stack;
145 stack = v;
147 if (stackFull) {
148 return;
151 AutoCheckRecursionLimit recursion(cx);
152 if (!recursion.checkSystemDontReport(cx)) {
153 stackFull = true;
154 return;
157 Node* old = cur;
158 cur = v;
159 for (auto r = cur->gcGraphEdges.all(); !r.empty(); r.popFront()) {
160 addEdgeTo(r.front());
162 cur = old;
164 if (stackFull) {
165 return;
168 if (v->gcLowLink == v->gcDiscoveryTime) {
169 Node* nextComponent = firstComponent;
170 Node* w;
171 do {
172 MOZ_ASSERT(stack);
173 w = stack;
174 stack = w->gcNextGraphNode;
177 * Record that the element is no longer on the stack by setting the
178 * discovery time to a special value that's not Undefined.
180 w->gcDiscoveryTime = Finished;
182 /* Figure out which group we're in. */
183 w->gcNextGraphComponent = nextComponent;
186 * Prepend the component to the beginning of the output list to
187 * reverse the list and achieve the desired order.
189 w->gcNextGraphNode = firstComponent;
190 firstComponent = w;
191 } while (w != v);
195 private:
196 unsigned clock = 1;
197 Node* stack = nullptr;
198 Node* firstComponent = nullptr;
199 Node* cur = nullptr;
200 JSContext* cx;
201 bool stackFull = false;
204 } /* namespace gc */
205 } /* namespace js */
207 #endif /* gc_FindSCCs_h */