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