43972
|
1 |
/*
|
|
2 |
* Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 |
*
|
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
|
7 |
* published by the Free Software Foundation.
|
|
8 |
*
|
|
9 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
10 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
11 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
12 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
13 |
* accompanied this code).
|
|
14 |
*
|
|
15 |
* You should have received a copy of the GNU General Public License version
|
|
16 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
17 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18 |
*
|
|
19 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
20 |
* or visit www.oracle.com if you need additional information or have any
|
|
21 |
* questions.
|
|
22 |
*/
|
|
23 |
package org.graalvm.compiler.phases.graph;
|
|
24 |
|
|
25 |
import java.util.ArrayDeque;
|
|
26 |
import java.util.ArrayList;
|
|
27 |
import java.util.Deque;
|
|
28 |
import java.util.List;
|
|
29 |
|
48861
|
30 |
import org.graalvm.collections.EconomicMap;
|
|
31 |
import org.graalvm.collections.Equivalence;
|
43972
|
32 |
import org.graalvm.compiler.graph.Node;
|
|
33 |
import org.graalvm.compiler.graph.NodeBitMap;
|
|
34 |
import org.graalvm.compiler.nodes.AbstractBeginNode;
|
|
35 |
import org.graalvm.compiler.nodes.AbstractMergeNode;
|
|
36 |
import org.graalvm.compiler.nodes.ControlSinkNode;
|
|
37 |
import org.graalvm.compiler.nodes.ControlSplitNode;
|
|
38 |
import org.graalvm.compiler.nodes.EndNode;
|
|
39 |
import org.graalvm.compiler.nodes.FixedNode;
|
|
40 |
import org.graalvm.compiler.nodes.FixedWithNextNode;
|
|
41 |
import org.graalvm.compiler.nodes.Invoke;
|
|
42 |
import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
|
|
43 |
import org.graalvm.compiler.nodes.LoopBeginNode;
|
|
44 |
import org.graalvm.compiler.nodes.LoopEndNode;
|
|
45 |
import org.graalvm.compiler.nodes.StartNode;
|
|
46 |
import org.graalvm.compiler.nodes.StructuredGraph;
|
|
47 |
|
|
48 |
/**
|
|
49 |
* A SinglePassNodeIterator iterates the fixed nodes of the graph in post order starting from its
|
|
50 |
* start node. Unlike in iterative dataflow analysis, a single pass is performed, which allows
|
|
51 |
* keeping a smaller working set of pending {@link MergeableState}. This iteration scheme requires:
|
|
52 |
* <ul>
|
|
53 |
* <li>{@link MergeableState#merge(AbstractMergeNode, List)} to always return <code>true</code> (an
|
|
54 |
* assertion checks this)</li>
|
|
55 |
* <li>{@link #controlSplit(ControlSplitNode)} to always return all successors (otherwise, not all
|
|
56 |
* associated {@link EndNode} will be visited. In turn, visiting all the end nodes for a given
|
|
57 |
* {@link AbstractMergeNode} is a precondition before that merge node can be visited)</li>
|
|
58 |
* </ul>
|
|
59 |
*
|
|
60 |
* <p>
|
|
61 |
* For this iterator the CFG is defined by the classical CFG nodes (
|
|
62 |
* {@link org.graalvm.compiler.nodes.ControlSplitNode},
|
|
63 |
* {@link org.graalvm.compiler.nodes.AbstractMergeNode} ...) and the
|
|
64 |
* {@link org.graalvm.compiler.nodes.FixedWithNextNode#next() next} pointers of
|
|
65 |
* {@link org.graalvm.compiler.nodes.FixedWithNextNode}.
|
|
66 |
* </p>
|
|
67 |
*
|
|
68 |
* <p>
|
|
69 |
* The lifecycle that single-pass node iterators go through is described in {@link #apply()}
|
|
70 |
* </p>
|
|
71 |
*
|
|
72 |
* @param <T> the type of {@link MergeableState} handled by this SinglePassNodeIterator
|
|
73 |
*/
|
|
74 |
public abstract class SinglePassNodeIterator<T extends MergeableState<T>> {
|
|
75 |
|
|
76 |
private final NodeBitMap visitedEnds;
|
|
77 |
|
|
78 |
/**
|
|
79 |
* @see SinglePassNodeIterator.PathStart
|
|
80 |
*/
|
|
81 |
private final Deque<PathStart<T>> nodeQueue;
|
|
82 |
|
|
83 |
/**
|
|
84 |
* The keys in this map may be:
|
|
85 |
* <ul>
|
|
86 |
* <li>loop-begins and loop-ends, see {@link #finishLoopEnds(LoopEndNode)}</li>
|
|
87 |
* <li>forward-ends of merge-nodes, see {@link #queueMerge(EndNode)}</li>
|
|
88 |
* </ul>
|
|
89 |
*
|
|
90 |
* <p>
|
|
91 |
* It's tricky to answer whether the state an entry contains is the pre-state or the post-state
|
|
92 |
* for the key in question, because states are mutable. Thus an entry may be created to contain
|
|
93 |
* a pre-state (at the time, as done for a loop-begin in {@link #apply()}) only to make it a
|
|
94 |
* post-state soon after (continuing with the loop-begin example, also in {@link #apply()}). In
|
|
95 |
* any case, given that keys are limited to the nodes mentioned in the previous paragraph, in
|
|
96 |
* all cases an entry can be considered to hold a post-state by the time such entry is
|
|
97 |
* retrieved.
|
|
98 |
* </p>
|
|
99 |
*
|
|
100 |
* <p>
|
|
101 |
* The only method that makes this map grow is {@link #keepForLater(FixedNode, MergeableState)}
|
|
102 |
* and the only one that shrinks it is {@link #pruneEntry(FixedNode)}. To make sure no entry is
|
|
103 |
* left behind inadvertently, asserts in {@link #finished()} are in place.
|
|
104 |
* </p>
|
|
105 |
*/
|
46344
|
106 |
private final EconomicMap<FixedNode, T> nodeStates;
|
43972
|
107 |
|
|
108 |
private final StartNode start;
|
|
109 |
|
|
110 |
protected T state;
|
|
111 |
|
|
112 |
/**
|
|
113 |
* An item queued in {@link #nodeQueue} can be used to continue with the single-pass visit after
|
|
114 |
* the previous path can't be followed anymore. Such items are:
|
|
115 |
* <ul>
|
|
116 |
* <li>de-queued via {@link #nextQueuedNode()}</li>
|
|
117 |
* <li>en-queued via {@link #queueMerge(EndNode)} and {@link #queueSuccessors(FixedNode)}</li>
|
|
118 |
* </ul>
|
|
119 |
*
|
|
120 |
* <p>
|
|
121 |
* Correspondingly each item may stand for:
|
|
122 |
* <ul>
|
|
123 |
* <li>a {@link AbstractMergeNode} whose pre-state results from merging those of its
|
|
124 |
* forward-ends, see {@link #nextQueuedNode()}</li>
|
|
125 |
* <li>a successor of a control-split node, in which case the state on entry to it (the
|
|
126 |
* successor) is also stored in the item, see {@link #nextQueuedNode()}</li>
|
|
127 |
* </ul>
|
|
128 |
* </p>
|
|
129 |
*/
|
|
130 |
private static final class PathStart<U> {
|
|
131 |
private final AbstractBeginNode node;
|
|
132 |
private final U stateOnEntry;
|
|
133 |
|
|
134 |
private PathStart(AbstractBeginNode node, U stateOnEntry) {
|
|
135 |
this.node = node;
|
|
136 |
this.stateOnEntry = stateOnEntry;
|
|
137 |
assert repOK();
|
|
138 |
}
|
|
139 |
|
|
140 |
/**
|
|
141 |
* @return true iff this instance is internally consistent (ie, its "representation is OK")
|
|
142 |
*/
|
|
143 |
private boolean repOK() {
|
|
144 |
if (node == null) {
|
|
145 |
return false;
|
|
146 |
}
|
|
147 |
if (node instanceof AbstractMergeNode) {
|
|
148 |
return stateOnEntry == null;
|
|
149 |
}
|
|
150 |
return (stateOnEntry != null);
|
|
151 |
}
|
|
152 |
}
|
|
153 |
|
|
154 |
public SinglePassNodeIterator(StartNode start, T initialState) {
|
|
155 |
StructuredGraph graph = start.graph();
|
|
156 |
visitedEnds = graph.createNodeBitMap();
|
|
157 |
nodeQueue = new ArrayDeque<>();
|
46344
|
158 |
nodeStates = EconomicMap.create(Equivalence.IDENTITY);
|
43972
|
159 |
this.start = start;
|
|
160 |
this.state = initialState;
|
|
161 |
}
|
|
162 |
|
|
163 |
/**
|
|
164 |
* Performs a single-pass iteration.
|
|
165 |
*
|
|
166 |
* <p>
|
|
167 |
* After this method has been invoked, the {@link SinglePassNodeIterator} instance can't be used
|
|
168 |
* again. This saves clearing up fields in {@link #finished()}, the assumption being that this
|
|
169 |
* instance will be garbage-collected soon afterwards.
|
|
170 |
* </p>
|
|
171 |
*/
|
|
172 |
public void apply() {
|
|
173 |
FixedNode current = start;
|
|
174 |
|
|
175 |
do {
|
|
176 |
if (current instanceof InvokeWithExceptionNode) {
|
|
177 |
invoke((Invoke) current);
|
|
178 |
queueSuccessors(current);
|
|
179 |
current = nextQueuedNode();
|
|
180 |
} else if (current instanceof LoopBeginNode) {
|
|
181 |
state.loopBegin((LoopBeginNode) current);
|
|
182 |
keepForLater(current, state);
|
|
183 |
state = state.clone();
|
|
184 |
loopBegin((LoopBeginNode) current);
|
|
185 |
current = ((LoopBeginNode) current).next();
|
|
186 |
assert current != null;
|
|
187 |
} else if (current instanceof LoopEndNode) {
|
|
188 |
loopEnd((LoopEndNode) current);
|
|
189 |
finishLoopEnds((LoopEndNode) current);
|
|
190 |
current = nextQueuedNode();
|
|
191 |
} else if (current instanceof AbstractMergeNode) {
|
|
192 |
merge((AbstractMergeNode) current);
|
|
193 |
current = ((AbstractMergeNode) current).next();
|
|
194 |
assert current != null;
|
|
195 |
} else if (current instanceof FixedWithNextNode) {
|
|
196 |
FixedNode next = ((FixedWithNextNode) current).next();
|
|
197 |
assert next != null : current;
|
|
198 |
node(current);
|
|
199 |
current = next;
|
|
200 |
} else if (current instanceof EndNode) {
|
|
201 |
end((EndNode) current);
|
|
202 |
queueMerge((EndNode) current);
|
|
203 |
current = nextQueuedNode();
|
|
204 |
} else if (current instanceof ControlSinkNode) {
|
|
205 |
node(current);
|
|
206 |
current = nextQueuedNode();
|
|
207 |
} else if (current instanceof ControlSplitNode) {
|
|
208 |
controlSplit((ControlSplitNode) current);
|
|
209 |
queueSuccessors(current);
|
|
210 |
current = nextQueuedNode();
|
|
211 |
} else {
|
|
212 |
assert false : current;
|
|
213 |
}
|
|
214 |
} while (current != null);
|
|
215 |
finished();
|
|
216 |
}
|
|
217 |
|
|
218 |
/**
|
|
219 |
* Two methods enqueue items in {@link #nodeQueue}. Of them, only this method enqueues items
|
|
220 |
* with non-null state (the other method being {@link #queueMerge(EndNode)}).
|
|
221 |
*
|
|
222 |
* <p>
|
|
223 |
* A space optimization is made: the state is cloned for all successors except the first. Given
|
|
224 |
* that right after invoking this method, {@link #nextQueuedNode()} is invoked, that single
|
|
225 |
* non-cloned state instance is in effect "handed over" to its next owner (thus realizing an
|
|
226 |
* owner-is-mutator access protocol).
|
|
227 |
* </p>
|
|
228 |
*/
|
|
229 |
private void queueSuccessors(FixedNode x) {
|
|
230 |
T startState = state;
|
|
231 |
T curState = startState;
|
|
232 |
for (Node succ : x.successors()) {
|
|
233 |
if (succ != null) {
|
|
234 |
if (curState == null) {
|
|
235 |
// the current state isn't cloned for the first successor
|
|
236 |
// conceptually, the state is handed over to it
|
|
237 |
curState = startState.clone();
|
|
238 |
}
|
|
239 |
AbstractBeginNode begin = (AbstractBeginNode) succ;
|
|
240 |
nodeQueue.addFirst(new PathStart<>(begin, curState));
|
|
241 |
}
|
|
242 |
}
|
|
243 |
}
|
|
244 |
|
|
245 |
/**
|
|
246 |
* This method is invoked upon not having a (single) next {@link FixedNode} to visit. This
|
|
247 |
* method picks such next-node-to-visit from {@link #nodeQueue} and updates {@link #state} with
|
|
248 |
* the pre-state for that node.
|
|
249 |
*
|
|
250 |
* <p>
|
|
251 |
* Upon reaching a {@link AbstractMergeNode}, some entries are pruned from {@link #nodeStates}
|
|
252 |
* (ie, the entries associated to forward-ends for that merge-node).
|
|
253 |
* </p>
|
|
254 |
*/
|
|
255 |
private FixedNode nextQueuedNode() {
|
|
256 |
if (nodeQueue.isEmpty()) {
|
|
257 |
return null;
|
|
258 |
}
|
|
259 |
PathStart<T> elem = nodeQueue.removeFirst();
|
|
260 |
if (elem.node instanceof AbstractMergeNode) {
|
|
261 |
AbstractMergeNode merge = (AbstractMergeNode) elem.node;
|
|
262 |
state = pruneEntry(merge.forwardEndAt(0));
|
|
263 |
ArrayList<T> states = new ArrayList<>(merge.forwardEndCount() - 1);
|
|
264 |
for (int i = 1; i < merge.forwardEndCount(); i++) {
|
|
265 |
T other = pruneEntry(merge.forwardEndAt(i));
|
|
266 |
states.add(other);
|
|
267 |
}
|
|
268 |
boolean ready = state.merge(merge, states);
|
|
269 |
assert ready : "Not a single-pass iterator after all";
|
|
270 |
return merge;
|
|
271 |
} else {
|
|
272 |
AbstractBeginNode begin = elem.node;
|
|
273 |
assert begin.predecessor() != null;
|
|
274 |
state = elem.stateOnEntry;
|
|
275 |
state.afterSplit(begin);
|
|
276 |
return begin;
|
|
277 |
}
|
|
278 |
}
|
|
279 |
|
|
280 |
/**
|
|
281 |
* Once all loop-end-nodes for a given loop-node have been visited.
|
|
282 |
* <ul>
|
|
283 |
* <li>the state for that loop-node is updated based on the states of the loop-end-nodes</li>
|
|
284 |
* <li>entries in {@link #nodeStates} are pruned for the loop (they aren't going to be looked up
|
|
285 |
* again, anyway)</li>
|
|
286 |
* </ul>
|
|
287 |
*
|
|
288 |
* <p>
|
|
289 |
* The entries removed by this method were inserted:
|
|
290 |
* <ul>
|
|
291 |
* <li>for the loop-begin, by {@link #apply()}</li>
|
|
292 |
* <li>for loop-ends, by (previous) invocations of this method</li>
|
|
293 |
* </ul>
|
|
294 |
* </p>
|
|
295 |
*/
|
|
296 |
private void finishLoopEnds(LoopEndNode end) {
|
|
297 |
assert !visitedEnds.isMarked(end);
|
|
298 |
visitedEnds.mark(end);
|
|
299 |
keepForLater(end, state);
|
|
300 |
LoopBeginNode begin = end.loopBegin();
|
|
301 |
boolean endsVisited = true;
|
|
302 |
for (LoopEndNode le : begin.loopEnds()) {
|
|
303 |
if (!visitedEnds.isMarked(le)) {
|
|
304 |
endsVisited = false;
|
|
305 |
break;
|
|
306 |
}
|
|
307 |
}
|
|
308 |
if (endsVisited) {
|
|
309 |
ArrayList<T> states = new ArrayList<>(begin.loopEnds().count());
|
|
310 |
for (LoopEndNode le : begin.orderedLoopEnds()) {
|
|
311 |
T leState = pruneEntry(le);
|
|
312 |
states.add(leState);
|
|
313 |
}
|
|
314 |
T loopBeginState = pruneEntry(begin);
|
|
315 |
loopBeginState.loopEnds(begin, states);
|
|
316 |
}
|
|
317 |
}
|
|
318 |
|
|
319 |
/**
|
|
320 |
* Once all end-nodes for a given merge-node have been visited, that merge-node is added to the
|
|
321 |
* {@link #nodeQueue}
|
|
322 |
*
|
|
323 |
* <p>
|
|
324 |
* {@link #nextQueuedNode()} is in charge of pruning entries (held by {@link #nodeStates}) for
|
|
325 |
* the forward-ends inserted by this method.
|
|
326 |
* </p>
|
|
327 |
*/
|
|
328 |
private void queueMerge(EndNode end) {
|
|
329 |
assert !visitedEnds.isMarked(end);
|
|
330 |
visitedEnds.mark(end);
|
|
331 |
keepForLater(end, state);
|
|
332 |
AbstractMergeNode merge = end.merge();
|
|
333 |
boolean endsVisited = true;
|
|
334 |
for (int i = 0; i < merge.forwardEndCount(); i++) {
|
|
335 |
if (!visitedEnds.isMarked(merge.forwardEndAt(i))) {
|
|
336 |
endsVisited = false;
|
|
337 |
break;
|
|
338 |
}
|
|
339 |
}
|
|
340 |
if (endsVisited) {
|
|
341 |
nodeQueue.add(new PathStart<>(merge, null));
|
|
342 |
}
|
|
343 |
}
|
|
344 |
|
|
345 |
protected abstract void node(FixedNode node);
|
|
346 |
|
|
347 |
protected void end(EndNode endNode) {
|
|
348 |
node(endNode);
|
|
349 |
}
|
|
350 |
|
|
351 |
protected void merge(AbstractMergeNode merge) {
|
|
352 |
node(merge);
|
|
353 |
}
|
|
354 |
|
|
355 |
protected void loopBegin(LoopBeginNode loopBegin) {
|
|
356 |
node(loopBegin);
|
|
357 |
}
|
|
358 |
|
|
359 |
protected void loopEnd(LoopEndNode loopEnd) {
|
|
360 |
node(loopEnd);
|
|
361 |
}
|
|
362 |
|
|
363 |
protected void controlSplit(ControlSplitNode controlSplit) {
|
|
364 |
node(controlSplit);
|
|
365 |
}
|
|
366 |
|
|
367 |
protected void invoke(Invoke invoke) {
|
|
368 |
node(invoke.asNode());
|
|
369 |
}
|
|
370 |
|
|
371 |
/**
|
|
372 |
* The lifecycle that single-pass node iterators go through is described in {@link #apply()}
|
|
373 |
*
|
|
374 |
* <p>
|
|
375 |
* When overriding this method don't forget to invoke this implementation, otherwise the
|
|
376 |
* assertions will be skipped.
|
|
377 |
* </p>
|
|
378 |
*/
|
|
379 |
protected void finished() {
|
|
380 |
assert nodeQueue.isEmpty();
|
|
381 |
assert nodeStates.isEmpty();
|
|
382 |
}
|
|
383 |
|
|
384 |
private void keepForLater(FixedNode x, T s) {
|
|
385 |
assert !nodeStates.containsKey(x);
|
|
386 |
assert (x instanceof LoopBeginNode) || (x instanceof LoopEndNode) || (x instanceof EndNode);
|
|
387 |
assert s != null;
|
|
388 |
nodeStates.put(x, s);
|
|
389 |
}
|
|
390 |
|
|
391 |
private T pruneEntry(FixedNode x) {
|
46344
|
392 |
T result = nodeStates.removeKey(x);
|
43972
|
393 |
assert result != null;
|
|
394 |
return result;
|
|
395 |
}
|
|
396 |
}
|