changeset 20896:c7f1ab98d950

Improve speed of Graph partial evaluation
author Christian Wimmer <christian.wimmer@oracle.com>
date Sat, 11 Apr 2015 00:16:29 -0700
parents 23433619a7cd
children ca13a009e38b
files graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java
diffstat 10 files changed, 614 insertions(+), 283 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java	Sat Apr 11 00:16:29 2015 -0700
@@ -41,6 +41,12 @@
     private final Assumptions assumptions;
     private final Set<ResolvedJavaMethod> inlinedMethods;
 
+    /**
+     * The "table of contents" of the encoded graph, i.e., the mapping from orderId numbers to the
+     * offset in the encoded byte[] array. Used as a cache during decoding.
+     */
+    protected long[] nodeStartOffsets;
+
     public EncodedGraph(byte[] encoding, long startOffset, Object[] objects, NodeClass<?>[] types, Assumptions assumptions, Set<ResolvedJavaMethod> inlinedMethods) {
         this.encoding = encoding;
         this.startOffset = startOffset;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Sat Apr 11 00:16:29 2015 -0700
@@ -227,11 +227,11 @@
      * the stack.
      */
     public FrameState duplicateModifiedDuringCall(int newBci, Kind popKind) {
-        return duplicateModified(newBci, rethrowException, true, popKind);
+        return duplicateModified(graph(), newBci, rethrowException, true, popKind);
     }
 
     public FrameState duplicateModifiedBeforeCall(int newBci, Kind popKind, ValueNode... pushedValues) {
-        return duplicateModified(newBci, rethrowException, false, popKind, pushedValues);
+        return duplicateModified(graph(), newBci, rethrowException, false, popKind, pushedValues);
     }
 
     /**
@@ -241,7 +241,7 @@
      * followed by a null slot.
      */
     public FrameState duplicateModified(int newBci, boolean newRethrowException, Kind popKind, ValueNode... pushedValues) {
-        return duplicateModified(newBci, newRethrowException, duringCall, popKind, pushedValues);
+        return duplicateModified(graph(), newBci, newRethrowException, duringCall, popKind, pushedValues);
     }
 
     /**
@@ -250,7 +250,7 @@
      */
     public FrameState duplicateModified(Kind popKind, ValueNode pushedValue) {
         assert pushedValue != null && pushedValue.getKind() == popKind;
-        return duplicateModified(bci, rethrowException, duringCall, popKind, pushedValue);
+        return duplicateModified(graph(), bci, rethrowException, duringCall, popKind, pushedValue);
     }
 
     /**
@@ -259,7 +259,7 @@
      * correctly in slot encoding: a long or double will be followed by a null slot. The bci will be
      * changed to newBci.
      */
-    private FrameState duplicateModified(int newBci, boolean newRethrowException, boolean newDuringCall, Kind popKind, ValueNode... pushedValues) {
+    public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean newRethrowException, boolean newDuringCall, Kind popKind, ValueNode... pushedValues) {
         ArrayList<ValueNode> copy;
         if (newRethrowException && !rethrowException && popKind == Kind.Void) {
             assert popKind == Kind.Void;
@@ -285,7 +285,7 @@
         copy.addAll(values.subList(localsSize + stackSize, values.size()));
 
         assert checkStackDepth(bci, stackSize, duringCall, rethrowException, newBci, newStackSize, newDuringCall, newRethrowException);
-        return graph().add(new FrameState(outerFrameState(), method, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings));
+        return graph.add(new FrameState(outerFrameState(), method, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings));
     }
 
     /**
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java	Sat Apr 11 00:16:29 2015 -0700
@@ -26,6 +26,7 @@
 
 import java.util.*;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.util.*;
 import com.oracle.graal.debug.*;
@@ -70,48 +71,38 @@
     protected static class MethodScope {
         /** The target graph where decoded nodes are added to. */
         public final StructuredGraph graph;
-        /** The state of the caller method. Only non-null during method inlining. */
-        public final MethodScope caller;
         /** The encode graph that is decoded. */
         public final EncodedGraph encodedGraph;
         /** Access to the encoded graph. */
         public final TypeReader reader;
-        /**
-         * The "table of contents" of the encoded graph, i.e., the mapping from orderId numbers to
-         * the offset in the encoded byte[] array.
-         */
-        public final long[] nodeStartOffsets;
         /** The kind of loop explosion to be performed during decoding. */
         public final LoopExplosionKind loopExplosion;
 
-        /**
-         * The start node of the decoded graph. This is a temporary node for inlined graphs that
-         * needs to be deleted after inlining.
-         */
-        public StartNode startNode;
         /** All return nodes encountered during decoding. */
         public final List<ReturnNode> returnNodes;
         /** The exception unwind node encountered during decoding, or null. */
         public UnwindNode unwindNode;
 
-        protected MethodScope(StructuredGraph graph, MethodScope caller, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) {
+        protected MethodScope(StructuredGraph graph, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) {
             this.graph = graph;
-            this.caller = caller;
             this.encodedGraph = encodedGraph;
             this.loopExplosion = loopExplosion;
             this.returnNodes = new ArrayList<>();
 
-            reader = new UnsafeArrayTypeReader(encodedGraph.getEncoding(), encodedGraph.getStartOffset());
-            int nodeCount = reader.getUVInt();
-            nodeStartOffsets = new long[nodeCount];
-            for (int i = 0; i < nodeCount; i++) {
-                nodeStartOffsets[i] = encodedGraph.getStartOffset() - reader.getUV();
+            if (encodedGraph != null) {
+                reader = new UnsafeArrayTypeReader(encodedGraph.getEncoding(), encodedGraph.getStartOffset());
+                if (encodedGraph.nodeStartOffsets == null) {
+                    int nodeCount = reader.getUVInt();
+                    long[] nodeStartOffsets = new long[nodeCount];
+                    for (int i = 0; i < nodeCount; i++) {
+                        nodeStartOffsets[i] = encodedGraph.getStartOffset() - reader.getUV();
+                    }
+                    encodedGraph.nodeStartOffsets = nodeStartOffsets;
+                }
+            } else {
+                reader = null;
             }
         }
-
-        public boolean isInlinedMethod() {
-            return caller != null;
-        }
     }
 
     /** Decoding state maintained for each loop in the encoded graph. */
@@ -153,7 +144,7 @@
             this.iterationStates = null;
             this.loopBeginOrderId = -1;
 
-            int nodeCount = methodScope.nodeStartOffsets.length;
+            int nodeCount = methodScope.encodedGraph.nodeStartOffsets.length;
             this.nodesToProcess = new BitSet(nodeCount);
             this.initialCreatedNodes = new Node[nodeCount];
             this.createdNodes = new Node[nodeCount];
@@ -226,22 +217,55 @@
         }
     }
 
+    /**
+     * Additional information encoded for {@link Invoke} nodes to allow method inlining without
+     * decoding the frame state and successors beforehand.
+     */
+    protected static class InvokeData {
+        public final Invoke invoke;
+        public final ResolvedJavaType contextType;
+        public final int invokeOrderId;
+        public final int callTargetOrderId;
+        public final int stateAfterOrderId;
+        public final int nextOrderId;
+
+        public final int nextNextOrderId;
+        public final int exceptionOrderId;
+        public final int exceptionStateOrderId;
+        public final int exceptionNextOrderId;
+
+        protected InvokeData(Invoke invoke, ResolvedJavaType contextType, int invokeOrderId, int callTargetOrderId, int stateAfterOrderId, int nextOrderId, int nextNextOrderId, int exceptionOrderId,
+                        int exceptionStateOrderId, int exceptionNextOrderId) {
+            this.invoke = invoke;
+            this.contextType = contextType;
+            this.invokeOrderId = invokeOrderId;
+            this.callTargetOrderId = callTargetOrderId;
+            this.stateAfterOrderId = stateAfterOrderId;
+            this.nextOrderId = nextOrderId;
+            this.nextNextOrderId = nextNextOrderId;
+            this.exceptionOrderId = exceptionOrderId;
+            this.exceptionStateOrderId = exceptionStateOrderId;
+            this.exceptionNextOrderId = exceptionNextOrderId;
+        }
+    }
+
     public final void decode(StructuredGraph graph, EncodedGraph encodedGraph) {
-        MethodScope methodScope = new MethodScope(graph, null, encodedGraph, LoopExplosionKind.NONE);
-        decode(methodScope);
+        MethodScope methodScope = new MethodScope(graph, encodedGraph, LoopExplosionKind.NONE);
+        decode(methodScope, null);
         cleanupGraph(methodScope);
         methodScope.graph.verify();
     }
 
-    protected final void decode(MethodScope methodScope) {
+    protected final void decode(MethodScope methodScope, FixedWithNextNode startNode) {
         LoopScope loopScope = new LoopScope(methodScope);
-        if (methodScope.isInlinedMethod()) {
-            methodScope.startNode = methodScope.graph.add(new StartNode());
-            methodScope.startNode.setNext(makeStubNode(methodScope, loopScope, GraphEncoder.FIRST_NODE_ORDER_ID));
+        FixedNode firstNode;
+        if (startNode != null) {
+            firstNode = makeStubNode(methodScope, loopScope, GraphEncoder.FIRST_NODE_ORDER_ID);
+            startNode.setNext(firstNode);
             loopScope.nodesToProcess.set(GraphEncoder.FIRST_NODE_ORDER_ID);
         } else {
-            methodScope.startNode = methodScope.graph.start();
-            registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, methodScope.startNode, false, false);
+            firstNode = methodScope.graph.start();
+            registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, firstNode, false, false);
             loopScope.nodesToProcess.set(GraphEncoder.START_NODE_ORDER_ID);
         }
 
@@ -262,7 +286,7 @@
         if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) {
             cleanupGraph(methodScope);
             Debug.dump(methodScope.graph, "Before loop detection");
-            detectLoops(methodScope.graph, methodScope.startNode);
+            detectLoops(methodScope.graph, firstNode);
         }
     }
 
