1 /* 2 * Copyright (c) 2011, 2017, 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.util; 24 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.Collection; 28 import java.util.Collections; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.function.BiFunction; 32 33 import org.graalvm.compiler.bytecode.Bytecode; 34 import org.graalvm.compiler.code.SourceStackTraceBailoutException; 35 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; 36 import org.graalvm.compiler.core.common.type.ObjectStamp; 37 import org.graalvm.compiler.debug.DebugContext; 38 import org.graalvm.compiler.graph.Graph; 39 import org.graalvm.compiler.graph.Node; 40 import org.graalvm.compiler.graph.NodeBitMap; 41 import org.graalvm.compiler.graph.NodeSourcePosition; 42 import org.graalvm.compiler.graph.NodeStack; 43 import org.graalvm.compiler.graph.Position; 44 import org.graalvm.compiler.graph.iterators.NodeIterable; 45 import org.graalvm.compiler.graph.spi.SimplifierTool; 46 import org.graalvm.compiler.nodes.AbstractBeginNode; 47 import org.graalvm.compiler.nodes.AbstractEndNode; 48 import org.graalvm.compiler.nodes.AbstractMergeNode; 49 import org.graalvm.compiler.nodes.ConstantNode; 50 import org.graalvm.compiler.nodes.ControlSplitNode; 51 import org.graalvm.compiler.nodes.FixedNode; 52 import org.graalvm.compiler.nodes.FixedWithNextNode; 53 import org.graalvm.compiler.nodes.FrameState; 54 import org.graalvm.compiler.nodes.GuardNode; 55 import org.graalvm.compiler.nodes.LoopBeginNode; 56 import org.graalvm.compiler.nodes.LoopEndNode; 57 import org.graalvm.compiler.nodes.LoopExitNode; 58 import org.graalvm.compiler.nodes.PhiNode; 59 import org.graalvm.compiler.nodes.PiNode; 60 import org.graalvm.compiler.nodes.ProxyNode; 61 import org.graalvm.compiler.nodes.StateSplit; 62 import org.graalvm.compiler.nodes.StructuredGraph; 63 import org.graalvm.compiler.nodes.ValueNode; 64 import org.graalvm.compiler.nodes.java.LoadIndexedNode; 65 import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 66 import org.graalvm.compiler.nodes.java.MonitorIdNode; 67 import org.graalvm.compiler.nodes.spi.ArrayLengthProvider; 68 import org.graalvm.compiler.nodes.spi.LimitedValueProxy; 69 import org.graalvm.compiler.nodes.spi.LoweringProvider; 70 import org.graalvm.compiler.nodes.spi.ValueProxy; 71 import org.graalvm.compiler.nodes.spi.VirtualizerTool; 72 import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; 73 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 74 import org.graalvm.compiler.options.Option; 75 import org.graalvm.compiler.options.OptionKey; 76 import org.graalvm.compiler.options.OptionType; 77 import org.graalvm.compiler.options.OptionValues; 78 import org.graalvm.util.EconomicMap; 79 import org.graalvm.util.EconomicSet; 80 import org.graalvm.util.Equivalence; 81 import org.graalvm.util.MapCursor; 82 83 import jdk.vm.ci.code.BailoutException; 84 import jdk.vm.ci.code.BytecodePosition; 85 import jdk.vm.ci.meta.Assumptions; 86 import jdk.vm.ci.meta.Constant; 87 import jdk.vm.ci.meta.ConstantReflectionProvider; 88 import jdk.vm.ci.meta.JavaKind; 89 import jdk.vm.ci.meta.MetaAccessProvider; 90 import jdk.vm.ci.meta.ResolvedJavaMethod; 91 import jdk.vm.ci.meta.ResolvedJavaType; 92 93 public class GraphUtil { 94 95 public static class Options { 96 @Option(help = "Verify that there are no new unused nodes when performing killCFG", type = OptionType.Debug)// 97 public static final OptionKey<Boolean> VerifyKillCFGUnusedNodes = new OptionKey<>(false); 98 } 99 100 private static void killCFGInner(FixedNode node) { 101 EconomicSet<Node> markedNodes = EconomicSet.create(); 102 EconomicMap<AbstractMergeNode, List<AbstractEndNode>> unmarkedMerges = EconomicMap.create(); 103 104 // Detach this node from CFG 105 node.replaceAtPredecessor(null); 106 107 markFixedNodes(node, markedNodes, unmarkedMerges); 108 109 fixSurvivingAffectedMerges(markedNodes, unmarkedMerges); 110 111 DebugContext debug = node.getDebug(); 112 debug.dump(DebugContext.DETAILED_LEVEL, node.graph(), "After fixing merges (killCFG %s)", node); 113 114 // Mark non-fixed nodes 115 markUsages(markedNodes); 116 117 // Detach marked nodes from non-marked nodes 118 for (Node marked : markedNodes) { 119 for (Node input : marked.inputs()) { 120 if (!markedNodes.contains(input)) { 121 marked.replaceFirstInput(input, null); 122 tryKillUnused(input); 123 } 124 } 125 } 126 debug.dump(DebugContext.VERY_DETAILED_LEVEL, node.graph(), "After disconnecting non-marked inputs (killCFG %s)", node); 127 // Kill marked nodes 128 for (Node marked : markedNodes) { 129 if (marked.isAlive()) { 130 marked.markDeleted(); 131 } 132 } 133 } 134 135 private static void markFixedNodes(FixedNode node, EconomicSet<Node> markedNodes, EconomicMap<AbstractMergeNode, List<AbstractEndNode>> unmarkedMerges) { 136 NodeStack workStack = new NodeStack(); 137 workStack.push(node); 138 while (!workStack.isEmpty()) { 139 Node fixedNode = workStack.pop(); 140 markedNodes.add(fixedNode); 141 if (fixedNode instanceof AbstractMergeNode) { 142 unmarkedMerges.removeKey((AbstractMergeNode) fixedNode); 143 } 144 while (fixedNode instanceof FixedWithNextNode) { 145 fixedNode = ((FixedWithNextNode) fixedNode).next(); 146 if (fixedNode != null) { 147 markedNodes.add(fixedNode); 148 } 149 } 150 if (fixedNode instanceof ControlSplitNode) { 151 for (Node successor : fixedNode.successors()) { 152 workStack.push(successor); 153 } 154 } else if (fixedNode instanceof AbstractEndNode) { 155 AbstractEndNode end = (AbstractEndNode) fixedNode; 156 AbstractMergeNode merge = end.merge(); 157 if (merge != null) { 158 assert !markedNodes.contains(merge) || (merge instanceof LoopBeginNode && end instanceof LoopEndNode) : merge; 159 if (merge instanceof LoopBeginNode) { 160 if (end == ((LoopBeginNode) merge).forwardEnd()) { 161 workStack.push(merge); 162 continue; 163 } 164 if (markedNodes.contains(merge)) { 165 continue; 166 } 167 } 168 List<AbstractEndNode> endsSeen = unmarkedMerges.get(merge); 169 if (endsSeen == null) { 170 endsSeen = new ArrayList<>(merge.forwardEndCount()); 171 unmarkedMerges.put(merge, endsSeen); 172 } 173 endsSeen.add(end); 174 if (!(end instanceof LoopEndNode) && endsSeen.size() == merge.forwardEndCount()) { 175 assert merge.forwardEnds().filter(n -> !markedNodes.contains(n)).isEmpty(); 176 // all this merge's forward ends are marked: it needs to be killed 177 workStack.push(merge); 178 } 179 } 180 } 181 } 182 } 183 184 private static void fixSurvivingAffectedMerges(EconomicSet<Node> markedNodes, EconomicMap<AbstractMergeNode, List<AbstractEndNode>> unmarkedMerges) { 185 MapCursor<AbstractMergeNode, List<AbstractEndNode>> cursor = unmarkedMerges.getEntries(); 186 while (cursor.advance()) { 187 AbstractMergeNode merge = cursor.getKey(); 188 for (AbstractEndNode end : cursor.getValue()) { 189 merge.removeEnd(end); 190 } 191 if (merge.phiPredecessorCount() == 1) { 192 if (merge instanceof LoopBeginNode) { 193 LoopBeginNode loopBegin = (LoopBeginNode) merge; 194 assert merge.forwardEndCount() == 1; 195 for (LoopExitNode loopExit : loopBegin.loopExits().snapshot()) { 196 if (markedNodes.contains(loopExit)) { 197 /* 198 * disconnect from loop begin so that reduceDegenerateLoopBegin doesn't 199 * transform it into a new beginNode 200 */ 201 loopExit.replaceFirstInput(loopBegin, null); 202 } 203 } 204 merge.graph().reduceDegenerateLoopBegin(loopBegin); 205 } else { 206 merge.graph().reduceTrivialMerge(merge); 207 } 208 } else { 209 assert merge.phiPredecessorCount() > 1 : merge; 210 } 211 } 212 } 213 214 private static void markUsages(EconomicSet<Node> markedNodes) { 215 NodeStack workStack = new NodeStack(markedNodes.size() + 4); 216 for (Node marked : markedNodes) { 217 workStack.push(marked); 218 } 219 while (!workStack.isEmpty()) { 220 Node marked = workStack.pop(); 221 for (Node usage : marked.usages()) { 222 if (!markedNodes.contains(usage)) { 223 workStack.push(usage); 224 markedNodes.add(usage); 225 } 226 } 227 } 228 } 229 230 @SuppressWarnings("try") 231 public static void killCFG(FixedNode node) { 232 DebugContext debug = node.getDebug(); 233 try (DebugContext.Scope scope = debug.scope("KillCFG", node)) { 234 EconomicSet<Node> unusedNodes = null; 235 EconomicSet<Node> unsafeNodes = null; 236 Graph.NodeEventScope nodeEventScope = null; 237 OptionValues options = node.getOptions(); 238 if (Graph.Options.VerifyGraalGraphEdges.getValue(options)) { 239 unsafeNodes = collectUnsafeNodes(node.graph()); 240 } 241 if (GraphUtil.Options.VerifyKillCFGUnusedNodes.getValue(options)) { 242 EconomicSet<Node> collectedUnusedNodes = unusedNodes = EconomicSet.create(Equivalence.IDENTITY); 243 nodeEventScope = node.graph().trackNodeEvents(new Graph.NodeEventListener() { 244 @Override 245 public void event(Graph.NodeEvent e, Node n) { 246 if (e == Graph.NodeEvent.ZERO_USAGES && isFloatingNode(n) && !(n instanceof GuardNode)) { 247 collectedUnusedNodes.add(n); 248 } 249 } 250 }); 251 } 252 debug.dump(DebugContext.VERY_DETAILED_LEVEL, node.graph(), "Before killCFG %s", node); 253 killCFGInner(node); 254 debug.dump(DebugContext.VERY_DETAILED_LEVEL, node.graph(), "After killCFG %s", node); 255 if (Graph.Options.VerifyGraalGraphEdges.getValue(options)) { 256 EconomicSet<Node> newUnsafeNodes = collectUnsafeNodes(node.graph()); 257 newUnsafeNodes.removeAll(unsafeNodes); 258 assert newUnsafeNodes.isEmpty() : "New unsafe nodes: " + newUnsafeNodes; 259 } 260 if (GraphUtil.Options.VerifyKillCFGUnusedNodes.getValue(options)) { 261 nodeEventScope.close(); 262 Iterator<Node> iterator = unusedNodes.iterator(); 263 while (iterator.hasNext()) { 264 Node curNode = iterator.next(); 265 if (curNode.isDeleted()) { 266 iterator.remove(); 267 } 268 } 269 assert unusedNodes.isEmpty() : "New unused nodes: " + unusedNodes; 270 } 271 } catch (Throwable t) { 272 throw debug.handle(t); 273 } 274 } 275 276 /** 277 * Collects all node in the graph which have non-optional inputs that are null. 278 */ 279 private static EconomicSet<Node> collectUnsafeNodes(Graph graph) { 280 EconomicSet<Node> unsafeNodes = EconomicSet.create(Equivalence.IDENTITY); 281 for (Node n : graph.getNodes()) { 282 for (Position pos : n.inputPositions()) { 283 Node input = pos.get(n); 284 if (input == null) { 285 if (!pos.isInputOptional()) { 286 unsafeNodes.add(n); 287 } 288 } 289 } 290 } 291 return unsafeNodes; 292 } 293 294 public static boolean isFloatingNode(Node n) { 295 return !(n instanceof FixedNode); 296 } 297 298 private static boolean checkKill(Node node, boolean mayKillGuard) { 299 node.assertTrue(mayKillGuard || !(node instanceof GuardNode), "must not be a guard node %s", node); 300 node.assertTrue(node.isAlive(), "must be alive"); 301 node.assertTrue(node.hasNoUsages(), "cannot kill node %s because of usages: %s", node, node.usages()); 302 node.assertTrue(node.predecessor() == null, "cannot kill node %s because of predecessor: %s", node, node.predecessor()); 303 return true; 304 } 305 306 public static void killWithUnusedFloatingInputs(Node node) { 307 killWithUnusedFloatingInputs(node, false); 308 } 309 310 public static void killWithUnusedFloatingInputs(Node node, boolean mayKillGuard) { 311 assert checkKill(node, mayKillGuard); 312 node.markDeleted(); 313 outer: for (Node in : node.inputs()) { 314 if (in.isAlive()) { 315 in.removeUsage(node); 316 if (in.hasNoUsages()) { 317 node.maybeNotifyZeroUsages(in); 318 } 319 if (isFloatingNode(in)) { 320 if (in.hasNoUsages()) { 321 if (in instanceof GuardNode) { 322 // Guard nodes are only killed if their anchor dies. 323 } else { 324 killWithUnusedFloatingInputs(in); 325 } 326 } else if (in instanceof PhiNode) { 327 for (Node use : in.usages()) { 328 if (use != in) { 329 continue outer; 330 } 331 } 332 in.replaceAtUsages(null); 333 killWithUnusedFloatingInputs(in); 334 } 335 } 336 } 337 } 338 } 339 340 /** 341 * Removes all nodes created after the {@code mark}, assuming no "old" nodes point to "new" 342 * nodes. 343 */ 344 public static void removeNewNodes(Graph graph, Graph.Mark mark) { 345 assert checkNoOldToNewEdges(graph, mark); 346 for (Node n : graph.getNewNodes(mark)) { 347 n.markDeleted(); 348 for (Node in : n.inputs()) { 349 in.removeUsage(n); 350 } 351 } 352 } 353 354 private static boolean checkNoOldToNewEdges(Graph graph, Graph.Mark mark) { 355 for (Node old : graph.getNodes()) { 356 if (graph.isNew(mark, old)) { 357 break; 358 } 359 for (Node n : old.successors()) { 360 assert !graph.isNew(mark, n) : old + " -> " + n; 361 } 362 for (Node n : old.inputs()) { 363 assert !graph.isNew(mark, n) : old + " -> " + n; 364 } 365 } 366 return true; 367 } 368 369 public static void removeFixedWithUnusedInputs(FixedWithNextNode fixed) { 370 if (fixed instanceof StateSplit) { 371 FrameState stateAfter = ((StateSplit) fixed).stateAfter(); 372 if (stateAfter != null) { 373 ((StateSplit) fixed).setStateAfter(null); 374 if (stateAfter.hasNoUsages()) { 375 killWithUnusedFloatingInputs(stateAfter); 376 } 377 } 378 } 379 unlinkFixedNode(fixed); 380 killWithUnusedFloatingInputs(fixed); 381 } 382 383 public static void unlinkFixedNode(FixedWithNextNode fixed) { 384 assert fixed.next() != null && fixed.predecessor() != null && fixed.isAlive() : fixed; 385 FixedNode next = fixed.next(); 386 fixed.setNext(null); 387 fixed.replaceAtPredecessor(next); 388 } 389 390 public static void checkRedundantPhi(PhiNode phiNode) { 391 if (phiNode.isDeleted() || phiNode.valueCount() == 1) { 392 return; 393 } 394 395 ValueNode singleValue = phiNode.singleValueOrThis(); 396 if (singleValue != phiNode) { 397 Collection<PhiNode> phiUsages = phiNode.usages().filter(PhiNode.class).snapshot(); 398 Collection<ProxyNode> proxyUsages = phiNode.usages().filter(ProxyNode.class).snapshot(); 399 phiNode.replaceAtUsagesAndDelete(singleValue); 400 for (PhiNode phi : phiUsages) { 401 checkRedundantPhi(phi); 402 } 403 for (ProxyNode proxy : proxyUsages) { 404 checkRedundantProxy(proxy); 405 } 406 } 407 } 408 409 public static void checkRedundantProxy(ProxyNode vpn) { 410 if (vpn.isDeleted()) { 411 return; 412 } 413 AbstractBeginNode proxyPoint = vpn.proxyPoint(); 414 if (proxyPoint instanceof LoopExitNode) { 415 LoopExitNode exit = (LoopExitNode) proxyPoint; 416 LoopBeginNode loopBegin = exit.loopBegin(); 417 Node vpnValue = vpn.value(); 418 for (ValueNode v : loopBegin.stateAfter().values()) { 419 ValueNode v2 = v; 420 if (loopBegin.isPhiAtMerge(v2)) { 421 v2 = ((PhiNode) v2).valueAt(loopBegin.forwardEnd()); 422 } 423 if (vpnValue == v2) { 424 Collection<PhiNode> phiUsages = vpn.usages().filter(PhiNode.class).snapshot(); 425 Collection<ProxyNode> proxyUsages = vpn.usages().filter(ProxyNode.class).snapshot(); 426 vpn.replaceAtUsagesAndDelete(vpnValue); 427 for (PhiNode phi : phiUsages) { 428 checkRedundantPhi(phi); 429 } 430 for (ProxyNode proxy : proxyUsages) { 431 checkRedundantProxy(proxy); 432 } 433 return; 434 } 435 } 436 } 437 } 438 439 /** 440 * Remove loop header without loop ends. This can happen with degenerated loops like this one: 441 * 442 * <pre> 443 * for (;;) { 444 * try { 445 * break; 446 * } catch (UnresolvedException iioe) { 447 * } 448 * } 449 * </pre> 450 */ 451 public static void normalizeLoops(StructuredGraph graph) { 452 boolean loopRemoved = false; 453 for (LoopBeginNode begin : graph.getNodes(LoopBeginNode.TYPE)) { 454 if (begin.loopEnds().isEmpty()) { 455 assert begin.forwardEndCount() == 1; 456 graph.reduceDegenerateLoopBegin(begin); 457 loopRemoved = true; 458 } else { 459 normalizeLoopBegin(begin); 460 } 461 } 462 463 if (loopRemoved) { 464 /* 465 * Removing a degenerated loop can make non-loop phi functions unnecessary. Therefore, 466 * we re-check all phi functions and remove redundant ones. 467 */ 468 for (Node node : graph.getNodes()) { 469 if (node instanceof PhiNode) { 470 checkRedundantPhi((PhiNode) node); 471 } 472 } 473 } 474 } 475 476 private static void normalizeLoopBegin(LoopBeginNode begin) { 477 // Delete unnecessary loop phi functions, i.e., phi functions where all inputs are either 478 // the same or the phi itself. 479 for (PhiNode phi : begin.phis().snapshot()) { 480 GraphUtil.checkRedundantPhi(phi); 481 } 482 for (LoopExitNode exit : begin.loopExits()) { 483 for (ProxyNode vpn : exit.proxies().snapshot()) { 484 GraphUtil.checkRedundantProxy(vpn); 485 } 486 } 487 } 488 489 /** 490 * Gets an approximate source code location for a node if possible. 491 * 492 * @return the StackTraceElements if an approximate source location is found, null otherwise 493 */ 494 public static StackTraceElement[] approxSourceStackTraceElement(Node node) { 495 NodeSourcePosition position = node.getNodeSourcePosition(); 496 if (position != null) { 497 // use GraphBuilderConfiguration and enable trackNodeSourcePosition to get better source 498 // positions. 499 return approxSourceStackTraceElement(position); 500 } 501 ArrayList<StackTraceElement> elements = new ArrayList<>(); 502 Node n = node; 503 while (n != null) { 504 if (n instanceof MethodCallTargetNode) { 505 elements.add(((MethodCallTargetNode) n).targetMethod().asStackTraceElement(-1)); 506 n = ((MethodCallTargetNode) n).invoke().asNode(); 507 } 508 509 if (n instanceof StateSplit) { 510 FrameState state = ((StateSplit) n).stateAfter(); 511 elements.addAll(Arrays.asList(approxSourceStackTraceElement(state))); 512 break; 513 } 514 n = n.predecessor(); 515 } 516 return elements.toArray(new StackTraceElement[elements.size()]); 517 } 518 519 /** 520 * Gets an approximate source code location for frame state. 521 * 522 * @return the StackTraceElements if an approximate source location is found, null otherwise 523 */ 524 public static StackTraceElement[] approxSourceStackTraceElement(FrameState frameState) { 525 ArrayList<StackTraceElement> elements = new ArrayList<>(); 526 FrameState state = frameState; 527 while (state != null) { 528 Bytecode code = state.getCode(); 529 if (code != null) { 530 elements.add(code.asStackTraceElement(state.bci - 1)); 531 } 532 state = state.outerFrameState(); 533 } 534 return elements.toArray(new StackTraceElement[0]); 535 } 536 537 /** 538 * Gets approximate stack trace elements for a bytecode position. 539 */ 540 public static StackTraceElement[] approxSourceStackTraceElement(BytecodePosition bytecodePosition) { 541 ArrayList<StackTraceElement> elements = new ArrayList<>(); 542 BytecodePosition position = bytecodePosition; 543 while (position != null) { 544 ResolvedJavaMethod method = position.getMethod(); 545 if (method != null) { 546 elements.add(method.asStackTraceElement(position.getBCI())); 547 } 548 position = position.getCaller(); 549 } 550 return elements.toArray(new StackTraceElement[0]); 551 } 552 553 /** 554 * Gets an approximate source code location for a node, encoded as an exception, if possible. 555 * 556 * @return the exception with the location 557 */ 558 public static RuntimeException approxSourceException(Node node, Throwable cause) { 559 final StackTraceElement[] elements = approxSourceStackTraceElement(node); 560 return createBailoutException(cause == null ? "" : cause.getMessage(), cause, elements); 561 } 562 563 /** 564 * Creates a bailout exception with the given stack trace elements and message. 565 * 566 * @param message the message of the exception 567 * @param elements the stack trace elements 568 * @return the exception 569 */ 570 public static BailoutException createBailoutException(String message, Throwable cause, StackTraceElement[] elements) { 571 return SourceStackTraceBailoutException.create(cause, message, elements); 572 } 573 574 /** 575 * Gets an approximate source code location for a node if possible. 576 * 577 * @return a file name and source line number in stack trace format (e.g. "String.java:32") if 578 * an approximate source location is found, null otherwise 579 */ 580 public static String approxSourceLocation(Node node) { 581 StackTraceElement[] stackTraceElements = approxSourceStackTraceElement(node); 582 if (stackTraceElements != null && stackTraceElements.length > 0) { 583 StackTraceElement top = stackTraceElements[0]; 584 if (top.getFileName() != null && top.getLineNumber() >= 0) { 585 return top.getFileName() + ":" + top.getLineNumber(); 586 } 587 } 588 return null; 589 } 590 591 /** 592 * Returns a string representation of the given collection of objects. 593 * 594 * @param objects The {@link Iterable} that will be used to iterate over the objects. 595 * @return A string of the format "[a, b, ...]". 596 */ 597 public static String toString(Iterable<?> objects) { 598 StringBuilder str = new StringBuilder(); 599 str.append("["); 600 for (Object o : objects) { 601 str.append(o).append(", "); 602 } 603 if (str.length() > 1) { 604 str.setLength(str.length() - 2); 605 } 606 str.append("]"); 607 return str.toString(); 608 } 609 610 /** 611 * Gets the original value by iterating through all {@link ValueProxy ValueProxies}. 612 * 613 * @param value the start value. 614 * @return the first non-proxy value encountered 615 */ 616 public static ValueNode unproxify(ValueNode value) { 617 if (value instanceof ValueProxy) { 618 return unproxify((ValueProxy) value); 619 } else { 620 return value; 621 } 622 } 623 624 /** 625 * Gets the original value by iterating through all {@link ValueProxy ValueProxies}. 626 * 627 * @param value the start value proxy. 628 * @return the first non-proxy value encountered 629 */ 630 public static ValueNode unproxify(ValueProxy value) { 631 if (value != null) { 632 ValueNode result = value.getOriginalNode(); 633 while (result instanceof ValueProxy) { 634 result = ((ValueProxy) result).getOriginalNode(); 635 } 636 return result; 637 } else { 638 return null; 639 } 640 } 641 642 public static ValueNode skipPi(ValueNode node) { 643 ValueNode n = node; 644 while (n instanceof PiNode) { 645 PiNode piNode = (PiNode) n; 646 n = piNode.getOriginalNode(); 647 } 648 return n; 649 } 650 651 public static ValueNode skipPiWhileNonNull(ValueNode node) { 652 ValueNode n = node; 653 while (n instanceof PiNode) { 654 PiNode piNode = (PiNode) n; 655 ObjectStamp originalStamp = (ObjectStamp) piNode.getOriginalNode().stamp(); 656 if (originalStamp.nonNull()) { 657 n = piNode.getOriginalNode(); 658 } else { 659 break; 660 } 661 } 662 return n; 663 } 664 665 /** 666 * Looks for an {@link ArrayLengthProvider} while iterating through all {@link ValueProxy 667 * ValueProxies}. 668 * 669 * @param value The start value. 670 * @return The array length if one was found, or null otherwise. 671 */ 672 public static ValueNode arrayLength(ValueNode value) { 673 ValueNode current = value; 674 do { 675 if (current instanceof ArrayLengthProvider) { 676 ValueNode length = ((ArrayLengthProvider) current).length(); 677 if (length != null) { 678 return length; 679 } 680 } 681 if (current instanceof ValueProxy) { 682 current = ((ValueProxy) current).getOriginalNode(); 683 } else { 684 break; 685 } 686 } while (true); 687 return null; 688 } 689 690 /** 691 * Tries to find an original value of the given node by traversing through proxies and 692 * unambiguous phis. Note that this method will perform an exhaustive search through phis. It is 693 * intended to be used during graph building, when phi nodes aren't yet canonicalized. 694 * 695 * @param value The node whose original value should be determined. 696 * @return The original value (which might be the input value itself). 697 */ 698 public static ValueNode originalValue(ValueNode value) { 699 ValueNode result = originalValueSimple(value); 700 assert result != null; 701 return result; 702 } 703 704 private static ValueNode originalValueSimple(ValueNode value) { 705 /* The very simple case: look through proxies. */ 706 ValueNode cur = originalValueForProxy(value); 707 708 while (cur instanceof PhiNode) { 709 /* 710 * We found a phi function. Check if we can analyze it without allocating temporary data 711 * structures. 712 */ 713 PhiNode phi = (PhiNode) cur; 714 715 ValueNode phiSingleValue = null; 716 int count = phi.valueCount(); 717 for (int i = 0; i < count; ++i) { 718 ValueNode phiCurValue = originalValueForProxy(phi.valueAt(i)); 719 if (phiCurValue == phi) { 720 /* Simple cycle, we can ignore the input value. */ 721 } else if (phiSingleValue == null) { 722 /* The first input. */ 723 phiSingleValue = phiCurValue; 724 } else if (phiSingleValue != phiCurValue) { 725 /* Another input that is different from the first input. */ 726 727 if (phiSingleValue instanceof PhiNode || phiCurValue instanceof PhiNode) { 728 /* 729 * We have two different input values for the phi function, and at least one 730 * of the inputs is another phi function. We need to do a complicated 731 * exhaustive check. 732 */ 733 return originalValueForComplicatedPhi(phi, new NodeBitMap(value.graph())); 734 } else { 735 /* 736 * We have two different input values for the phi function, but none of them 737 * is another phi function. This phi function cannot be reduce any further, 738 * so the phi function is the original value. 739 */ 740 return phi; 741 } 742 } 743 } 744 745 /* 746 * Successfully reduced the phi function to a single input value. The single input value 747 * can itself be a phi function again, so we might take another loop iteration. 748 */ 749 assert phiSingleValue != null; 750 cur = phiSingleValue; 751 } 752 753 /* We reached a "normal" node, which is the original value. */ 754 assert !(cur instanceof LimitedValueProxy) && !(cur instanceof PhiNode); 755 return cur; 756 } 757 758 private static ValueNode originalValueForProxy(ValueNode value) { 759 ValueNode cur = value; 760 while (cur instanceof LimitedValueProxy) { 761 cur = ((LimitedValueProxy) cur).getOriginalNode(); 762 } 763 return cur; 764 } 765 766 /** 767 * Handling for complicated nestings of phi functions. We need to reduce phi functions 768 * recursively, and need a temporary map of visited nodes to avoid endless recursion of cycles. 769 */ 770 private static ValueNode originalValueForComplicatedPhi(PhiNode phi, NodeBitMap visited) { 771 if (visited.isMarked(phi)) { 772 /* 773 * Found a phi function that was already seen. Either a cycle, or just a second phi 774 * input to a path we have already processed. 775 */ 776 return null; 777 } 778 visited.mark(phi); 779 780 ValueNode phiSingleValue = null; 781 int count = phi.valueCount(); 782 for (int i = 0; i < count; ++i) { 783 ValueNode phiCurValue = originalValueForProxy(phi.valueAt(i)); 784 if (phiCurValue instanceof PhiNode) { 785 /* Recursively process a phi function input. */ 786 phiCurValue = originalValueForComplicatedPhi((PhiNode) phiCurValue, visited); 787 } 788 789 if (phiCurValue == null) { 790 /* Cycle to a phi function that was already seen. We can ignore this input. */ 791 } else if (phiSingleValue == null) { 792 /* The first input. */ 793 phiSingleValue = phiCurValue; 794 } else if (phiCurValue != phiSingleValue) { 795 /* 796 * Another input that is different from the first input. Since we already 797 * recursively looked through other phi functions, we now know that this phi 798 * function cannot be reduce any further, so the phi function is the original value. 799 */ 800 return phi; 801 } 802 } 803 return phiSingleValue; 804 } 805 806 public static boolean tryKillUnused(Node node) { 807 if (node.isAlive() && isFloatingNode(node) && node.hasNoUsages() && !(node instanceof GuardNode)) { 808 killWithUnusedFloatingInputs(node); 809 return true; 810 } 811 return false; 812 } 813 814 /** 815 * Returns an iterator that will return the given node followed by all its predecessors, up 816 * until the point where {@link Node#predecessor()} returns null. 817 * 818 * @param start the node at which to start iterating 819 */ 820 public static NodeIterable<FixedNode> predecessorIterable(final FixedNode start) { 821 return new NodeIterable<FixedNode>() { 822 @Override 823 public Iterator<FixedNode> iterator() { 824 return new Iterator<FixedNode>() { 825 public FixedNode current = start; 826 827 @Override 828 public boolean hasNext() { 829 return current != null; 830 } 831 832 @Override 833 public FixedNode next() { 834 try { 835 return current; 836 } finally { 837 current = (FixedNode) current.predecessor(); 838 } 839 } 840 }; 841 } 842 }; 843 } 844 845 private static final class DefaultSimplifierTool implements SimplifierTool { 846 private final MetaAccessProvider metaAccess; 847 private final ConstantReflectionProvider constantReflection; 848 private final ConstantFieldProvider constantFieldProvider; 849 private final boolean canonicalizeReads; 850 private final Assumptions assumptions; 851 private final OptionValues options; 852 private final LoweringProvider loweringProvider; 853 854 DefaultSimplifierTool(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, boolean canonicalizeReads, 855 Assumptions assumptions, OptionValues options, LoweringProvider loweringProvider) { 856 this.metaAccess = metaAccess; 857 this.constantReflection = constantReflection; 858 this.constantFieldProvider = constantFieldProvider; 859 this.canonicalizeReads = canonicalizeReads; 860 this.assumptions = assumptions; 861 this.options = options; 862 this.loweringProvider = loweringProvider; 863 } 864 865 @Override 866 public MetaAccessProvider getMetaAccess() { 867 return metaAccess; 868 } 869 870 @Override 871 public ConstantReflectionProvider getConstantReflection() { 872 return constantReflection; 873 } 874 875 @Override 876 public ConstantFieldProvider getConstantFieldProvider() { 877 return constantFieldProvider; 878 } 879 880 @Override 881 public boolean canonicalizeReads() { 882 return canonicalizeReads; 883 } 884 885 @Override 886 public boolean allUsagesAvailable() { 887 return true; 888 } 889 890 @Override 891 public void deleteBranch(Node branch) { 892 FixedNode fixedBranch = (FixedNode) branch; 893 fixedBranch.predecessor().replaceFirstSuccessor(fixedBranch, null); 894 GraphUtil.killCFG(fixedBranch); 895 } 896 897 @Override 898 public void removeIfUnused(Node node) { 899 GraphUtil.tryKillUnused(node); 900 } 901 902 @Override 903 public void addToWorkList(Node node) { 904 } 905 906 @Override 907 public void addToWorkList(Iterable<? extends Node> nodes) { 908 } 909 910 @Override 911 public Assumptions getAssumptions() { 912 return assumptions; 913 } 914 915 @Override 916 public OptionValues getOptions() { 917 return options; 918 } 919 920 @Override 921 public Integer smallestCompareWidth() { 922 if (loweringProvider != null) { 923 return loweringProvider.smallestCompareWidth(); 924 } else { 925 return null; 926 } 927 } 928 } 929 930 public static SimplifierTool getDefaultSimplifier(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, 931 boolean canonicalizeReads, Assumptions assumptions, OptionValues options) { 932 return getDefaultSimplifier(metaAccess, constantReflection, constantFieldProvider, canonicalizeReads, assumptions, options, null); 933 } 934 935 public static SimplifierTool getDefaultSimplifier(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, 936 boolean canonicalizeReads, Assumptions assumptions, OptionValues options, LoweringProvider loweringProvider) { 937 return new DefaultSimplifierTool(metaAccess, constantReflection, constantFieldProvider, canonicalizeReads, assumptions, options, loweringProvider); 938 } 939 940 public static Constant foldIfConstantAndRemove(ValueNode node, ValueNode constant) { 941 assert node.inputs().contains(constant); 942 if (constant.isConstant()) { 943 node.replaceFirstInput(constant, null); 944 Constant result = constant.asConstant(); 945 tryKillUnused(constant); 946 return result; 947 } 948 return null; 949 } 950 951 /** 952 * Virtualize an array copy. 953 * 954 * @param tool the virtualization tool 955 * @param source the source array 956 * @param sourceLength the length of the source array 957 * @param newLength the length of the new array 958 * @param from the start index in the source array 959 * @param newComponentType the component type of the new array 960 * @param elementKind the kind of the new array elements 961 * @param graph the node graph 962 * @param virtualArrayProvider a functional provider that returns a new virtual array given the 963 * component type and length 964 */ 965 public static void virtualizeArrayCopy(VirtualizerTool tool, ValueNode source, ValueNode sourceLength, ValueNode newLength, ValueNode from, ResolvedJavaType newComponentType, JavaKind elementKind, 966 StructuredGraph graph, BiFunction<ResolvedJavaType, Integer, VirtualArrayNode> virtualArrayProvider) { 967 968 ValueNode sourceAlias = tool.getAlias(source); 969 ValueNode replacedSourceLength = tool.getAlias(sourceLength); 970 ValueNode replacedNewLength = tool.getAlias(newLength); 971 ValueNode replacedFrom = tool.getAlias(from); 972 if (!replacedNewLength.isConstant() || !replacedFrom.isConstant() || !replacedSourceLength.isConstant()) { 973 return; 974 } 975 976 assert newComponentType != null : "An array copy can be virtualized only if the real type of the resulting array is known statically."; 977 978 int fromInt = replacedFrom.asJavaConstant().asInt(); 979 int newLengthInt = replacedNewLength.asJavaConstant().asInt(); 980 int sourceLengthInt = replacedSourceLength.asJavaConstant().asInt(); 981 if (sourceAlias instanceof VirtualObjectNode) { 982 VirtualObjectNode sourceVirtual = (VirtualObjectNode) sourceAlias; 983 assert sourceLengthInt == sourceVirtual.entryCount(); 984 } 985 986 if (fromInt < 0 || newLengthInt < 0 || fromInt > sourceLengthInt) { 987 /* Illegal values for either from index, the new length or the source length. */ 988 return; 989 } 990 991 if (newLengthInt >= tool.getMaximumEntryCount()) { 992 /* The new array size is higher than maximum allowed size of virtualized objects. */ 993 return; 994 } 995 996 ValueNode[] newEntryState = new ValueNode[newLengthInt]; 997 int readLength = Math.min(newLengthInt, sourceLengthInt - fromInt); 998 999 if (sourceAlias instanceof VirtualObjectNode) { 1000 /* The source array is virtualized, just copy over the values. */ 1001 VirtualObjectNode sourceVirtual = (VirtualObjectNode) sourceAlias; 1002 for (int i = 0; i < readLength; i++) { 1003 newEntryState[i] = tool.getEntry(sourceVirtual, fromInt + i); 1004 } 1005 } else { 1006 /* The source array is not virtualized, emit index loads. */ 1007 for (int i = 0; i < readLength; i++) { 1008 LoadIndexedNode load = new LoadIndexedNode(null, sourceAlias, ConstantNode.forInt(i + fromInt, graph), elementKind); 1009 tool.addNode(load); 1010 newEntryState[i] = load; 1011 } 1012 } 1013 if (readLength < newLengthInt) { 1014 /* Pad the copy with the default value of its elment kind. */ 1015 ValueNode defaultValue = ConstantNode.defaultForKind(elementKind, graph); 1016 for (int i = readLength; i < newLengthInt; i++) { 1017 newEntryState[i] = defaultValue; 1018 } 1019 } 1020 /* Perform the replacement. */ 1021 VirtualArrayNode newVirtualArray = virtualArrayProvider.apply(newComponentType, newLengthInt); 1022 tool.createVirtualObject(newVirtualArray, newEntryState, Collections.<MonitorIdNode> emptyList(), false); 1023 tool.replaceWithVirtual(newVirtualArray); 1024 } 1025 }