1 /* 2 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.nodes; 24 25 import static org.graalvm.compiler.debug.GraalError.shouldNotReachHere; 26 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; 27 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; 28 29 import java.util.ArrayDeque; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.BitSet; 33 import java.util.Deque; 34 import java.util.HashMap; 35 import java.util.Iterator; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.SortedMap; 39 import java.util.TreeMap; 40 41 import org.graalvm.compiler.common.PermanentBailoutException; 42 import org.graalvm.compiler.core.common.Fields; 43 import org.graalvm.compiler.core.common.util.TypeReader; 44 import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader; 45 import org.graalvm.compiler.debug.Debug; 46 import org.graalvm.compiler.debug.GraalError; 47 import org.graalvm.compiler.graph.Edges; 48 import org.graalvm.compiler.graph.Graph; 49 import org.graalvm.compiler.graph.Node; 50 import org.graalvm.compiler.graph.NodeBitMap; 51 import org.graalvm.compiler.graph.NodeClass; 52 import org.graalvm.compiler.graph.NodeInputList; 53 import org.graalvm.compiler.graph.NodeList; 54 import org.graalvm.compiler.graph.NodeSourcePosition; 55 import org.graalvm.compiler.graph.NodeSuccessorList; 56 import org.graalvm.compiler.graph.spi.Canonicalizable; 57 import org.graalvm.compiler.graph.spi.CanonicalizerTool; 58 import org.graalvm.compiler.nodeinfo.InputType; 59 import org.graalvm.compiler.nodeinfo.NodeInfo; 60 import org.graalvm.compiler.nodes.GraphDecoder.MethodScope; 61 import org.graalvm.compiler.nodes.GraphDecoder.ProxyPlaceholder; 62 import org.graalvm.compiler.nodes.calc.FloatingNode; 63 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode; 64 import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin.LoopExplosionKind; 65 66 import jdk.vm.ci.code.Architecture; 67 import jdk.vm.ci.meta.DeoptimizationAction; 68 import jdk.vm.ci.meta.DeoptimizationReason; 69 import jdk.vm.ci.meta.JavaConstant; 70 import jdk.vm.ci.meta.JavaKind; 71 import jdk.vm.ci.meta.PrimitiveConstant; 72 import jdk.vm.ci.meta.ResolvedJavaType; 73 74 /** 75 * Decoder for {@link EncodedGraph encoded graphs} produced by {@link GraphEncoder}. Support for 76 * loop explosion during decoding is built into this class, because it requires many interactions 77 * with the decoding process. Subclasses can provide canonicalization and simplification of nodes 78 * during decoding, as well as method inlining during decoding. 79 */ 80 public class GraphDecoder { 81 82 /** Decoding state maintained for each encoded graph. */ 83 protected class MethodScope { 84 /** The loop that contains the call. Only non-null during method inlining. */ 85 public final LoopScope callerLoopScope; 86 /** The target graph where decoded nodes are added to. */ 87 public final StructuredGraph graph; 88 /** 89 * Mark for nodes that were present before the decoding of this method started. Note that 90 * nodes that were decoded after the mark can still be part of an outer method, since 91 * floating nodes of outer methods are decoded lazily. 92 */ 93 public final Graph.Mark methodStartMark; 94 /** The encode graph that is decoded. */ 95 public final EncodedGraph encodedGraph; 96 /** Access to the encoded graph. */ 97 public final TypeReader reader; 98 /** The kind of loop explosion to be performed during decoding. */ 99 public final LoopExplosionKind loopExplosion; 100 /** A list of tasks to run before the method scope is closed. */ 101 public final List<Runnable> cleanupTasks; 102 103 /** All return nodes encountered during decoding. */ 104 public final List<ReturnNode> returnNodes; 105 /** The exception unwind node encountered during decoding, or null. */ 106 public final List<UnwindNode> unwindNodes; 107 108 /** All merges created during loop explosion. */ 109 public final NodeBitMap loopExplosionMerges; 110 /** 111 * The start of explosion, and the merge point for when irreducible loops are detected. Only 112 * used when {@link MethodScope#loopExplosion} is {@link LoopExplosionKind#MERGE_EXPLODE}. 113 */ 114 public MergeNode loopExplosionHead; 115 116 protected MethodScope(LoopScope callerLoopScope, StructuredGraph graph, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) { 117 this.callerLoopScope = callerLoopScope; 118 this.graph = graph; 119 this.methodStartMark = graph.getMark(); 120 this.encodedGraph = encodedGraph; 121 this.loopExplosion = loopExplosion; 122 this.cleanupTasks = new ArrayList<>(); 123 this.returnNodes = new ArrayList<>(); 124 this.unwindNodes = new ArrayList<>(); 125 126 if (encodedGraph != null) { 127 reader = UnsafeArrayTypeReader.create(encodedGraph.getEncoding(), encodedGraph.getStartOffset(), architecture.supportsUnalignedMemoryAccess()); 128 if (encodedGraph.nodeStartOffsets == null) { 129 int nodeCount = reader.getUVInt(); 130 long[] nodeStartOffsets = new long[nodeCount]; 131 for (int i = 0; i < nodeCount; i++) { 132 nodeStartOffsets[i] = encodedGraph.getStartOffset() - reader.getUV(); 133 } 134 encodedGraph.nodeStartOffsets = nodeStartOffsets; 135 } 136 } else { 137 reader = null; 138 } 139 140 if (loopExplosion != LoopExplosionKind.NONE) { 141 loopExplosionMerges = new NodeBitMap(graph); 142 } else { 143 loopExplosionMerges = null; 144 } 145 } 146 } 147 148 /** Decoding state maintained for each loop in the encoded graph. */ 149 protected static class LoopScope { 150 public final MethodScope methodScope; 151 public final LoopScope outer; 152 public final int loopDepth; 153 public final int loopIteration; 154 /** 155 * Upcoming loop iterations during loop explosions that have not been processed yet. Only 156 * used when {@link MethodScope#loopExplosion} is not {@link LoopExplosionKind#NONE}. 157 */ 158 public Deque<LoopScope> nextIterations; 159 /** 160 * Information about already processed loop iterations for state merging during loop 161 * explosion. Only used when {@link MethodScope#loopExplosion} is 162 * {@link LoopExplosionKind#MERGE_EXPLODE}. 163 */ 164 public final Map<LoopExplosionState, LoopExplosionState> iterationStates; 165 public final int loopBeginOrderId; 166 /** 167 * The worklist of fixed nodes to process. Since we already the correct processing order 168 * from the orderId, we just set the orderId bit in the bitset when a node is ready for 169 * processing. The lowest set bit is the next node to process. 170 */ 171 public final BitSet nodesToProcess; 172 /** Nodes that have been created, indexed by the orderId. */ 173 public final Node[] createdNodes; 174 /** 175 * Nodes that have been created in outer loop scopes and existed before starting to process 176 * this loop, indexed by the orderId. 177 */ 178 public final Node[] initialCreatedNodes; 179 180 protected LoopScope(MethodScope methodScope) { 181 this.methodScope = methodScope; 182 this.outer = null; 183 this.nextIterations = methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE_UNTIL_RETURN ? new ArrayDeque<>() : null; 184 this.loopDepth = 0; 185 this.loopIteration = 0; 186 this.iterationStates = null; 187 this.loopBeginOrderId = -1; 188 189 int nodeCount = methodScope.encodedGraph.nodeStartOffsets.length; 190 this.nodesToProcess = new BitSet(nodeCount); 191 this.initialCreatedNodes = new Node[nodeCount]; 192 this.createdNodes = new Node[nodeCount]; 193 } 194 195 protected LoopScope(MethodScope methodScope, LoopScope outer, int loopDepth, int loopIteration, int loopBeginOrderId, Node[] initialCreatedNodes, Node[] createdNodes, 196 Deque<LoopScope> nextIterations, Map<LoopExplosionState, LoopExplosionState> iterationStates) { 197 this.methodScope = methodScope; 198 this.outer = outer; 199 this.loopDepth = loopDepth; 200 this.loopIteration = loopIteration; 201 this.nextIterations = nextIterations; 202 this.iterationStates = iterationStates; 203 this.loopBeginOrderId = loopBeginOrderId; 204 this.nodesToProcess = new BitSet(initialCreatedNodes.length); 205 this.initialCreatedNodes = initialCreatedNodes; 206 this.createdNodes = Arrays.copyOf(createdNodes, createdNodes.length); 207 } 208 209 @Override 210 public String toString() { 211 return loopDepth + "," + loopIteration + (loopBeginOrderId == -1 ? "" : "#" + loopBeginOrderId); 212 } 213 } 214 215 protected static class LoopExplosionState { 216 public final FrameState state; 217 public final MergeNode merge; 218 public final int hashCode; 219 220 protected LoopExplosionState(FrameState state, MergeNode merge) { 221 this.state = state; 222 this.merge = merge; 223 224 int h = 0; 225 for (ValueNode value : state.values()) { 226 if (value == null) { 227 h = h * 31 + 1234; 228 } else { 229 h = h * 31 + ProxyPlaceholder.unwrap(value).hashCode(); 230 } 231 } 232 this.hashCode = h; 233 } 234 235 @Override 236 public boolean equals(Object obj) { 237 if (!(obj instanceof LoopExplosionState)) { 238 return false; 239 } 240 241 FrameState otherState = ((LoopExplosionState) obj).state; 242 FrameState thisState = state; 243 assert thisState.outerFrameState() == otherState.outerFrameState(); 244 245 Iterator<ValueNode> thisIter = thisState.values().iterator(); 246 Iterator<ValueNode> otherIter = otherState.values().iterator(); 247 while (thisIter.hasNext() && otherIter.hasNext()) { 248 ValueNode thisValue = ProxyPlaceholder.unwrap(thisIter.next()); 249 ValueNode otherValue = ProxyPlaceholder.unwrap(otherIter.next()); 250 if (thisValue != otherValue) { 251 return false; 252 } 253 } 254 return thisIter.hasNext() == otherIter.hasNext(); 255 } 256 257 @Override 258 public int hashCode() { 259 return hashCode; 260 } 261 } 262 263 /** 264 * Additional information encoded for {@link Invoke} nodes to allow method inlining without 265 * decoding the frame state and successors beforehand. 266 */ 267 protected static class InvokeData { 268 public final Invoke invoke; 269 public final ResolvedJavaType contextType; 270 public final int invokeOrderId; 271 public final int callTargetOrderId; 272 public final int stateAfterOrderId; 273 public final int nextOrderId; 274 275 public final int nextNextOrderId; 276 public final int exceptionOrderId; 277 public final int exceptionStateOrderId; 278 public final int exceptionNextOrderId; 279 public JavaConstant constantReceiver; 280 281 protected InvokeData(Invoke invoke, ResolvedJavaType contextType, int invokeOrderId, int callTargetOrderId, int stateAfterOrderId, int nextOrderId, int nextNextOrderId, int exceptionOrderId, 282 int exceptionStateOrderId, int exceptionNextOrderId) { 283 this.invoke = invoke; 284 this.contextType = contextType; 285 this.invokeOrderId = invokeOrderId; 286 this.callTargetOrderId = callTargetOrderId; 287 this.stateAfterOrderId = stateAfterOrderId; 288 this.nextOrderId = nextOrderId; 289 this.nextNextOrderId = nextNextOrderId; 290 this.exceptionOrderId = exceptionOrderId; 291 this.exceptionStateOrderId = exceptionStateOrderId; 292 this.exceptionNextOrderId = exceptionNextOrderId; 293 } 294 } 295 296 /** 297 * A node that is created during {@link LoopExplosionKind#MERGE_EXPLODE loop explosion} that can 298 * later be replaced by a ProxyNode if {@link LoopDetector loop detection} finds out that the 299 * value is defined in the loop, but used outside the loop. 300 */ 301 @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED) 302 protected static final class ProxyPlaceholder extends FloatingNode implements Canonicalizable { 303 public static final NodeClass<ProxyPlaceholder> TYPE = NodeClass.create(ProxyPlaceholder.class); 304 305 @Input ValueNode value; 306 @Input(InputType.Unchecked) Node proxyPoint; 307 308 public ProxyPlaceholder(ValueNode value, MergeNode proxyPoint) { 309 super(TYPE, value.stamp()); 310 this.value = value; 311 this.proxyPoint = proxyPoint; 312 } 313 314 void setValue(ValueNode value) { 315 updateUsages(this.value, value); 316 this.value = value; 317 } 318 319 @Override 320 public Node canonical(CanonicalizerTool tool) { 321 if (tool.allUsagesAvailable()) { 322 /* The node is always unnecessary after graph decoding. */ 323 return value; 324 } else { 325 return this; 326 } 327 } 328 329 public static ValueNode unwrap(ValueNode value) { 330 ValueNode result = value; 331 while (result instanceof ProxyPlaceholder) { 332 result = ((ProxyPlaceholder) result).value; 333 } 334 return result; 335 } 336 } 337 338 protected final Architecture architecture; 339 340 public GraphDecoder(Architecture architecture) { 341 this.architecture = architecture; 342 } 343 344 @SuppressWarnings("try") 345 public final void decode(StructuredGraph graph, EncodedGraph encodedGraph) { 346 try (Debug.Scope scope = Debug.scope("GraphDecoder", graph)) { 347 MethodScope methodScope = new MethodScope(null, graph, encodedGraph, LoopExplosionKind.NONE); 348 decode(createInitialLoopScope(methodScope, null)); 349 cleanupGraph(methodScope); 350 assert methodScope.graph.verify(); 351 } catch (Throwable ex) { 352 Debug.handle(ex); 353 } 354 } 355 356 protected final LoopScope createInitialLoopScope(MethodScope methodScope, FixedWithNextNode startNode) { 357 LoopScope loopScope = new LoopScope(methodScope); 358 FixedNode firstNode; 359 if (startNode != null) { 360 /* 361 * The start node of a graph can be referenced as the guard for a GuardedNode. We 362 * register the previous block node, so that such guards are correctly anchored when 363 * doing inlining during graph decoding. 364 */ 365 registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, AbstractBeginNode.prevBegin(startNode), false, false); 366 367 firstNode = makeStubNode(methodScope, loopScope, GraphEncoder.FIRST_NODE_ORDER_ID); 368 startNode.setNext(firstNode); 369 loopScope.nodesToProcess.set(GraphEncoder.FIRST_NODE_ORDER_ID); 370 } else { 371 firstNode = methodScope.graph.start(); 372 registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, firstNode, false, false); 373 loopScope.nodesToProcess.set(GraphEncoder.START_NODE_ORDER_ID); 374 } 375 376 if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) { 377 methodScope.cleanupTasks.add(new LoopDetector(methodScope, startNode)); 378 } 379 return loopScope; 380 } 381 382 protected final void decode(LoopScope initialLoopScope) { 383 LoopScope loopScope = initialLoopScope; 384 /* Process inlined methods. */ 385 while (loopScope != null) { 386 MethodScope methodScope = loopScope.methodScope; 387 388 /* Process loops of method. */ 389 while (loopScope != null) { 390 391 /* Process nodes of loop. */ 392 while (!loopScope.nodesToProcess.isEmpty()) { 393 loopScope = processNextNode(methodScope, loopScope); 394 methodScope = loopScope.methodScope; 395 /* 396 * We can have entered a new loop, and we can have entered a new inlined method. 397 */ 398 } 399 400 /* Finished with a loop. */ 401 if (loopScope.nextIterations != null && !loopScope.nextIterations.isEmpty()) { 402 /* Loop explosion: process the loop iteration. */ 403 assert loopScope.nextIterations.peekFirst().loopIteration == loopScope.loopIteration + 1; 404 loopScope = loopScope.nextIterations.removeFirst(); 405 } else { 406 propagateCreatedNodes(loopScope); 407 loopScope = loopScope.outer; 408 } 409 } 410 411 /* 412 * Finished with an inlined method. Perform all registered end-of-method cleanup tasks 413 * and continue with loop that contained the call. 414 */ 415 for (Runnable task : methodScope.cleanupTasks) { 416 task.run(); 417 } 418 loopScope = methodScope.callerLoopScope; 419 } 420 } 421 422 private static void propagateCreatedNodes(LoopScope loopScope) { 423 if (loopScope.outer == null) { 424 return; 425 } 426 427 /* Register nodes that were created while decoding the loop to the outside scope. */ 428 for (int i = 0; i < loopScope.createdNodes.length; i++) { 429 if (loopScope.outer.createdNodes[i] == null) { 430 loopScope.outer.createdNodes[i] = loopScope.createdNodes[i]; 431 } 432 } 433 } 434 435 protected LoopScope processNextNode(MethodScope methodScope, LoopScope loopScope) { 436 int nodeOrderId = loopScope.nodesToProcess.nextSetBit(0); 437 loopScope.nodesToProcess.clear(nodeOrderId); 438 439 FixedNode node = (FixedNode) lookupNode(loopScope, nodeOrderId); 440 if (node.isDeleted()) { 441 return loopScope; 442 } 443 444 if ((node instanceof MergeNode || 445 (node instanceof LoopBeginNode && (methodScope.loopExplosion == LoopExplosionKind.FULL_UNROLL || methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE || 446 methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE_UNTIL_RETURN))) && 447 ((AbstractMergeNode) node).forwardEndCount() == 1) { 448 AbstractMergeNode merge = (AbstractMergeNode) node; 449 EndNode singleEnd = merge.forwardEndAt(0); 450 451 /* Nodes that would use this merge as the guard need to use the previous block. */ 452 registerNode(loopScope, nodeOrderId, AbstractBeginNode.prevBegin(singleEnd), true, false); 453 454 FixedNode next = makeStubNode(methodScope, loopScope, nodeOrderId + GraphEncoder.BEGIN_NEXT_ORDER_ID_OFFSET); 455 singleEnd.replaceAtPredecessor(next); 456 457 merge.safeDelete(); 458 singleEnd.safeDelete(); 459 return loopScope; 460 } 461 462 LoopScope successorAddScope = loopScope; 463 boolean updatePredecessors = true; 464 if (node instanceof LoopExitNode) { 465 if (methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE_UNTIL_RETURN || (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE && loopScope.loopDepth > 1)) { 466 /* 467 * We do not want to merge loop exits of inner loops. Instead, we want to keep 468 * exploding the outer loop separately for every loop exit and then merge the outer 469 * loop. Therefore, we create a new LoopScope of the outer loop for every loop exit 470 * of the inner loop. 471 */ 472 LoopScope outerScope = loopScope.outer; 473 int nextIterationNumber = outerScope.nextIterations.isEmpty() ? outerScope.loopIteration + 1 : outerScope.nextIterations.getLast().loopIteration + 1; 474 successorAddScope = new LoopScope(methodScope, outerScope.outer, outerScope.loopDepth, nextIterationNumber, outerScope.loopBeginOrderId, outerScope.initialCreatedNodes, 475 loopScope.initialCreatedNodes, outerScope.nextIterations, outerScope.iterationStates); 476 checkLoopExplosionIteration(methodScope, successorAddScope); 477 478 /* 479 * Nodes that are still unprocessed in the outer scope might be merge nodes that are 480 * also reachable from the new exploded scope. Clearing them ensures that we do not 481 * merge, but instead keep exploding. 482 */ 483 for (int id = outerScope.nodesToProcess.nextSetBit(0); id >= 0; id = outerScope.nodesToProcess.nextSetBit(id + 1)) { 484 successorAddScope.createdNodes[id] = null; 485 } 486 487 outerScope.nextIterations.addLast(successorAddScope); 488 } else { 489 successorAddScope = loopScope.outer; 490 } 491 updatePredecessors = methodScope.loopExplosion == LoopExplosionKind.NONE; 492 } 493 494 methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); 495 int typeId = methodScope.reader.getUVInt(); 496 assert node.getNodeClass() == methodScope.encodedGraph.getNodeClasses()[typeId]; 497 readProperties(methodScope, node); 498 makeSuccessorStubs(methodScope, successorAddScope, node, updatePredecessors); 499 makeInputNodes(methodScope, loopScope, node, true); 500 501 LoopScope resultScope = loopScope; 502 if (node instanceof LoopBeginNode) { 503 if (methodScope.loopExplosion != LoopExplosionKind.NONE) { 504 handleLoopExplosionBegin(methodScope, loopScope, (LoopBeginNode) node); 505 } 506 507 } else if (node instanceof LoopExitNode) { 508 if (methodScope.loopExplosion != LoopExplosionKind.NONE) { 509 handleLoopExplosionProxyNodes(methodScope, loopScope, successorAddScope, (LoopExitNode) node, nodeOrderId); 510 } else { 511 handleProxyNodes(methodScope, loopScope, (LoopExitNode) node); 512 } 513 514 } else if (node instanceof MergeNode) { 515 handleMergeNode(((MergeNode) node)); 516 517 } else if (node instanceof AbstractEndNode) { 518 LoopScope phiInputScope = loopScope; 519 LoopScope phiNodeScope = loopScope; 520 521 if (methodScope.loopExplosion != LoopExplosionKind.NONE && node instanceof LoopEndNode) { 522 node = handleLoopExplosionEnd(methodScope, loopScope, (LoopEndNode) node); 523 phiNodeScope = loopScope.nextIterations.getLast(); 524 } 525 526 int mergeOrderId = readOrderId(methodScope); 527 AbstractMergeNode merge = (AbstractMergeNode) lookupNode(phiNodeScope, mergeOrderId); 528 if (merge == null) { 529 merge = (AbstractMergeNode) makeStubNode(methodScope, phiNodeScope, mergeOrderId); 530 531 if (merge instanceof LoopBeginNode) { 532 assert phiNodeScope == phiInputScope && phiNodeScope == loopScope; 533 resultScope = new LoopScope(methodScope, loopScope, loopScope.loopDepth + 1, 0, mergeOrderId, 534 Arrays.copyOf(loopScope.createdNodes, loopScope.createdNodes.length), loopScope.createdNodes, // 535 methodScope.loopExplosion != LoopExplosionKind.NONE ? new ArrayDeque<>() : null, // 536 methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE ? new HashMap<>() : null); 537 phiInputScope = resultScope; 538 phiNodeScope = resultScope; 539 540 registerNode(loopScope, mergeOrderId, null, true, true); 541 loopScope.nodesToProcess.clear(mergeOrderId); 542 resultScope.nodesToProcess.set(mergeOrderId); 543 } 544 } 545 546 handlePhiFunctions(methodScope, phiInputScope, phiNodeScope, (AbstractEndNode) node, merge); 547 548 } else if (node instanceof Invoke) { 549 InvokeData invokeData = readInvokeData(methodScope, nodeOrderId, (Invoke) node); 550 resultScope = handleInvoke(methodScope, loopScope, invokeData); 551 552 } else if (node instanceof ReturnNode) { 553 methodScope.returnNodes.add((ReturnNode) node); 554 } else if (node instanceof UnwindNode) { 555 methodScope.unwindNodes.add((UnwindNode) node); 556 557 } else { 558 handleFixedNode(methodScope, loopScope, nodeOrderId, node); 559 } 560 561 return resultScope; 562 } 563 564 private InvokeData readInvokeData(MethodScope methodScope, int invokeOrderId, Invoke invoke) { 565 ResolvedJavaType contextType = (ResolvedJavaType) readObject(methodScope); 566 int callTargetOrderId = readOrderId(methodScope); 567 int stateAfterOrderId = readOrderId(methodScope); 568 int nextOrderId = readOrderId(methodScope); 569 570 if (invoke instanceof InvokeWithExceptionNode) { 571 int nextNextOrderId = readOrderId(methodScope); 572 int exceptionOrderId = readOrderId(methodScope); 573 int exceptionStateOrderId = readOrderId(methodScope); 574 int exceptionNextOrderId = readOrderId(methodScope); 575 return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, nextNextOrderId, exceptionOrderId, exceptionStateOrderId, 576 exceptionNextOrderId); 577 } else { 578 return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, -1, -1, -1, -1); 579 } 580 } 581 582 /** 583 * {@link Invoke} nodes do not have the {@link CallTargetNode}, {@link FrameState}, and 584 * successors encoded. Instead, this information is provided separately to allow method inlining 585 * without decoding and adding them to the graph upfront. For non-inlined methods, this method 586 * restores the normal state. Subclasses can override it to perform method inlining. 587 * 588 * The return value is the loop scope where decoding should continue. When method inlining 589 * should be performed, the returned loop scope must be a new loop scope for the inlined method. 590 * Without inlining, the original loop scope must be returned. 591 */ 592 protected LoopScope handleInvoke(MethodScope methodScope, LoopScope loopScope, InvokeData invokeData) { 593 assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke"; 594 CallTargetNode callTarget = (CallTargetNode) ensureNodeCreated(methodScope, loopScope, invokeData.callTargetOrderId); 595 if (invokeData.invoke instanceof InvokeWithExceptionNode) { 596 ((InvokeWithExceptionNode) invokeData.invoke).setCallTarget(callTarget); 597 } else { 598 ((InvokeNode) invokeData.invoke).setCallTarget(callTarget); 599 } 600 601 assert invokeData.invoke.stateAfter() == null && invokeData.invoke.stateDuring() == null : "FrameState edges are ignored during decoding of Invoke"; 602 invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId)); 603 604 invokeData.invoke.setNext(makeStubNode(methodScope, loopScope, invokeData.nextOrderId)); 605 if (invokeData.invoke instanceof InvokeWithExceptionNode) { 606 ((InvokeWithExceptionNode) invokeData.invoke).setExceptionEdge((AbstractBeginNode) makeStubNode(methodScope, loopScope, invokeData.exceptionOrderId)); 607 } 608 return loopScope; 609 } 610 611 /** 612 * Hook for subclasses to perform simplifications for a non-loop-header control flow merge. 613 * 614 * @param merge The control flow merge. 615 */ 616 protected void handleMergeNode(MergeNode merge) { 617 } 618 619 protected void handleLoopExplosionBegin(MethodScope methodScope, LoopScope loopScope, LoopBeginNode loopBegin) { 620 checkLoopExplosionIteration(methodScope, loopScope); 621 622 List<EndNode> predecessors = loopBegin.forwardEnds().snapshot(); 623 FixedNode successor = loopBegin.next(); 624 FrameState frameState = loopBegin.stateAfter(); 625 626 if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) { 627 LoopExplosionState queryState = new LoopExplosionState(frameState, null); 628 LoopExplosionState existingState = loopScope.iterationStates.get(queryState); 629 if (existingState != null) { 630 loopBegin.replaceAtUsagesAndDelete(existingState.merge); 631 successor.safeDelete(); 632 for (EndNode predecessor : predecessors) { 633 existingState.merge.addForwardEnd(predecessor); 634 } 635 return; 636 } 637 } 638 639 MergeNode merge = methodScope.graph.add(new MergeNode()); 640 methodScope.loopExplosionMerges.markAndGrow(merge); 641 642 if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) { 643 if (loopScope.iterationStates.size() == 0 && loopScope.loopDepth == 1) { 644 if (methodScope.loopExplosionHead != null) { 645 throw new PermanentBailoutException("Graal implementation restriction: Method with %s loop explosion must not have more than one top-level loop", LoopExplosionKind.MERGE_EXPLODE); 646 } 647 methodScope.loopExplosionHead = merge; 648 } 649 650 List<ValueNode> newFrameStateValues = new ArrayList<>(); 651 for (ValueNode frameStateValue : frameState.values) { 652 if (frameStateValue == null || frameStateValue.isConstant() || !methodScope.graph.isNew(methodScope.methodStartMark, frameStateValue)) { 653 newFrameStateValues.add(frameStateValue); 654 655 } else { 656 ProxyPlaceholder newFrameStateValue = methodScope.graph.unique(new ProxyPlaceholder(frameStateValue, merge)); 657 newFrameStateValues.add(newFrameStateValue); 658 659 /* 660 * We do not have the orderID of the value anymore, so we need to search through 661 * the complete list of nodes to find a match. 662 */ 663 for (int i = 0; i < loopScope.createdNodes.length; i++) { 664 if (loopScope.createdNodes[i] == frameStateValue) { 665 loopScope.createdNodes[i] = newFrameStateValue; 666 } 667 if (loopScope.initialCreatedNodes[i] == frameStateValue) { 668 loopScope.initialCreatedNodes[i] = newFrameStateValue; 669 } 670 } 671 } 672 } 673 674 FrameState newFrameState = methodScope.graph.add(new FrameState(frameState.outerFrameState(), frameState.getCode(), frameState.bci, newFrameStateValues, frameState.localsSize(), 675 frameState.stackSize(), frameState.rethrowException(), frameState.duringCall(), frameState.monitorIds(), frameState.virtualObjectMappings())); 676 677 frameState.replaceAtUsages(newFrameState); 678 frameState.safeDelete(); 679 frameState = newFrameState; 680 } 681 682 loopBegin.replaceAtUsagesAndDelete(merge); 683 merge.setStateAfter(frameState); 684 merge.setNext(successor); 685 for (EndNode predecessor : predecessors) { 686 merge.addForwardEnd(predecessor); 687 } 688 689 if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) { 690 LoopExplosionState explosionState = new LoopExplosionState(frameState, merge); 691 loopScope.iterationStates.put(explosionState, explosionState); 692 } 693 } 694 695 /** 696 * Hook for subclasses. 697 * 698 * @param methodScope The current method. 699 * @param loopScope The current loop. 700 */ 701 protected void checkLoopExplosionIteration(MethodScope methodScope, LoopScope loopScope) { 702 throw shouldNotReachHere("when subclass uses loop explosion, it needs to implement this method"); 703 } 704 705 protected FixedNode handleLoopExplosionEnd(MethodScope methodScope, LoopScope loopScope, LoopEndNode loopEnd) { 706 EndNode replacementNode = methodScope.graph.add(new EndNode()); 707 loopEnd.replaceAtPredecessor(replacementNode); 708 loopEnd.safeDelete(); 709 710 assert methodScope.loopExplosion != LoopExplosionKind.NONE; 711 if (methodScope.loopExplosion != LoopExplosionKind.FULL_UNROLL || loopScope.nextIterations.isEmpty()) { 712 int nextIterationNumber = loopScope.nextIterations.isEmpty() ? loopScope.loopIteration + 1 : loopScope.nextIterations.getLast().loopIteration + 1; 713 LoopScope nextIterationScope = new LoopScope(methodScope, loopScope.outer, loopScope.loopDepth, nextIterationNumber, loopScope.loopBeginOrderId, loopScope.initialCreatedNodes, 714 loopScope.initialCreatedNodes, loopScope.nextIterations, loopScope.iterationStates); 715 checkLoopExplosionIteration(methodScope, nextIterationScope); 716 loopScope.nextIterations.addLast(nextIterationScope); 717 registerNode(nextIterationScope, loopScope.loopBeginOrderId, null, true, true); 718 makeStubNode(methodScope, nextIterationScope, loopScope.loopBeginOrderId); 719 } 720 return replacementNode; 721 } 722 723 /** 724 * Hook for subclasses. 725 * 726 * @param methodScope The current method. 727 * @param loopScope The current loop. 728 * @param nodeOrderId The orderId of the node. 729 * @param node The node to be simplified. 730 */ 731 protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) { 732 } 733 734 protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit) { 735 assert loopExit.stateAfter() == null; 736 int stateAfterOrderId = readOrderId(methodScope); 737 loopExit.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, stateAfterOrderId)); 738 739 int numProxies = methodScope.reader.getUVInt(); 740 for (int i = 0; i < numProxies; i++) { 741 int proxyOrderId = readOrderId(methodScope); 742 ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId); 743 /* 744 * The ProxyNode transports a value from the loop to the outer scope. We therefore 745 * register it in the outer scope. 746 */ 747 registerNode(loopScope.outer, proxyOrderId, proxy, false, false); 748 } 749 } 750 751 protected void handleLoopExplosionProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopScope outerScope, LoopExitNode loopExit, int loopExitOrderId) { 752 assert loopExit.stateAfter() == null; 753 int stateAfterOrderId = readOrderId(methodScope); 754 755 BeginNode begin = methodScope.graph.add(new BeginNode()); 756 757 FixedNode loopExitSuccessor = loopExit.next(); 758 loopExit.replaceAtPredecessor(begin); 759 760 MergeNode loopExitPlaceholder = null; 761 if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE && loopScope.loopDepth == 1) { 762 /* 763 * This exit might end up as a loop exit of a loop detected after partial evaluation. We 764 * need to be able to create a FrameState and the necessary proxy nodes in this case. 765 */ 766 loopExitPlaceholder = methodScope.graph.add(new MergeNode()); 767 methodScope.loopExplosionMerges.markAndGrow(loopExitPlaceholder); 768 769 EndNode end = methodScope.graph.add(new EndNode()); 770 begin.setNext(end); 771 loopExitPlaceholder.addForwardEnd(end); 772 773 begin = methodScope.graph.add(new BeginNode()); 774 loopExitPlaceholder.setNext(begin); 775 } 776 777 /* 778 * In the original graph, the loop exit is not a merge node. Multiple exploded loop 779 * iterations now take the same loop exit, so we have to introduce a new merge node to 780 * handle the merge. 781 */ 782 MergeNode merge = null; 783 Node existingExit = lookupNode(outerScope, loopExitOrderId); 784 if (existingExit == null) { 785 /* First loop iteration that exits. No merge necessary yet. */ 786 registerNode(outerScope, loopExitOrderId, begin, false, false); 787 begin.setNext(loopExitSuccessor); 788 789 } else if (existingExit instanceof BeginNode) { 790 /* Second loop iteration that exits. Create the merge. */ 791 merge = methodScope.graph.add(new MergeNode()); 792 registerNode(outerScope, loopExitOrderId, merge, true, false); 793 /* Add the first iteration. */ 794 EndNode firstEnd = methodScope.graph.add(new EndNode()); 795 ((BeginNode) existingExit).setNext(firstEnd); 796 merge.addForwardEnd(firstEnd); 797 merge.setNext(loopExitSuccessor); 798 799 } else { 800 /* Subsequent loop iteration. Merge already created. */ 801 merge = (MergeNode) existingExit; 802 } 803 804 if (merge != null) { 805 EndNode end = methodScope.graph.add(new EndNode()); 806 begin.setNext(end); 807 merge.addForwardEnd(end); 808 } 809 810 /* 811 * Possibly create phi nodes for the original proxy nodes that flow out of the loop. Note 812 * that we definitely do not need a proxy node itself anymore, since the loop was exploded 813 * and is no longer present. 814 */ 815 int numProxies = methodScope.reader.getUVInt(); 816 boolean phiCreated = false; 817 for (int i = 0; i < numProxies; i++) { 818 int proxyOrderId = readOrderId(methodScope); 819 ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId); 820 ValueNode phiInput = proxy.value(); 821 822 if (loopExitPlaceholder != null) { 823 if (!phiInput.isConstant()) { 824 phiInput = methodScope.graph.unique(new ProxyPlaceholder(phiInput, loopExitPlaceholder)); 825 } 826 registerNode(loopScope, proxyOrderId, phiInput, true, false); 827 } 828 829 ValueNode replacement; 830 ValueNode existing = (ValueNode) outerScope.createdNodes[proxyOrderId]; 831 if (existing == null || existing == phiInput) { 832 /* 833 * We are at the first loop exit, or the proxy carries the same value for all exits. 834 * We do not need a phi node yet. 835 */ 836 registerNode(outerScope, proxyOrderId, phiInput, true, false); 837 replacement = phiInput; 838 839 } else if (!merge.isPhiAtMerge(existing)) { 840 /* Now we have two different values, so we need to create a phi node. */ 841 PhiNode phi = methodScope.graph.addWithoutUnique(new ValuePhiNode(proxy.stamp(), merge)); 842 /* Add the inputs from all previous exits. */ 843 for (int j = 0; j < merge.phiPredecessorCount() - 1; j++) { 844 phi.addInput(existing); 845 } 846 /* Add the input from this exit. */ 847 phi.addInput(phiInput); 848 registerNode(outerScope, proxyOrderId, phi, true, false); 849 replacement = phi; 850 phiCreated = true; 851 852 } else { 853 /* Phi node has been created before, so just add the new input. */ 854 PhiNode phi = (PhiNode) existing; 855 phi.addInput(phiInput); 856 replacement = phi; 857 } 858 859 proxy.replaceAtUsagesAndDelete(replacement); 860 } 861 862 if (loopExitPlaceholder != null) { 863 registerNode(loopScope, stateAfterOrderId, null, true, true); 864 loopExitPlaceholder.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, stateAfterOrderId)); 865 } 866 867 if (merge != null && (merge.stateAfter() == null || phiCreated)) { 868 FrameState oldStateAfter = merge.stateAfter(); 869 registerNode(outerScope, stateAfterOrderId, null, true, true); 870 merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, outerScope, stateAfterOrderId)); 871 if (oldStateAfter != null) { 872 oldStateAfter.safeDelete(); 873 } 874 } 875 loopExit.safeDelete(); 876 assert loopExitSuccessor.predecessor() == null; 877 if (merge != null) { 878 merge.getNodeClass().getSuccessorEdges().update(merge, null, loopExitSuccessor); 879 } else { 880 begin.getNodeClass().getSuccessorEdges().update(begin, null, loopExitSuccessor); 881 } 882 } 883 884 protected void handlePhiFunctions(MethodScope methodScope, LoopScope phiInputScope, LoopScope phiNodeScope, AbstractEndNode end, AbstractMergeNode merge) { 885 886 if (end instanceof LoopEndNode) { 887 /* 888 * Fix the loop end index and the number of loop ends. When we do canonicalization 889 * during decoding, we can end up with fewer ends than the encoded graph had. And the 890 * order of loop ends can be different. 891 */ 892 int numEnds = ((LoopBeginNode) merge).loopEnds().count(); 893 ((LoopBeginNode) merge).nextEndIndex = numEnds; 894 ((LoopEndNode) end).endIndex = numEnds - 1; 895 896 } else { 897 if (merge.ends == null) { 898 merge.ends = new NodeInputList<>(merge); 899 } 900 merge.addForwardEnd((EndNode) end); 901 } 902 903 /* 904 * We create most phi functions lazily. Canonicalization and simplification during decoding 905 * can lead to dead branches that are not decoded, so we might not need all phi functions 906 * that the original graph contained. Since we process all predecessors before actually 907 * processing the merge node, we have the final phi function when processing the merge node. 908 * The only exception are loop headers of non-exploded loops: since backward branches are 909 * not processed yet when processing the loop body, we need to create all phi functions 910 * upfront. 911 */ 912 boolean lazyPhi = allowLazyPhis() && (!(merge instanceof LoopBeginNode) || methodScope.loopExplosion != LoopExplosionKind.NONE); 913 int numPhis = methodScope.reader.getUVInt(); 914 for (int i = 0; i < numPhis; i++) { 915 int phiInputOrderId = readOrderId(methodScope); 916 int phiNodeOrderId = readOrderId(methodScope); 917 918 ValueNode phiInput = (ValueNode) ensureNodeCreated(methodScope, phiInputScope, phiInputOrderId); 919 920 ValueNode existing = (ValueNode) lookupNode(phiNodeScope, phiNodeOrderId); 921 if (lazyPhi && (existing == null || existing == phiInput)) { 922 /* Phi function not yet necessary. */ 923 registerNode(phiNodeScope, phiNodeOrderId, phiInput, true, false); 924 925 } else if (!merge.isPhiAtMerge(existing)) { 926 /* 927 * Phi function is necessary. Create it and fill it with existing inputs as well as 928 * the new input. 929 */ 930 registerNode(phiNodeScope, phiNodeOrderId, null, true, true); 931 PhiNode phi = (PhiNode) ensureNodeCreated(methodScope, phiNodeScope, phiNodeOrderId); 932 933 phi.setMerge(merge); 934 for (int j = 0; j < merge.phiPredecessorCount() - 1; j++) { 935 phi.addInput(existing); 936 } 937 phi.addInput(phiInput); 938 939 } else { 940 /* Phi node has been created before, so just add the new input. */ 941 PhiNode phi = (PhiNode) existing; 942 phi.addInput(phiInput); 943 } 944 } 945 } 946 947 protected boolean allowLazyPhis() { 948 /* We need to exactly reproduce the encoded graph, including unnecessary phi functions. */ 949 return false; 950 } 951 952 protected Node instantiateNode(MethodScope methodScope, int nodeOrderId) { 953 methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); 954 NodeClass<?> nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()]; 955 return nodeClass.allocateInstance(); 956 } 957 958 protected void readProperties(MethodScope methodScope, Node node) { 959 node.setNodeSourcePosition((NodeSourcePosition) readObject(methodScope)); 960 Fields fields = node.getNodeClass().getData(); 961 for (int pos = 0; pos < fields.getCount(); pos++) { 962 if (fields.getType(pos).isPrimitive()) { 963 long primitive = methodScope.reader.getSV(); 964 fields.setRawPrimitive(node, pos, primitive); 965 } else { 966 Object value = readObject(methodScope); 967 fields.set(node, pos, value); 968 } 969 } 970 } 971 972 /** 973 * Process the input edges of a node. Input nodes that have not yet been created must be 974 * non-fixed nodes (because fixed nodes are processed in reverse postorder. Such non-fixed nodes 975 * are created on demand (recursively since they can themselves reference not yet created 976 * nodes). 977 */ 978 protected void makeInputNodes(MethodScope methodScope, LoopScope loopScope, Node node, boolean updateUsages) { 979 Edges edges = node.getNodeClass().getEdges(Edges.Type.Inputs); 980 for (int index = 0; index < edges.getDirectCount(); index++) { 981 if (skipEdge(node, edges, index, true, true)) { 982 continue; 983 } 984 int orderId = readOrderId(methodScope); 985 Node value = ensureNodeCreated(methodScope, loopScope, orderId); 986 edges.initializeNode(node, index, value); 987 if (updateUsages && value != null && !value.isDeleted()) { 988 edges.update(node, null, value); 989 990 } 991 } 992 for (int index = edges.getDirectCount(); index < edges.getCount(); index++) { 993 if (skipEdge(node, edges, index, false, true)) { 994 continue; 995 } 996 int size = methodScope.reader.getSVInt(); 997 if (size != -1) { 998 NodeList<Node> nodeList = new NodeInputList<>(node, size); 999 edges.initializeList(node, index, nodeList); 1000 for (int idx = 0; idx < size; idx++) { 1001 int orderId = readOrderId(methodScope); 1002 Node value = ensureNodeCreated(methodScope, loopScope, orderId); 1003 nodeList.initialize(idx, value); 1004 if (updateUsages && value != null && !value.isDeleted()) { 1005 edges.update(node, null, value); 1006 } 1007 } 1008 } 1009 } 1010 } 1011 1012 protected Node ensureNodeCreated(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) { 1013 if (nodeOrderId == GraphEncoder.NULL_ORDER_ID) { 1014 return null; 1015 } 1016 Node node = lookupNode(loopScope, nodeOrderId); 1017 if (node != null) { 1018 return node; 1019 } 1020 1021 node = decodeFloatingNode(methodScope, loopScope, nodeOrderId); 1022 1023 if (node instanceof ProxyNode || node instanceof PhiNode) { 1024 /* 1025 * We need these nodes as they were in the original graph, without any canonicalization 1026 * or value numbering. 1027 */ 1028 node = methodScope.graph.addWithoutUnique(node); 1029 } else { 1030 /* Allow subclasses to canonicalize and intercept nodes. */ 1031 node = handleFloatingNodeBeforeAdd(methodScope, loopScope, node); 1032 if (!node.isAlive()) { 1033 node = addFloatingNode(methodScope, node); 1034 } 1035 node = handleFloatingNodeAfterAdd(methodScope, loopScope, node); 1036 } 1037 registerNode(loopScope, nodeOrderId, node, false, false); 1038 return node; 1039 } 1040 1041 protected Node addFloatingNode(MethodScope methodScope, Node node) { 1042 /* 1043 * We want to exactly reproduce the encoded graph. Even though nodes should be unique in the 1044 * encoded graph, this is not always guaranteed. 1045 */ 1046 return methodScope.graph.addWithoutUnique(node); 1047 } 1048 1049 /** 1050 * Decodes a non-fixed node, but does not do any post-processing and does not register it. 1051 */ 1052 protected Node decodeFloatingNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) { 1053 long readerByteIndex = methodScope.reader.getByteIndex(); 1054 Node node = instantiateNode(methodScope, nodeOrderId); 1055 if (node instanceof FixedNode) { 1056 /* 1057 * This is a severe error that will lead to a corrupted graph, so it is better not to 1058 * continue decoding at all. 1059 */ 1060 throw shouldNotReachHere("Not a floating node: " + node.getClass().getName()); 1061 } 1062 1063 /* Read the properties of the node. */ 1064 readProperties(methodScope, node); 1065 /* There must not be any successors to read, since it is a non-fixed node. */ 1066 assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0; 1067 /* Read the inputs of the node, possibly creating them recursively. */ 1068 makeInputNodes(methodScope, loopScope, node, false); 1069 methodScope.reader.setByteIndex(readerByteIndex); 1070 return node; 1071 } 1072 1073 /** 1074 * Hook for subclasses to process a non-fixed node before it is added to the graph. 1075 * 1076 * @param methodScope The current method. 1077 * @param loopScope The current loop. 1078 * @param node The node to be canonicalized. 1079 * @return The replacement for the node, or the node itself. 1080 */ 1081 protected Node handleFloatingNodeBeforeAdd(MethodScope methodScope, LoopScope loopScope, Node node) { 1082 return node; 1083 } 1084 1085 /** 1086 * Hook for subclasses to process a non-fixed node after it is added to the graph. 1087 * 1088 * If this method replaces a node with another node, it must update its source position if the 1089 * original node has the source position set. 1090 * 1091 * @param methodScope The current method. 1092 * @param loopScope The current loop. 1093 * @param node The node to be canonicalized. 1094 * @return The replacement for the node, or the node itself. 1095 */ 1096 protected Node handleFloatingNodeAfterAdd(MethodScope methodScope, LoopScope loopScope, Node node) { 1097 return node; 1098 } 1099 1100 /** 1101 * Process successor edges of a node. We create the successor nodes so that we can fill the 1102 * successor list, but no properties or edges are loaded yet. That is done when the successor is 1103 * on top of the worklist in {@link #processNextNode}. 1104 */ 1105 protected void makeSuccessorStubs(MethodScope methodScope, LoopScope loopScope, Node node, boolean updatePredecessors) { 1106 Edges edges = node.getNodeClass().getEdges(Edges.Type.Successors); 1107 for (int index = 0; index < edges.getDirectCount(); index++) { 1108 if (skipEdge(node, edges, index, true, true)) { 1109 continue; 1110 } 1111 int orderId = readOrderId(methodScope); 1112 Node value = makeStubNode(methodScope, loopScope, orderId); 1113 edges.initializeNode(node, index, value); 1114 if (updatePredecessors && value != null) { 1115 edges.update(node, null, value); 1116 } 1117 } 1118 for (int index = edges.getDirectCount(); index < edges.getCount(); index++) { 1119 if (skipEdge(node, edges, index, false, true)) { 1120 continue; 1121 } 1122 int size = methodScope.reader.getSVInt(); 1123 if (size != -1) { 1124 NodeList<Node> nodeList = new NodeSuccessorList<>(node, size); 1125 edges.initializeList(node, index, nodeList); 1126 for (int idx = 0; idx < size; idx++) { 1127 int orderId = readOrderId(methodScope); 1128 Node value = makeStubNode(methodScope, loopScope, orderId); 1129 nodeList.initialize(idx, value); 1130 if (updatePredecessors && value != null) { 1131 edges.update(node, null, value); 1132 } 1133 } 1134 } 1135 } 1136 } 1137 1138 protected FixedNode makeStubNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) { 1139 if (nodeOrderId == GraphEncoder.NULL_ORDER_ID) { 1140 return null; 1141 } 1142 FixedNode node = (FixedNode) lookupNode(loopScope, nodeOrderId); 1143 if (node != null) { 1144 return node; 1145 } 1146 1147 long readerByteIndex = methodScope.reader.getByteIndex(); 1148 node = (FixedNode) methodScope.graph.add(instantiateNode(methodScope, nodeOrderId)); 1149 /* Properties and edges are not filled yet, the node remains uninitialized. */ 1150 methodScope.reader.setByteIndex(readerByteIndex); 1151 1152 registerNode(loopScope, nodeOrderId, node, false, false); 1153 loopScope.nodesToProcess.set(nodeOrderId); 1154 return node; 1155 } 1156 1157 /** 1158 * Returns false for {@link Edges} that are not necessary in the encoded graph because they are 1159 * reconstructed using other sources of information. 1160 */ 1161 protected static boolean skipEdge(Node node, Edges edges, int index, boolean direct, boolean decode) { 1162 if (node instanceof PhiNode) { 1163 /* The inputs of phi functions are filled manually when the end nodes are processed. */ 1164 assert edges.type() == Edges.Type.Inputs; 1165 if (direct) { 1166 assert index == edges.getDirectCount() - 1 : "PhiNode has one direct input (the MergeNode)"; 1167 } else { 1168 assert index == edges.getCount() - 1 : "PhiNode has one variable size input (the values)"; 1169 if (decode) { 1170 /* The values must not be null, so initialize with an empty list. */ 1171 edges.initializeList(node, index, new NodeInputList<>(node)); 1172 } 1173 } 1174 return true; 1175 1176 } else if (node instanceof AbstractMergeNode && edges.type() == Edges.Type.Inputs && !direct) { 1177 /* The ends of merge nodes are filled manually when the ends are processed. */ 1178 assert index == edges.getCount() - 1 : "MergeNode has one variable size input (the ends)"; 1179 assert Edges.getNodeList(node, edges.getOffsets(), index) != null : "Input list must have been already created"; 1180 return true; 1181 1182 } else if (node instanceof LoopExitNode && edges.type() == Edges.Type.Inputs && edges.getType(index) == FrameState.class) { 1183 /* The stateAfter of the loop exit is filled manually. */ 1184 return true; 1185 1186 } else if (node instanceof Invoke) { 1187 assert node instanceof InvokeNode || node instanceof InvokeWithExceptionNode : "The only two Invoke node classes. Got " + node.getClass(); 1188 assert direct : "Invoke and InvokeWithException only have direct successor and input edges"; 1189 if (edges.type() == Edges.Type.Successors) { 1190 assert edges.getCount() == (node instanceof InvokeWithExceptionNode ? 2 : 1) : "InvokeNode has one successor (next); InvokeWithExceptionNode has two successors (next, exceptionEdge)"; 1191 return true; 1192 } else { 1193 assert edges.type() == Edges.Type.Inputs; 1194 if (edges.getType(index) == CallTargetNode.class) { 1195 return true; 1196 } else if (edges.getType(index) == FrameState.class) { 1197 assert edges.get(node, index) == null || edges.get(node, index) == ((Invoke) node).stateAfter() : "Only stateAfter can be a FrameState during encoding"; 1198 return true; 1199 } 1200 } 1201 } 1202 return false; 1203 } 1204 1205 protected Node lookupNode(LoopScope loopScope, int nodeOrderId) { 1206 return loopScope.createdNodes[nodeOrderId]; 1207 } 1208 1209 protected void registerNode(LoopScope loopScope, int nodeOrderId, Node node, boolean allowOverwrite, boolean allowNull) { 1210 assert node == null || node.isAlive(); 1211 assert allowNull || node != null; 1212 assert allowOverwrite || lookupNode(loopScope, nodeOrderId) == null; 1213 loopScope.createdNodes[nodeOrderId] = node; 1214 } 1215 1216 protected int readOrderId(MethodScope methodScope) { 1217 return methodScope.reader.getUVInt(); 1218 } 1219 1220 protected Object readObject(MethodScope methodScope) { 1221 return methodScope.encodedGraph.getObjects()[methodScope.reader.getUVInt()]; 1222 } 1223 1224 /** 1225 * Removes unnecessary nodes from the graph after decoding. 1226 * 1227 * @param methodScope The current method. 1228 */ 1229 protected void cleanupGraph(MethodScope methodScope) { 1230 assert verifyEdges(methodScope); 1231 } 1232 1233 protected boolean verifyEdges(MethodScope methodScope) { 1234 for (Node node : methodScope.graph.getNodes()) { 1235 assert node.isAlive(); 1236 for (Node i : node.inputs()) { 1237 assert i.isAlive(); 1238 assert i.usages().contains(node); 1239 } 1240 for (Node s : node.successors()) { 1241 assert s.isAlive(); 1242 assert s.predecessor() == node; 1243 } 1244 1245 for (Node usage : node.usages()) { 1246 assert usage.isAlive(); 1247 assert usage.inputs().contains(node) : node + " / " + usage + " / " + usage.inputs().count(); 1248 } 1249 if (node.predecessor() != null) { 1250 assert node.predecessor().isAlive(); 1251 assert node.predecessor().successors().contains(node); 1252 } 1253 } 1254 return true; 1255 } 1256 } 1257 1258 class LoopDetector implements Runnable { 1259 1260 /** 1261 * Information about loops before the actual loop nodes are inserted. 1262 */ 1263 static class Loop { 1264 /** 1265 * The header, i.e., the target of backward branches. 1266 */ 1267 MergeNode header; 1268 /** 1269 * The ends, i.e., the source of backward branches. The {@link EndNode#successors successor} 1270 * is the {@link #header loop header}. 1271 */ 1272 List<EndNode> ends = new ArrayList<>(); 1273 /** 1274 * Exits of the loop. The successor is a {@link MergeNode} marked in 1275 * {@link MethodScope#loopExplosionMerges}. 1276 */ 1277 List<AbstractEndNode> exits = new ArrayList<>(); 1278 /** 1279 * Set to true when the loop is irreducible, i.e., has multiple entries. See 1280 * {@link #handleIrreducibleLoop} for details on the handling. 1281 */ 1282 boolean irreducible; 1283 } 1284 1285 private final MethodScope methodScope; 1286 private final FixedNode startInstruction; 1287 1288 private Loop irreducibleLoopHandler; 1289 private IntegerSwitchNode irreducibleLoopSwitch; 1290 1291 protected LoopDetector(MethodScope methodScope, FixedNode startInstruction) { 1292 this.methodScope = methodScope; 1293 this.startInstruction = startInstruction; 1294 } 1295 1296 @Override 1297 public void run() { 1298 Debug.dump(Debug.VERBOSE_LOG_LEVEL, methodScope.graph, "Before loop detection"); 1299 1300 List<Loop> orderedLoops = findLoops(); 1301 assert orderedLoops.get(orderedLoops.size() - 1) == irreducibleLoopHandler : "outermost loop must be the last element in the list"; 1302 1303 for (Loop loop : orderedLoops) { 1304 if (loop.ends.isEmpty()) { 1305 assert loop == irreducibleLoopHandler; 1306 continue; 1307 } 1308 1309 /* 1310 * The algorithm to find loop exits requires that inner loops have already been 1311 * processed. Therefore, we need to iterate the loops in order (inner loops before outer 1312 * loops), and we cannot find the exits for all loops before we start inserting nodes. 1313 */ 1314 findLoopExits(loop); 1315 1316 if (loop.irreducible) { 1317 handleIrreducibleLoop(loop); 1318 } else { 1319 insertLoopNodes(loop); 1320 } 1321 Debug.dump(Debug.VERBOSE_LOG_LEVEL, methodScope.graph, "After handling of loop %s", loop.header); 1322 } 1323 1324 logIrreducibleLoops(); 1325 Debug.dump(Debug.VERBOSE_LOG_LEVEL, methodScope.graph, "After loop detection"); 1326 } 1327 1328 private List<Loop> findLoops() { 1329 /* Mapping from the loop header node to additional loop information. */ 1330 Map<MergeNode, Loop> unorderedLoops = new HashMap<>(); 1331 /* Loops in reverse order of, i.e., inner loops before outer loops. */ 1332 List<Loop> orderedLoops = new ArrayList<>(); 1333 1334 /* 1335 * Ensure we have an outermost loop that we can use to eliminate irreducible loops. This 1336 * loop can remain empty (no ends), in which case it is ignored. 1337 */ 1338 irreducibleLoopHandler = findOrCreateLoop(unorderedLoops, methodScope.loopExplosionHead); 1339 1340 NodeBitMap visited = methodScope.graph.createNodeBitMap(); 1341 NodeBitMap active = methodScope.graph.createNodeBitMap(); 1342 Deque<Node> stack = new ArrayDeque<>(); 1343 visited.mark(startInstruction); 1344 stack.push(startInstruction); 1345 1346 while (!stack.isEmpty()) { 1347 Node current = stack.peek(); 1348 assert visited.isMarked(current); 1349 1350 if (active.isMarked(current)) { 1351 /* We are back-tracking, i.e., all successor nodes have been processed. */ 1352 stack.pop(); 1353 active.clear(current); 1354 1355 Loop loop = unorderedLoops.get(current); 1356 if (loop != null) { 1357 /* 1358 * Since nodes are popped in reverse order that they were pushed, we add inner 1359 * loops before outer loops here. 1360 */ 1361 assert !orderedLoops.contains(loop); 1362 orderedLoops.add(loop); 1363 } 1364 1365 } else { 1366 /* 1367 * Process the node. Note that we do not remove the node from the stack, i.e., we 1368 * will peek it again. But the next time the node is marked as active, so we do not 1369 * execute this code again. 1370 */ 1371 active.mark(current); 1372 for (Node successor : current.cfgSuccessors()) { 1373 if (active.isMarked(successor)) { 1374 /* Detected a cycle, i.e., a backward branch of a loop. */ 1375 Loop loop = findOrCreateLoop(unorderedLoops, (MergeNode) successor); 1376 assert !loop.ends.contains(current); 1377 loop.ends.add((EndNode) current); 1378 1379 } else if (visited.isMarked(successor)) { 1380 /* Forward merge into a branch we are already exploring. */ 1381 1382 } else { 1383 /* Forward branch to a node we have not seen yet. */ 1384 visited.mark(successor); 1385 stack.push(successor); 1386 } 1387 } 1388 } 1389 } 1390 return orderedLoops; 1391 } 1392 1393 private Loop findOrCreateLoop(Map<MergeNode, Loop> unorderedLoops, MergeNode loopHeader) { 1394 assert methodScope.loopExplosionMerges.isMarkedAndGrow(loopHeader) : loopHeader; 1395 Loop loop = unorderedLoops.get(loopHeader); 1396 if (loop == null) { 1397 loop = new Loop(); 1398 loop.header = loopHeader; 1399 unorderedLoops.put(loopHeader, loop); 1400 } 1401 return loop; 1402 } 1403 1404 private void findLoopExits(Loop loop) { 1405 /* 1406 * Backward marking of loop nodes: Starting with the known loop ends, we mark all nodes that 1407 * are reachable until we hit the loop begin. All successors of loop nodes that are not 1408 * marked as loop nodes themselves are exits of the loop. We mark all successors, and then 1409 * subtract the loop nodes, to find the exits. 1410 */ 1411 1412 NodeBitMap possibleExits = methodScope.graph.createNodeBitMap(); 1413 NodeBitMap visited = methodScope.graph.createNodeBitMap(); 1414 Deque<Node> stack = new ArrayDeque<>(); 1415 for (EndNode loopEnd : loop.ends) { 1416 stack.push(loopEnd); 1417 visited.mark(loopEnd); 1418 } 1419 1420 while (!stack.isEmpty()) { 1421 Node current = stack.pop(); 1422 if (current == loop.header) { 1423 continue; 1424 } 1425 if (!methodScope.graph.isNew(methodScope.methodStartMark, current)) { 1426 /* 1427 * The current node is before the method that contains the exploded loop. The loop 1428 * must have a second entry point, i.e., it is an irreducible loop. 1429 */ 1430 loop.irreducible = true; 1431 return; 1432 } 1433 1434 for (Node predecessor : current.cfgPredecessors()) { 1435 if (predecessor instanceof LoopExitNode) { 1436 /* 1437 * Inner loop. We do not need to mark every node of it, instead we just continue 1438 * marking at the loop header. 1439 */ 1440 LoopBeginNode innerLoopBegin = ((LoopExitNode) predecessor).loopBegin(); 1441 if (!visited.isMarked(innerLoopBegin)) { 1442 stack.push(innerLoopBegin); 1443 visited.mark(innerLoopBegin); 1444 1445 /* 1446 * All loop exits of the inner loop possibly need a LoopExit of our loop. 1447 * Because we are processing inner loops first, we are guaranteed to already 1448 * have all exits of the inner loop. 1449 */ 1450 for (LoopExitNode exit : innerLoopBegin.loopExits()) { 1451 possibleExits.mark(exit); 1452 } 1453 } 1454 1455 } else if (!visited.isMarked(predecessor)) { 1456 stack.push(predecessor); 1457 visited.mark(predecessor); 1458 1459 if (predecessor instanceof ControlSplitNode) { 1460 for (Node succ : predecessor.cfgSuccessors()) { 1461 /* 1462 * We would not need to mark the current node, and would not need to 1463 * mark visited nodes. But it is easier to just mark everything, since 1464 * we subtract all visited nodes in the end anyway. Note that at this 1465 * point we do not have the complete visited information, so we would 1466 * always mark too many possible exits. 1467 */ 1468 possibleExits.mark(succ); 1469 } 1470 } 1471 } 1472 } 1473 } 1474 1475 /* All visited nodes are not exits of our loop. */ 1476 possibleExits.subtract(visited); 1477 1478 /* 1479 * Now we know all the actual loop exits. Ideally, we would insert LoopExit nodes for them. 1480 * However, a LoopExit needs a valid FrameState that captures the state at the point where 1481 * we exit the loop. During graph decoding, we create a FrameState for every exploded loop 1482 * iteration. We need to do a forward marking until we hit the next such point. This puts 1483 * some nodes into the loop that are actually not part of the loop. 1484 * 1485 * In some cases, we did not create a FrameState during graph decoding: when there was no 1486 * LoopExit in the original loop that we exploded. This happens for code paths that lead 1487 * immediately to a DeoptimizeNode. 1488 * 1489 * Both cases mimic the behavior of the BytecodeParser, which also puts more nodes than 1490 * necessary into a loop because it computes loop information based on bytecodes, before the 1491 * actual parsing. 1492 */ 1493 1494 for (Node succ : possibleExits) { 1495 stack.push(succ); 1496 visited.mark(succ); 1497 assert !methodScope.loopExplosionMerges.isMarkedAndGrow(succ); 1498 } 1499 1500 while (!stack.isEmpty()) { 1501 Node current = stack.pop(); 1502 assert visited.isMarked(current); 1503 assert current instanceof ControlSinkNode || current instanceof LoopEndNode || current.cfgSuccessors().iterator().hasNext() : "Must not reach a node that has not been decoded yet"; 1504 1505 for (Node successor : current.cfgSuccessors()) { 1506 if (visited.isMarked(successor)) { 1507 /* Already processed this successor. */ 1508 1509 } else if (methodScope.loopExplosionMerges.isMarkedAndGrow(successor)) { 1510 /* 1511 * We have a FrameState for the successor. The LoopExit will be inserted between 1512 * the current node and the successor node. Since the successor node is a 1513 * MergeNode, the current node mus be a AbstractEndNode with only that MergeNode 1514 * as the successor. 1515 */ 1516 assert successor instanceof MergeNode; 1517 assert !loop.exits.contains(current); 1518 loop.exits.add((AbstractEndNode) current); 1519 1520 } else { 1521 /* Node we have not seen yet. */ 1522 visited.mark(successor); 1523 stack.push(successor); 1524 } 1525 } 1526 } 1527 } 1528 1529 private void insertLoopNodes(Loop loop) { 1530 MergeNode merge = loop.header; 1531 FrameState stateAfter = merge.stateAfter().duplicate(); 1532 FixedNode afterMerge = merge.next(); 1533 merge.setNext(null); 1534 EndNode preLoopEnd = methodScope.graph.add(new EndNode()); 1535 LoopBeginNode loopBegin = methodScope.graph.add(new LoopBeginNode()); 1536 1537 merge.setNext(preLoopEnd); 1538 /* Add the single non-loop predecessor of the loop header. */ 1539 loopBegin.addForwardEnd(preLoopEnd); 1540 loopBegin.setNext(afterMerge); 1541 loopBegin.setStateAfter(stateAfter); 1542 1543 /* 1544 * Phi functions of the original merge need to be split: inputs that come from forward edges 1545 * remain with the original phi function; inputs that come from backward edges are added to 1546 * new phi functions. 1547 */ 1548 List<PhiNode> mergePhis = merge.phis().snapshot(); 1549 List<PhiNode> loopBeginPhis = new ArrayList<>(mergePhis.size()); 1550 for (int i = 0; i < mergePhis.size(); i++) { 1551 PhiNode mergePhi = mergePhis.get(i); 1552 PhiNode loopBeginPhi = methodScope.graph.addWithoutUnique(new ValuePhiNode(mergePhi.stamp(), loopBegin)); 1553 mergePhi.replaceAtUsages(loopBeginPhi); 1554 /* 1555 * The first input of the new phi function is the original phi function, for the one 1556 * forward edge of the LoopBeginNode. 1557 */ 1558 loopBeginPhi.addInput(mergePhi); 1559 loopBeginPhis.add(loopBeginPhi); 1560 } 1561 1562 for (EndNode endNode : loop.ends) { 1563 for (int i = 0; i < mergePhis.size(); i++) { 1564 PhiNode mergePhi = mergePhis.get(i); 1565 PhiNode loopBeginPhi = loopBeginPhis.get(i); 1566 loopBeginPhi.addInput(mergePhi.valueAt(endNode)); 1567 } 1568 1569 merge.removeEnd(endNode); 1570 LoopEndNode loopEnd = methodScope.graph.add(new LoopEndNode(loopBegin)); 1571 endNode.replaceAndDelete(loopEnd); 1572 } 1573 1574 /* 1575 * Insert the LoopExit nodes (the easy part) and compute the FrameState for the new exits 1576 * (the difficult part). 1577 */ 1578 for (AbstractEndNode exit : loop.exits) { 1579 AbstractMergeNode loopExplosionMerge = exit.merge(); 1580 assert methodScope.loopExplosionMerges.isMarkedAndGrow(loopExplosionMerge); 1581 1582 LoopExitNode loopExit = methodScope.graph.add(new LoopExitNode(loopBegin)); 1583 exit.replaceAtPredecessor(loopExit); 1584 loopExit.setNext(exit); 1585 assignLoopExitState(loopExit, loopExplosionMerge, exit); 1586 } 1587 } 1588 1589 /** 1590 * During graph decoding, we create a FrameState for every exploded loop iteration. This is 1591 * mostly the state that we want, we only need to tweak it a little bit: we need to insert the 1592 * appropriate ProxyNodes for all values that are created inside the loop and that flow out of 1593 * the loop. 1594 */ 1595 private void assignLoopExitState(LoopExitNode loopExit, AbstractMergeNode loopExplosionMerge, AbstractEndNode loopExplosionEnd) { 1596 FrameState oldState = loopExplosionMerge.stateAfter(); 1597 1598 /* Collect all nodes that are in the FrameState at the LoopBegin. */ 1599 NodeBitMap loopBeginValues = new NodeBitMap(methodScope.graph); 1600 for (FrameState state = loopExit.loopBegin().stateAfter(); state != null; state = state.outerFrameState()) { 1601 for (ValueNode value : state.values()) { 1602 if (value != null && !value.isConstant() && !loopExit.loopBegin().isPhiAtMerge(value)) { 1603 loopBeginValues.mark(ProxyPlaceholder.unwrap(value)); 1604 } 1605 } 1606 } 1607 1608 List<ValueNode> newValues = new ArrayList<>(oldState.values().size()); 1609 for (ValueNode v : oldState.values()) { 1610 ValueNode value = v; 1611 ValueNode realValue = ProxyPlaceholder.unwrap(value); 1612 1613 /* 1614 * The LoopExit is inserted before the existing merge, i.e., separately for every branch 1615 * that leads to the merge. So for phi functions of the merge, we need to take the input 1616 * that corresponds to our branch. 1617 */ 1618 if (realValue instanceof PhiNode && loopExplosionMerge.isPhiAtMerge(realValue)) { 1619 value = ((PhiNode) realValue).valueAt(loopExplosionEnd); 1620 realValue = ProxyPlaceholder.unwrap(value); 1621 } 1622 1623 if (realValue == null || realValue.isConstant() || loopBeginValues.contains(realValue) || !methodScope.graph.isNew(methodScope.methodStartMark, realValue)) { 1624 newValues.add(realValue); 1625 } else { 1626 /* 1627 * The node is not in the FrameState of the LoopBegin, i.e., it is a value computed 1628 * inside the loop. 1629 */ 1630 GraalError.guarantee(value instanceof ProxyPlaceholder && ((ProxyPlaceholder) value).proxyPoint == loopExplosionMerge, 1631 "Value flowing out of loop, but we are not prepared to insert a ProxyNode"); 1632 1633 ProxyPlaceholder proxyPlaceholder = (ProxyPlaceholder) value; 1634 ValueProxyNode proxy = ProxyNode.forValue(proxyPlaceholder.value, loopExit, methodScope.graph); 1635 proxyPlaceholder.setValue(proxy); 1636 newValues.add(proxy); 1637 } 1638 } 1639 1640 FrameState newState = new FrameState(oldState.outerFrameState(), oldState.getCode(), oldState.bci, newValues, oldState.localsSize(), oldState.stackSize(), oldState.rethrowException(), 1641 oldState.duringCall(), oldState.monitorIds(), oldState.virtualObjectMappings()); 1642 1643 assert loopExit.stateAfter() == null; 1644 loopExit.setStateAfter(methodScope.graph.add(newState)); 1645 } 1646 1647 /** 1648 * Graal does not support irreducible loops (loops with more than one entry point). There are 1649 * two ways to make them reducible: 1) duplicate nodes (peel a loop iteration starting at the 1650 * second entry point until we reach the first entry point), or 2) insert a big outer loop 1651 * covering the whole method and build a state machine for the different loop entry points. 1652 * Since node duplication can lead to an exponential explosion of nodes in the worst case, we 1653 * use the second approach. 1654 * 1655 * We already did some preparations to insert a big outer loop: 1656 * {@link MethodScope#loopExplosionHead} is the loop header for the outer loop, and we ensured 1657 * that we have a {@link Loop} data object for it in {@link #irreducibleLoopHandler}. 1658 * 1659 * Now we need to insert the state machine. We have several implementation restrictions to make 1660 * that efficient: 1661 * <ul> 1662 * <li>There must be only one loop variable, i.e., one value that is different in the 1663 * {@link FrameState} of the different loop headers.</li> 1664 * <li>The loop variable must use the primitive {@code int} type, because Graal only has a 1665 * {@link IntegerSwitchNode switch node} for {@code int}.</li> 1666 * <li>The values of the loop variable that are merged are {@link PrimitiveConstant compile time 1667 * constants}.</li> 1668 * </ul> 1669 */ 1670 private void handleIrreducibleLoop(Loop loop) { 1671 assert loop != irreducibleLoopHandler; 1672 1673 FrameState loopState = loop.header.stateAfter(); 1674 FrameState explosionHeadState = irreducibleLoopHandler.header.stateAfter(); 1675 assert loopState.outerFrameState() == explosionHeadState.outerFrameState(); 1676 NodeInputList<ValueNode> loopValues = loopState.values(); 1677 NodeInputList<ValueNode> explosionHeadValues = explosionHeadState.values(); 1678 assert loopValues.size() == explosionHeadValues.size(); 1679 1680 /* 1681 * Find the loop variable, and the value of the loop variable for our loop and the outermost 1682 * loop. There must be exactly one loop variable. 1683 */ 1684 int loopVariableIndex = -1; 1685 ValueNode loopValue = null; 1686 ValueNode explosionHeadValue = null; 1687 for (int i = 0; i < loopValues.size(); i++) { 1688 ValueNode curLoopValue = loopValues.get(i); 1689 ValueNode curExplosionHeadValue = explosionHeadValues.get(i); 1690 1691 if (curLoopValue != curExplosionHeadValue) { 1692 if (loopVariableIndex != -1) { 1693 throw bailout("must have only one variable that is changed in loop. " + loopValue + " != " + explosionHeadValue + " and " + curLoopValue + " != " + curExplosionHeadValue); 1694 } 1695 1696 loopVariableIndex = i; 1697 loopValue = curLoopValue; 1698 explosionHeadValue = curExplosionHeadValue; 1699 } 1700 } 1701 assert loopVariableIndex != -1; 1702 1703 ValuePhiNode loopVariablePhi; 1704 SortedMap<Integer, AbstractBeginNode> dispatchTable = new TreeMap<>(); 1705 AbstractBeginNode unreachableDefaultSuccessor; 1706 if (irreducibleLoopSwitch == null) { 1707 /* 1708 * This is the first irreducible loop. We need to build the initial state machine 1709 * (dispatch for the loop header of the outermost loop). 1710 */ 1711 assert !irreducibleLoopHandler.header.isPhiAtMerge(explosionHeadValue); 1712 assert irreducibleLoopHandler.header.phis().isEmpty(); 1713 1714 /* The new phi function for the loop variable. */ 1715 loopVariablePhi = methodScope.graph.addWithoutUnique(new ValuePhiNode(explosionHeadValue.stamp().unrestricted(), irreducibleLoopHandler.header)); 1716 for (int i = 0; i < irreducibleLoopHandler.header.phiPredecessorCount(); i++) { 1717 loopVariablePhi.addInput(explosionHeadValue); 1718 } 1719 1720 /* 1721 * Build the new FrameState for the loop header. There is only once change in comparison 1722 * to the old FrameState: the loop variable is replaced with the phi function. 1723 */ 1724 FrameState oldFrameState = explosionHeadState; 1725 List<ValueNode> newFrameStateValues = new ArrayList<>(); 1726 for (int i = 0; i < explosionHeadValues.size(); i++) { 1727 if (i == loopVariableIndex) { 1728 newFrameStateValues.add(loopVariablePhi); 1729 } else { 1730 newFrameStateValues.add(explosionHeadValues.get(i)); 1731 } 1732 } 1733 FrameState newFrameState = methodScope.graph.add( 1734 new FrameState(oldFrameState.outerFrameState(), oldFrameState.getCode(), oldFrameState.bci, newFrameStateValues, oldFrameState.localsSize(), 1735 oldFrameState.stackSize(), oldFrameState.rethrowException(), oldFrameState.duringCall(), oldFrameState.monitorIds(), 1736 oldFrameState.virtualObjectMappings())); 1737 oldFrameState.replaceAtUsages(newFrameState); 1738 1739 /* 1740 * Disconnect the outermost loop header from its loop body, so that we can later on 1741 * insert the switch node. Collect dispatch information for the outermost loop. 1742 */ 1743 FixedNode handlerNext = irreducibleLoopHandler.header.next(); 1744 irreducibleLoopHandler.header.setNext(null); 1745 BeginNode handlerBegin = methodScope.graph.add(new BeginNode()); 1746 handlerBegin.setNext(handlerNext); 1747 dispatchTable.put(asInt(explosionHeadValue), handlerBegin); 1748 1749 /* 1750 * We know that there will always be a matching key in the switch. But Graal always 1751 * wants a default successor, so we build a dummy block that just deoptimizes. 1752 */ 1753 unreachableDefaultSuccessor = methodScope.graph.add(new BeginNode()); 1754 DeoptimizeNode deopt = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.UnreachedCode)); 1755 unreachableDefaultSuccessor.setNext(deopt); 1756 1757 } else { 1758 /* 1759 * This is the second or a subsequent irreducible loop, i.e., we already inserted a 1760 * switch node before. We re-create the dispatch state machine of that switch, so that 1761 * we can extend it with one more branch. 1762 */ 1763 assert irreducibleLoopHandler.header.isPhiAtMerge(explosionHeadValue); 1764 assert irreducibleLoopHandler.header.phis().count() == 1 && irreducibleLoopHandler.header.phis().first() == explosionHeadValue; 1765 assert irreducibleLoopSwitch.value() == explosionHeadValue; 1766 1767 /* We can modify the phi function used by the old switch node. */ 1768 loopVariablePhi = (ValuePhiNode) explosionHeadValue; 1769 1770 /* 1771 * We cannot modify the old switch node. Insert all information from the old switch node 1772 * into our temporary data structures for the new, larger, switch node. 1773 */ 1774 for (int i = 0; i < irreducibleLoopSwitch.keyCount(); i++) { 1775 int key = irreducibleLoopSwitch.keyAt(i).asInt(); 1776 dispatchTable.put(key, irreducibleLoopSwitch.successorAtKey(key)); 1777 } 1778 unreachableDefaultSuccessor = irreducibleLoopSwitch.defaultSuccessor(); 1779 1780 /* Unlink and delete the old switch node, we do not need it anymore. */ 1781 assert irreducibleLoopHandler.header.next() == irreducibleLoopSwitch; 1782 irreducibleLoopHandler.header.setNext(null); 1783 irreducibleLoopSwitch.clearSuccessors(); 1784 irreducibleLoopSwitch.safeDelete(); 1785 } 1786 1787 /* Insert our loop into the dispatch state machine. */ 1788 assert loop.header.phis().isEmpty(); 1789 BeginNode dispatchBegin = methodScope.graph.add(new BeginNode()); 1790 EndNode dispatchEnd = methodScope.graph.add(new EndNode()); 1791 dispatchBegin.setNext(dispatchEnd); 1792 loop.header.addForwardEnd(dispatchEnd); 1793 int intLoopValue = asInt(loopValue); 1794 assert !dispatchTable.containsKey(intLoopValue); 1795 dispatchTable.put(intLoopValue, dispatchBegin); 1796 1797 /* Disconnect the ends of our loop and re-connect them to the outermost loop header. */ 1798 for (EndNode end : loop.ends) { 1799 loop.header.removeEnd(end); 1800 irreducibleLoopHandler.ends.add(end); 1801 irreducibleLoopHandler.header.addForwardEnd(end); 1802 loopVariablePhi.addInput(loopValue); 1803 } 1804 1805 /* Build and insert the switch node. */ 1806 irreducibleLoopSwitch = methodScope.graph.add(createSwitch(loopVariablePhi, dispatchTable, unreachableDefaultSuccessor)); 1807 irreducibleLoopHandler.header.setNext(irreducibleLoopSwitch); 1808 } 1809 1810 private static int asInt(ValueNode node) { 1811 if (!node.isConstant() || node.asJavaConstant().getJavaKind() != JavaKind.Int) { 1812 throw bailout("must have a loop variable of type int. " + node); 1813 } 1814 return node.asJavaConstant().asInt(); 1815 } 1816 1817 private static RuntimeException bailout(String msg) { 1818 throw new PermanentBailoutException("Graal implementation restriction: Method with %s loop explosion %s", LoopExplosionKind.MERGE_EXPLODE, msg); 1819 } 1820 1821 private static IntegerSwitchNode createSwitch(ValuePhiNode switchedValue, SortedMap<Integer, AbstractBeginNode> dispatchTable, AbstractBeginNode defaultSuccessor) { 1822 int numKeys = dispatchTable.size(); 1823 int numSuccessors = numKeys + 1; 1824 1825 AbstractBeginNode[] switchSuccessors = new AbstractBeginNode[numSuccessors]; 1826 int[] switchKeys = new int[numKeys]; 1827 double[] switchKeyProbabilities = new double[numSuccessors]; 1828 int[] switchKeySuccessors = new int[numSuccessors]; 1829 1830 int idx = 0; 1831 for (Map.Entry<Integer, AbstractBeginNode> entry : dispatchTable.entrySet()) { 1832 switchSuccessors[idx] = entry.getValue(); 1833 switchKeys[idx] = entry.getKey(); 1834 switchKeyProbabilities[idx] = 1d / numKeys; 1835 switchKeySuccessors[idx] = idx; 1836 idx++; 1837 } 1838 switchSuccessors[idx] = defaultSuccessor; 1839 /* We know the default branch is never going to be executed. */ 1840 switchKeyProbabilities[idx] = 0; 1841 switchKeySuccessors[idx] = idx; 1842 1843 return new IntegerSwitchNode(switchedValue, switchSuccessors, switchKeys, switchKeyProbabilities, switchKeySuccessors); 1844 } 1845 1846 /** 1847 * Print information about irreducible loops, when enabled with -Dgraal.Log=IrreducibleLoops. 1848 */ 1849 @SuppressWarnings("try") 1850 private void logIrreducibleLoops() { 1851 try (Debug.Scope s = Debug.scope("IrreducibleLoops")) { 1852 if (Debug.isLogEnabled(Debug.BASIC_LOG_LEVEL) && irreducibleLoopSwitch != null) { 1853 StringBuilder msg = new StringBuilder("Inserted state machine to remove irreducible loops. Dispatching to the following states: "); 1854 String sep = ""; 1855 for (int i = 0; i < irreducibleLoopSwitch.keyCount(); i++) { 1856 msg.append(sep).append(irreducibleLoopSwitch.keyAt(i).asInt()); 1857 sep = ", "; 1858 } 1859 Debug.log(Debug.BASIC_LOG_LEVEL, "%s", msg); 1860 } 1861 } 1862 } 1863 }