@@ -275,6 +299,18 @@
             return loopScope;
         }
 
+        if ((node instanceof MergeNode || (node instanceof LoopBeginNode && (methodScope.loopExplosion == LoopExplosionKind.FULL_UNROLL || methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE))) &&
+                        ((AbstractMergeNode) node).forwardEndCount() == 1) {
+            AbstractMergeNode merge = (AbstractMergeNode) node;
+            EndNode singleEnd = merge.forwardEndAt(0);
+            FixedNode next = makeStubNode(methodScope, loopScope, nodeOrderId + GraphEncoder.BEGIN_NEXT_ORDER_ID_OFFSET);
+            singleEnd.replaceAtPredecessor(next);
+
+            merge.safeDelete();
+            singleEnd.safeDelete();
+            return loopScope;
+        }
+
         LoopScope successorAddScope = loopScope;
         boolean updatePredecessors = true;
         if (node instanceof LoopExitNode) {
@@ -282,7 +318,7 @@
             updatePredecessors = methodScope.loopExplosion == LoopExplosionKind.NONE;
         }
 
-        methodScope.reader.setByteIndex(methodScope.nodeStartOffsets[nodeOrderId]);
+        methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
         int typeId = methodScope.reader.getUVInt();
         assert node.getNodeClass() == methodScope.encodedGraph.getNodeClasses()[typeId];
         readProperties(methodScope, node);
@@ -299,7 +335,7 @@
             if (methodScope.loopExplosion != LoopExplosionKind.NONE) {
                 handleLoopExplosionProxyNodes(methodScope, loopScope, (LoopExitNode) node, nodeOrderId);
             } else {
-                handleProxyNodes(methodScope, loopScope);
+                handleProxyNodes(methodScope, loopScope, (LoopExitNode) node);
             }
 
         } else if (node instanceof AbstractEndNode) {
@@ -311,7 +347,7 @@
                 phiNodeScope = loopScope.nextIterations.getLast();
             }
 
-            int mergeOrderId = methodScope.reader.getUVInt();
+            int mergeOrderId = readOrderId(methodScope);
             AbstractMergeNode merge = (AbstractMergeNode) lookupNode(phiNodeScope, mergeOrderId);
             if (merge == null) {
                 merge = (AbstractMergeNode) makeStubNode(methodScope, phiNodeScope, mergeOrderId);
@@ -332,7 +368,8 @@
             handlePhiFunctions(methodScope, phiInputScope, phiNodeScope, (AbstractEndNode) node, merge);
 
         } else if (node instanceof Invoke) {
-            simplifyInvoke(methodScope, loopScope, nodeOrderId, (Invoke) node);
+            InvokeData invokeData = readInvokeData(methodScope, nodeOrderId, (Invoke) node);
+            handleInvoke(methodScope, loopScope, invokeData);
 
         } else if (node instanceof ReturnNode) {
             methodScope.returnNodes.add((ReturnNode) node);
@@ -347,15 +384,45 @@
         return resultScope;
     }
 
+    private InvokeData readInvokeData(MethodScope methodScope, int invokeOrderId, Invoke invoke) {
+        ResolvedJavaType contextType = (ResolvedJavaType) readObject(methodScope);
+        int callTargetOrderId = readOrderId(methodScope);
+        int stateAfterOrderId = readOrderId(methodScope);
+        int nextOrderId = readOrderId(methodScope);
+
+        if (invoke instanceof InvokeWithExceptionNode) {
+            int nextNextOrderId = readOrderId(methodScope);
+            int exceptionOrderId = readOrderId(methodScope);
+            int exceptionStateOrderId = readOrderId(methodScope);
+            int exceptionNextOrderId = readOrderId(methodScope);
+            return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, nextNextOrderId, exceptionOrderId, exceptionStateOrderId, exceptionNextOrderId);
+        } else {
+            return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, -1, -1, -1, -1);
+        }
+    }
+
     /**
-     * Hook for subclasses.
-     *
-     * @param methodScope The current method.
-     * @param loopScope The current loop.
-     * @param invokeOrderId The orderId of the method invocation node.
-     * @param invoke The invocation node.
+     * {@link Invoke} nodes do not have the {@link CallTargetNode}, {@link FrameState}, and
+     * successors encoded. Instead, this information is provided separately to allow method inlining
+     * without decoding and adding them to the graph upfront. For non-inlined methods, this method
+     * restores the normal state. Subclasses can override it to perform method inlining.
      */
-    protected void simplifyInvoke(MethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
+    protected void handleInvoke(MethodScope methodScope, LoopScope loopScope, InvokeData invokeData) {
+        assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke";
+        CallTargetNode callTarget = (CallTargetNode) ensureNodeCreated(methodScope, loopScope, invokeData.callTargetOrderId);
+        if (invokeData.invoke instanceof InvokeWithExceptionNode) {
+            ((InvokeWithExceptionNode) invokeData.invoke).setCallTarget(callTarget);
+        } else {
+            ((InvokeNode) invokeData.invoke).setCallTarget(callTarget);
+        }
+
+        assert invokeData.invoke.stateAfter() == null && invokeData.invoke.stateDuring() == null : "FrameState edges are ignored during decoding of Invoke";
+        invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
+
+        invokeData.invoke.setNext(makeStubNode(methodScope, loopScope, invokeData.nextOrderId));
+        if (invokeData.invoke instanceof InvokeWithExceptionNode) {
+            ((InvokeWithExceptionNode) invokeData.invoke).setExceptionEdge((AbstractBeginNode) makeStubNode(methodScope, loopScope, invokeData.exceptionOrderId));
+        }
     }
 
     protected void handleLoopExplosionBegin(MethodScope methodScope, LoopScope loopScope, LoopBeginNode loopBegin) {
@@ -432,10 +499,14 @@
     protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
     }
 
-    protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope) {
+    protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit) {
+        assert loopExit.stateAfter() == null;
+        int stateAfterOrderId = readOrderId(methodScope);
+        loopExit.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, stateAfterOrderId));
+
         int numProxies = methodScope.reader.getUVInt();
         for (int i = 0; i < numProxies; i++) {
-            int proxyOrderId = methodScope.reader.getUVInt();
+            int proxyOrderId = readOrderId(methodScope);
             ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId);
             /*
              * The ProxyNode transports a value from the loop to the outer scope. We therefore
@@ -446,9 +517,11 @@
     }
 
     protected void handleLoopExplosionProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit, int loopExitOrderId) {
+        assert loopExit.stateAfter() == null;
+        int stateAfterOrderId = readOrderId(methodScope);
+
         BeginNode begin = methodScope.graph.add(new BeginNode());
 
-        FrameState loopExitState = loopExit.stateAfter();
         FixedNode loopExitSuccessor = loopExit.next();
         loopExit.replaceAtPredecessor(begin);
 
@@ -457,33 +530,43 @@
          * iterations now take the same loop exit, so we have to introduce a new merge node to
          * handle the merge.
          */
