changeset 19144:b8b8f0fcb8c3

Merge with 41af11212ed308e59dbd87fe1b5c4dfc790cbba3
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Wed, 04 Feb 2015 17:02:31 -0800
parents f10ca512eb40 (current diff) 41af11212ed3 (diff)
children 183f7d3a93e5
files
diffstat 9 files changed, 209 insertions(+), 122 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Wed Feb 04 17:02:31 2015 -0800
@@ -298,6 +298,8 @@
         NodeMap<Integer> canonicalId = graph.createNodeMap();
         int nextId = 0;
 
+        List<String> constantsLines = new ArrayList<>();
+
         StringBuilder result = new StringBuilder();
         for (Block block : schedule.getCFG().getBlocks()) {
             result.append("Block " + block + " ");
@@ -312,20 +314,36 @@
             for (Node node : schedule.getBlockToNodesMap().get(block)) {
                 if (node.isAlive()) {
                     if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode)) {
-                        int id;
-                        if (canonicalId.get(node) != null) {
-                            id = canonicalId.get(node);
+                        if (node instanceof ConstantNode) {
+                            String name = checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName();
+                            String str = name + (excludeVirtual ? "\n" : "    (" + node.getUsageCount() + ")\n");
+                            constantsLines.add(str);
                         } else {
-                            id = nextId++;
-                            canonicalId.set(node, id);
+                            int id;
+                            if (canonicalId.get(node) != null) {
+                                id = canonicalId.get(node);
+                            } else {
+                                id = nextId++;
+                                canonicalId.set(node, id);
+                            }
+                            String name = node.getClass().getSimpleName();
+                            String str = "  " + id + "|" + name + (excludeVirtual ? "\n" : "    (" + node.getUsageCount() + ")\n");
+                            result.append(str);
                         }
-                        String name = node instanceof ConstantNode && checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName();
-                        result.append("  " + id + "|" + name + (excludeVirtual ? "\n" : "    (" + node.getUsageCount() + ")\n"));
                     }
                 }
             }
         }
