Mercurial > hg > graal-jvmci-8
comparison graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java @ 5061:e808627bd16f
Renamed projects.
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Thu, 08 Mar 2012 19:24:17 +0100 |
parents | graal/com.oracle.max.graal.graph/src/com/oracle/graal/graph/Node.java@4ed4295ce15f |
children | 3ac351ed7270 |
comparison
equal
deleted
inserted
replaced
5060:4ed4295ce15f | 5061:e808627bd16f |
---|---|
1 /* | |
2 * Copyright (c) 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 com.oracle.graal.graph; | |
24 | |
25 import java.lang.annotation.*; | |
26 import java.util.*; | |
27 | |
28 import com.oracle.graal.graph.NodeClass.*; | |
29 | |
30 | |
31 /** | |
32 * This class is the base class for all nodes, it represent a node which can be inserted in a {@link Graph}.<br> | |
33 * Once a node has been added to a graph, it has a graph-unique {@link #id()}. Edges in the subclasses are represented | |
34 * with annotated fields. There are two kind of edges : {@link Input} and {@link Successor}. If a field, of a type | |
35 * compatible with {@link Node}, annotated with either {@link Input} and {@link Successor} is not null, then there is an | |
36 * edge from this node to the node this field points to.<br> | |
37 * Fields in a node subclass can also be annotated with {@link Data} : such fields will be used when comparing 2 nodes | |
38 * with {@link #equals(Object)}, for value numbering and for debugging purposes.<br> | |
39 * Nodes which are be value numberable should implement the {@link ValueNumberable} interface. | |
40 * | |
41 * <h1>Assertions and Verification</h1> | |
42 * | |
43 * The Node class supplies the {@link #assertTrue(boolean, String, Object...)} and | |
44 * {@link #assertFalse(boolean, String, Object...)} methods, which will check the supplied boolean and throw a | |
45 * VerificationError if it has the wrong value. Both methods will always either throw an exception or return true. | |
46 * They can thus be used within an assert statement, so that the check is only performed if assertions are enabled. | |
47 * | |
48 * | |
49 */ | |
50 public abstract class Node implements Cloneable, Formattable { | |
51 | |
52 static final int DELETED_ID_START = -1000000000; | |
53 static final int INITIAL_ID = -1; | |
54 static final int ALIVE_ID_START = 0; | |
55 | |
56 @Retention(RetentionPolicy.RUNTIME) | |
57 @Target(ElementType.FIELD) | |
58 public static @interface Input { | |
59 boolean notDataflow() default false; | |
60 } | |
61 | |
62 @Retention(RetentionPolicy.RUNTIME) | |
63 @Target(ElementType.FIELD) | |
64 public static @interface Successor {} | |
65 | |
66 @Retention(RetentionPolicy.RUNTIME) | |
67 @Target(ElementType.FIELD) | |
68 public static @interface Data {} | |
69 | |
70 /** | |
71 * Denotes that a parameter of an {@linkplain NodeIntrinsic intrinsic} method | |
72 * must be a compile time constant at all call sites to the intrinic method. | |
73 */ | |
74 @Retention(RetentionPolicy.RUNTIME) | |
75 @Target(ElementType.PARAMETER) | |
76 public static @interface ConstantNodeParameter {} | |
77 | |
78 /** | |
79 * Annotates a method that can be replaced by a compiler intrinsic. | |
80 * That is, a (resolved) call to the annotated method can be replaced | |
81 * with an instance of the node class denoted by {@link #value()}. | |
82 * For this reason, the signature of the annotated method must match | |
83 * the signature of a constructor in the node class. | |
84 */ | |
85 @Retention(RetentionPolicy.RUNTIME) | |
86 @Target(ElementType.METHOD) | |
87 public static @interface NodeIntrinsic { | |
88 /** | |
89 * Gets the {@link Node} subclass instantiated when intrinsifying a call to the annotated method. | |
90 * If not specified, then the class in which the annotated method is declared is used | |
91 * (and is assumed to be a {@link Node} subclass). | |
92 */ | |
93 Class value() default NodeIntrinsic.class; | |
94 } | |
95 | |
96 @Retention(RetentionPolicy.RUNTIME) | |
97 @Target(ElementType.METHOD) | |
98 public static @interface NodePhase { | |
99 Class value() default NodePhase.class; | |
100 } | |
101 | |
102 public interface ValueNumberable {} | |
103 | |
104 public interface IterableNodeType {} | |
105 | |
106 private Graph graph; | |
107 int id; | |
108 | |
109 // this next pointer is used in Graph to implement fast iteration over NodeClass types, it therefore points to the next Node of the same type. | |
110 Node typeCacheNext; | |
111 | |
112 private NodeUsagesList usages; | |
113 private Node predecessor; | |
114 private int modCount; | |
115 private NodeClass nodeClass; | |
116 | |
117 public Node() { | |
118 this.graph = null; | |
119 this.id = INITIAL_ID; | |
120 nodeClass = NodeClass.get(getClass()); | |
121 } | |
122 | |
123 protected int id() { | |
124 return id; | |
125 } | |
126 | |
127 public Graph graph() { | |
128 return graph; | |
129 } | |
130 | |
131 /** | |
132 * Returns an {@link NodeInputsIterable iterable} which can be used to traverse all non-null input edges of this node. | |
133 * @return an {@link NodeInputsIterable iterable} for all non-null input edges. | |
134 */ | |
135 public NodeInputsIterable inputs() { | |
136 return getNodeClass().getInputIterable(this); | |
137 } | |
138 | |
139 /** | |
140 * Returns an {@link NodeSuccessorsIterable iterable} which can be used to traverse all non-null successor edges of this node. | |
141 * @return an {@link NodeSuccessorsIterable iterable} for all non-null successor edges. | |
142 */ | |
143 public NodeSuccessorsIterable successors() { | |
144 return getNodeClass().getSuccessorIterable(this); | |
145 } | |
146 | |
147 public final NodeUsagesList usages() { | |
148 return usages; | |
149 } | |
150 | |
151 public final Node predecessor() { | |
152 return predecessor; | |
153 } | |
154 | |
155 final int modCount() { | |
156 return modCount; | |
157 } | |
158 | |
159 final void incModCount() { | |
160 modCount++; | |
161 } | |
162 | |
163 public boolean isDeleted() { | |
164 return id <= DELETED_ID_START; | |
165 } | |
166 | |
167 public boolean isAlive() { | |
168 return id >= ALIVE_ID_START; | |
169 } | |
170 | |
171 /** | |
172 * Updates the usages sets of the given nodes after an input slot is changed from oldInput to newInput: | |
173 * removes this node from oldInput's usages and adds this node to newInput's usages. | |
174 */ | |
175 protected void updateUsages(Node oldInput, Node newInput) { | |
176 assert assertTrue(usages != null, "usages == null while adding %s to %s", newInput, this); | |
177 if (oldInput != newInput) { | |
178 if (oldInput != null) { | |
179 boolean result = removeThisFromUsages(oldInput); | |
180 assert assertTrue(result, "not found in usages, old input: %s", oldInput); | |
181 } | |
182 if (newInput != null) { | |
183 NodeWorkList inputChanged = graph.inputChanged; | |
184 if (inputChanged != null) { | |
185 inputChanged.addAgain(this); | |
186 } | |
187 newInput.usages.add(this); | |
188 } | |
189 } | |
190 } | |
191 | |
192 /** | |
193 * Updates the predecessor sets of the given nodes after a successor slot is changed from oldSuccessor to newSuccessor: | |
194 * removes this node from oldSuccessor's predecessors and adds this node to newSuccessor's predecessors. | |
195 */ | |
196 protected void updatePredecessors(Node oldSuccessor, Node newSuccessor) { | |
197 assert assertTrue(usages != null, "usages == null while adding %s to %s", newSuccessor, this); | |
198 if (oldSuccessor != newSuccessor) { | |
199 if (oldSuccessor != null) { | |
200 assert assertTrue(oldSuccessor.predecessor == this, "wrong predecessor in old successor (%s): %s", oldSuccessor, oldSuccessor.predecessor); | |
201 oldSuccessor.predecessor = null; | |
202 } | |
203 if (newSuccessor != null) { | |
204 assert assertTrue(newSuccessor.predecessor == null, "unexpected non-null predecessor in new successor (%s): %s", newSuccessor, newSuccessor.predecessor); | |
205 newSuccessor.predecessor = this; | |
206 } | |
207 } | |
208 } | |
209 | |
210 void initialize(Graph newGraph) { | |
211 assert assertTrue(id == INITIAL_ID, "unexpected id: %d", id); | |
212 this.graph = newGraph; | |
213 newGraph.register(this); | |
214 usages = new NodeUsagesList(); | |
215 for (Node input : inputs()) { | |
216 updateUsages(null, input); | |
217 } | |
218 for (Node successor : successors()) { | |
219 updatePredecessors(null, successor); | |
220 } | |
221 } | |
222 | |
223 public final NodeClass getNodeClass() { | |
224 return nodeClass; | |
225 } | |
226 | |
227 // TODO (thomaswue): Do not allow to replace with null. | |
228 private boolean checkReplaceWith(Node other) { | |
229 assert assertFalse(other == this, "cannot replace a node with itself"); | |
230 assert assertFalse(isDeleted(), "cannot replace deleted node"); | |
231 // assert assertTrue(other != null, "cannot replace with null node"); | |
232 assert assertTrue(other == null || !other.isDeleted(), "cannot replace with deleted node %s", other); | |
233 assert assertTrue(other == null || other.graph() == graph, "cannot replace with node in different graph: %s", other == null ? null : other.graph()); | |
234 return true; | |
235 } | |
236 | |
237 public void replaceAtUsages(Node other) { | |
238 assert checkReplaceWith(other); | |
239 for (Node usage : usages) { | |
240 boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other); | |
241 assert assertTrue(result, "not found in inputs, usage: %s", usage); | |
242 if (other != null) { | |
243 other.usages.add(usage); | |
244 } | |
245 } | |
246 usages.clear(); | |
247 } | |
248 | |
249 public void replaceAtPredecessors(Node other) { | |
250 assert checkReplaceWith(other); | |
251 if (predecessor != null) { | |
252 boolean result = predecessor.getNodeClass().replaceFirstSuccessor(predecessor, this, other); | |
253 assert assertTrue(result, "not found in successors, predecessor: %s", predecessor); | |
254 predecessor.updatePredecessors(this, other); | |
255 } | |
256 } | |
257 | |
258 public void replaceAndDelete(Node other) { | |
259 assert checkReplaceWith(other); | |
260 if (other != null) { | |
261 clearSuccessors(); | |
262 replaceAtUsages(other); | |
263 replaceAtPredecessors(other); | |
264 } | |
265 safeDelete(); | |
266 } | |
267 | |
268 public void replaceFirstSuccessor(Node oldSuccessor, Node newSuccessor) { | |
269 if (getNodeClass().replaceFirstSuccessor(this, oldSuccessor, newSuccessor)) { | |
270 updatePredecessors(oldSuccessor, newSuccessor); | |
271 } | |
272 } | |
273 | |
274 public void replaceFirstInput(Node oldInput, Node newInput) { | |
275 if (getNodeClass().replaceFirstInput(this, oldInput, newInput)) { | |
276 updateUsages(oldInput, newInput); | |
277 } | |
278 } | |
279 | |
280 public void clearInputs() { | |
281 assert assertFalse(isDeleted(), "cannot clear inputs of deleted node"); | |
282 | |
283 for (Node input : inputs()) { | |
284 removeThisFromUsages(input); | |
285 } | |
286 getNodeClass().clearInputs(this); | |
287 } | |
288 | |
289 private boolean removeThisFromUsages(Node n) { | |
290 if (n.usages.remove(this)) { | |
291 if (n.usages.size() == 0) { | |
292 graph.usagesDropped.add(n); | |
293 } | |
294 return true; | |
295 } else { | |
296 return false; | |
297 } | |
298 } | |
299 | |
300 public void clearSuccessors() { | |
301 assert assertFalse(isDeleted(), "cannot clear successors of deleted node"); | |
302 | |
303 for (Node successor : successors()) { | |
304 assert assertTrue(successor.predecessor == this, "wrong predecessor in old successor (%s): %s", successor, successor.predecessor); | |
305 successor.predecessor = null; | |
306 } | |
307 getNodeClass().clearSuccessors(this); | |
308 } | |
309 | |
310 private boolean checkDeletion() { | |
311 assertTrue(usages.isEmpty(), "cannot delete node %s because of usages: %s", this, usages); | |
312 assertTrue(predecessor == null, "cannot delete node %s because of predecessor: %s", this, predecessor); | |
313 return true; | |
314 } | |
315 | |
316 public void safeDelete() { | |
317 assert checkDeletion(); | |
318 clearInputs(); | |
319 clearSuccessors(); | |
320 graph.unregister(this); | |
321 id = DELETED_ID_START - id; | |
322 assert isDeleted(); | |
323 } | |
324 | |
325 public final Node copyWithInputs() { | |
326 Node newNode = clone(graph); | |
327 NodeClass clazz = getNodeClass(); | |
328 clazz.copyInputs(this, newNode); | |
329 for (Node input : inputs()) { | |
330 input.usages.add(newNode); | |
331 } | |
332 return newNode; | |
333 } | |
334 | |
335 public Node clone(Graph into) { | |
336 Node newNode = null; | |
337 try { | |
338 newNode = (Node) this.clone(); | |
339 } catch (CloneNotSupportedException e) { | |
340 throw new GraalInternalError(e).addContext(this); | |
341 } | |
342 getNodeClass().clearInputs(newNode); | |
343 getNodeClass().clearSuccessors(newNode); | |
344 newNode.graph = into; | |
345 newNode.typeCacheNext = null; | |
346 newNode.id = INITIAL_ID; | |
347 into.register(newNode); | |
348 newNode.usages = new NodeUsagesList(); | |
349 newNode.predecessor = null; | |
350 newNode.modCount = 0; | |
351 return newNode; | |
352 } | |
353 | |
354 public boolean verify() { | |
355 assertTrue(isAlive(), "cannot verify inactive nodes (id=%d)", id); | |
356 assertTrue(graph() != null, "null graph"); | |
357 for (Node input : inputs()) { | |
358 assertTrue(input.usages().contains(this), "missing usage in input %s", input); | |
359 assertTrue(input.graph() == graph(), "mismatching graph in input %s", input); | |
360 } | |
361 for (Node successor : successors()) { | |
362 assertTrue(successor.predecessor() == this, "missing predecessor in %s (actual: %s)", successor, successor.predecessor()); | |
363 assertTrue(successor.graph() == graph(), "mismatching graph in successor %s", successor); | |
364 } | |
365 for (Node usage : usages()) { | |
366 assertFalse(usage.isDeleted(), "usage must never be deleted"); | |
367 assertTrue(usage.inputs().contains(this), "missing input in usage %s", usage); | |
368 } | |
369 if (predecessor != null) { | |
370 assertFalse(predecessor.isDeleted(), "predecessor must never be deleted"); | |
371 assertTrue(predecessor.successors().contains(this), "missing successor in predecessor %s", predecessor); | |
372 } | |
373 return true; | |
374 } | |
375 | |
376 public boolean assertTrue(boolean condition, String message, Object... args) { | |
377 if (condition) { | |
378 return true; | |
379 } else { | |
380 throw new VerificationError(message, args).addContext(this); | |
381 } | |
382 } | |
383 | |
384 public boolean assertFalse(boolean condition, String message, Object... args) { | |
385 if (condition) { | |
386 throw new VerificationError(message, args).addContext(this); | |
387 } else { | |
388 return true; | |
389 } | |
390 } | |
391 | |
392 public Iterable<? extends Node> cfgPredecessors() { | |
393 if (predecessor == null) { | |
394 return Collections.emptySet(); | |
395 } else { | |
396 return Collections.singleton(predecessor); | |
397 } | |
398 } | |
399 | |
400 /** | |
401 * Returns an iterator that will provide all control-flow successors of this node. Normally this will be the contents of all fields marked as NodeSuccessor, | |
402 * but some node classes (like EndNode) may return different nodes. | |
403 * Note that the iterator may generate null values if the fields contain them. | |
404 */ | |
405 public Iterable<? extends Node> cfgSuccessors() { | |
406 return successors(); | |
407 } | |
408 | |
409 /** | |
410 * hashCode and equals should always rely on object identity alone, thus hashCode and equals are final. | |
411 */ | |
412 @Override | |
413 public final int hashCode() { | |
414 return super.hashCode(); | |
415 } | |
416 | |
417 /** | |
418 * hashCode and equals should always rely on object identity alone, thus hashCode and equals are final. | |
419 */ | |
420 @Override | |
421 public final boolean equals(Object obj) { | |
422 return super.equals(obj); | |
423 } | |
424 | |
425 /** | |
426 * Provides a {@link Map} of properties of this node for use in debugging (e.g., to view in the ideal graph | |
427 * visualizer). Subclasses overriding this method should add to the map returned by their superclass. | |
428 */ | |
429 public Map<Object, Object> getDebugProperties() { | |
430 Map<Object, Object> map = new HashMap<>(); | |
431 map.put("usageCount", usages.size()); | |
432 map.put("predecessorCount", predecessor == null ? 0 : 1); | |
433 getNodeClass().getDebugProperties(this, map); | |
434 return map; | |
435 } | |
436 | |
437 /** | |
438 * This method is a shortcut for {@link #toString(Verbosity)} with {@link Verbosity#Short}. | |
439 */ | |
440 @Override | |
441 public final String toString() { | |
442 return toString(Verbosity.Short); | |
443 } | |
444 | |
445 public enum Verbosity { | |
446 /** | |
447 * Only the id of the node. | |
448 */ | |
449 Id, | |
450 /** | |
451 * Only the name of the node, which may contain some more information for certain node types (constants, ...). | |
452 */ | |
453 Name, | |
454 /** | |
455 * {@link #Id} + {@link #Name}. | |
456 */ | |
457 Short, | |
458 /** | |
459 * Defaults to {@link #Short} and may be enhanced by subclasses. | |
460 */ | |
461 Long, | |
462 /** | |
463 * For use by a custom formatting facility in an IDE. | |
464 */ | |
465 Debugger, | |
466 /** | |
467 * All the other information plus all debug properties of the node. | |
468 */ | |
469 All | |
470 } | |
471 | |
472 /** | |
473 * Creates a String representation for this node with a given {@link Verbosity}. | |
474 */ | |
475 public String toString(Verbosity verbosity) { | |
476 switch (verbosity) { | |
477 case Id: | |
478 return Integer.toString(id); | |
479 case Name: | |
480 return getNodeClass().shortName(); | |
481 case Short: | |
482 return toString(Verbosity.Id) + "|" + toString(Verbosity.Name); | |
483 case Long: | |
484 return toString(Verbosity.Short); | |
485 case Debugger: | |
486 case All: { | |
487 StringBuilder str = new StringBuilder(); | |
488 str.append(toString(Verbosity.Short)).append(" { "); | |
489 for (Map.Entry<Object, Object> entry : getDebugProperties().entrySet()) { | |
490 str.append(entry.getKey()).append("=").append(entry.getValue()).append(", "); | |
491 } | |
492 str.append(" }"); | |
493 return str.toString(); | |
494 } | |
495 default: | |
496 throw new RuntimeException("unknown verbosity: " + verbosity); | |
497 } | |
498 } | |
499 | |
500 @Override | |
501 public void formatTo(Formatter formatter, int flags, int width, int precision) { | |
502 if ((flags & FormattableFlags.ALTERNATE) == FormattableFlags.ALTERNATE) { | |
503 formatter.format("%s", toString(Verbosity.Id)); | |
504 } else if ((flags & FormattableFlags.UPPERCASE) == FormattableFlags.UPPERCASE) { | |
505 formatter.format("%s", toString(Verbosity.Long)); | |
506 } else { | |
507 formatter.format("%s", toString(Verbosity.Short)); | |
508 } | |
509 | |
510 boolean neighborsAlternate = ((flags & FormattableFlags.LEFT_JUSTIFY) == FormattableFlags.LEFT_JUSTIFY); | |
511 int neighborsFlags = (neighborsAlternate ? FormattableFlags.ALTERNATE | FormattableFlags.LEFT_JUSTIFY : 0); | |
512 if (width > 0) { | |
513 if (this.predecessor != null) { | |
514 formatter.format(" pred={"); | |
515 this.predecessor.formatTo(formatter, neighborsFlags, width - 1, 0); | |
516 formatter.format("}"); | |
517 } | |
518 | |
519 NodeClassIterator inputIter = inputs().iterator(); | |
520 while (inputIter.hasNext()) { | |
521 Position position = inputIter.nextPosition(); | |
522 Node input = getNodeClass().get(this, position); | |
523 if (input != null) { | |
524 formatter.format(" "); | |
525 formatter.format(getNodeClass().getName(position)); | |
526 formatter.format("={"); | |
527 input.formatTo(formatter, neighborsFlags, width - 1, 0); | |
528 formatter.format("}"); | |
529 } | |
530 } | |
531 } | |
532 | |
533 if (precision > 0) { | |
534 if (this.usages.size() > 0) { | |
535 formatter.format(" usages={"); | |
536 int z = 0; | |
537 for (Node usage : this.usages) { | |
538 if (z != 0) { | |
539 formatter.format(", "); | |
540 } | |
541 usage.formatTo(formatter, neighborsFlags, 0, precision - 1); | |
542 ++z; | |
543 } | |
544 formatter.format("}"); | |
545 } | |
546 | |
547 NodeClassIterator succIter = successors().iterator(); | |
548 while (succIter.hasNext()) { | |
549 Position position = succIter.nextPosition(); | |
550 Node successor = getNodeClass().get(this, position); | |
551 if (successor != null) { | |
552 formatter.format(" "); | |
553 formatter.format(getNodeClass().getName(position)); | |
554 formatter.format("={"); | |
555 successor.formatTo(formatter, neighborsFlags, 0, precision - 1); | |
556 formatter.format("}"); | |
557 } | |
558 } | |
559 } | |
560 } | |
561 } |