-        MergeNode merge;
-        if (lookupNode(loopScope.outer, loopExitOrderId) == null) {
+        MergeNode merge = null;
+        Node existingExit = lookupNode(loopScope.outer, loopExitOrderId);
+        if (existingExit == null) {
+            /* First loop iteration that exits. No merge necessary yet. */
+            registerNode(loopScope.outer, loopExitOrderId, begin, false, false);
+            begin.setNext(loopExitSuccessor);
+
+        } else if (existingExit instanceof BeginNode) {
+            /* Second loop iteration that exits. Create the merge. */
             merge = methodScope.graph.add(new MergeNode());
-            registerNode(loopScope.outer, loopExitOrderId, merge, false, false);
+            registerNode(loopScope.outer, loopExitOrderId, merge, true, false);
+            /* Add the first iteration. */
+            EndNode firstEnd = methodScope.graph.add(new EndNode());
+            ((BeginNode) existingExit).setNext(firstEnd);
+            merge.addForwardEnd(firstEnd);
             merge.setNext(loopExitSuccessor);
+
         } else {
-            merge = (MergeNode) loopScope.outer.createdNodes[loopExitOrderId];
+            /* Subsequent loop iteration. Merge already created. */
+            merge = (MergeNode) existingExit;
         }
 
-        FrameState oldStateAfter = merge.stateAfter();
-        merge.setStateAfter(loopExitState);
-        if (oldStateAfter != null) {
-            oldStateAfter.safeDelete();
+        if (merge != null) {
+            EndNode end = methodScope.graph.add(new EndNode());
+            begin.setNext(end);
+            merge.addForwardEnd(end);
         }
 
-        EndNode end = methodScope.graph.add(new EndNode());
-        begin.setNext(end);
-        merge.addForwardEnd(end);
-
         /*
          * Possibly create phi nodes for the original proxy nodes that flow out of the loop. Note
          * that we definitely do not need a proxy node itself anymore, since the loop was exploded
          * and is no longer present.
          */
         int numProxies = methodScope.reader.getUVInt();
+        boolean phiCreated = false;
         for (int i = 0; i < numProxies; i++) {
-            int proxyOrderId = methodScope.reader.getUVInt();
+            int proxyOrderId = readOrderId(methodScope);
             ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId);
             ValueNode phiInput = proxy.value();
             ValueNode replacement;
@@ -508,6 +591,7 @@
                 phi.addInput(phiInput);
                 registerNode(loopScope.outer, proxyOrderId, phi, true, false);
                 replacement = phi;
+                phiCreated = true;
 
             } else {
                 /* Phi node has been created before, so just add the new input. */
@@ -519,9 +603,22 @@
             methodScope.graph.replaceFloating(proxy, replacement);
         }
 
+        if (merge != null && (merge.stateAfter() == null || phiCreated)) {
+            FrameState oldStateAfter = merge.stateAfter();
+            registerNode(loopScope.outer, stateAfterOrderId, null, true, true);
+            merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope.outer, stateAfterOrderId));
+            if (oldStateAfter != null) {
+                oldStateAfter.safeDelete();
+            }
+        }
+
         loopExit.safeDelete();
         assert loopExitSuccessor.predecessor() == null;
-        merge.getNodeClass().getSuccessorEdges().update(merge, null, loopExitSuccessor);
+        if (merge != null) {
+            merge.getNodeClass().getSuccessorEdges().update(merge, null, loopExitSuccessor);
+        } else {
+            begin.getNodeClass().getSuccessorEdges().update(begin, null, loopExitSuccessor);
+        }
     }
 
     protected void handlePhiFunctions(MethodScope methodScope, LoopScope phiInputScope, LoopScope phiNodeScope, AbstractEndNode end, AbstractMergeNode merge) {
@@ -555,8 +652,8 @@
         boolean lazyPhi = !(merge instanceof LoopBeginNode) || methodScope.loopExplosion != LoopExplosionKind.NONE;
         int numPhis = methodScope.reader.getUVInt();
         for (int i = 0; i < numPhis; i++) {
-            int phiInputOrderId = methodScope.reader.getUVInt();
-            int phiNodeOrderId = methodScope.reader.getUVInt();
+            int phiInputOrderId = readOrderId(methodScope);
+            int phiNodeOrderId = readOrderId(methodScope);
 
             ValueNode phiInput = (ValueNode) ensureNodeCreated(methodScope, phiInputScope, phiInputOrderId);
 
@@ -588,7 +685,7 @@
     }
 
     protected Node instantiateNode(MethodScope methodScope, int nodeOrderId) {
-        methodScope.reader.setByteIndex(methodScope.nodeStartOffsets[nodeOrderId]);
+        methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
         NodeClass<?> nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()];
         return nodeClass.allocateInstance();
     }
@@ -600,8 +697,7 @@
                 long primitive = methodScope.reader.getSV();
                 fields.setRawPrimitive(node, pos, primitive);
             } else {
-                int objectId = methodScope.reader.getUVInt();
-                Object value = methodScope.encodedGraph.getObjects()[objectId];
+                Object value = readObject(methodScope);
                 fields.set(node, pos, value);
             }
         }
@@ -619,7 +715,7 @@
             if (skipEdge(node, edges, index, true, true)) {
                 continue;
             }
-            int orderId = methodScope.reader.getUVInt();
+            int orderId = readOrderId(methodScope);
             Node value = ensureNodeCreated(methodScope, loopScope, orderId);
             Edges.initializeNode(node, edges.getOffsets(), index, value);
             if (updateUsages && value != null && !value.isDeleted()) {
@@ -636,7 +732,7 @@
                 NodeList<Node> nodeList = new NodeInputList<>(node, size);
                 Edges.initializeList(node, edges.getOffsets(), index, nodeList);
                 for (int idx = 0; idx < size; idx++) {
-                    int orderId = methodScope.reader.getUVInt();
+                    int orderId = readOrderId(methodScope);
                     Node value = ensureNodeCreated(methodScope, loopScope, orderId);
                     nodeList.initialize(idx, value);
                     if (updateUsages && value != null && !value.isDeleted()) {
@@ -656,17 +752,7 @@
             return node;
         }
 
-        long readerByteIndex = methodScope.reader.getByteIndex();
-        node = instantiateNode(methodScope, nodeOrderId);
-        assert !(node instanceof FixedNode);
-
-        /* Read the properties of the node. */
-        readProperties(methodScope, node);
-        /* There must not be any successors to read, since it is a non-fixed node. */
-        assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0;
-        /* Read the inputs of the node, possibly creating them recursively. */
-        makeInputNodes(methodScope, loopScope, node, false);
-        methodScope.reader.setByteIndex(readerByteIndex);
+        node = decodeFloatingNode(methodScope, loopScope, nodeOrderId);
 
         if (node instanceof ProxyNode || node instanceof PhiNode) {
             /*
@@ -688,6 +774,24 @@
     }
 
     /**
+     * Decodes a non-fixed node, but does not do any post-processing and does not register it.
+     */
+    protected Node decodeFloatingNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) {
+        long readerByteIndex = methodScope.reader.getByteIndex();
+        Node node = instantiateNode(methodScope, nodeOrderId);
+        assert !(node instanceof FixedNode);
+
+        /* Read the properties of the node. */
+        readProperties(methodScope, node);
+        /* There must not be any successors to read, since it is a non-fixed node. */
+        assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0;
+        /* Read the inputs of the node, possibly creating them recursively. */
+        makeInputNodes(methodScope, loopScope, node, false);
+        methodScope.reader.setByteIndex(readerByteIndex);
+        return node;
+    }
+
+    /**
      * Hook for subclasses to process a non-fixed node before it is added to the graph.
      *
      * @param methodScope The current method.
@@ -722,7 +826,7 @@
             if (skipEdge(node, edges, index, true, true)) {
                 continue;
             }
-            int orderId = methodScope.reader.getUVInt();
+            int orderId = readOrderId(methodScope);
             Node value = makeStubNode(methodScope, loopScope, orderId);
             Edges.initializeNode(node, edges.getOffsets(), index, value);
             if (updatePredecessors && value != null) {
@@ -738,7 +842,7 @@
                 NodeList<Node> nodeList = new NodeSuccessorList<>(node, size);
                 Edges.initializeList(node, edges.getOffsets(), index, nodeList);
                 for (int idx = 0; idx < size; idx++) {
-                    int orderId = methodScope.reader.getUVInt();
+                    int orderId = readOrderId(methodScope);
                     Node value = makeStubNode(methodScope, loopScope, orderId);
                     nodeList.initialize(idx, value);
                     if (updatePredecessors && value != null) {
@@ -792,6 +896,26 @@
             assert index == edges.getCount() - 1 : "MergeNode has one variable size input (the ends)";
             assert Edges.getNodeList(node, edges.getOffsets(), index) != null : "Input list must have been already created";
             return true;
+
+        } else if (node instanceof LoopExitNode && edges.type() == Edges.Type.Inputs && edges.getType(index) == FrameState.class) {
+            /* The stateAfter of the loop exit is filled manually. */
+            return true;
+
+        } else if (node instanceof Invoke) {
+            assert node instanceof InvokeNode || node instanceof InvokeWithExceptionNode : "The only two Invoke node classes";
+            assert direct : "Invoke and InvokeWithException only have direct successor and input edges";
+            if (edges.type() == Edges.Type.Successors) {
+                assert edges.getCount() == (node instanceof InvokeWithExceptionNode ? 2 : 1) : "InvokeNode has one successor (next); InvokeWithExceptionNode has two successors (next, exceptionEdge)";
+                return true;
+            } else {
+                assert edges.type() == Edges.Type.Inputs;
+                if (edges.getType(index) == CallTargetNode.class) {
+                    return true;
+                } else if (edges.getType(index) == FrameState.class) {
+                    assert edges.get(node, index) == null || edges.get(node, index) == ((Invoke) node).stateAfter() : "Only stateAfter can be a FrameState during encoding";
+                    return true;
+                }
+            }
         }
         return false;
     }
