1 /* 2 * Copyright (c) 2015, 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.nodes; 26 27 import java.util.ArrayDeque; 28 import java.util.Deque; 29 import java.util.Iterator; 30 import java.util.Objects; 31 32 import jdk.internal.vm.compiler.collections.Pair; 33 import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor; 34 import org.graalvm.compiler.core.common.Fields; 35 import org.graalvm.compiler.core.common.util.FrequencyEncoder; 36 import org.graalvm.compiler.core.common.util.TypeConversion; 37 import org.graalvm.compiler.core.common.util.TypeReader; 38 import org.graalvm.compiler.core.common.util.TypeWriter; 39 import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter; 40 import org.graalvm.compiler.debug.DebugContext; 41 import org.graalvm.compiler.graph.Edges; 42 import org.graalvm.compiler.graph.Node; 43 import org.graalvm.compiler.graph.NodeClass; 44 import org.graalvm.compiler.graph.NodeList; 45 import org.graalvm.compiler.graph.NodeMap; 46 import org.graalvm.compiler.graph.iterators.NodeIterable; 47 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 48 import org.graalvm.compiler.nodes.java.ExceptionObjectNode; 49 50 import jdk.vm.ci.code.Architecture; 51 52 /** 53 * Encodes a {@link StructuredGraph} to a compact byte[] array. All nodes of the graph and edges 54 * between the nodes are encoded. Primitive data fields of nodes are stored in the byte[] array. 55 * Object data fields of nodes are stored in a separate Object[] array. 56 * 57 * One encoder instance can be used to encode multiple graphs. This requires that {@link #prepare} 58 * is called for all graphs first, followed by one call to {@link #finishPrepare}. Then 59 * {@link #encode} can be called for all graphs. The {@link #getObjects() objects} and 60 * {@link #getNodeClasses() node classes} arrays do not change anymore after preparation. 61 * 62 * Multiple encoded graphs share the Object[] array, and elements of the Object[] array are 63 * de-duplicated using {@link Object#equals Object equality}. This uses the assumption and good 64 * coding practice that data objects are immutable if {@link Object#equals} is implemented. 65 * Unfortunately, this cannot be enforced. 66 * 67 * The Graal {@link NodeClass} does not have a unique id that allows class lookup from an id. 68 * Therefore, the encoded graph contains a {@link NodeClass}[] array for lookup, and type ids are 69 * encoding-local. 70 * 71 * The encoded graph has the following structure: First, all nodes and their edges are serialized. 72 * The start offset of every node is then known. The raw node data is followed by metadata, i.e., 73 * the maximum fixed node order id and a "table of contents" that lists the start offset for every 74 * node. 75 * 76 * The beginning of this metadata is the return value of {@link #encode} and stored in 77 * {@link EncodedGraph#getStartOffset()}. The order of nodes in the table of contents is the 78 * {@link NodeOrder#orderIds orderId} of a node. Note that the orderId is not the regular node id 79 * that every Graal graph node gets assigned. The orderId is computed and used just for encoding and 80 * decoding. The orderId of fixed nodes is assigned in reverse postorder. The decoder processes 81 * nodes using that order, which ensures that all predecessors of a node (including all 82 * {@link EndNode predecessors} of a {@link AbstractBeginNode block}) are decoded before the node. 83 * The order id of floating node does not matter during decoding, so floating nodes get order ids 84 * after all fixed nodes. The order id is used to encode edges between nodes 85 * 86 * Structure of an encoded node: 87 * 88 * <pre> 89 * struct Node { 90 * unsigned typeId 91 * unsigned[] inputOrderIds 92 * signed[] properties 93 * unsigned[] successorOrderIds 94 * } 95 * </pre> 96 * 97 * All numbers (unsigned and signed) are stored using a variable-length encoding as defined in 98 * {@link TypeReader} and {@link TypeWriter}. Especially orderIds are small, so the variable-length 99 * encoding is important to keep the encoding compact. 100 * 101 * The properties, successors, and inputs are written in the order as defined in 102 * {@link NodeClass#getData}, {@link NodeClass#getSuccessorEdges()}, and 103 * {@link NodeClass#getInputEdges()}. For variable-length successors and input lists, first the 104 * length is written and then the orderIds. There is a distinction between null lists (encoded as 105 * length -1) and empty lists (encoded as length 0). No reverse edges are written (predecessors, 106 * usages) since that information can be easily restored during decoding. 107 * 108 * Some nodes have additional information written after the properties, successors, and inputs: 109 * <li><item>{@link AbstractEndNode}: the orderId of the merge node and then all {@link PhiNode phi 110 * mappings} from this end to the merge node are written. <item>{@link LoopExitNode}: the orderId of 111 * all {@link ProxyNode proxy nodes} of the loop exit is written.</li> 112 */ 113 public class GraphEncoder { 114 115 /** 116 * The orderId that always represents {@code null}. 117 */ 118 public static final int NULL_ORDER_ID = 0; 119 /** 120 * The orderId of the {@link StructuredGraph#start() start node} of the encoded graph. 121 */ 122 public static final int START_NODE_ORDER_ID = 1; 123 /** 124 * The orderId of the first actual node after the {@link StructuredGraph#start() start node}. 125 */ 126 public static final int FIRST_NODE_ORDER_ID = 2; 127 128 /** 129 * The known offset between the orderId of a {@link AbstractBeginNode} and its 130 * {@link AbstractBeginNode#next() successor}. 131 */ 132 protected static final int BEGIN_NEXT_ORDER_ID_OFFSET = 1; 133 134 protected final Architecture architecture; 135 136 /** 137 * Collects all non-primitive data referenced from nodes. The encoding uses an index into an 138 * array for decoding. Because of the variable-length encoding, it is beneficial that frequently 139 * used objects have the small indices. 140 */ 141 protected final FrequencyEncoder<Object> objects; 142 /** 143 * Collects all node classes referenced in graphs. This is necessary because {@link NodeClass} 144 * currently does not have a unique id. 145 */ 146 protected final FrequencyEncoder<NodeClass<?>> nodeClasses; 147 /** The writer for the encoded graphs. */ 148 protected final UnsafeArrayTypeWriter writer; 149 150 /** The last snapshot of {@link #objects} that was retrieved. */ 151 protected Object[] objectsArray; 152 /** The last snapshot of {@link #nodeClasses} that was retrieved. */ 153 protected NodeClass<?>[] nodeClassesArray; 154 155 protected DebugContext debug; 156 157 /** 158 * Utility method that does everything necessary to encode a single graph. 159 */ 160 public static EncodedGraph encodeSingleGraph(StructuredGraph graph, Architecture architecture) { 161 GraphEncoder encoder = new GraphEncoder(architecture); 162 encoder.prepare(graph); 163 encoder.finishPrepare(); 164 int startOffset = encoder.encode(graph); 165 return new EncodedGraph(encoder.getEncoding(), startOffset, encoder.getObjects(), encoder.getNodeClasses(), graph); 166 } 167 168 public GraphEncoder(Architecture architecture) { 169 this(architecture, null); 170 } 171 172 public GraphEncoder(Architecture architecture, DebugContext debug) { 173 this.architecture = architecture; 174 this.debug = debug; 175 objects = FrequencyEncoder.createEqualityEncoder(); 176 nodeClasses = FrequencyEncoder.createIdentityEncoder(); 177 writer = UnsafeArrayTypeWriter.create(architecture.supportsUnalignedMemoryAccess()); 178 } 179 180 /** 181 * Must be invoked before {@link #finishPrepare()} and {@link #encode}. 182 */ 183 public void prepare(StructuredGraph graph) { 184 objects.addObject(graph.getGuardsStage()); 185 for (Node node : graph.getNodes()) { 186 NodeClass<? extends Node> nodeClass = node.getNodeClass(); 187 nodeClasses.addObject(nodeClass); 188 objects.addObject(node.getNodeSourcePosition()); 189 for (int i = 0; i < nodeClass.getData().getCount(); i++) { 190 if (!nodeClass.getData().getType(i).isPrimitive()) { 191 objects.addObject(nodeClass.getData().get(node, i)); 192 } 193 } 194 if (node instanceof Invoke) { 195 objects.addObject(((Invoke) node).getContextType()); 196 } 197 } 198 } 199 200 public void finishPrepare() { 201 objectsArray = objects.encodeAll(new Object[objects.getLength()]); 202 nodeClassesArray = nodeClasses.encodeAll(new NodeClass<?>[nodeClasses.getLength()]); 203 } 204 205 public Object[] getObjects() { 206 return objectsArray; 207 } 208 209 public NodeClass<?>[] getNodeClasses() { 210 return nodeClassesArray; 211 } 212 213 /** 214 * Compresses a graph to a byte array. Multiple graphs can be compressed with the same 215 * {@link GraphEncoder}. 216 * 217 * @param graph The graph to encode 218 */ 219 public int encode(StructuredGraph graph) { 220 assert objectsArray != null && nodeClassesArray != null : "finishPrepare() must be called before encode()"; 221 222 NodeOrder nodeOrder = new NodeOrder(graph); 223 int nodeCount = nodeOrder.nextOrderId; 224 assert nodeOrder.orderIds.get(graph.start()) == START_NODE_ORDER_ID; 225 assert nodeOrder.orderIds.get(graph.start().next()) == FIRST_NODE_ORDER_ID; 226 227 long[] nodeStartOffsets = new long[nodeCount]; 228 UnmodifiableMapCursor<Node, Integer> cursor = nodeOrder.orderIds.getEntries(); 229 while (cursor.advance()) { 230 Node node = cursor.getKey(); 231 Integer orderId = cursor.getValue(); 232 233 assert !(node instanceof AbstractBeginNode) || nodeOrder.orderIds.get(((AbstractBeginNode) node).next()) == orderId + BEGIN_NEXT_ORDER_ID_OFFSET; 234 assert nodeStartOffsets[orderId] == 0; 235 nodeStartOffsets[orderId] = writer.getBytesWritten(); 236 237 /* Write out the type, properties, and edges. */ 238 NodeClass<?> nodeClass = node.getNodeClass(); 239 writer.putUV(nodeClasses.getIndex(nodeClass)); 240 writeEdges(node, nodeClass.getEdges(Edges.Type.Inputs), nodeOrder); 241 writeProperties(node, nodeClass.getData()); 242 writeEdges(node, nodeClass.getEdges(Edges.Type.Successors), nodeOrder); 243 244 /* Special handling for some nodes that require additional information for decoding. */ 245 if (node instanceof AbstractEndNode) { 246 AbstractEndNode end = (AbstractEndNode) node; 247 AbstractMergeNode merge = end.merge(); 248 /* 249 * Write the orderId of the merge. The merge is not a successor in the Graal graph 250 * (only the merge has an input edge to the EndNode). 251 */ 252 writeOrderId(merge, nodeOrder); 253 254 /* 255 * Write all phi mappings (the oderId of the phi input for this EndNode, and the 256 * orderId of the phi node. 257 */ 258 writer.putUV(merge.phis().count()); 259 for (PhiNode phi : merge.phis()) { 260 writeOrderId(phi.valueAt(end), nodeOrder); 261 writeOrderId(phi, nodeOrder); 262 } 263 264 } else if (node instanceof LoopExitNode) { 265 LoopExitNode exit = (LoopExitNode) node; 266 writeOrderId(exit.stateAfter(), nodeOrder); 267 /* Write all proxy nodes of the LoopExitNode. */ 268 writer.putUV(exit.proxies().count()); 269 for (ProxyNode proxy : exit.proxies()) { 270 writeOrderId(proxy, nodeOrder); 271 } 272 273 } else if (node instanceof Invoke) { 274 Invoke invoke = (Invoke) node; 275 assert invoke.stateDuring() == null : "stateDuring is not used in high-level graphs"; 276 277 writeObjectId(invoke.getContextType()); 278 writeOrderId(invoke.callTarget(), nodeOrder); 279 writeOrderId(invoke.stateAfter(), nodeOrder); 280 writeOrderId(invoke.next(), nodeOrder); 281 if (invoke instanceof InvokeWithExceptionNode) { 282 InvokeWithExceptionNode invokeWithExcpetion = (InvokeWithExceptionNode) invoke; 283 ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExcpetion.exceptionEdge(); 284 285 writeOrderId(invokeWithExcpetion.next().next(), nodeOrder); 286 writeOrderId(invokeWithExcpetion.exceptionEdge(), nodeOrder); 287 writeOrderId(exceptionEdge.stateAfter(), nodeOrder); 288 writeOrderId(exceptionEdge.next(), nodeOrder); 289 } 290 } 291 } 292 293 /* 294 * Write out the metadata (maximum fixed node order id and the table of contents with the 295 * start offset for all nodes). 296 */ 297 int metadataStart = TypeConversion.asS4(writer.getBytesWritten()); 298 writer.putUV(nodeOrder.maxFixedNodeOrderId); 299 writer.putUV(nodeCount); 300 for (int i = 0; i < nodeCount; i++) { 301 writer.putUV(metadataStart - nodeStartOffsets[i]); 302 } 303 writeObjectId(graph.getGuardsStage()); 304 305 /* Check that the decoding of the encode graph is the same as the input. */ 306 assert verifyEncoding(graph, new EncodedGraph(getEncoding(), metadataStart, getObjects(), getNodeClasses(), graph)); 307 308 return metadataStart; 309 } 310 311 public byte[] getEncoding() { 312 return writer.toArray(new byte[TypeConversion.asS4(writer.getBytesWritten())]); 313 } 314 315 static class NodeOrder { 316 protected final NodeMap<Integer> orderIds; 317 protected int nextOrderId; 318 protected int maxFixedNodeOrderId; 319 320 NodeOrder(StructuredGraph graph) { 321 this.orderIds = new NodeMap<>(graph); 322 this.nextOrderId = START_NODE_ORDER_ID; 323 324 /* Order the fixed nodes of the graph in reverse postorder. */ 325 Deque<AbstractBeginNode> nodeQueue = new ArrayDeque<>(); 326 FixedNode current = graph.start(); 327 do { 328 add(current); 329 if (current instanceof AbstractBeginNode) { 330 add(((AbstractBeginNode) current).next()); 331 } 332 333 if (current instanceof FixedWithNextNode) { 334 current = ((FixedWithNextNode) current).next; 335 } else { 336 if (current instanceof ControlSplitNode) { 337 for (Node successor : current.successors()) { 338 if (successor != null) { 339 nodeQueue.addFirst((AbstractBeginNode) successor); 340 } 341 } 342 } else if (current instanceof EndNode) { 343 AbstractMergeNode merge = ((AbstractEndNode) current).merge(); 344 boolean allForwardEndsVisited = true; 345 for (int i = 0; i < merge.forwardEndCount(); i++) { 346 if (orderIds.get(merge.forwardEndAt(i)) == null) { 347 allForwardEndsVisited = false; 348 break; 349 } 350 } 351 if (allForwardEndsVisited) { 352 nodeQueue.add(merge); 353 } 354 } 355 current = nodeQueue.pollFirst(); 356 } 357 } while (current != null); 358 359 maxFixedNodeOrderId = nextOrderId - 1; 360 361 /* 362 * Emit all parameters consecutively at a known location (after all fixed nodes). This 363 * allows substituting parameters when inlining during decoding by pre-initializing the 364 * decoded node list. 365 * 366 * Note that not all parameters must be present (unused parameters are deleted after 367 * parsing). This leads to holes in the orderId, i.e., unused orderIds. 368 */ 369 int parameterCount = graph.method().getSignature().getParameterCount(!graph.method().isStatic()); 370 for (ParameterNode node : graph.getNodes(ParameterNode.TYPE)) { 371 assert orderIds.get(node) == null : "Parameter node must not be ordered yet"; 372 assert node.index() < parameterCount : "Parameter index out of range"; 373 orderIds.set(node, nextOrderId + node.index()); 374 } 375 nextOrderId += parameterCount; 376 377 for (Node node : graph.getNodes()) { 378 assert (node instanceof FixedNode || node instanceof ParameterNode) == (orderIds.get(node) != null) : "all fixed nodes and ParameterNodes must be ordered: " + node; 379 add(node); 380 } 381 } 382 383 private void add(Node node) { 384 if (orderIds.get(node) == null) { 385 orderIds.set(node, nextOrderId); 386 nextOrderId++; 387 } 388 } 389 } 390 391 protected void writeProperties(Node node, Fields fields) { 392 writeObjectId(node.getNodeSourcePosition()); 393 for (int idx = 0; idx < fields.getCount(); idx++) { 394 if (fields.getType(idx).isPrimitive()) { 395 long primitive = fields.getRawPrimitive(node, idx); 396 writer.putSV(primitive); 397 } else { 398 Object property = fields.get(node, idx); 399 writeObjectId(property); 400 } 401 } 402 } 403 404 protected void writeEdges(Node node, Edges edges, NodeOrder nodeOrder) { 405 if (node instanceof PhiNode) { 406 /* Edges are not needed for decoding, so we must not write it. */ 407 return; 408 } 409 410 for (int idx = 0; idx < edges.getDirectCount(); idx++) { 411 if (GraphDecoder.skipDirectEdge(node, edges, idx)) { 412 /* Edge is not needed for decoding, so we must not write it. */ 413 continue; 414 } 415 Node edge = Edges.getNode(node, edges.getOffsets(), idx); 416 writeOrderId(edge, nodeOrder); 417 } 418 419 if (node instanceof AbstractMergeNode && edges.type() == Edges.Type.Inputs) { 420 /* The ends of merge nodes are decoded manually when the ends are processed. */ 421 } else { 422 for (int idx = edges.getDirectCount(); idx < edges.getCount(); idx++) { 423 NodeList<Node> edgeList = Edges.getNodeList(node, edges.getOffsets(), idx); 424 if (edgeList == null) { 425 writer.putSV(-1); 426 } else { 427 writer.putSV(edgeList.size()); 428 for (Node edge : edgeList) { 429 writeOrderId(edge, nodeOrder); 430 } 431 } 432 } 433 } 434 435 } 436 437 protected void writeOrderId(Node node, NodeOrder nodeOrder) { 438 writer.putUV(node == null ? NULL_ORDER_ID : nodeOrder.orderIds.get(node)); 439 } 440 441 protected void writeObjectId(Object object) { 442 writer.putUV(objects.getIndex(object)); 443 } 444 445 /** 446 * Verification code that checks that the decoding of an encode graph is the same as the 447 * original graph. 448 */ 449 @SuppressWarnings("try") 450 public boolean verifyEncoding(StructuredGraph originalGraph, EncodedGraph encodedGraph) { 451 DebugContext debugContext = debug != null ? debug : originalGraph.getDebug(); 452 // @formatter:off 453 StructuredGraph decodedGraph = new StructuredGraph.Builder(originalGraph.getOptions(), debugContext, AllowAssumptions.YES). 454 method(originalGraph.method()). 455 setIsSubstitution(originalGraph.isSubstitution()). 456 trackNodeSourcePosition(originalGraph.trackNodeSourcePosition()). 457 build(); 458 // @formatter:off 459 GraphDecoder decoder = new GraphDecoder(architecture, decodedGraph); 460 decoder.decode(encodedGraph); 461 462 decodedGraph.verify(); 463 try { 464 GraphComparison.verifyGraphsEqual(originalGraph, decodedGraph); 465 } catch (Throwable ex) { 466 originalGraph.getDebug(); 467 try (DebugContext.Scope scope = debugContext.scope("GraphEncoder")) { 468 debugContext.dump(DebugContext.VERBOSE_LEVEL, originalGraph, "Original Graph"); 469 debugContext.dump(DebugContext.VERBOSE_LEVEL, decodedGraph, "Decoded Graph"); 470 } 471 throw ex; 472 } 473 return true; 474 } 475 } 476 477 class GraphComparison { 478 public static boolean verifyGraphsEqual(StructuredGraph expectedGraph, StructuredGraph actualGraph) { 479 NodeMap<Node> nodeMapping = new NodeMap<>(expectedGraph); 480 Deque<Pair<Node, Node>> workList = new ArrayDeque<>(); 481 482 pushToWorklist(expectedGraph.start(), actualGraph.start(), nodeMapping, workList); 483 while (!workList.isEmpty()) { 484 Pair<Node, Node> pair = workList.removeFirst(); 485 Node expectedNode = pair.getLeft(); 486 Node actualNode = pair.getRight(); 487 assert expectedNode.getClass() == actualNode.getClass(); 488 489 NodeClass<?> nodeClass = expectedNode.getNodeClass(); 490 assert nodeClass == actualNode.getNodeClass(); 491 492 if (expectedNode instanceof MergeNode) { 493 /* The order of the ends can be different, so ignore them. */ 494 verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, true); 495 } else if (expectedNode instanceof PhiNode) { 496 verifyPhi((PhiNode) expectedNode, (PhiNode) actualNode, nodeMapping, workList); 497 } else { 498 verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, false); 499 } 500 verifyNodesEqual(expectedNode.successors(), actualNode.successors(), nodeMapping, workList, false); 501 502 if (expectedNode instanceof LoopEndNode) { 503 LoopEndNode actualLoopEnd = (LoopEndNode) actualNode; 504 assert actualLoopEnd.loopBegin().loopEnds().snapshot().indexOf(actualLoopEnd) == actualLoopEnd.endIndex(); 505 } else { 506 for (int i = 0; i < nodeClass.getData().getCount(); i++) { 507 Object expectedProperty = nodeClass.getData().get(expectedNode, i); 508 Object actualProperty = nodeClass.getData().get(actualNode, i); 509 assert Objects.equals(expectedProperty, actualProperty); 510 } 511 } 512 513 if (expectedNode instanceof EndNode) { 514 /* Visit the merge node, which is the one and only usage of the EndNode. */ 515 assert expectedNode.usages().count() == 1; 516 assert actualNode.usages().count() == 1; 517 verifyNodesEqual(expectedNode.usages(), actualNode.usages(), nodeMapping, workList, false); 518 } 519 520 if (expectedNode instanceof AbstractEndNode) { 521 /* Visit the input values of the merge phi functions for this EndNode. */ 522 verifyPhis((AbstractEndNode) expectedNode, (AbstractEndNode) actualNode, nodeMapping, workList); 523 } 524 } 525 526 return true; 527 } 528 529 protected static void verifyPhi(PhiNode expectedPhi, PhiNode actualPhi, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) { 530 AbstractMergeNode expectedMergeNode = expectedPhi.merge(); 531 AbstractMergeNode actualMergeNode = actualPhi.merge(); 532 assert actualMergeNode == nodeMapping.get(expectedMergeNode); 533 534 for (EndNode expectedEndNode : expectedMergeNode.ends) { 535 EndNode actualEndNode = (EndNode) nodeMapping.get(expectedEndNode); 536 if (actualEndNode != null) { 537 ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode); 538 ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode); 539 verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false); 540 } 541 } 542 } 543 544 protected static void verifyPhis(AbstractEndNode expectedEndNode, AbstractEndNode actualEndNode, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) { 545 AbstractMergeNode expectedMergeNode = expectedEndNode.merge(); 546 AbstractMergeNode actualMergeNode = (AbstractMergeNode) nodeMapping.get(expectedMergeNode); 547 assert actualMergeNode != null; 548 549 for (PhiNode expectedPhi : expectedMergeNode.phis()) { 550 PhiNode actualPhi = (PhiNode) nodeMapping.get(expectedPhi); 551 if (actualPhi != null) { 552 ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode); 553 ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode); 554 verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false); 555 } 556 } 557 } 558 559 private static void verifyNodesEqual(NodeIterable<Node> expectedNodes, NodeIterable<Node> actualNodes, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList, boolean ignoreEndNode) { 560 Iterator<Node> actualIter = actualNodes.iterator(); 561 for (Node expectedNode : expectedNodes) { 562 verifyNodeEqual(expectedNode, actualIter.next(), nodeMapping, workList, ignoreEndNode); 563 } 564 assert !actualIter.hasNext(); 565 } 566 567 protected static void verifyNodeEqual(Node expectedNode, Node actualNode, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList, boolean ignoreEndNode) { 568 assert expectedNode.getClass() == actualNode.getClass(); 569 if (ignoreEndNode && expectedNode instanceof EndNode) { 570 return; 571 } 572 573 Node existing = nodeMapping.get(expectedNode); 574 if (existing != null) { 575 assert existing == actualNode; 576 } else { 577 pushToWorklist(expectedNode, actualNode, nodeMapping, workList); 578 } 579 } 580 581 protected static void pushToWorklist(Node expectedNode, Node actualNode, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) { 582 nodeMapping.set(expectedNode, actualNode); 583 if (expectedNode instanceof AbstractEndNode) { 584 /* To ensure phi nodes have been added, we handle everything before block ends. */ 585 workList.addLast(Pair.create(expectedNode, actualNode)); 586 } else { 587 workList.addFirst(Pair.create(expectedNode, actualNode)); 588 } 589 } 590 }