-        return result.toString();
+
+        StringBuilder constantsLinesResult = new StringBuilder();
+        constantsLinesResult.append(constantsLines.size() + " constants:\n");
+        Collections.sort(constantsLines);
+        for (String s : constantsLines) {
+            constantsLinesResult.append(s);
+            constantsLinesResult.append("\n");
+        }
+
+        return constantsLines.toString() + result.toString();
     }
 
     protected Backend getBackend() {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Wed Feb 04 17:02:31 2015 -0800
@@ -26,11 +26,14 @@
 
 import java.util.function.*;
 
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.phases.*;
 import com.oracle.graal.java.*;
 import com.oracle.graal.java.GraphBuilderConfiguration.DebugInfoMode;
+import com.oracle.graal.java.GraphBuilderPlugins.InlineInvokePlugin;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.tiers.*;
@@ -84,7 +87,12 @@
     protected PhaseSuite<HighTierContext> createGraphBuilderSuite() {
         PhaseSuite<HighTierContext> suite = new PhaseSuite<>();
         GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault();
-        config = config.withInlineTrivial(true);
+        config.setInlineInvokePlugin(new InlineInvokePlugin() {
+            public boolean shouldInlineInvoke(ResolvedJavaMethod method, int depth) {
+                return GraalOptions.InlineDuringParsing.getValue() && method.getCode().length <= GraalOptions.TrivialInliningSize.getValue() &&
+                                depth < GraalOptions.InlineDuringParsingMaxDepth.getValue();
+            }
+        });
         suite.appendPhase(new GraphBuilderPhase(config));
         return suite;
     }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderConfiguration.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderConfiguration.java	Wed Feb 04 17:02:31 2015 -0800
@@ -36,9 +36,9 @@
     private final ResolvedJavaType[] skippedExceptionTypes;
     private final DebugInfoMode debugInfoMode;
     private final boolean doLivenessAnalysis;
-    private final boolean inlineTrivial;
     private GraphBuilderPlugins.LoadFieldPlugin loadFieldPlugin;
     private GraphBuilderPlugins.ParameterPlugin parameterPlugin;
+    private GraphBuilderPlugins.InlineInvokePlugin inlineInvokePlugin;
 
     public static enum DebugInfoMode {
         SafePointsOnly,
@@ -64,41 +64,35 @@
         Full,
     }
 
-    protected GraphBuilderConfiguration(boolean eagerResolving, boolean omitAllExceptionEdges, DebugInfoMode debugInfoMode, ResolvedJavaType[] skippedExceptionTypes, boolean doLivenessAnalysis,
-                    boolean inlineTrivial) {
+    protected GraphBuilderConfiguration(boolean eagerResolving, boolean omitAllExceptionEdges, DebugInfoMode debugInfoMode, ResolvedJavaType[] skippedExceptionTypes, boolean doLivenessAnalysis) {
         this.eagerResolving = eagerResolving;
         this.omitAllExceptionEdges = omitAllExceptionEdges;
         this.debugInfoMode = debugInfoMode;
         this.skippedExceptionTypes = skippedExceptionTypes;
         this.doLivenessAnalysis = doLivenessAnalysis;
-        this.inlineTrivial = inlineTrivial;
     }
 
     public GraphBuilderConfiguration copy() {
-        GraphBuilderConfiguration result = new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, debugInfoMode, skippedExceptionTypes, doLivenessAnalysis, inlineTrivial);
+        GraphBuilderConfiguration result = new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, debugInfoMode, skippedExceptionTypes, doLivenessAnalysis);
         result.loadFieldPlugin = loadFieldPlugin;
         return result;
     }
 
     public GraphBuilderConfiguration withSkippedExceptionTypes(ResolvedJavaType[] newSkippedExceptionTypes) {
-        return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, debugInfoMode, newSkippedExceptionTypes, doLivenessAnalysis, inlineTrivial);
+        return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, debugInfoMode, newSkippedExceptionTypes, doLivenessAnalysis);
     }
 
     public GraphBuilderConfiguration withOmitAllExceptionEdges(boolean newOmitAllExceptionEdges) {
-        return new GraphBuilderConfiguration(eagerResolving, newOmitAllExceptionEdges, debugInfoMode, skippedExceptionTypes, doLivenessAnalysis, inlineTrivial);
+        return new GraphBuilderConfiguration(eagerResolving, newOmitAllExceptionEdges, debugInfoMode, skippedExceptionTypes, doLivenessAnalysis);
     }
 
     public GraphBuilderConfiguration withDebugInfoMode(DebugInfoMode newDebugInfoMode) {
         ResolvedJavaType[] newSkippedExceptionTypes = skippedExceptionTypes == EMPTY ? EMPTY : Arrays.copyOf(skippedExceptionTypes, skippedExceptionTypes.length);
-        return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, newDebugInfoMode, newSkippedExceptionTypes, doLivenessAnalysis, inlineTrivial);
+        return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, newDebugInfoMode, newSkippedExceptionTypes, doLivenessAnalysis);
     }
 
     public GraphBuilderConfiguration withDoLivenessAnalysis(boolean newLivenessAnalysis) {
-        return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, debugInfoMode, skippedExceptionTypes, newLivenessAnalysis, inlineTrivial);
-    }
-
-    public GraphBuilderConfiguration withInlineTrivial(boolean newInlineTrivial) {
-        return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, debugInfoMode, skippedExceptionTypes, doLivenessAnalysis, newInlineTrivial);
+        return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, debugInfoMode, skippedExceptionTypes, newLivenessAnalysis);
     }
 
     public GraphBuilderPlugins.LoadFieldPlugin getLoadFieldPlugin() {
@@ -134,19 +128,19 @@
     }
 
     public static GraphBuilderConfiguration getDefault() {
-        return new GraphBuilderConfiguration(false, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptLivenessAnalysis.getValue(), false);
+        return new GraphBuilderConfiguration(false, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptLivenessAnalysis.getValue());
     }
 
     public static GraphBuilderConfiguration getEagerDefault() {
-        return new GraphBuilderConfiguration(true, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptLivenessAnalysis.getValue(), false);
+        return new GraphBuilderConfiguration(true, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptLivenessAnalysis.getValue());
     }
 
     public static GraphBuilderConfiguration getSnippetDefault() {
-        return new GraphBuilderConfiguration(true, true, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptLivenessAnalysis.getValue(), false);
+        return new GraphBuilderConfiguration(true, true, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptLivenessAnalysis.getValue());
     }
 
     public static GraphBuilderConfiguration getFullDebugDefault() {
-        return new GraphBuilderConfiguration(true, false, DebugInfoMode.Full, EMPTY, GraalOptions.OptLivenessAnalysis.getValue(), false);
+        return new GraphBuilderConfiguration(true, false, DebugInfoMode.Full, EMPTY, GraalOptions.OptLivenessAnalysis.getValue());
     }
 
     /**
@@ -158,10 +152,6 @@
         return eagerResolving;
     }
 
-    public boolean shouldInlineTrivial() {
-        return inlineTrivial;
-    }
-
     public GraphBuilderPlugins.ParameterPlugin getParameterPlugin() {
         return parameterPlugin;
     }
@@ -169,4 +159,12 @@
     public void setParameterPlugin(GraphBuilderPlugins.ParameterPlugin parameterPlugin) {
         this.parameterPlugin = parameterPlugin;
     }
+
+    public GraphBuilderPlugins.InlineInvokePlugin getInlineInvokePlugin() {
+        return inlineInvokePlugin;
+    }
+
+    public void setInlineInvokePlugin(GraphBuilderPlugins.InlineInvokePlugin inlineInvokePlugin) {
+        this.inlineInvokePlugin = inlineInvokePlugin;
+    }
 }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Feb 04 17:02:31 2015 -0800
@@ -42,7 +42,7 @@
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
 import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
 import com.oracle.graal.java.BciBlockMapping.LocalLiveness;
-import com.oracle.graal.java.GraphBuilderPlugins.InvocationPlugin;
+import com.oracle.graal.java.GraphBuilderPlugins.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.calc.*;
@@ -760,7 +760,18 @@
                 }
             }
 
-            private void appendInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args) {
+            private void appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) {
+                ResolvedJavaMethod targetMethod = initialTargetMethod;
+                InvokeKind invokeKind = initialInvokeKind;
+                if (initialInvokeKind.isIndirect()) {
+                    ResolvedJavaType contextType = this.frameState.method.getDeclaringClass();
+                    ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(initialInvokeKind, args[0], initialTargetMethod, assumptions, contextType);
+                    if (specialCallTarget != null) {
+                        invokeKind = InvokeKind.Special;
+                        targetMethod = specialCallTarget;
+                    }
+                }
+
                 Kind resultType = targetMethod.getSignature().getReturnKind();
                 if (DeoptALot.getValue()) {
                     append(new DeoptimizeNode(DeoptimizationAction.None, RuntimeConstraint));
@@ -779,55 +790,26 @@
                         args[0] = TypeProfileProxyNode.proxify(args[0], profile);
                     }
                 }
-                if (GraalOptions.InlineDuringParsing.getValue() && invokeKind.isDirect()) {
 
-                    if (graphBuilderPlugins != null) {
-                        InvocationPlugin plugin = graphBuilderPlugins.lookupInvocation(targetMethod);
-                        if (plugin != null) {
-                            int beforeStackSize = frameState.stackSize;
-                            if (plugin.apply(this, args)) {
-                                assert beforeStackSize + resultType.getSlotCount() == frameState.stackSize;
-                                return;
-                            }
-                            assert beforeStackSize == frameState.stackSize;
-                        }
-                    }
-
-                    if (targetMethod.canBeInlined() && targetMethod.hasBytecodes()) {
-                        if (targetMethod.getCode().length <= GraalOptions.TrivialInliningSize.getValue() && currentDepth < GraalOptions.InlineDuringParsingMaxDepth.getValue() &&
-                                        graphBuilderConfig.shouldInlineTrivial()) {
-                            BytecodeParser parser = new BytecodeParser(metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, StructuredGraph.INVOCATION_ENTRY_BCI);
-                            final FrameState[] lazyFrameState = new FrameState[1];
-                            HIRFrameStateBuilder startFrameState = new HIRFrameStateBuilder(targetMethod, currentGraph, () -> {
-                                if (lazyFrameState[0] == null) {
-                                    lazyFrameState[0] = frameState.create(bci());
-                                }
-                                return lazyFrameState[0];
-                            });
-                            startFrameState.initializeFromArgumentsArray(args);
-                            parser.build(currentDepth + 1, this.lastInstr, startFrameState);
-
-                            FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode();
-                            this.lastInstr = calleeBeforeReturnNode;
-                            if (calleeBeforeReturnNode != null) {
-                                ValueNode calleeReturnValue = parser.getReturnValue();
-                                if (calleeReturnValue != null) {
-                                    frameState.push(calleeReturnValue.getKind().getStackKind(), calleeReturnValue);
-                                }
-                            }
-
-                            FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode();
-                            if (calleeBeforeUnwindNode != null) {
-                                ValueNode calleeUnwindValue = parser.getUnwindValue();
-                                assert calleeUnwindValue != null;
-                                calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci()));
-                            }
-
+                if (graphBuilderPlugins != null) {
+                    InvocationPlugin plugin = graphBuilderPlugins.lookupInvocation(targetMethod);
+                    if (plugin != null) {
+                        int beforeStackSize = frameState.stackSize;
+                        if (plugin.apply(this, args)) {
+                            assert beforeStackSize + resultType.getSlotCount() == frameState.stackSize;
                             return;
                         }
+                        assert beforeStackSize == frameState.stackSize;
                     }
                 }
 
+                InlineInvokePlugin inlineInvokePlugin = graphBuilderConfig.getInlineInvokePlugin();
+                if (inlineInvokePlugin != null && invokeKind.isDirect() && targetMethod.canBeInlined() && targetMethod.hasBytecodes() &&
+                                inlineInvokePlugin.shouldInlineInvoke(targetMethod, currentDepth)) {
+                    parseAndInlineCallee(targetMethod, args);
+                    return;
+                }
+
                 MethodCallTargetNode callTarget = currentGraph.add(createMethodCallTarget(invokeKind, targetMethod, args, returnType));
 
                 // be conservative if information was not recorded (could result in endless
@@ -842,6 +824,35 @@
                 }
             }
 
+            private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args) {
+                BytecodeParser parser = new BytecodeParser(metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, StructuredGraph.INVOCATION_ENTRY_BCI);
+                final FrameState[] lazyFrameState = new FrameState[1];
+                HIRFrameStateBuilder startFrameState = new HIRFrameStateBuilder(targetMethod, currentGraph, () -> {
+                    if (lazyFrameState[0] == null) {
+                        lazyFrameState[0] = frameState.create(bci());
+                    }
+                    return lazyFrameState[0];
+                });
+                startFrameState.initializeFromArgumentsArray(args);
+                parser.build(currentDepth + 1, this.lastInstr, startFrameState);
+
+                FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode();
+                this.lastInstr = calleeBeforeReturnNode;
+                if (calleeBeforeReturnNode != null) {
+                    ValueNode calleeReturnValue = parser.getReturnValue();
+                    if (calleeReturnValue != null) {
+                        frameState.push(calleeReturnValue.getKind().getStackKind(), calleeReturnValue);
+                    }
+                }
+
+                FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode();
+                if (calleeBeforeUnwindNode != null) {
+                    ValueNode calleeUnwindValue = parser.getUnwindValue();
+                    assert calleeUnwindValue != null;
+                    calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci()));
+                }
+            }
+
             protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, JavaType returnType) {
                 return new MethodCallTargetNode(invokeKind, targetMethod, args, returnType);
             }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPlugins.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPlugins.java	Wed Feb 04 17:02:31 2015 -0800
@@ -45,6 +45,10 @@
         FloatingNode interceptParameter(int index);
     }
 
+    public interface InlineInvokePlugin extends GraphBuilderPlugin {
+        boolean shouldInlineInvoke(ResolvedJavaMethod method, int depth);
+    }
+
     /**
      * Plugin for handling a method invocation.
      */
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/StandardGraphBuilderPluginsProvider.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/StandardGraphBuilderPluginsProvider.java	Wed Feb 04 17:02:31 2015 -0800
@@ -22,14 +22,21 @@
  */
 package com.oracle.graal.java;
 