@@ -807,6 +931,14 @@
         loopScope.createdNodes[nodeOrderId] = node;
     }
 
+    protected int readOrderId(MethodScope methodScope) {
+        return methodScope.reader.getUVInt();
+    }
+
+    protected Object readObject(MethodScope methodScope) {
+        return methodScope.encodedGraph.getObjects()[methodScope.reader.getUVInt()];
+    }
+
     /*
      * The following methods are a literal copy from GraphBuilderPhase.
      */
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java	Sat Apr 11 00:16:29 2015 -0700
@@ -29,6 +29,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
+import com.oracle.graal.nodes.java.*;
 
 /**
  * Encodes a {@link StructuredGraph} to a compact byte[] array. All nodes of the graph and edges
@@ -100,6 +101,12 @@
     public static final int FIRST_NODE_ORDER_ID = 2;
 
     /**
+     * The known offset between the orderId of a {@link AbstractBeginNode} and its
+     * {@link AbstractBeginNode#next() successor}.
+     */
+    protected static final int BEGIN_NEXT_ORDER_ID_OFFSET = 1;
+
+    /**
      * Collects all non-primitive data referenced from nodes. The encoding uses an index into an
      * array for decoding. Because of the variable-length encoding, it is beneficial that frequently
      * used objects have the small indices.
@@ -148,6 +155,9 @@
                     objects.addObject(nodeClass.getData().get(node, i));
                 }
             }
+            if (node instanceof Invoke) {
+                objects.addObject(((Invoke) node).getContextType());
+            }
         }
     }
 
@@ -182,7 +192,10 @@
         long[] nodeStartOffsets = new long[nodeCount];
         for (Map.Entry<Node, Integer> entry : nodeOrder.orderIds.entries()) {
             Node node = entry.getKey();
-            nodeStartOffsets[entry.getValue()] = writer.getBytesWritten();
+            Integer orderId = entry.getValue();
+
+            assert !(node instanceof AbstractBeginNode) || nodeOrder.orderIds.get(((AbstractBeginNode) node).next()) == orderId + BEGIN_NEXT_ORDER_ID_OFFSET;
+            nodeStartOffsets[orderId] = writer.getBytesWritten();
 
             /* Write out the type, properties, and edges. */
             NodeClass<?> nodeClass = node.getNodeClass();
@@ -213,11 +226,30 @@
 
             } else if (node instanceof LoopExitNode) {
                 LoopExitNode exit = (LoopExitNode) node;
+                writeOrderId(exit.stateAfter(), nodeOrder);
                 /* Write all proxy nodes of the LoopExitNode. */
                 writer.putUV(exit.proxies().count());
                 for (ProxyNode proxy : exit.proxies()) {
                     writeOrderId(proxy, nodeOrder);
                 }
+
+            } else if (node instanceof Invoke) {
+                Invoke invoke = (Invoke) node;
+                assert invoke.stateDuring() == null : "stateDuring is not used in high-level graphs";
+
+                writeObjectId(invoke.getContextType());
+                writeOrderId(invoke.callTarget(), nodeOrder);
+                writeOrderId(invoke.stateAfter(), nodeOrder);
+                writeOrderId(invoke.next(), nodeOrder);
+                if (invoke instanceof InvokeWithExceptionNode) {
+                    InvokeWithExceptionNode invokeWithExcpetion = (InvokeWithExceptionNode) invoke;
+                    ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExcpetion.exceptionEdge();
+
+                    writeOrderId(invokeWithExcpetion.next().next(), nodeOrder);
+                    writeOrderId(invokeWithExcpetion.exceptionEdge(), nodeOrder);
+                    writeOrderId(exceptionEdge.stateAfter(), nodeOrder);
+                    writeOrderId(exceptionEdge.next(), nodeOrder);
+                }
             }
         }
 
@@ -252,13 +284,8 @@
             FixedNode current = graph.start();
             do {
                 add(current);
-                if (current instanceof InvokeWithExceptionNode) {
-                    /*
-                     * Special handling for invokes: the orderID of the invocation, the regular
-                     * successor, and the exception edge must be consecutive.
-                     */
-                    add(((InvokeWithExceptionNode) current).next());
-                    add(((InvokeWithExceptionNode) current).exceptionEdge());
+                if (current instanceof AbstractBeginNode) {
+                    add(((AbstractBeginNode) current).next());
                 }
 
                 if (current instanceof FixedWithNextNode) {
@@ -308,7 +335,7 @@
                 writer.putSV(primitive);
             } else {
                 Object property = fields.get(node, idx);
-                writer.putUV(objects.getIndex(property));
+                writeObjectId(property);
             }
         }
     }
@@ -343,6 +370,10 @@
         writer.putUV(node == null ? NULL_ORDER_ID : nodeOrder.orderIds.get(node));
     }
 
+    protected void writeObjectId(Object object) {
+        writer.putUV(objects.getIndex(object));
+    }
+
     /**
      * Verification code that checks that the decoding of an encode graph is the same as the
      * original graph.
@@ -365,7 +396,7 @@
 
         pushToWorklist(expectedGraph.start(), actualGraph.start(), nodeMapping, workList);
         while (!workList.isEmpty()) {
-            Pair<Node, Node> pair = workList.pop();
+            Pair<Node, Node> pair = workList.removeFirst();
             Node expectedNode = pair.first;
             Node actualNode = pair.second;
             assert expectedNode.getClass() == actualNode.getClass();
@@ -377,7 +408,7 @@
                 /* The order of the ends can be different, so ignore them. */
                 verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, true);
             } else if (expectedNode instanceof PhiNode) {
-                /* The order of phi inputs can be different, so they are checked manually below. */
+                verifyPhi((PhiNode) expectedNode, (PhiNode) actualNode, nodeMapping, workList);
             } else {
                 verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, false);
             }
@@ -413,20 +444,34 @@
         return true;
     }
 
