1 /* 2 * Copyright (c) 2011, 2018, 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 24 25 package org.graalvm.compiler.graph; 26 27 import static org.graalvm.compiler.graph.Edges.Type.Inputs; 28 import static org.graalvm.compiler.graph.Edges.Type.Successors; 29 import static org.graalvm.compiler.graph.Graph.isModificationCountsEnabled; 30 import static org.graalvm.compiler.graph.UnsafeAccess.UNSAFE; 31 32 import java.lang.annotation.ElementType; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.EnumSet; 37 import java.util.Formattable; 38 import java.util.FormattableFlags; 39 import java.util.Formatter; 40 import java.util.HashMap; 41 import java.util.Map; 42 import java.util.Objects; 43 import java.util.function.Predicate; 44 import java.util.function.Supplier; 45 46 import org.graalvm.compiler.core.common.Fields; 47 import org.graalvm.compiler.core.common.type.AbstractPointerStamp; 48 import org.graalvm.compiler.core.common.type.Stamp; 49 import org.graalvm.compiler.debug.DebugCloseable; 50 import org.graalvm.compiler.debug.DebugContext; 51 import org.graalvm.compiler.graph.Graph.NodeEventListener; 52 import org.graalvm.compiler.graph.Graph.Options; 53 import org.graalvm.compiler.graph.iterators.NodeIterable; 54 import org.graalvm.compiler.graph.iterators.NodePredicate; 55 import org.graalvm.compiler.graph.spi.Simplifiable; 56 import org.graalvm.compiler.graph.spi.SimplifierTool; 57 import org.graalvm.compiler.nodeinfo.InputType; 58 import org.graalvm.compiler.nodeinfo.NodeCycles; 59 import org.graalvm.compiler.nodeinfo.NodeInfo; 60 import org.graalvm.compiler.nodeinfo.NodeSize; 61 import org.graalvm.compiler.nodeinfo.Verbosity; 62 import org.graalvm.compiler.options.OptionValues; 63 64 import sun.misc.Unsafe; 65 66 /** 67 * This class is the base class for all nodes. It represents a node that can be inserted in a 68 * {@link Graph}. 69 * <p> 70 * Once a node has been added to a graph, it has a graph-unique {@link #id()}. Edges in the 71 * subclasses are represented with annotated fields. There are two kind of edges : {@link Input} and 72 * {@link Successor}. If a field, of a type compatible with {@link Node}, annotated with either 73 * {@link Input} and {@link Successor} is not null, then there is an edge from this node to the node 74 * this field points to. 75 * <p> 76 * Nodes which are be value numberable should implement the {@link ValueNumberable} interface. 77 * 78 * <h1>Assertions and Verification</h1> 79 * 80 * The Node class supplies the {@link #assertTrue(boolean, String, Object...)} and 81 * {@link #assertFalse(boolean, String, Object...)} methods, which will check the supplied boolean 82 * and throw a VerificationError if it has the wrong value. Both methods will always either throw an 83 * exception or return true. They can thus be used within an assert statement, so that the check is 84 * only performed if assertions are enabled. 85 */ 86 @NodeInfo 87 public abstract class Node implements Cloneable, Formattable, NodeInterface { 88 89 public static final NodeClass<?> TYPE = null; 90 91 public static final boolean TRACK_CREATION_POSITION = Boolean.getBoolean("debug.graal.TrackNodeCreationPosition"); 92 93 static final int DELETED_ID_START = -1000000000; 94 static final int INITIAL_ID = -1; 95 static final int ALIVE_ID_START = 0; 96 97 // The use of fully qualified class names here and in the rest 98 // of this file works around a problem javac has resolving symbols 99 100 /** 101 * Denotes a non-optional (non-null) node input. This should be applied to exactly the fields of 102 * a node that are of type {@link Node} or {@link NodeInputList}. Nodes that update fields of 103 * type {@link Node} outside of their constructor should call 104 * {@link Node#updateUsages(Node, Node)} just prior to doing the update of the input. 105 */ 106 @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) 107 @java.lang.annotation.Target(ElementType.FIELD) 108 public static @interface Input { value()109 InputType value() default InputType.Value; 110 } 111 112 /** 113 * Denotes an optional (nullable) node input. This should be applied to exactly the fields of a 114 * node that are of type {@link Node} or {@link NodeInputList}. Nodes that update fields of type 115 * {@link Node} outside of their constructor should call {@link Node#updateUsages(Node, Node)} 116 * just prior to doing the update of the input. 117 */ 118 @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) 119 @java.lang.annotation.Target(ElementType.FIELD) 120 public static @interface OptionalInput { value()121 InputType value() default InputType.Value; 122 } 123 124 @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) 125 @java.lang.annotation.Target(ElementType.FIELD) 126 public static @interface Successor { 127 } 128 129 /** 130 * Denotes that a parameter of an {@linkplain NodeIntrinsic intrinsic} method must be a compile 131 * time constant at all call sites to the intrinsic method. 132 */ 133 @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) 134 @java.lang.annotation.Target(ElementType.PARAMETER) 135 public static @interface ConstantNodeParameter { 136 } 137 138 /** 139 * Denotes an injected parameter in a {@linkplain NodeIntrinsic node intrinsic} constructor. If 140 * the constructor is called as part of node intrinsification, the node intrinsifier will inject 141 * an argument for the annotated parameter. Injected parameters must precede all non-injected 142 * parameters in a constructor. If the type of the annotated parameter is {@link Stamp}, the 143 * {@linkplain Stamp#javaType type} of the injected stamp is the return type of the annotated 144 * method (which cannot be {@code void}). 145 */ 146 @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) 147 @java.lang.annotation.Target(ElementType.PARAMETER) 148 public static @interface InjectedNodeParameter { 149 } 150 151 /** 152 * Annotates a method that can be replaced by a compiler intrinsic. A (resolved) call to the 153 * annotated method will be processed by a generated {@code InvocationPlugin} that calls either 154 * a factory method or a constructor corresponding with the annotated method. 155 * <p> 156 * A factory method corresponding to an annotated method is a static method named 157 * {@code intrinsify} defined in the class denoted by {@link #value()}. In order, its signature 158 * is as follows: 159 * <ol> 160 * <li>A {@code GraphBuilderContext} parameter.</li> 161 * <li>A {@code ResolvedJavaMethod} parameter.</li> 162 * <li>A sequence of zero or more {@linkplain InjectedNodeParameter injected} parameters.</li> 163 * <li>Remaining parameters that match the declared parameters of the annotated method.</li> 164 * </ol> 165 * A constructor corresponding to an annotated method is defined in the class denoted by 166 * {@link #value()}. In order, its signature is as follows: 167 * <ol> 168 * <li>A sequence of zero or more {@linkplain InjectedNodeParameter injected} parameters.</li> 169 * <li>Remaining parameters that match the declared parameters of the annotated method.</li> 170 * </ol> 171 * There must be exactly one such factory method or constructor corresponding to a 172 * {@link NodeIntrinsic} annotated method. 173 */ 174 @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) 175 @java.lang.annotation.Target(ElementType.METHOD) 176 public static @interface NodeIntrinsic { 177 178 /** 179 * The class declaring the factory method or {@link Node} subclass declaring the constructor 180 * used to intrinsify a call to the annotated method. The default value is the class in 181 * which the annotated method is declared. 182 */ value()183 Class<?> value() default NodeIntrinsic.class; 184 185 /** 186 * If {@code true}, the factory method or constructor selected by the annotation must have 187 * an {@linkplain InjectedNodeParameter injected} {@link Stamp} parameter. Calling 188 * {@link AbstractPointerStamp#nonNull()} on the injected stamp is guaranteed to return 189 * {@code true}. 190 */ injectedStampIsNonNull()191 boolean injectedStampIsNonNull() default false; 192 } 193 194 /** 195 * Marker for a node that can be replaced by another node via global value numbering. A 196 * {@linkplain NodeClass#isLeafNode() leaf} node can be replaced by another node of the same 197 * type that has exactly the same {@linkplain NodeClass#getData() data} values. A non-leaf node 198 * can be replaced by another node of the same type that has exactly the same data values as 199 * well as the same {@linkplain Node#inputs() inputs} and {@linkplain Node#successors() 200 * successors}. 201 */ 202 public interface ValueNumberable { 203 } 204 205 /** 206 * Marker interface for nodes that contains other nodes. When the inputs to this node changes, 207 * users of this node should also be placed on the work list for canonicalization. 208 */ 209 public interface IndirectCanonicalization { 210 } 211 212 private Graph graph; 213 int id; 214 215 // this next pointer is used in Graph to implement fast iteration over NodeClass types, it 216 // therefore points to the next Node of the same type. 217 Node typeCacheNext; 218 219 static final int INLINE_USAGE_COUNT = 2; 220 private static final Node[] NO_NODES = {}; 221 222 /** 223 * Head of usage list. The elements of the usage list in order are {@link #usage0}, 224 * {@link #usage1} and {@link #extraUsages}. The first null entry terminates the list. 225 */ 226 Node usage0; 227 Node usage1; 228 Node[] extraUsages; 229 int extraUsagesCount; 230 231 private Node predecessor; 232 private NodeClass<? extends Node> nodeClass; 233 234 public static final int NODE_LIST = -2; 235 public static final int NOT_ITERABLE = -1; 236 237 static class NodeStackTrace { 238 final StackTraceElement[] stackTrace; 239 NodeStackTrace()240 NodeStackTrace() { 241 this.stackTrace = new Throwable().getStackTrace(); 242 } 243 getString(String label)244 private String getString(String label) { 245 StringBuilder sb = new StringBuilder(); 246 if (label != null) { 247 sb.append(label).append(": "); 248 } 249 for (StackTraceElement ste : stackTrace) { 250 sb.append("at ").append(ste.toString()).append('\n'); 251 } 252 return sb.toString(); 253 } 254 getStrackTraceString()255 String getStrackTraceString() { 256 return getString(null); 257 } 258 259 @Override toString()260 public String toString() { 261 return getString(getClass().getSimpleName()); 262 } 263 } 264 265 static class NodeCreationStackTrace extends NodeStackTrace { 266 } 267 268 public static class NodeInsertionStackTrace extends NodeStackTrace { 269 } 270 Node(NodeClass<? extends Node> c)271 public Node(NodeClass<? extends Node> c) { 272 init(c); 273 } 274 init(NodeClass<? extends Node> c)275 final void init(NodeClass<? extends Node> c) { 276 assert c.getJavaClass() == this.getClass(); 277 this.nodeClass = c; 278 id = INITIAL_ID; 279 extraUsages = NO_NODES; 280 if (TRACK_CREATION_POSITION) { 281 setCreationPosition(new NodeCreationStackTrace()); 282 } 283 } 284 id()285 final int id() { 286 return id; 287 } 288 289 @Override asNode()290 public Node asNode() { 291 return this; 292 } 293 294 /** 295 * Gets the graph context of this node. 296 */ graph()297 public Graph graph() { 298 return graph; 299 } 300 301 /** 302 * Gets the option values associated with this node's graph. 303 */ getOptions()304 public final OptionValues getOptions() { 305 return graph == null ? null : graph.getOptions(); 306 } 307 308 /** 309 * Gets the debug context associated with this node's graph. 310 */ getDebug()311 public final DebugContext getDebug() { 312 return graph.getDebug(); 313 } 314 315 /** 316 * Returns an {@link NodeIterable iterable} which can be used to traverse all non-null input 317 * edges of this node. 318 * 319 * @return an {@link NodeIterable iterable} for all non-null input edges. 320 */ inputs()321 public NodeIterable<Node> inputs() { 322 return nodeClass.getInputIterable(this); 323 } 324 325 /** 326 * Returns an {@link Iterable iterable} which can be used to traverse all non-null input edges 327 * of this node. 328 * 329 * @return an {@link Iterable iterable} for all non-null input edges. 330 */ inputPositions()331 public Iterable<Position> inputPositions() { 332 return nodeClass.getInputEdges().getPositionsIterable(this); 333 } 334 335 public abstract static class EdgeVisitor { 336 apply(Node source, Node target)337 public abstract Node apply(Node source, Node target); 338 339 } 340 341 /** 342 * Applies the given visitor to all inputs of this node. 343 * 344 * @param visitor the visitor to be applied to the inputs 345 */ applyInputs(EdgeVisitor visitor)346 public void applyInputs(EdgeVisitor visitor) { 347 nodeClass.applyInputs(this, visitor); 348 } 349 350 /** 351 * Applies the given visitor to all successors of this node. 352 * 353 * @param visitor the visitor to be applied to the successors 354 */ applySuccessors(EdgeVisitor visitor)355 public void applySuccessors(EdgeVisitor visitor) { 356 nodeClass.applySuccessors(this, visitor); 357 } 358 359 /** 360 * Returns an {@link NodeIterable iterable} which can be used to traverse all non-null successor 361 * edges of this node. 362 * 363 * @return an {@link NodeIterable iterable} for all non-null successor edges. 364 */ successors()365 public NodeIterable<Node> successors() { 366 assert !this.isDeleted() : this; 367 return nodeClass.getSuccessorIterable(this); 368 } 369 370 /** 371 * Returns an {@link Iterable iterable} which can be used to traverse all successor edge 372 * positions of this node. 373 * 374 * @return an {@link Iterable iterable} for all successor edge positoins. 375 */ successorPositions()376 public Iterable<Position> successorPositions() { 377 return nodeClass.getSuccessorEdges().getPositionsIterable(this); 378 } 379 380 /** 381 * Gets the maximum number of usages this node has had at any point in time. 382 */ getUsageCount()383 public int getUsageCount() { 384 if (usage0 == null) { 385 return 0; 386 } 387 if (usage1 == null) { 388 return 1; 389 } 390 return INLINE_USAGE_COUNT + extraUsagesCount; 391 } 392 393 /** 394 * Gets the list of nodes that use this node (i.e., as an input). 395 */ usages()396 public final NodeIterable<Node> usages() { 397 return new NodeUsageIterable(this); 398 } 399 400 /** 401 * Checks whether this node has no usages. 402 */ hasNoUsages()403 public final boolean hasNoUsages() { 404 return this.usage0 == null; 405 } 406 407 /** 408 * Checks whether this node has usages. 409 */ hasUsages()410 public final boolean hasUsages() { 411 return this.usage0 != null; 412 } 413 414 /** 415 * Checks whether this node has more than one usages. 416 */ hasMoreThanOneUsage()417 public final boolean hasMoreThanOneUsage() { 418 return this.usage1 != null; 419 } 420 421 /** 422 * Checks whether this node has exactly one usgae. 423 */ hasExactlyOneUsage()424 public final boolean hasExactlyOneUsage() { 425 return hasUsages() && !hasMoreThanOneUsage(); 426 } 427 428 /** 429 * Adds a given node to this node's {@linkplain #usages() usages}. 430 * 431 * @param node the node to add 432 */ addUsage(Node node)433 void addUsage(Node node) { 434 incUsageModCount(); 435 if (usage0 == null) { 436 usage0 = node; 437 } else if (usage1 == null) { 438 usage1 = node; 439 } else { 440 int length = extraUsages.length; 441 if (length == 0) { 442 extraUsages = new Node[4]; 443 } else if (extraUsagesCount == length) { 444 Node[] newExtraUsages = new Node[length * 2 + 1]; 445 System.arraycopy(extraUsages, 0, newExtraUsages, 0, length); 446 extraUsages = newExtraUsages; 447 } 448 extraUsages[extraUsagesCount++] = node; 449 } 450 } 451 movUsageFromEndTo(int destIndex)452 private void movUsageFromEndTo(int destIndex) { 453 if (destIndex >= INLINE_USAGE_COUNT) { 454 movUsageFromEndToExtraUsages(destIndex - INLINE_USAGE_COUNT); 455 } else if (destIndex == 1) { 456 movUsageFromEndToIndexOne(); 457 } else { 458 assert destIndex == 0; 459 movUsageFromEndToIndexZero(); 460 } 461 } 462 movUsageFromEndToExtraUsages(int destExtraIndex)463 private void movUsageFromEndToExtraUsages(int destExtraIndex) { 464 this.extraUsagesCount--; 465 Node n = extraUsages[extraUsagesCount]; 466 extraUsages[destExtraIndex] = n; 467 extraUsages[extraUsagesCount] = null; 468 } 469 movUsageFromEndToIndexZero()470 private void movUsageFromEndToIndexZero() { 471 if (extraUsagesCount > 0) { 472 this.extraUsagesCount--; 473 usage0 = extraUsages[extraUsagesCount]; 474 extraUsages[extraUsagesCount] = null; 475 } else if (usage1 != null) { 476 usage0 = usage1; 477 usage1 = null; 478 } else { 479 usage0 = null; 480 } 481 } 482 movUsageFromEndToIndexOne()483 private void movUsageFromEndToIndexOne() { 484 if (extraUsagesCount > 0) { 485 this.extraUsagesCount--; 486 usage1 = extraUsages[extraUsagesCount]; 487 extraUsages[extraUsagesCount] = null; 488 } else { 489 assert usage1 != null; 490 usage1 = null; 491 } 492 } 493 494 /** 495 * Removes a given node from this node's {@linkplain #usages() usages}. 496 * 497 * @param node the node to remove 498 * @return whether or not {@code usage} was in the usage list 499 */ removeUsage(Node node)500 public boolean removeUsage(Node node) { 501 assert node != null; 502 // For large graphs, usage removal is performance critical. 503 // Furthermore, it is critical that this method maintains the invariant that the usage list 504 // has no null element preceding a non-null element. 505 incUsageModCount(); 506 if (usage0 == node) { 507 movUsageFromEndToIndexZero(); 508 return true; 509 } 510 if (usage1 == node) { 511 movUsageFromEndToIndexOne(); 512 return true; 513 } 514 for (int i = this.extraUsagesCount - 1; i >= 0; i--) { 515 if (extraUsages[i] == node) { 516 movUsageFromEndToExtraUsages(i); 517 return true; 518 } 519 } 520 return false; 521 } 522 predecessor()523 public final Node predecessor() { 524 return predecessor; 525 } 526 modCount()527 public final int modCount() { 528 if (isModificationCountsEnabled() && graph != null) { 529 return graph.modCount(this); 530 } 531 return 0; 532 } 533 incModCount()534 final void incModCount() { 535 if (isModificationCountsEnabled() && graph != null) { 536 graph.incModCount(this); 537 } 538 } 539 usageModCount()540 final int usageModCount() { 541 if (isModificationCountsEnabled() && graph != null) { 542 return graph.usageModCount(this); 543 } 544 return 0; 545 } 546 incUsageModCount()547 final void incUsageModCount() { 548 if (isModificationCountsEnabled() && graph != null) { 549 graph.incUsageModCount(this); 550 } 551 } 552 isDeleted()553 public final boolean isDeleted() { 554 return id <= DELETED_ID_START; 555 } 556 isAlive()557 public final boolean isAlive() { 558 return id >= ALIVE_ID_START; 559 } 560 isUnregistered()561 public final boolean isUnregistered() { 562 return id == INITIAL_ID; 563 } 564 565 /** 566 * Updates the usages sets of the given nodes after an input slot is changed from 567 * {@code oldInput} to {@code newInput} by removing this node from {@code oldInput}'s usages and 568 * adds this node to {@code newInput}'s usages. 569 */ updateUsages(Node oldInput, Node newInput)570 protected void updateUsages(Node oldInput, Node newInput) { 571 assert isAlive() && (newInput == null || newInput.isAlive()) : "adding " + newInput + " to " + this + " instead of " + oldInput; 572 if (oldInput != newInput) { 573 if (oldInput != null) { 574 boolean result = removeThisFromUsages(oldInput); 575 assert assertTrue(result, "not found in usages, old input: %s", oldInput); 576 } 577 maybeNotifyInputChanged(this); 578 if (newInput != null) { 579 newInput.addUsage(this); 580 } 581 if (oldInput != null && oldInput.hasNoUsages()) { 582 maybeNotifyZeroUsages(oldInput); 583 } 584 } 585 } 586 updateUsagesInterface(NodeInterface oldInput, NodeInterface newInput)587 protected void updateUsagesInterface(NodeInterface oldInput, NodeInterface newInput) { 588 updateUsages(oldInput == null ? null : oldInput.asNode(), newInput == null ? null : newInput.asNode()); 589 } 590 591 /** 592 * Updates the predecessor of the given nodes after a successor slot is changed from 593 * oldSuccessor to newSuccessor: removes this node from oldSuccessor's predecessors and adds 594 * this node to newSuccessor's predecessors. 595 */ updatePredecessor(Node oldSuccessor, Node newSuccessor)596 protected void updatePredecessor(Node oldSuccessor, Node newSuccessor) { 597 assert isAlive() && (newSuccessor == null || newSuccessor.isAlive()) || newSuccessor == null && !isAlive() : "adding " + newSuccessor + " to " + this + " instead of " + oldSuccessor; 598 assert graph == null || !graph.isFrozen(); 599 if (oldSuccessor != newSuccessor) { 600 if (oldSuccessor != null) { 601 assert assertTrue(newSuccessor == null || oldSuccessor.predecessor == this, "wrong predecessor in old successor (%s): %s, should be %s", oldSuccessor, oldSuccessor.predecessor, this); 602 oldSuccessor.predecessor = null; 603 } 604 if (newSuccessor != null) { 605 assert assertTrue(newSuccessor.predecessor == null, "unexpected non-null predecessor in new successor (%s): %s, this=%s", newSuccessor, newSuccessor.predecessor, this); 606 newSuccessor.predecessor = this; 607 } 608 } 609 } 610 initialize(Graph newGraph)611 void initialize(Graph newGraph) { 612 assert assertTrue(id == INITIAL_ID, "unexpected id: %d", id); 613 this.graph = newGraph; 614 newGraph.register(this); 615 NodeClass<? extends Node> nc = nodeClass; 616 nc.registerAtInputsAsUsage(this); 617 nc.registerAtSuccessorsAsPredecessor(this); 618 } 619 620 /** 621 * Information associated with this node. A single value is stored directly in the field. 622 * Multiple values are stored by creating an Object[]. 623 */ 624 private Object annotation; 625 getNodeInfo(Class<T> clazz)626 private <T> T getNodeInfo(Class<T> clazz) { 627 assert clazz != Object[].class; 628 if (annotation == null) { 629 return null; 630 } 631 if (clazz.isInstance(annotation)) { 632 return clazz.cast(annotation); 633 } 634 if (annotation.getClass() == Object[].class) { 635 Object[] annotations = (Object[]) annotation; 636 for (Object ann : annotations) { 637 if (clazz.isInstance(ann)) { 638 return clazz.cast(ann); 639 } 640 } 641 } 642 return null; 643 } 644 setNodeInfo(Class<T> clazz, T value)645 private <T> void setNodeInfo(Class<T> clazz, T value) { 646 assert clazz != Object[].class; 647 if (annotation == null || clazz.isInstance(annotation)) { 648 // Replace the current value 649 this.annotation = value; 650 } else if (annotation.getClass() == Object[].class) { 651 Object[] annotations = (Object[]) annotation; 652 for (int i = 0; i < annotations.length; i++) { 653 if (clazz.isInstance(annotations[i])) { 654 annotations[i] = value; 655 return; 656 } 657 } 658 Object[] newAnnotations = Arrays.copyOf(annotations, annotations.length + 1); 659 newAnnotations[annotations.length] = value; 660 this.annotation = newAnnotations; 661 } else { 662 this.annotation = new Object[]{this.annotation, value}; 663 } 664 } 665 666 /** 667 * Gets the source position information for this node or null if it doesn't exist. 668 */ 669 getNodeSourcePosition()670 public NodeSourcePosition getNodeSourcePosition() { 671 return getNodeInfo(NodeSourcePosition.class); 672 } 673 674 /** 675 * Set the source position to {@code sourcePosition}. Setting it to null is ignored so that it's 676 * not accidentally cleared. Use {@link #clearNodeSourcePosition()} instead. 677 */ setNodeSourcePosition(NodeSourcePosition sourcePosition)678 public void setNodeSourcePosition(NodeSourcePosition sourcePosition) { 679 if (sourcePosition == null) { 680 return; 681 } 682 setNodeInfo(NodeSourcePosition.class, sourcePosition); 683 } 684 clearNodeSourcePosition()685 public void clearNodeSourcePosition() { 686 setNodeInfo(NodeSourcePosition.class, null); 687 } 688 getCreationPosition()689 public NodeCreationStackTrace getCreationPosition() { 690 return getNodeInfo(NodeCreationStackTrace.class); 691 } 692 setCreationPosition(NodeCreationStackTrace trace)693 public void setCreationPosition(NodeCreationStackTrace trace) { 694 setNodeInfo(NodeCreationStackTrace.class, trace); 695 } 696 getInsertionPosition()697 public NodeInsertionStackTrace getInsertionPosition() { 698 return getNodeInfo(NodeInsertionStackTrace.class); 699 } 700 setInsertionPosition(NodeInsertionStackTrace trace)701 public void setInsertionPosition(NodeInsertionStackTrace trace) { 702 setNodeInfo(NodeInsertionStackTrace.class, trace); 703 } 704 705 /** 706 * Update the source position only if it is null. 707 */ updateNodeSourcePosition(Supplier<NodeSourcePosition> sourcePositionSupp)708 public void updateNodeSourcePosition(Supplier<NodeSourcePosition> sourcePositionSupp) { 709 if (this.getNodeSourcePosition() == null) { 710 setNodeSourcePosition(sourcePositionSupp.get()); 711 } 712 } 713 withNodeSourcePosition()714 public DebugCloseable withNodeSourcePosition() { 715 return graph.withNodeSourcePosition(this); 716 } 717 getNodeClass()718 public final NodeClass<? extends Node> getNodeClass() { 719 return nodeClass; 720 } 721 isAllowedUsageType(InputType type)722 public boolean isAllowedUsageType(InputType type) { 723 if (type == InputType.Value) { 724 return false; 725 } 726 return getNodeClass().getAllowedUsageTypes().contains(type); 727 } 728 checkReplaceWith(Node other)729 private boolean checkReplaceWith(Node other) { 730 if (graph != null && graph.isFrozen()) { 731 fail("cannot modify frozen graph"); 732 } 733 if (other == this) { 734 fail("cannot replace a node with itself"); 735 } 736 if (isDeleted()) { 737 fail("cannot replace deleted node"); 738 } 739 if (other != null && other.isDeleted()) { 740 fail("cannot replace with deleted node %s", other); 741 } 742 return true; 743 } 744 replaceAtUsages(Node other)745 public final void replaceAtUsages(Node other) { 746 replaceAtAllUsages(other, (Node) null); 747 } 748 replaceAtUsages(Node other, Predicate<Node> filter)749 public final void replaceAtUsages(Node other, Predicate<Node> filter) { 750 replaceAtUsages(other, filter, null); 751 } 752 replaceAtUsagesAndDelete(Node other)753 public final void replaceAtUsagesAndDelete(Node other) { 754 replaceAtUsages(other, null, this); 755 safeDelete(); 756 } 757 replaceAtUsagesAndDelete(Node other, Predicate<Node> filter)758 public final void replaceAtUsagesAndDelete(Node other, Predicate<Node> filter) { 759 replaceAtUsages(other, filter, this); 760 safeDelete(); 761 } 762 replaceAtUsages(Node other, Predicate<Node> filter, Node toBeDeleted)763 protected void replaceAtUsages(Node other, Predicate<Node> filter, Node toBeDeleted) { 764 if (filter == null) { 765 replaceAtAllUsages(other, toBeDeleted); 766 } else { 767 replaceAtMatchingUsages(other, filter, toBeDeleted); 768 } 769 } 770 replaceAtAllUsages(Node other, Node toBeDeleted)771 protected void replaceAtAllUsages(Node other, Node toBeDeleted) { 772 checkReplaceWith(other); 773 if (usage0 == null) { 774 return; 775 } 776 replaceAtUsage(other, toBeDeleted, usage0); 777 usage0 = null; 778 779 if (usage1 == null) { 780 return; 781 } 782 replaceAtUsage(other, toBeDeleted, usage1); 783 usage1 = null; 784 785 if (extraUsagesCount <= 0) { 786 return; 787 } 788 for (int i = 0; i < extraUsagesCount; i++) { 789 Node usage = extraUsages[i]; 790 replaceAtUsage(other, toBeDeleted, usage); 791 } 792 this.extraUsages = NO_NODES; 793 this.extraUsagesCount = 0; 794 } 795 replaceAtUsage(Node other, Node toBeDeleted, Node usage)796 private void replaceAtUsage(Node other, Node toBeDeleted, Node usage) { 797 boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other); 798 assert assertTrue(result, "not found in inputs, usage: %s", usage); 799 /* 800 * Don't notify for nodes which are about to be deleted. 801 */ 802 if (toBeDeleted == null || usage != toBeDeleted) { 803 maybeNotifyInputChanged(usage); 804 } 805 if (other != null) { 806 other.addUsage(usage); 807 } 808 } 809 replaceAtMatchingUsages(Node other, Predicate<Node> filter, Node toBeDeleted)810 private void replaceAtMatchingUsages(Node other, Predicate<Node> filter, Node toBeDeleted) { 811 if (filter == null) { 812 fail("filter cannot be null"); 813 } 814 checkReplaceWith(other); 815 int i = 0; 816 while (i < this.getUsageCount()) { 817 Node usage = this.getUsageAt(i); 818 if (filter.test(usage)) { 819 replaceAtUsage(other, toBeDeleted, usage); 820 this.movUsageFromEndTo(i); 821 } else { 822 ++i; 823 } 824 } 825 } 826 getUsageAt(int index)827 public Node getUsageAt(int index) { 828 if (index == 0) { 829 return this.usage0; 830 } else if (index == 1) { 831 return this.usage1; 832 } else { 833 return this.extraUsages[index - INLINE_USAGE_COUNT]; 834 } 835 } 836 replaceAtMatchingUsages(Node other, NodePredicate usagePredicate)837 public void replaceAtMatchingUsages(Node other, NodePredicate usagePredicate) { 838 checkReplaceWith(other); 839 replaceAtMatchingUsages(other, usagePredicate, null); 840 } 841 replaceAtUsages(InputType type, Node other)842 public void replaceAtUsages(InputType type, Node other) { 843 checkReplaceWith(other); 844 for (Node usage : usages().snapshot()) { 845 for (Position pos : usage.inputPositions()) { 846 if (pos.getInputType() == type && pos.get(usage) == this) { 847 pos.set(usage, other); 848 } 849 } 850 } 851 } 852 maybeNotifyInputChanged(Node node)853 private void maybeNotifyInputChanged(Node node) { 854 if (graph != null) { 855 assert !graph.isFrozen(); 856 NodeEventListener listener = graph.nodeEventListener; 857 if (listener != null) { 858 listener.event(Graph.NodeEvent.INPUT_CHANGED, node); 859 } 860 } 861 } 862 maybeNotifyZeroUsages(Node node)863 public void maybeNotifyZeroUsages(Node node) { 864 if (graph != null) { 865 assert !graph.isFrozen(); 866 NodeEventListener listener = graph.nodeEventListener; 867 if (listener != null && node.isAlive()) { 868 listener.event(Graph.NodeEvent.ZERO_USAGES, node); 869 } 870 } 871 } 872 replaceAtPredecessor(Node other)873 public void replaceAtPredecessor(Node other) { 874 checkReplaceWith(other); 875 if (predecessor != null) { 876 if (!predecessor.getNodeClass().replaceFirstSuccessor(predecessor, this, other)) { 877 fail("not found in successors, predecessor: %s", predecessor); 878 } 879 predecessor.updatePredecessor(this, other); 880 } 881 } 882 replaceAndDelete(Node other)883 public void replaceAndDelete(Node other) { 884 checkReplaceWith(other); 885 if (other == null) { 886 fail("cannot replace with null"); 887 } 888 if (this.hasUsages()) { 889 replaceAtUsages(other); 890 } 891 replaceAtPredecessor(other); 892 this.safeDelete(); 893 } 894 replaceFirstSuccessor(Node oldSuccessor, Node newSuccessor)895 public void replaceFirstSuccessor(Node oldSuccessor, Node newSuccessor) { 896 if (nodeClass.replaceFirstSuccessor(this, oldSuccessor, newSuccessor)) { 897 updatePredecessor(oldSuccessor, newSuccessor); 898 } 899 } 900 replaceFirstInput(Node oldInput, Node newInput)901 public void replaceFirstInput(Node oldInput, Node newInput) { 902 if (nodeClass.replaceFirstInput(this, oldInput, newInput)) { 903 updateUsages(oldInput, newInput); 904 } 905 } 906 clearInputs()907 public void clearInputs() { 908 assert assertFalse(isDeleted(), "cannot clear inputs of deleted node"); 909 getNodeClass().unregisterAtInputsAsUsage(this); 910 } 911 removeThisFromUsages(Node n)912 boolean removeThisFromUsages(Node n) { 913 return n.removeUsage(this); 914 } 915 clearSuccessors()916 public void clearSuccessors() { 917 assert assertFalse(isDeleted(), "cannot clear successors of deleted node"); 918 getNodeClass().unregisterAtSuccessorsAsPredecessor(this); 919 } 920 checkDeletion()921 private boolean checkDeletion() { 922 assertTrue(isAlive(), "must be alive"); 923 assertTrue(hasNoUsages(), "cannot delete node %s because of usages: %s", this, usages()); 924 assertTrue(predecessor == null, "cannot delete node %s because of predecessor: %s", this, predecessor); 925 return true; 926 } 927 928 /** 929 * Removes this node from its graph. This node must have no {@linkplain Node#usages() usages} 930 * and no {@linkplain #predecessor() predecessor}. 931 */ safeDelete()932 public void safeDelete() { 933 assert checkDeletion(); 934 this.clearInputs(); 935 this.clearSuccessors(); 936 markDeleted(); 937 } 938 markDeleted()939 public void markDeleted() { 940 graph.unregister(this); 941 id = DELETED_ID_START - id; 942 assert isDeleted(); 943 } 944 copyWithInputs()945 public final Node copyWithInputs() { 946 return copyWithInputs(true); 947 } 948 copyWithInputs(boolean insertIntoGraph)949 public final Node copyWithInputs(boolean insertIntoGraph) { 950 Node newNode = clone(insertIntoGraph ? graph : null, WithOnlyInputEdges); 951 if (insertIntoGraph) { 952 for (Node input : inputs()) { 953 input.addUsage(newNode); 954 } 955 } 956 return newNode; 957 } 958 959 /** 960 * Must be overridden by subclasses that implement {@link Simplifiable}. The implementation in 961 * {@link Node} exists to obviate the need to cast a node before invoking 962 * {@link Simplifiable#simplify(SimplifierTool)}. 963 * 964 * @param tool 965 */ simplify(SimplifierTool tool)966 public void simplify(SimplifierTool tool) { 967 throw new UnsupportedOperationException(); 968 } 969 970 /** 971 * @param newNode the result of cloning this node or {@link Unsafe#allocateInstance(Class) raw 972 * allocating} a copy of this node 973 * @param type the type of edges to process 974 * @param edgesToCopy if {@code type} is in this set, the edges are copied otherwise they are 975 * cleared 976 */ copyOrClearEdgesForClone(Node newNode, Edges.Type type, EnumSet<Edges.Type> edgesToCopy)977 private void copyOrClearEdgesForClone(Node newNode, Edges.Type type, EnumSet<Edges.Type> edgesToCopy) { 978 if (edgesToCopy.contains(type)) { 979 getNodeClass().getEdges(type).copy(this, newNode); 980 } else { 981 // The direct edges are already null 982 getNodeClass().getEdges(type).initializeLists(newNode, this); 983 } 984 } 985 986 public static final EnumSet<Edges.Type> WithNoEdges = EnumSet.noneOf(Edges.Type.class); 987 public static final EnumSet<Edges.Type> WithAllEdges = EnumSet.allOf(Edges.Type.class); 988 public static final EnumSet<Edges.Type> WithOnlyInputEdges = EnumSet.of(Inputs); 989 public static final EnumSet<Edges.Type> WithOnlySucessorEdges = EnumSet.of(Successors); 990 991 /** 992 * Makes a copy of this node in(to) a given graph. 993 * 994 * @param into the graph in which the copy will be registered (which may be this node's graph) 995 * or null if the copy should not be registered in a graph 996 * @param edgesToCopy specifies the edges to be copied. The edges not specified in this set are 997 * initialized to their default value (i.e., {@code null} for a direct edge, an empty 998 * list for an edge list) 999 * @return the copy of this node 1000 */ clone(Graph into, EnumSet<Edges.Type> edgesToCopy)1001 final Node clone(Graph into, EnumSet<Edges.Type> edgesToCopy) { 1002 final NodeClass<? extends Node> nodeClassTmp = getNodeClass(); 1003 boolean useIntoLeafNodeCache = false; 1004 if (into != null) { 1005 if (nodeClassTmp.valueNumberable() && nodeClassTmp.isLeafNode()) { 1006 useIntoLeafNodeCache = true; 1007 Node otherNode = into.findNodeInCache(this); 1008 if (otherNode != null) { 1009 return otherNode; 1010 } 1011 } 1012 } 1013 1014 Node newNode = null; 1015 try { 1016 newNode = (Node) UNSAFE.allocateInstance(getClass()); 1017 newNode.nodeClass = nodeClassTmp; 1018 nodeClassTmp.getData().copy(this, newNode); 1019 copyOrClearEdgesForClone(newNode, Inputs, edgesToCopy); 1020 copyOrClearEdgesForClone(newNode, Successors, edgesToCopy); 1021 } catch (Exception e) { 1022 throw new GraalGraphError(e).addContext(this); 1023 } 1024 newNode.graph = into; 1025 newNode.id = INITIAL_ID; 1026 if (getNodeSourcePosition() != null && (into == null || into.trackNodeSourcePosition())) { 1027 newNode.setNodeSourcePosition(getNodeSourcePosition()); 1028 } 1029 if (into != null) { 1030 into.register(newNode); 1031 } 1032 newNode.extraUsages = NO_NODES; 1033 1034 if (into != null && useIntoLeafNodeCache) { 1035 into.putNodeIntoCache(newNode); 1036 } 1037 newNode.afterClone(this); 1038 return newNode; 1039 } 1040 afterClone(@uppressWarningsR) Node other)1041 protected void afterClone(@SuppressWarnings("unused") Node other) { 1042 } 1043 verifyInputs()1044 protected boolean verifyInputs() { 1045 for (Position pos : inputPositions()) { 1046 Node input = pos.get(this); 1047 if (input == null) { 1048 assertTrue(pos.isInputOptional(), "non-optional input %s cannot be null in %s (fix nullness or use @OptionalInput)", pos, this); 1049 } else { 1050 assertFalse(input.isDeleted(), "input was deleted %s", input); 1051 assertTrue(input.isAlive(), "input is not alive yet, i.e., it was not yet added to the graph"); 1052 assertTrue(pos.getInputType() == InputType.Unchecked || input.isAllowedUsageType(pos.getInputType()), "invalid usage type %s %s", input, pos.getInputType()); 1053 } 1054 } 1055 return true; 1056 } 1057 verify()1058 public boolean verify() { 1059 assertTrue(isAlive(), "cannot verify inactive nodes (id=%d)", id); 1060 assertTrue(graph() != null, "null graph"); 1061 verifyInputs(); 1062 if (Options.VerifyGraalGraphEdges.getValue(getOptions())) { 1063 verifyEdges(); 1064 } 1065 return true; 1066 } 1067 verifySourcePosition()1068 public boolean verifySourcePosition() { 1069 return true; 1070 } 1071 1072 /** 1073 * Perform expensive verification of inputs, usages, predecessors and successors. 1074 * 1075 * @return true 1076 */ verifyEdges()1077 public boolean verifyEdges() { 1078 for (Node input : inputs()) { 1079 assertTrue(input == null || input.usages().contains(this), "missing usage of %s in input %s", this, input); 1080 } 1081 1082 for (Node successor : successors()) { 1083 assertTrue(successor.predecessor() == this, "missing predecessor in %s (actual: %s)", successor, successor.predecessor()); 1084 assertTrue(successor.graph() == graph(), "mismatching graph in successor %s", successor); 1085 } 1086 for (Node usage : usages()) { 1087 assertFalse(usage.isDeleted(), "usage %s must never be deleted", usage); 1088 assertTrue(usage.inputs().contains(this), "missing input in usage %s", usage); 1089 boolean foundThis = false; 1090 for (Position pos : usage.inputPositions()) { 1091 if (pos.get(usage) == this) { 1092 foundThis = true; 1093 if (pos.getInputType() != InputType.Unchecked) { 1094 assertTrue(isAllowedUsageType(pos.getInputType()), "invalid input of type %s from %s to %s (%s)", pos.getInputType(), usage, this, pos.getName()); 1095 } 1096 } 1097 } 1098 assertTrue(foundThis, "missing input in usage %s", usage); 1099 } 1100 1101 if (predecessor != null) { 1102 assertFalse(predecessor.isDeleted(), "predecessor %s must never be deleted", predecessor); 1103 assertTrue(predecessor.successors().contains(this), "missing successor in predecessor %s", predecessor); 1104 } 1105 return true; 1106 } 1107 assertTrue(boolean condition, String message, Object... args)1108 public boolean assertTrue(boolean condition, String message, Object... args) { 1109 if (condition) { 1110 return true; 1111 } else { 1112 throw fail(message, args); 1113 } 1114 } 1115 assertFalse(boolean condition, String message, Object... args)1116 public boolean assertFalse(boolean condition, String message, Object... args) { 1117 if (condition) { 1118 throw fail(message, args); 1119 } else { 1120 return true; 1121 } 1122 } 1123 fail(String message, Object... args)1124 protected VerificationError fail(String message, Object... args) throws GraalGraphError { 1125 throw new VerificationError(message, args).addContext(this); 1126 } 1127 cfgPredecessors()1128 public Iterable<? extends Node> cfgPredecessors() { 1129 if (predecessor == null) { 1130 return Collections.emptySet(); 1131 } else { 1132 return Collections.singleton(predecessor); 1133 } 1134 } 1135 1136 /** 1137 * Returns an iterator that will provide all control-flow successors of this node. Normally this 1138 * will be the contents of all fields annotated with {@link Successor}, but some node classes 1139 * (like EndNode) may return different nodes. 1140 */ cfgSuccessors()1141 public Iterable<? extends Node> cfgSuccessors() { 1142 return successors(); 1143 } 1144 1145 /** 1146 * Nodes using their {@link #id} as the hash code. This works very well when nodes of the same 1147 * graph are stored in sets. It can give bad behavior when storing nodes of different graphs in 1148 * the same set. 1149 */ 1150 @Override hashCode()1151 public final int hashCode() { 1152 assert !this.isUnregistered() : "node not yet constructed"; 1153 if (this.isDeleted()) { 1154 return -id + DELETED_ID_START; 1155 } 1156 return id; 1157 } 1158 1159 /** 1160 * Do not overwrite the equality test of a node in subclasses. Equality tests must rely solely 1161 * on identity. 1162 */ 1163 1164 /** 1165 * Provides a {@link Map} of properties of this node for use in debugging (e.g., to view in the 1166 * ideal graph visualizer). 1167 */ getDebugProperties()1168 public final Map<Object, Object> getDebugProperties() { 1169 return getDebugProperties(new HashMap<>()); 1170 } 1171 1172 /** 1173 * Fills a {@link Map} with properties of this node for use in debugging (e.g., to view in the 1174 * ideal graph visualizer). Subclasses overriding this method should also fill the map using 1175 * their superclass. 1176 * 1177 * @param map 1178 */ getDebugProperties(Map<Object, Object> map)1179 public Map<Object, Object> getDebugProperties(Map<Object, Object> map) { 1180 Fields properties = getNodeClass().getData(); 1181 for (int i = 0; i < properties.getCount(); i++) { 1182 map.put(properties.getName(i), properties.get(this, i)); 1183 } 1184 NodeSourcePosition pos = getNodeSourcePosition(); 1185 if (pos != null) { 1186 map.put("nodeSourcePosition", pos); 1187 } 1188 NodeCreationStackTrace creation = getCreationPosition(); 1189 if (creation != null) { 1190 map.put("nodeCreationPosition", creation.getStrackTraceString()); 1191 } 1192 NodeInsertionStackTrace insertion = getInsertionPosition(); 1193 if (insertion != null) { 1194 map.put("nodeInsertionPosition", insertion.getStrackTraceString()); 1195 } 1196 return map; 1197 } 1198 1199 /** 1200 * This method is a shortcut for {@link #toString(Verbosity)} with {@link Verbosity#Short}. 1201 */ 1202 @Override toString()1203 public final String toString() { 1204 return toString(Verbosity.Short); 1205 } 1206 1207 /** 1208 * Creates a String representation for this node with a given {@link Verbosity}. 1209 */ toString(Verbosity verbosity)1210 public String toString(Verbosity verbosity) { 1211 switch (verbosity) { 1212 case Id: 1213 return Integer.toString(id); 1214 case Name: 1215 return getNodeClass().shortName(); 1216 case Short: 1217 return toString(Verbosity.Id) + "|" + toString(Verbosity.Name); 1218 case Long: 1219 return toString(Verbosity.Short); 1220 case Debugger: 1221 case All: { 1222 StringBuilder str = new StringBuilder(); 1223 str.append(toString(Verbosity.Short)).append(" { "); 1224 for (Map.Entry<Object, Object> entry : getDebugProperties().entrySet()) { 1225 str.append(entry.getKey()).append("=").append(entry.getValue()).append(", "); 1226 } 1227 str.append(" }"); 1228 return str.toString(); 1229 } 1230 default: 1231 throw new RuntimeException("unknown verbosity: " + verbosity); 1232 } 1233 } 1234 1235 @Deprecated getId()1236 public int getId() { 1237 return id; 1238 } 1239 1240 @Override formatTo(Formatter formatter, int flags, int width, int precision)1241 public void formatTo(Formatter formatter, int flags, int width, int precision) { 1242 if ((flags & FormattableFlags.ALTERNATE) == FormattableFlags.ALTERNATE) { 1243 formatter.format("%s", toString(Verbosity.Id)); 1244 } else if ((flags & FormattableFlags.UPPERCASE) == FormattableFlags.UPPERCASE) { 1245 // Use All here since Long is only slightly longer than Short. 1246 formatter.format("%s", toString(Verbosity.All)); 1247 } else { 1248 formatter.format("%s", toString(Verbosity.Short)); 1249 } 1250 1251 boolean neighborsAlternate = ((flags & FormattableFlags.LEFT_JUSTIFY) == FormattableFlags.LEFT_JUSTIFY); 1252 int neighborsFlags = (neighborsAlternate ? FormattableFlags.ALTERNATE | FormattableFlags.LEFT_JUSTIFY : 0); 1253 if (width > 0) { 1254 if (this.predecessor != null) { 1255 formatter.format(" pred={"); 1256 this.predecessor.formatTo(formatter, neighborsFlags, width - 1, 0); 1257 formatter.format("}"); 1258 } 1259 1260 for (Position position : this.inputPositions()) { 1261 Node input = position.get(this); 1262 if (input != null) { 1263 formatter.format(" "); 1264 formatter.format(position.getName()); 1265 formatter.format("={"); 1266 input.formatTo(formatter, neighborsFlags, width - 1, 0); 1267 formatter.format("}"); 1268 } 1269 } 1270 } 1271 1272 if (precision > 0) { 1273 if (!hasNoUsages()) { 1274 formatter.format(" usages={"); 1275 int z = 0; 1276 for (Node usage : usages()) { 1277 if (z != 0) { 1278 formatter.format(", "); 1279 } 1280 usage.formatTo(formatter, neighborsFlags, 0, precision - 1); 1281 ++z; 1282 } 1283 formatter.format("}"); 1284 } 1285 1286 for (Position position : this.successorPositions()) { 1287 Node successor = position.get(this); 1288 if (successor != null) { 1289 formatter.format(" "); 1290 formatter.format(position.getName()); 1291 formatter.format("={"); 1292 successor.formatTo(formatter, neighborsFlags, 0, precision - 1); 1293 formatter.format("}"); 1294 } 1295 } 1296 } 1297 } 1298 1299 /** 1300 * Determines if this node's {@link NodeClass#getData() data} fields are equal to the data 1301 * fields of another node of the same type. Primitive fields are compared by value and 1302 * non-primitive fields are compared by {@link Objects#equals(Object, Object)}. 1303 * 1304 * The result of this method undefined if {@code other.getClass() != this.getClass()}. 1305 * 1306 * @param other a node of exactly the same type as this node 1307 * @return true if the data fields of this object and {@code other} are equal 1308 */ valueEquals(Node other)1309 public boolean valueEquals(Node other) { 1310 return getNodeClass().dataEquals(this, other); 1311 } 1312 1313 /** 1314 * Determines if this node is equal to the other node while ignoring differences in 1315 * {@linkplain Successor control-flow} edges. 1316 * 1317 */ dataFlowEquals(Node other)1318 public boolean dataFlowEquals(Node other) { 1319 return this == other || nodeClass == other.getNodeClass() && this.valueEquals(other) && nodeClass.equalInputs(this, other); 1320 } 1321 pushInputs(NodeStack stack)1322 public final void pushInputs(NodeStack stack) { 1323 getNodeClass().pushInputs(this, stack); 1324 } 1325 estimatedNodeSize()1326 public NodeSize estimatedNodeSize() { 1327 return nodeClass.size(); 1328 } 1329 estimatedNodeCycles()1330 public NodeCycles estimatedNodeCycles() { 1331 return nodeClass.cycles(); 1332 } 1333 1334 } 1335