Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(855)

Side by Side Diff: pkg/compiler/tool/graph.dart

Issue 1284843002: dart2js: add simple script to compute dependency queries. Currently only (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// A library to work with graphs. It contains a couple algorithms, including
6 /// Tarjan's algorithm to compute strongest connected components in a graph and
7 /// Cooper et al's dominator algorithm.
8 ///
9 /// Portions of the code in this library was adapted from
10 /// `package:analyzer/src/generated/collection_utilities.dart`.
11 // TODO(sigmund): move this into a shared place?
12 library compiler.tool.graph;
13
14 import 'dart:math' as math;
15
16 abstract class Graph<N> {
17 Iterable<N> get nodes;
18 bool get isEmpty;
19 int get nodeCount;
20 Iterable<N> targetsOf(N source);
21 Iterable<N> sourcesOf(N source);
22
23 /// Run a topological sort of the graph. Since the graph may contain cycles,
24 /// this results in a list of strongly connected components rather than a list
25 /// of nodes. The nodes in each strongly connected components only have edges
26 /// that point to nodes in the same component or earlier components.
27 List<List<N>> computeTopologicalSort() {
28 _SccFinder<N> finder = new _SccFinder<N>(this);
29 return finder.computeTopologicalSort();
30 }
31
32 /// Whether [source] can transitively reach [target].
33 bool containsPath(N source, N target) {
34 Set<N> seen = new Set<N>();
35 bool helper(N node) {
36 if (identical(node, target)) return true;
37 if (!seen.add(node)) return false;
38 return targetsOf(node).any(helper);
39 }
40 return helper(source);
41 }
42
43 /// Returns all nodes reachable from [root] in post order.
44 List<N> postOrder(N root) {
45 var seen = new Set<N>();
46 var result = <N>[];
47 helper(n) {
48 if (!seen.add(n)) return;
49 targetsOf(n).forEach(helper);
50 result.add(n);
51 }
52 helper(root);
53 return result;
54 }
55
56 /// Return a list of nodes that form a cycle containing the given node. If the
57 /// node is not part of this graph, then a list containing only the node
Johnni Winther 2015/08/18 13:17:11 Shouldn't it be `is not a part of a cycle in this
Siggi Cherem (dart-lang) 2015/08/18 19:13:18 Good point yes
58 /// itself will be returned.
59 List<N> findCycleContaining(N node) {
60 assert(node != null);
61 _SccFinder<N> finder = new _SccFinder<N>(this);
62 return finder._componentContaining(node);
63 }
64
65 Graph<N> dominatorTree(N root) {
Johnni Winther 2015/08/18 13:17:11 Add documentation.
Siggi Cherem (dart-lang) 2015/08/18 19:13:17 Done.
66 var iDom = (new _DominatorFinder(this)..run(root)).immediateDominators;
67 var graph = new EdgeListGraph<N>();
68 for (N node in iDom.keys) {
69 if (node != root) graph.addEdge(iDom[node], node);
70 }
71 return graph;
72 }
73 }
74
75 class EdgeListGraph<N> extends Graph<N> {
76 /// Edges in the graph.
77 Map<N, Set<N>> _edges = new Map<N, Set<N>>();
78
79 /// The reverse of _edges.
80 Map<N, Set<N>> _revEdges = new Map<N, Set<N>>();
81
82 Iterable<N> get nodes => _edges.keys;
83 bool get isEmpty => _edges.isEmpty;
84 int get nodeCount => _edges.length;
85
86 final _empty = new Set<N>();
87
88 Iterable<N> targetsOf(N source) => _edges[source] ?? _empty;
89 Iterable<N> sourcesOf(N source) => _revEdges[source] ?? _empty;
90
91 void addEdge(N source, N target) {
92 assert(source != null);
93 assert(target != null);
94 addNode(source);
95 addNode(target);
96 _edges[source].add(target);
97 _revEdges[target].add(source);
98 }
99
100 void addNode(N node) {
101 assert(node != null);
102 _edges.putIfAbsent(node, () => new Set<N>());
103 _revEdges.putIfAbsent(node, () => new Set<N>());
104 }
105
106 /// Remove the edge from the given [source] node to the given [target] node.
107 /// If there was no such edge then the graph will be unmodified: the number of
108 /// edges will be the same and the set of nodes will be the same (neither node
109 /// will either be added or removed).
110 void removeEdge(N source, N target) {
111 _edges[source]?.remove(target);
112 }
113
114 /// Remove the given node from this graph. As a consequence, any edges for
115 /// which that node was either a head or a tail will also be removed.
116 void removeNode(N node) {
117 _edges.remove(node);
118 var sources = _revEdges[node];
119 if (sources == null) return;
120 for (var source in sources) {
121 _edges[source].remove(node);
122 }
123 }
124
125 /// Remove all of the given nodes from this graph. As a consequence, any edges
126 /// for which those nodes were either a head or a tail will also be removed.
127 void removeAllNodes(List<N> nodes) => nodes.forEach(removeNode);
128 }
129
130 /// Used by the [SccFinder] to maintain information about the nodes that have
131 /// been examined. There is an instance of this class per node in the graph.
132 class _NodeInfo<N> {
133 /// Depth of the node corresponding to this info.
134 int index = 0;
135
136 /// Depth of the first node in a cycle.
137 int lowlink = 0;
138
139 /// Whether the corresponding node is on the stack. Used to remove the need
140 /// for searching a collection for the node each time the question needs to be
141 /// asked.
142 bool onStack = false;
143
144 /// Component that contains the corresponding node.
145 List<N> component;
146
147 _NodeInfo(int depth)
148 : index = depth, lowlink = depth, onStack = false;
149 }
150
151 /// Implements Tarjan's Algorithm for finding the strongly connected components
152 /// in a graph.
153 class _SccFinder<N> {
154 /// The graph to process.
155 final Graph<N> _graph;
156
157 /// The index used to uniquely identify the depth of nodes.
158 int _index = 0;
159
160 /// Nodes that are being visited in order to identify components.
161 List<N> _stack = new List<N>();
162
163 /// Information associated with each node.
164 Map<N, _NodeInfo<N>> _info = <N, _NodeInfo<N>>{};
165
166 /// All strongly connected components found, in topological sort order (each
167 /// node in a strongly connected component only has edges that point to nodes
168 /// in the same component or earlier components).
169 List<List<N>> _allComponents = new List<List<N>>();
170
171 _SccFinder(this._graph) : super();
Johnni Winther 2015/08/18 13:17:11 Why the explicit super call?
Siggi Cherem (dart-lang) 2015/08/18 19:13:17 removed, good catch! (Except for the dominance co
172
173 /// Return a list containing the nodes that are part of the strongly connected
174 /// component that contains the given node.
175 List<N> _componentContaining(N node) => _strongConnect(node).component;
176
177 /// Run Tarjan's algorithm and return the resulting list of strongly connected
178 /// components. The list is in topological sort order (each node in a strongly
179 /// connected component only has edges that point to nodes in the same
180 /// component or earlier components).
181 List<List<N>> computeTopologicalSort() {
182 for (N node in _graph.nodes) {
183 var nodeInfo = _info[node];
184 if (nodeInfo == null) _strongConnect(node);
185 }
186 return _allComponents;
187 }
188
189 /// Remove and return the top-most element from the stack.
190 N _pop() {
191 N node = _stack.removeAt(_stack.length - 1);
192 _info[node].onStack = false;
193 return node;
194 }
195
196 /// Add the given node to the stack.
197 void _push(N node) {
198 _info[node].onStack = true;
199 _stack.add(node);
200 }
201
202 /// Compute the strongly connected component that contains the given node as
203 /// well as any components containing nodes that are reachable from the given
204 /// component.
205 _NodeInfo<N> _strongConnect(N v) {
206 // Set the depth index for v to the smallest unused index
207 var vInfo = new _NodeInfo<N>(_index++);
208 _info[v] = vInfo;
209 _push(v);
210
211 for (N w in _graph.targetsOf(v)) {
212 var wInfo = _info[w];
213 if (wInfo == null) {
214 // Successor w has not yet been visited; recurse on it
215 wInfo = _strongConnect(w);
216 vInfo.lowlink = math.min(vInfo.lowlink, wInfo.lowlink);
217 } else if (wInfo.onStack) {
218 // Successor w is in stack S and hence in the current SCC
219 vInfo.lowlink = math.min(vInfo.lowlink, wInfo.index);
220 }
221 }
222
223 // If v is a root node, pop the stack and generate an SCC
224 if (vInfo.lowlink == vInfo.index) {
225 var component = new List<N>();
226 N w;
227 do {
228 w = _pop();
229 component.add(w);
230 _info[w].component = component;
231 } while (!identical(w, v));
232 _allComponents.add(component);
233 }
234 return vInfo;
235 }
236 }
237
238 /// Computes dominators using (Cooper, Harvey, and Kennedy's
239 /// algorithm)[http://www.cs.rice.edu/~keith/EMBED/dom.pdf].
240 class _DominatorFinder<N> {
241 final Graph<N> _graph;
242 Map<N, N> immediateDominators = {};
243 Map<N, int> postOrderId = {};
244 _DominatorFinder(this._graph);
245
246 run(N root) {
247 immediateDominators[root] = root;
248 bool changed = true;
249 int i = 0;
250 var nodesInPostOrder = _graph.postOrder(root);
251 for (var n in nodesInPostOrder) {
252 postOrderId[n] = i++;
253 }
254 var nodesInReversedPostOrder = nodesInPostOrder.reversed;
255 while (changed) {
256 changed = false;
257 for (var n in nodesInReversedPostOrder) {
258 if (n == root) continue;
259 bool first = true;
260 var idom = null;
261 for (var p in _graph.sourcesOf(n)) {
262 if (immediateDominators[p] != null) {
263 if (first) {
264 idom = p;
265 first = false;
266 } else {
267 idom = _intersect(p, idom);
268 }
269 }
270 }
271 if (immediateDominators[n] != idom) {
272 immediateDominators[n] = idom;
273 changed = true;
274 }
275 }
276 }
277 }
278
279 N _intersect(N b1, N b2) {
280 var finger1 = b1;
281 var finger2 = b2;
282 while (finger1 != finger2) {
283 while (postOrderId[finger1] < postOrderId[finger2]) {
284 finger1 = immediateDominators[finger1];
285 }
286 while (postOrderId[finger2] < postOrderId[finger1]) {
287 finger2 = immediateDominators[finger2];
288 }
289 }
290 return finger1;
291 }
292 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698