+    protected static void verifyPhi(PhiNode expectedPhi, PhiNode actualPhi, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) {
+        AbstractMergeNode expectedMergeNode = expectedPhi.merge();
+        AbstractMergeNode actualMergeNode = actualPhi.merge();
+        assert actualMergeNode == nodeMapping.get(expectedMergeNode);
+
+        for (EndNode expectedEndNode : expectedMergeNode.ends) {
+            EndNode actualEndNode = (EndNode) nodeMapping.get(expectedEndNode);
+            if (actualEndNode != null) {
+                ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode);
+                ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode);
+                verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false);
+            }
+        }
+    }
+
     protected static void verifyPhis(AbstractEndNode expectedEndNode, AbstractEndNode actualEndNode, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) {
         AbstractMergeNode expectedMergeNode = expectedEndNode.merge();
         AbstractMergeNode actualMergeNode = (AbstractMergeNode) nodeMapping.get(expectedMergeNode);
-
-        Iterator<PhiNode> actualPhis = actualMergeNode.phis().iterator();
-        for (PhiNode expectedPhi : expectedMergeNode.phis()) {
-            PhiNode actualPhi = actualPhis.next();
-            verifyNodeEqual(expectedPhi, actualPhi, nodeMapping, workList, false);
+        assert actualMergeNode != null;
 
-            ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode);
-            ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode);
-            verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false);
+        for (PhiNode expectedPhi : expectedMergeNode.phis()) {
+            PhiNode actualPhi = (PhiNode) nodeMapping.get(expectedPhi);
+            if (actualPhi != null) {
+                ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode);
+                ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode);
+                verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false);
+            }
         }
-        assert !actualPhis.hasNext();
     }
 
     private static void verifyNodesEqual(NodeIterable<Node> expectedNodes, NodeIterable<Node> actualNodes, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList, boolean ignoreEndNode) {
@@ -453,7 +498,12 @@
 
     protected static void pushToWorklist(Node expectedNode, Node actualNode, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) {
         nodeMapping.set(expectedNode, actualNode);
-        workList.push(new Pair<>(expectedNode, actualNode));
+        if (expectedNode instanceof AbstractEndNode) {
+            /* To ensure phi nodes have been added, we handle everything before block ends. */
+            workList.addLast(new Pair<>(expectedNode, actualNode));
+        } else {
+            workList.addFirst(new Pair<>(expectedNode, actualNode));
+        }
     }
 }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Sat Apr 11 00:16:29 2015 -0700
@@ -140,6 +140,15 @@
         return super.verify();
     }
 
+    public void eliminateNegation() {
+        AbstractBeginNode oldTrueSuccessor = trueSuccessor;
+        AbstractBeginNode oldFalseSuccessor = falseSuccessor;
+        trueSuccessor = oldFalseSuccessor;
+        falseSuccessor = oldTrueSuccessor;
+        trueSuccessorProbability = 1 - trueSuccessorProbability;
+        setCondition(((LogicNegationNode) condition).getValue());
+    }
+
     @Override
     public void simplify(SimplifierTool tool) {
         if (trueSuccessor().next() instanceof DeoptimizeNode) {
@@ -155,15 +164,7 @@
         }
 
         if (condition() instanceof LogicNegationNode) {
-            AbstractBeginNode trueSucc = trueSuccessor();
-            AbstractBeginNode falseSucc = falseSuccessor();
-            setTrueSuccessor(null);
-            setFalseSuccessor(null);
-            LogicNegationNode negation = (LogicNegationNode) condition();
-            IfNode newIfNode = graph().add(new IfNode(negation.getValue(), falseSucc, trueSucc, 1 - trueSuccessorProbability));
-            predecessor().replaceFirstSuccessor(this, newIfNode);
-            GraphUtil.killWithUnusedFloatingInputs(this);
-            return;
+            eliminateNegation();
         }
         if (condition() instanceof LogicConstantNode) {
             LogicConstantNode c = (LogicConstantNode) condition();
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java	Sat Apr 11 00:16:29 2015 -0700
@@ -64,6 +64,11 @@
         return callTarget;
     }
 
+    void setCallTarget(CallTargetNode callTarget) {
+        updateUsages(this.callTarget, callTarget);
+        this.callTarget = callTarget;
+    }
+
     @Override
     public boolean isPolymorphic() {
         return polymorphic;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java	Sat Apr 11 00:16:29 2015 -0700
@@ -81,6 +81,11 @@
         return callTarget;
     }
 
+    void setCallTarget(CallTargetNode callTarget) {
+        updateUsages(this.callTarget, callTarget);
+        this.callTarget = callTarget;
+    }
+
     public MethodCallTargetNode methodCallTarget() {
         return (MethodCallTargetNode) callTarget;
     }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Sat Apr 11 00:16:29 2015 -0700
@@ -306,7 +306,7 @@
             if (callerLockDepth != 0) {
                 for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.TYPE)) {
                     MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original);
-                    processMonitorId(invoke, monitor);
+                    processMonitorId(invoke.stateAfter(), monitor);
                 }
             }
         } else {
@@ -432,8 +432,7 @@
         return pos;
     }
 
-    public static void processMonitorId(Invoke invoke, MonitorIdNode monitorIdNode) {
-        FrameState stateAfter = invoke.stateAfter();
+    public static void processMonitorId(FrameState stateAfter, MonitorIdNode monitorIdNode) {
         if (stateAfter != null) {
             int callerLockDepth = stateAfter.nestedLockDepth();
             monitorIdNode.setLockDepth(monitorIdNode.getLockDepth() + callerLockDepth);
@@ -579,26 +578,43 @@
     }
 
     public static ValueNode mergeReturns(AbstractMergeNode merge, List<? extends ReturnNode> returnNodes, List<Node> canonicalizedNodes) {
+        ValueNode singleReturnValue = null;
         PhiNode returnValuePhi = null;
-
         for (ReturnNode returnNode : returnNodes) {
-            // create and wire up a new EndNode
-            EndNode endNode = merge.graph().add(new EndNode());
-            merge.addForwardEnd(endNode);
+            if (returnNode.result() != null) {
+                if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == returnNode.result())) {
+                    /* Only one return value, so no need yet for a phi node. */
+                    singleReturnValue = returnNode.result();
 
-            if (returnNode.result() != null) {
-                if (returnValuePhi == null) {
+                } else if (returnValuePhi == null) {
+                    /* Found a second return value, so create phi node. */
                     returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge));
                     if (canonicalizedNodes != null) {
                         canonicalizedNodes.add(returnValuePhi);
                     }
+                    for (int i = 0; i < merge.forwardEndCount(); i++) {
+                        returnValuePhi.addInput(singleReturnValue);
+                    }
+                    returnValuePhi.addInput(returnNode.result());
+
+                } else {
+                    /* Multiple return values, just add to existing phi node. */
+                    returnValuePhi.addInput(returnNode.result());
                 }
-                returnValuePhi.addInput(returnNode.result());
             }
+
+            // create and wire up a new EndNode
+            EndNode endNode = merge.graph().add(new EndNode());
+            merge.addForwardEnd(endNode);
             returnNode.replaceAndDelete(endNode);
-
         }
-        return returnValuePhi;
+
+        if (returnValuePhi != null) {
+            assert returnValuePhi.verify();
+            return returnValuePhi;
+        } else {
+            return singleReturnValue;
+        }
     }
 
     private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map<Node, Node> duplicates) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java	Sat Apr 11 00:16:29 2015 -0700
@@ -29,13 +29,14 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.debug.*;
+import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo;
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver;
 import com.oracle.graal.java.*;
+import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.extended.*;
@@ -63,8 +64,11 @@
     protected final StampProvider stampProvider;
 
     protected class PEMethodScope extends MethodScope {
+        /** The state of the caller method. Only non-null during method inlining. */
+        protected final PEMethodScope caller;
+        protected final LoopScope callerLoopScope;
         protected final ResolvedJavaMethod method;
-        protected final Invoke invoke;
+        protected final InvokeData invokeData;
         protected final int inliningDepth;
 
         protected final LoopExplosionPlugin loopExplosionPlugin;
@@ -73,15 +77,20 @@
         protected final ParameterPlugin parameterPlugin;
         protected final ValueNode[] arguments;
 
-        protected FrameState outerFrameState;
+        protected FrameState outerState;
+        protected FrameState exceptionState;
+        protected ExceptionPlaceholderNode exceptionPlaceholderNode;
         protected BytecodePosition bytecodePosition;
 
-        protected PEMethodScope(StructuredGraph targetGraph, MethodScope caller, EncodedGraph encodedGraph, ResolvedJavaMethod method, Invoke invoke, int inliningDepth,
-                        LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin, ValueNode[] arguments) {
-            super(targetGraph, caller, encodedGraph, loopExplosionKind(method, loopExplosionPlugin));
+        protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData,
+                        int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin,
+                        ValueNode[] arguments) {
+            super(targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin));
 
+            this.caller = caller;
+            this.callerLoopScope = callerLoopScope;
             this.method = method;
-            this.invoke = invoke;
+            this.invokeData = invokeData;
             this.inliningDepth = inliningDepth;
             this.loopExplosionPlugin = loopExplosionPlugin;
             this.invocationPlugins = invocationPlugins;
@@ -89,6 +98,10 @@
             this.parameterPlugin = parameterPlugin;
             this.arguments = arguments;
         }