+import static com.oracle.graal.api.meta.DeoptimizationAction.*;
+import static com.oracle.graal.api.meta.DeoptimizationReason.*;
+import static com.oracle.graal.compiler.common.type.StampFactory.*;
+
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.java.GraphBuilderPlugins.InvocationPlugin;
 import com.oracle.graal.java.GraphBuilderPlugins.Registration;
 import com.oracle.graal.java.GraphBuilderPlugins.Registration.Receiver;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.type.*;
 
 /**
  * Provider of non-runtime specific {@link GraphBuilderPlugin}s.
@@ -86,7 +93,14 @@
         }
 
         public boolean apply(GraphBuilderContext builder, ValueNode value) {
-            builder.push(kind, builder.append(new UnboxNode(value, kind)));
+            ValueNode nonNullValue = value;
+            ObjectStamp receiverStamp = (ObjectStamp) value.stamp();
+            if (!StampTool.isPointerNonNull(receiverStamp)) {
+                IsNullNode condition = builder.append(new IsNullNode(value));
+                Stamp stamp = receiverStamp.join(objectNonNull());
+                nonNullValue = builder.append(new GuardingPiNode(value, condition, true, NullCheckException, InvalidateReprofile, stamp));
+            }
+            builder.push(kind.getStackKind(), builder.append(new UnboxNode(nonNullValue, kind)));
             return true;
         }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/CallTargetNode.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/CallTargetNode.java	Wed Feb 04 17:02:31 2015 -0800
@@ -55,6 +55,10 @@
         public boolean isIndirect() {
             return !direct;
         }
+
+        public boolean isInterface() {
+            return this == InvokeKind.Interface;
+        }
     }
 
     @Input protected NodeInputList<ValueNode> arguments;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Wed Feb 04 17:02:31 2015 -0800
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.nodes.java;
 
+import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
@@ -93,67 +94,79 @@
         }
     }
 
+    public static ResolvedJavaMethod findSpecialCallTarget(InvokeKind invokeKind, ValueNode receiver, ResolvedJavaMethod targetMethod, Assumptions assumptions, ResolvedJavaType contextType) {
+        if (invokeKind.isDirect()) {
+            return null;
+        }
+
+        // check for trivial cases (e.g. final methods, nonvirtual methods)
+        if (targetMethod.canBeStaticallyBound()) {
+            return targetMethod;
+        }
+
+        ResolvedJavaType type = StampTool.typeOrNull(receiver);
+        if (type == null && invokeKind == InvokeKind.Virtual) {
+            // For virtual calls, we are guaranteed to receive a correct receiver type.
+            type = targetMethod.getDeclaringClass();
+        }
+
+        if (type != null) {
+            /*
+             * either the holder class is exact, or the receiver object has an exact type, or it's
+             * an array type
+             */
+            ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(targetMethod, contextType);
+            if (resolvedMethod != null && (resolvedMethod.canBeStaticallyBound() || StampTool.isExactType(receiver) || type.isArray())) {
+                return resolvedMethod;
+            }
+            if (assumptions != null && assumptions.useOptimisticAssumptions()) {
+                ResolvedJavaType uniqueConcreteType = type.findUniqueConcreteSubtype();
+                if (uniqueConcreteType != null) {
+                    ResolvedJavaMethod methodFromUniqueType = uniqueConcreteType.resolveConcreteMethod(targetMethod, contextType);
+                    if (methodFromUniqueType != null) {
+                        assumptions.recordConcreteSubtype(type, uniqueConcreteType);
+                        return methodFromUniqueType;
+                    }
+                }
+
+                ResolvedJavaMethod uniqueConcreteMethod = type.findUniqueConcreteMethod(targetMethod);
+                if (uniqueConcreteMethod != null) {
+                    assumptions.recordConcreteMethod(targetMethod, type, uniqueConcreteMethod);
+                    return uniqueConcreteMethod;
+                }
+            }
+        }
+
+        return null;
+    }
+
     @Override
     public void simplify(SimplifierTool tool) {
-        if (invokeKind().isIndirect()) {
-            // attempt to devirtualize the call
+        // attempt to devirtualize the call
+        ResolvedJavaType contextType = (invoke().stateAfter() == null && invoke().stateDuring() == null) ? null : invoke().getContextType();
+        ResolvedJavaMethod specialCallTarget = findSpecialCallTarget(invokeKind, receiver(), targetMethod, tool.assumptions(), contextType);
+        if (specialCallTarget != null) {
+            this.setTargetMethod(specialCallTarget);
+            setInvokeKind(InvokeKind.Special);
+            return;
+        }
 
-            // check for trivial cases (e.g. final methods, nonvirtual methods)
-            if (targetMethod().canBeStaticallyBound()) {
-                setInvokeKind(InvokeKind.Special);
-                return;
-            }
+        if (invokeKind().isIndirect() && invokeKind().isInterface()) {
 
             // check if the type of the receiver can narrow the result
             ValueNode receiver = receiver();
-            ResolvedJavaType type = StampTool.typeOrNull(receiver);
-            if (type == null && invokeKind == InvokeKind.Virtual) {
-                // For virtual calls, we are guaranteed to receive a correct receiver type.
-                type = targetMethod.getDeclaringClass();
-            }
-            if (type != null && (invoke().stateAfter() != null || invoke().stateDuring() != null)) {
-                /*
-                 * either the holder class is exact, or the receiver object has an exact type, or
-                 * it's an array type
-                 */
-                ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(targetMethod(), invoke().getContextType());
-                if (resolvedMethod != null && (resolvedMethod.canBeStaticallyBound() || StampTool.isExactType(receiver) || type.isArray())) {
-                    setInvokeKind(InvokeKind.Special);
-                    setTargetMethod(resolvedMethod);
-                    return;
-                }
-                if (tool.assumptions() != null && tool.assumptions().useOptimisticAssumptions()) {
-                    ResolvedJavaType uniqueConcreteType = type.findUniqueConcreteSubtype();
-                    if (uniqueConcreteType != null) {
-                        ResolvedJavaMethod methodFromUniqueType = uniqueConcreteType.resolveConcreteMethod(targetMethod(), invoke().getContextType());
-                        if (methodFromUniqueType != null) {
-                            tool.assumptions().recordConcreteSubtype(type, uniqueConcreteType);
-                            setInvokeKind(InvokeKind.Special);
-                            setTargetMethod(methodFromUniqueType);
-                            return;
-                        }
-                    }
 
-                    ResolvedJavaMethod uniqueConcreteMethod = type.findUniqueConcreteMethod(targetMethod());
-                    if (uniqueConcreteMethod != null) {
-                        tool.assumptions().recordConcreteMethod(targetMethod(), type, uniqueConcreteMethod);
-                        setInvokeKind(InvokeKind.Special);
-                        setTargetMethod(uniqueConcreteMethod);
-                        return;
-                    }
-                }
-            }
             // try to turn a interface call into a virtual call
             ResolvedJavaType declaredReceiverType = targetMethod().getDeclaringClass();
             /*
              * We need to check the invoke kind to avoid recursive simplification for virtual
              * interface methods calls.
              */
-            if (declaredReceiverType.isInterface() && !invokeKind().equals(InvokeKind.Virtual)) {
+            if (declaredReceiverType.isInterface()) {
                 tryCheckCastSingleImplementor(receiver, declaredReceiverType);
             }
 
-            if (invokeKind().equals(InvokeKind.Interface) && receiver instanceof UncheckedInterfaceProvider) {
+            if (receiver instanceof UncheckedInterfaceProvider) {
                 UncheckedInterfaceProvider uncheckedInterfaceProvider = (UncheckedInterfaceProvider) receiver;
                 Stamp uncheckedStamp = uncheckedInterfaceProvider.uncheckedStamp();
                 if (uncheckedStamp != null) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Feb 04 16:38:09 2015 -0800
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Feb 04 17:02:31 2015 -0800
@@ -30,6 +30,7 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.api.runtime.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.debug.internal.*;
@@ -59,6 +60,7 @@
 import com.oracle.graal.truffle.phases.*;
 import com.oracle.graal.virtual.phases.ea.*;
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.nodes.*;
 
 /**
@@ -175,12 +177,27 @@
         }
     }
 
+    private class InlineInvokePlugin implements GraphBuilderPlugins.InlineInvokePlugin {
+
+        public boolean shouldInlineInvoke(ResolvedJavaMethod method, int depth) {
+            return method.getAnnotation(TruffleBoundary.class) == null;
+        }
+
+    }
+
     @SuppressWarnings("unused")
     private void fastPartialEvaluation(OptimizedCallTarget callTarget, Assumptions assumptions, StructuredGraph graph, PhaseContext baseContext, HighTierContext tierContext) {
         GraphBuilderConfiguration newConfig = configForRoot.copy();
         newConfig.setLoadFieldPlugin(new InterceptLoadFieldPlugin());
         newConfig.setParameterPlugin(new InterceptReceiverPlugin(callTarget));
-        new GraphBuilderPhase.Instance(providers.getMetaAccess(), providers.getStampProvider(), new Assumptions(false), providers.getConstantReflection(), newConfig, TruffleCompilerImpl.Optimizations).apply(graph);
+        newConfig.setInlineInvokePlugin(new InlineInvokePlugin());
+        DefaultGraphBuilderPlugins plugins = new DefaultGraphBuilderPlugins();
+        Iterable<GraphBuilderPluginsProvider> sl = Services.load(GraphBuilderPluginsProvider.class);
+        for (GraphBuilderPluginsProvider p : sl) {
+            p.registerPlugins(providers.getMetaAccess(), plugins);
+        }
+        new GraphBuilderPhase.Instance(providers.getMetaAccess(), providers.getStampProvider(), new Assumptions(false), providers.getConstantReflection(), newConfig, plugins,
+                        TruffleCompilerImpl.Optimizations).apply(graph);
         Debug.dump(graph, "After FastPE");
     }