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