+
+        public boolean isInlinedMethod() {
+            return caller != null;
+        }
     }
 
     protected class PECanonicalizerTool implements CanonicalizerTool {
@@ -114,7 +127,7 @@
     }
 
     protected class PENonAppendGraphBuilderContext implements GraphBuilderContext {
-        private final PEMethodScope methodScope;
+        protected final PEMethodScope methodScope;
 
         public PENonAppendGraphBuilderContext(PEMethodScope methodScope) {
             this.methodScope = methodScope;
@@ -212,13 +225,11 @@
     }
 
     protected class PEAppendGraphBuilderContext extends PENonAppendGraphBuilderContext {
-        protected final Invoke invoke;
         protected FixedWithNextNode lastInstr;
         protected ValueNode pushedNode;
 
-        public PEAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke, FixedWithNextNode lastInstr) {
+        public PEAppendGraphBuilderContext(PEMethodScope methodScope, FixedWithNextNode lastInstr) {
             super(methodScope);
-            this.invoke = invoke;
             this.lastInstr = lastInstr;
         }
 
@@ -232,7 +243,9 @@
 
         @Override
         public FrameState createStateAfter() {
-            return invoke.stateAfter().duplicate();
+            Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
+            getGraph().add(stateAfter);
+            return (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter);
         }
 
         @Override
@@ -274,6 +287,15 @@
         }
     }
 
+    @NodeInfo
+    static class ExceptionPlaceholderNode extends ValueNode {
+        public static final NodeClass<ExceptionPlaceholderNode> TYPE = NodeClass.create(ExceptionPlaceholderNode.class);
+
+        public ExceptionPlaceholderNode() {
+            super(TYPE, StampFactory.object());
+        }
+    }
+
     public PEGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, StampProvider stampProvider) {
         this.metaAccess = metaAccess;
         this.constantReflection = constantReflection;
@@ -294,8 +316,9 @@
 
     public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin,
                     ParameterPlugin parameterPlugin) {
-        PEMethodScope methodScope = new PEMethodScope(targetGraph, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin, parameterPlugin, null);
-        decode(methodScope);
+        PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin,
+                        parameterPlugin, null);
+        decode(methodScope, null);
         cleanupGraph(methodScope);
         methodScope.graph.verify();
     }
@@ -310,8 +333,6 @@
     protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
-        Debug.dump(methodScope.graph, "Loop iteration " + loopScope.loopIteration);
-
         if (loopScope.loopIteration > MaximumLoopExplosionCount.getValue()) {
             String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?";
             if (FailedLoopExplosionIsFatal.getValue()) {
@@ -323,39 +344,52 @@
     }
 
     @Override
-    protected void simplifyInvoke(MethodScope s, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
-        if (!(invoke.callTarget() instanceof MethodCallTargetNode)) {
-            return;
-        }
+    protected void handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) {
         PEMethodScope methodScope = (PEMethodScope) s;
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+
+        /*
+         * Decode the call target, but do not add it to the graph yet. This avoids adding usages for
+         * all the arguments, which are expensive to remove again when we can inline the method.
+         */
+        assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke";
+        CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId);
+        if (!(callTarget instanceof MethodCallTargetNode) || !trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget)) {
 
+            /* We know that we need an invoke, so now we can add the call target to the graph. */
+            methodScope.graph.add(callTarget);
+            registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false);
+            super.handleInvoke(methodScope, loopScope, invokeData);
+        }
+    }
+
+    protected boolean trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
         // attempt to devirtualize the call
-        ResolvedJavaType contextType = (invoke.stateAfter() == null && invoke.stateDuring() == null) ? null : invoke.getContextType();
-        ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), contextType);
+        ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), invokeData.contextType);
         if (specialCallTarget != null) {
             callTarget.setTargetMethod(specialCallTarget);
             callTarget.setInvokeKind(InvokeKind.Special);
         }
 
-        if (tryInvocationPlugin(methodScope, loopScope, invokeOrderId, invoke)) {
-            return;
+        if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) {
+            return true;
         }
-        if (tryInline(methodScope, loopScope, invokeOrderId, invoke)) {
-            return;
+        if (tryInline(methodScope, loopScope, invokeData, callTarget)) {
+            return true;
         }
 
         if (methodScope.inlineInvokePlugin != null) {
-            methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope), callTarget.targetMethod(), invoke);
+            methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope), callTarget.targetMethod(), invokeData.invoke);
         }
+        return false;
     }
 
-    protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
+    protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
         if (methodScope.invocationPlugins == null) {
             return false;
         }
 
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+        Invoke invoke = invokeData.invoke;
+
         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
         InvocationPlugin invocationPlugin = methodScope.invocationPlugins.lookupInvocation(targetMethod);
         if (invocationPlugin == null) {
@@ -364,52 +398,41 @@
 
         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
         FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor();
-        FixedNode invokeNext = invoke.next();
-        AbstractBeginNode invokeException = null;
-        if (invoke instanceof InvokeWithExceptionNode) {
-            invokeException = ((InvokeWithExceptionNode) invoke).exceptionEdge();
-        }
 
+        /* Remove invoke from graph so that invocation plugin can append nodes to the predecessor. */
         invoke.asNode().replaceAtPredecessor(null);
 
-        PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, invoke, invokePredecessor);
+        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin,
+                        methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
+        PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor);
         InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext);
+
         if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) {
 
             if (graphBuilderContext.lastInstr != null) {
-                registerNode(loopScope, invokeOrderId, graphBuilderContext.pushedNode, true, true);
+                registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true);
                 invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode);
+                graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData));
             } else {
                 assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?";
                 invoke.asNode().replaceAtUsages(null);
             }
 
             deleteInvoke(invoke);
-            if (invokeException != null) {
-                invokeException.safeDelete();
-            }
-
-            if (graphBuilderContext.lastInstr != null) {
-                graphBuilderContext.lastInstr.setNext(invokeNext);
-            } else {
-                invokeNext.replaceAtPredecessor(null);
-                invokeNext.safeDelete();
-            }
             return true;
 
         } else {
-            /* Restore original state: invoke is in Graph. */
+            /* Intrinsification failed, restore original state: invoke is in Graph. */
             invokePredecessor.setNext(invoke.asNode());
             return false;
         }
     }
 
-    protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
-        if (methodScope.inlineInvokePlugin == null || !invoke.getInvokeKind().isDirect()) {
+    protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
+        if (methodScope.inlineInvokePlugin == null || !callTarget.invokeKind().isDirect()) {
             return false;
         }
 
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
         if (!targetMethod.canBeInlined()) {
             return false;
@@ -429,50 +452,72 @@
             return false;
         }
 
-        int exceptionObjectOrderId = -1;
-        if (invoke instanceof InvokeWithExceptionNode) {
-            /*
-             * We need to have the regular next node (usually a KillingBeginNode) and the exception
-             * edge node (always an ExceptionObjectNode) fully decoded, because both can be changed
-             * or replaced as part of the inlining process. The GraphEncoder adds these two
-             * successors in a known order (first the regular next node, then the exception edge)
-             * that we can rely on here.
-             */
-            assert ((InvokeWithExceptionNode) invoke).next().next() == null;
-            processNextNode(methodScope, loopScope);
-            assert ((InvokeWithExceptionNode) invoke).next().next() != null;
+        Invoke invoke = invokeData.invoke;
+        FixedNode invokeNode = invoke.asNode();
+        FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor();
+        invokeNode.replaceAtPredecessor(null);
 
-            assert ((InvokeWithExceptionNode) invoke).exceptionEdge().next() == null;
-            exceptionObjectOrderId = loopScope.nodesToProcess.nextSetBit(0);
-            processNextNode(methodScope, loopScope);
-            assert ((InvokeWithExceptionNode) invoke).exceptionEdge().next() != null;
-        }
-
-        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, graphToInline, inlineMethod, invoke, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin,
-                        methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
+        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1,
+                        methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
         /* Do the actual inlining by decoding the inlineMethod */
-        decode(inlineScope);
+        decode(inlineScope, predecessor);
 
         ValueNode exceptionValue = null;
         if (inlineScope.unwindNode != null) {
             exceptionValue = inlineScope.unwindNode.exception();
         }
+        UnwindNode unwindNode = inlineScope.unwindNode;
 
-        FixedNode firstInlinedNode = inlineScope.startNode.next();
-        /* The StartNode was only necessary as a placeholder during decoding. */
-        inlineScope.startNode.safeDelete();
+        if (invoke instanceof InvokeWithExceptionNode) {
+            InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
+            assert invokeWithException.next() == null;
+            assert invokeWithException.exceptionEdge() == null;
+
+            if (unwindNode != null) {
+                assert unwindNode.predecessor() != null;
+                Node n = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId);
+                unwindNode.replaceAndDelete(n);
+            }
+
+        } else {
+            if (unwindNode != null && !unwindNode.isDeleted()) {
+                DeoptimizeNode deoptimizeNode = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
+                unwindNode.replaceAndDelete(deoptimizeNode);
+            }
+        }
+
+        assert invoke.next() == null;
 
-        assert inlineScope.startNode.stateAfter() == null;
-        ValueNode returnValue = InliningUtil.finishInlining(invoke, methodScope.graph, firstInlinedNode, inlineScope.returnNodes, inlineScope.unwindNode, inlineScope.encodedGraph.getAssumptions(),
-                        inlineScope.encodedGraph.getInlinedMethods(), null);
+        ValueNode returnValue;
+        List<ReturnNode> returnNodes = inlineScope.returnNodes;
+        if (!returnNodes.isEmpty()) {
+            FixedNode n;
+            n = nodeAfterInvoke(methodScope, loopScope, invokeData);
+            if (returnNodes.size() == 1) {
+                ReturnNode returnNode = returnNodes.get(0);
+                returnValue = returnNode.result();
+                returnNode.replaceAndDelete(n);
+            } else {
+                AbstractMergeNode merge = methodScope.graph.add(new MergeNode());
+                merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
+                returnValue = InliningUtil.mergeReturns(merge, returnNodes, null);
+                merge.setNext(n);
+            }
+        } else {
+            returnValue = null;
+        }
+        invokeNode.replaceAtUsages(returnValue);
 
         /*
          * Usage the handles that we have on the return value and the exception to update the
          * orderId->Node table.
          */
-        registerNode(loopScope, invokeOrderId, returnValue, true, true);
+        registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true);
         if (invoke instanceof InvokeWithExceptionNode) {
-            registerNode(loopScope, exceptionObjectOrderId, exceptionValue, true, true);
+            registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true);
+        }
+        if (inlineScope.exceptionPlaceholderNode != null) {
+            inlineScope.exceptionPlaceholderNode.replaceAndDelete(exceptionValue);
         }
         deleteInvoke(invoke);
 
@@ -480,6 +525,16 @@
         return true;
     }
 
+    public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData) {
+        FixedNode n;
+        if (invokeData.invoke instanceof InvokeWithExceptionNode) {
+            n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId);
+        } else {
+            n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId);
+        }
+        return n;
+    }
+
     private static void deleteInvoke(Invoke invoke) {
         /*
          * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can
@@ -487,7 +542,7 @@
          */
         FrameState frameState = invoke.stateAfter();
         invoke.asNode().safeDelete();
-        invoke.callTarget().safeDelete();
+        assert invoke.callTarget() == null : "must not have been added to the graph yet";
         if (frameState != null && frameState.hasNoUsages()) {
             frameState.safeDelete();
         }
@@ -499,15 +554,20 @@
     protected void simplifyFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
-        if (node instanceof IfNode && ((IfNode) node).condition() instanceof LogicConstantNode) {
+        if (node instanceof IfNode) {
             IfNode ifNode = (IfNode) node;
-            boolean condition = ((LogicConstantNode) ifNode.condition()).getValue();
-            AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition);
-            AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition);
+            if (ifNode.condition() instanceof LogicNegationNode) {
+                ifNode.eliminateNegation();
+            }
+            if (ifNode.condition() instanceof LogicConstantNode) {
+                boolean condition = ((LogicConstantNode) ifNode.condition()).getValue();
+                AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition);
+                AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition);
 
-            methodScope.graph.removeSplit(ifNode, survivingSuccessor);
-            assert deadSuccessor.next() == null : "must not be parsed yet";
-            deadSuccessor.safeDelete();
+                methodScope.graph.removeSplit(ifNode, survivingSuccessor);
+                assert deadSuccessor.next() == null : "must not be parsed yet";
+                deadSuccessor.safeDelete();
+            }
 
         } else if (node instanceof IntegerSwitchNode && ((IntegerSwitchNode) node).value().isConstant()) {
             IntegerSwitchNode switchNode = (IntegerSwitchNode) node;
@@ -587,31 +647,68 @@
         return node;
     }
 
+    protected void ensureOuterStateDecoded(PEMethodScope methodScope) {
+        if (methodScope.outerState == null && methodScope.caller != null) {
+            FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter();
+            if (stateAtReturn == null) {
+                stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
+            }
+
+            Kind invokeReturnKind = methodScope.invokeData.invoke.asNode().getKind();
+            FrameState outerState = stateAtReturn.duplicateModified(methodScope.graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind);
+
+            if (methodScope.caller != null) {
+                ensureOuterStateDecoded(methodScope.caller);
+                outerState.setOuterFrameState(methodScope.caller.outerState);
+            }
+            methodScope.outerState = outerState;
+        }
+    }
+
+    protected void ensureStateAfterDecoded(PEMethodScope methodScope) {
+        if (methodScope.invokeData.invoke.stateAfter() == null) {
+            methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId));
+        }
+    }
+
+    protected void ensureExceptionStateDecoded(PEMethodScope methodScope) {
+        if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) {
+            ensureStateAfterDecoded(methodScope);
+
+            assert methodScope.exceptionPlaceholderNode == null;
+            methodScope.exceptionPlaceholderNode = methodScope.graph.add(new ExceptionPlaceholderNode());
+            registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false);
+            FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId);
+
+            if (methodScope.caller != null) {
+                ensureOuterStateDecoded(methodScope.caller);
+                exceptionState.setOuterFrameState(methodScope.caller.outerState);
+            }
+            methodScope.exceptionState = exceptionState;
+        }
+    }
+
     @Override
     protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
         if (methodScope.isInlinedMethod()) {
             if (node instanceof SimpleInfopointNode) {
-                methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition);
+                methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition);
                 return node;
 
             } else if (node instanceof FrameState) {
-                FrameState stateAtExceptionEdge = null;
-                if (methodScope.invoke instanceof InvokeWithExceptionNode) {
-                    InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) methodScope.invoke);
-                    ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
-                    stateAtExceptionEdge = obj.stateAfter();
+                FrameState frameState = (FrameState) node;
+
+                ensureOuterStateDecoded(methodScope);
+                if (frameState.bci < 0) {
+                    ensureExceptionStateDecoded(methodScope);
                 }
-                if (methodScope.outerFrameState == null) {
-                    FrameState stateAtReturn = methodScope.invoke.stateAfter();
-                    Kind invokeReturnKind = methodScope.invoke.asNode().getKind();
-                    methodScope.outerFrameState = stateAtReturn.duplicateModifiedDuringCall(methodScope.invoke.bci(), invokeReturnKind);
-                }
-                return InliningUtil.processFrameState((FrameState) node, methodScope.invoke, methodScope.method, stateAtExceptionEdge, methodScope.outerFrameState, true);
+                return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, methodScope.method, methodScope.exceptionState, methodScope.outerState, true);
 
             } else if (node instanceof MonitorIdNode) {
-                InliningUtil.processMonitorId(methodScope.invoke, (MonitorIdNode) node);
+                ensureOuterStateDecoded(methodScope);
+                InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node);
                 return node;
             }
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Sat Apr 11 00:15:55 2015 -0700
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Sat Apr 11 00:16:29 2015 -0700
@@ -164,28 +164,74 @@
 
     private class PEInlineInvokePlugin implements InlineInvokePlugin {
 
-        private final boolean duringParsing;
         private Deque<TruffleInlining> inlining;
         private OptimizedDirectCallNode lastDirectCallNode;
         private final Replacements replacements;
 
-        private final InvocationPlugins invocationPlugins;
-        private final LoopExplosionPlugin loopExplosionPlugin;
-
-        public PEInlineInvokePlugin(TruffleInlining inlining, Replacements replacements, boolean duringParsing, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin) {
+        public PEInlineInvokePlugin(TruffleInlining inlining, Replacements replacements) {
             this.inlining = new ArrayDeque<>();
             this.inlining.push(inlining);
             this.replacements = replacements;
-            this.duringParsing = duringParsing;
+        }
+
+        @Override
+        public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
+            if (original.getAnnotation(TruffleBoundary.class) != null) {
+                return null;
+            }
+            if (replacements != null && replacements.hasSubstitution(original)) {
+                return null;
+            }
+            assert !builder.parsingReplacement();
+            if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
+                if (original.equals(callSiteProxyMethod)) {
+                    ValueNode arg1 = arguments[0];
+                    if (!arg1.isConstant()) {
+                        GraalInternalError.shouldNotReachHere("The direct call node does not resolve to a constant!");
+                    }
+
+                    Object callNode = snippetReflection.asObject(Object.class, (JavaConstant) arg1.asConstant());
+                    if (callNode instanceof OptimizedDirectCallNode) {
+                        OptimizedDirectCallNode directCallNode = (OptimizedDirectCallNode) callNode;
+                        lastDirectCallNode = directCallNode;
+                    }
+                } else if (original.equals(callDirectMethod)) {
+                    TruffleInliningDecision decision = getDecision(inlining.peek(), lastDirectCallNode);
+                    lastDirectCallNode = null;
+                    if (decision != null && decision.isInline()) {
+                        inlining.push(decision);
+                        builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption()));
+                        return new InlineInfo(callInlinedMethod, false, false);
+                    }
+                }
+            }
+
+            return new InlineInfo(original, false, false);
+        }
+
+        @Override
+        public void postInline(ResolvedJavaMethod inlinedTargetMethod) {
+            if (inlinedTargetMethod.equals(callInlinedMethod)) {
+                inlining.pop();
+            }
+        }
+    }
+
+    private class ParsingInlineInvokePlugin implements InlineInvokePlugin {
+
+        private final Replacements replacements;
+        private final InvocationPlugins invocationPlugins;
+        private final LoopExplosionPlugin loopExplosionPlugin;
+        private final boolean inlineDuringParsing;
+
+        public ParsingInlineInvokePlugin(Replacements replacements, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin, boolean inlineDuringParsing) {
+            this.replacements = replacements;
             this.invocationPlugins = invocationPlugins;
             this.loopExplosionPlugin = loopExplosionPlugin;
+            this.inlineDuringParsing = inlineDuringParsing;
         }
 
         private boolean hasMethodHandleArgument(ValueNode[] arguments) {
-            /*
-             * We want to process invokes that have a constant MethodHandle parameter. And the
-             * method must be statically bound, otherwise we do not have a single target method.
-             */
             for (ValueNode argument : arguments) {
                 if (argument.isConstant()) {
                     JavaConstant constant = argument.asJavaConstant();
@@ -197,58 +243,31 @@
             return false;
         }
 
+        @Override
         public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
-            if (duringParsing && (invocationPlugins.lookupInvocation(original) != null || loopExplosionPlugin.shouldExplodeLoops(original))) {
+            if (invocationPlugins.lookupInvocation(original) != null || loopExplosionPlugin.shouldExplodeLoops(original)) {
                 return null;
             }
-
             if (original.getAnnotation(TruffleBoundary.class) != null) {
                 return null;
             }
             if (replacements != null && replacements.hasSubstitution(original)) {
                 return null;
             }
-            assert !builder.parsingReplacement();
-            if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
-                if (original.equals(callSiteProxyMethod)) {
-                    if (duringParsing) {
-                        return null;
-                    }
-                    ValueNode arg1 = arguments[0];
-                    if (!arg1.isConstant()) {
-                        GraalInternalError.shouldNotReachHere("The direct call node does not resolve to a constant!");
-                    }
-
-                    Object callNode = snippetReflection.asObject(Object.class, (JavaConstant) arg1.asConstant());
-                    if (callNode instanceof OptimizedDirectCallNode) {
-                        OptimizedDirectCallNode directCallNode = (OptimizedDirectCallNode) callNode;
-                        lastDirectCallNode = directCallNode;
-                    }
-                } else if (original.equals(callDirectMethod)) {
-                    if (duringParsing) {
-                        return null;
-                    }
-                    TruffleInliningDecision decision = getDecision(inlining.peek(), lastDirectCallNode);
-                    lastDirectCallNode = null;
-                    if (decision != null && decision.isInline()) {
-                        inlining.push(decision);
-                        builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption()));
-                        return new InlineInfo(callInlinedMethod, false, false);
-                    }
-                }
-            }
-
-            if (duringParsing && (!original.hasBytecodes() || original.getCode().length >= TrivialInliningSize.getValue() || builder.getDepth() >= InlineDuringParsingMaxDepth.getValue()) &&
-                            !hasMethodHandleArgument(arguments)) {
+            if (original.equals(callSiteProxyMethod) || original.equals(callDirectMethod)) {
                 return null;
             }
-            return new InlineInfo(original, false, false);
-        }
-
-        public void postInline(ResolvedJavaMethod inlinedTargetMethod) {
-            if (inlinedTargetMethod.equals(callInlinedMethod)) {
-                inlining.pop();
+            if (hasMethodHandleArgument(arguments)) {
+                /*
+                 * We want to inline invokes that have a constant MethodHandle parameter to remove
+                 * invokedynamic related calls as early as possible.
+                 */
+                return new InlineInfo(original, false, false);
             }
+            if (inlineDuringParsing && original.hasBytecodes() && original.getCode().length < TrivialInliningSize.getValue() && builder.getDepth() < InlineDuringParsingMaxDepth.getValue()) {
+                return new InlineInfo(original, false, false);
+            }
+            return null;
         }
     }
 
@@ -279,7 +298,7 @@
         plugins.setParameterPlugin(new InterceptReceiverPlugin(callTarget));
         callTarget.setInlining(new TruffleInlining(callTarget, new DefaultInliningPolicy()));
 
-        InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), false, null, null);
+        InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements());
         if (PrintTruffleExpansionHistogram.getValue()) {
             inlinePlugin = new HistogramInlineInvokePlugin(graph, inlinePlugin);
         }
@@ -303,7 +322,7 @@
         newConfig.setUseProfiling(false);
         Plugins plugins = newConfig.getPlugins();
         plugins.setLoadFieldPlugin(new InterceptLoadFieldPlugin());
-        plugins.setInlineInvokePlugin(new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), true, parsingInvocationPlugins, loopExplosionPlugin));
+        plugins.setInlineInvokePlugin(new ParsingInlineInvokePlugin(providers.getReplacements(), parsingInvocationPlugins, loopExplosionPlugin, !PrintTruffleExpansionHistogram.getValue()));
 
         CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(providers, newConfig, AllowAssumptions.from(graph.getAssumptions() != null));
 
@@ -311,7 +330,7 @@
 
         InvocationPlugins decodingInvocationPlugins = new InvocationPlugins(providers.getMetaAccess());
         TruffleGraphBuilderPlugins.registerInvocationPlugins(providers.getMetaAccess(), decodingInvocationPlugins, false, snippetReflection);
-        InlineInvokePlugin decodingInlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), false, decodingInvocationPlugins, loopExplosionPlugin);
+        InlineInvokePlugin decodingInlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements());
         if (PrintTruffleExpansionHistogram.getValue()) {
             decodingInlinePlugin = new HistogramInlineInvokePlugin(graph, decodingInlinePlugin);
         }