changeset 21608:4a8d4ee0fdd6

Allow multiple ParameterPlugin and InlineInvokePlugin in graph builder plugins; cleanup InlineInvokePlugin and implementations, including in Truffle
author Christian Wimmer <christian.wimmer@oracle.com>
date Fri, 29 May 2015 17:01:31 -0700
parents 71b338926f2e
children 2221d959de64
files graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PEGraphDecoderTest.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ConstantBindingParameterPlugin.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultInlineInvokePlugin.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InlineDuringParsingPlugin.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/WordOperationPlugin.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/debug/HistogramInlineInvokePlugin.java
diffstat 16 files changed, 284 insertions(+), 200 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java	Fri May 29 17:01:31 2015 -0700
@@ -33,8 +33,8 @@
     public static class Plugins {
         private final InvocationPlugins invocationPlugins;
         private NodePlugin[] nodePlugins;
-        private ParameterPlugin parameterPlugin;
-        private InlineInvokePlugin inlineInvokePlugin;
+        private ParameterPlugin[] parameterPlugins;
+        private InlineInvokePlugin[] inlineInvokePlugins;
         private LoopExplosionPlugin loopExplosionPlugin;
 
         /**
@@ -44,9 +44,9 @@
          */
         public Plugins(Plugins copyFrom) {
             this.invocationPlugins = new InvocationPlugins(copyFrom.invocationPlugins);
-            this.parameterPlugin = copyFrom.parameterPlugin;
             this.nodePlugins = copyFrom.nodePlugins;
-            this.inlineInvokePlugin = copyFrom.inlineInvokePlugin;
+            this.parameterPlugins = copyFrom.parameterPlugins;
+            this.inlineInvokePlugins = copyFrom.inlineInvokePlugins;
             this.loopExplosionPlugin = copyFrom.loopExplosionPlugin;
         }
 
@@ -59,6 +59,8 @@
         public Plugins(InvocationPlugins invocationPlugins) {
             this.invocationPlugins = invocationPlugins;
             this.nodePlugins = new NodePlugin[0];
+            this.parameterPlugins = new ParameterPlugin[0];
+            this.inlineInvokePlugins = new InlineInvokePlugin[0];
         }
 
         public InvocationPlugins getInvocationPlugins() {
@@ -74,20 +76,55 @@
             nodePlugins[nodePlugins.length - 1] = plugin;
         }
 
-        public ParameterPlugin getParameterPlugin() {
-            return parameterPlugin;
+        public void prependNodePlugin(NodePlugin plugin) {
+            NodePlugin[] newPlugins = new NodePlugin[nodePlugins.length + 1];
+            System.arraycopy(nodePlugins, 0, newPlugins, 1, nodePlugins.length);
+            newPlugins[0] = plugin;
+            nodePlugins = newPlugins;
+        }
+
+        public void clearNodePlugin() {
+            nodePlugins = new NodePlugin[0];
+        }
+
+        public ParameterPlugin[] getParameterPlugins() {
+            return parameterPlugins;
+        }
+
+        public void appendParameterPlugin(ParameterPlugin plugin) {
+            parameterPlugins = Arrays.copyOf(parameterPlugins, parameterPlugins.length + 1);
+            parameterPlugins[parameterPlugins.length - 1] = plugin;
         }
 
-        public void setParameterPlugin(ParameterPlugin plugin) {
-            this.parameterPlugin = plugin;
+        public void prependParameterPlugin(ParameterPlugin plugin) {
+            ParameterPlugin[] newPlugins = new ParameterPlugin[parameterPlugins.length + 1];
+            System.arraycopy(parameterPlugins, 0, newPlugins, 1, parameterPlugins.length);
+            newPlugins[0] = plugin;
+            parameterPlugins = newPlugins;
+        }
+
+        public void clearParameterPlugin() {
+            parameterPlugins = new ParameterPlugin[0];
+        }
+
+        public InlineInvokePlugin[] getInlineInvokePlugins() {
+            return inlineInvokePlugins;
         }
 
-        public InlineInvokePlugin getInlineInvokePlugin() {
-            return inlineInvokePlugin;
+        public void appendInlineInvokePlugin(InlineInvokePlugin plugin) {
+            inlineInvokePlugins = Arrays.copyOf(inlineInvokePlugins, inlineInvokePlugins.length + 1);
+            inlineInvokePlugins[inlineInvokePlugins.length - 1] = plugin;
         }
 
-        public void setInlineInvokePlugin(InlineInvokePlugin plugin) {
-            this.inlineInvokePlugin = plugin;
+        public void prependInlineInvokePlugin(InlineInvokePlugin plugin) {
+            InlineInvokePlugin[] newPlugins = new InlineInvokePlugin[inlineInvokePlugins.length + 1];
+            System.arraycopy(inlineInvokePlugins, 0, newPlugins, 1, inlineInvokePlugins.length);
+            newPlugins[0] = plugin;
+            inlineInvokePlugins = newPlugins;
+        }
+
+        public void clearInlineInvokePlugins() {
+            inlineInvokePlugins = new InlineInvokePlugin[0];
         }
 
         public LoopExplosionPlugin getLoopExplosionPlugin() {
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java	Fri May 29 17:01:31 2015 -0700
@@ -27,55 +27,93 @@
 import com.oracle.graal.nodes.*;
 
 /**
- * Plugin for specifying what is inlined during graph parsing. This plugin is also
- * {@linkplain #notifyOfNoninlinedInvoke notified} of non-inlined invocations (i.e., those for which
- * an {@link Invoke} node is created).
+ * Plugin for specifying what is inlined during graph parsing. This plugin is also notified
+ * {@link #notifyBeforeInline before} and {@link #notifyAfterInline} the inlining, as well as of
+ * {@link #notifyNotInlined non-inlined} invocations (i.e., those for which an {@link Invoke} node
+ * is created).
  */
 public interface InlineInvokePlugin extends GraphBuilderPlugin {
 
+    /**
+     * Result of a {@link #shouldInlineInvoke inlining decision}.
+     */
     public static class InlineInfo {
 
-        /**
-         * The method to be inlined.
-         */
-        public final ResolvedJavaMethod methodToInline;
+        /** Marker return value to use when the call site must not be inlined. */
+        public static final InlineInfo DO_NOT_INLINE = new InlineInfo(null, false);
 
-        /**
-         * Specifies if {@link #methodToInline} is an intrinsic for the original method (i.e., the
-         * {@code method} passed to {@link InlineInvokePlugin#getInlineInfo}).
-         */
-        public final boolean isIntrinsic;
+        private final ResolvedJavaMethod methodToInline;
+        private final boolean isIntrinsic;
 
         public InlineInfo(ResolvedJavaMethod methodToInline, boolean isIntrinsic) {
             this.methodToInline = methodToInline;
             this.isIntrinsic = isIntrinsic;
         }
+
+        /**
+         * Returns the method to be inlined, or {@code null} if the call site must not be inlined.
+         */
+        public ResolvedJavaMethod getMethodToInline() {
+            return methodToInline;
+        }
+
+        /**
+         * Specifies if {@link #methodToInline} is an intrinsic for the original method (i.e., the
+         * {@code method} passed to {@link InlineInvokePlugin#shouldInlineInvoke}).
+         */
+        public boolean isIntrinsic() {
+            return isIntrinsic;
+        }
     }
 
     /**
-     * Determines whether a call to a given method is to be inlined.
+     * Determines whether a call to a given method is to be inlined. The return value is a
+     * tri-state:
+     * <p>
+     * Non-null return value with a non-null {@link InlineInfo#getMethodToInline method}: That
+     * {@link InlineInfo#getMethodToInline method} is inlined. Note that it can be a different
+     * method than the one specified here as the parameter, which allows method substitutions.
+     * <p>
+     * Non-null return value with a null {@link InlineInfo#getMethodToInline method}, e.g.,
+     * {@link InlineInfo#DO_NOT_INLINE}: The method is not inlined, and other plugins with a lower
+     * priority cannot overwrite this decision.
+     * <p>
+     * Null return value: This plugin made no decision, other plugins with a lower priority are
+     * asked.
      *
+     * @param b the context
      * @param method the target method of an invoke
      * @param args the arguments to the invoke
      * @param returnType the return type derived from {@code method}'s signature
      */
-    default InlineInvokePlugin.InlineInfo getInlineInfo(@SuppressWarnings("unused") GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
+    default InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
         return null;
     }
 
     /**
-     * @param inlinedTargetMethod
+     * Notification that a method is about to be inlined.
+     *
+     * @param methodToInline the inlined method
      */
-    default void postInline(ResolvedJavaMethod inlinedTargetMethod) {
+    default void notifyBeforeInline(ResolvedJavaMethod methodToInline) {
+    }
+
+    /**
+     * Notification that a method was inlined.
+     *
+     * @param methodToInline the inlined method
+     */
+    default void notifyAfterInline(ResolvedJavaMethod methodToInline) {
     }
 
     /**
      * Notifies this plugin of the {@link Invoke} node created for a method that was not inlined per
-     * {@link #getInlineInfo}.
+     * {@link #shouldInlineInvoke}.
      *
+     * @param b the context
      * @param method the method that was not inlined
      * @param invoke the invoke node created for the call to {@code method}
      */
-    default void notifyOfNoninlinedInvoke(@SuppressWarnings("unused") GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
+    default void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Fri May 29 17:01:31 2015 -0700
@@ -28,8 +28,10 @@
 import com.oracle.jvmci.meta.ConstantReflectionProvider;
 import com.oracle.jvmci.meta.ResolvedJavaMethod;
 import com.oracle.jvmci.meta.Kind;
+
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 import static com.oracle.graal.hotspot.replacements.SystemSubstitutions.*;
+import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
 
 import java.lang.invoke.*;
 import java.util.zip.*;
@@ -78,10 +80,14 @@
         HotSpotWordOperationPlugin wordOperationPlugin = new HotSpotWordOperationPlugin(snippetReflection, wordTypes);
         HotSpotNodePlugin nodePlugin = new HotSpotNodePlugin(metaAccess, constantReflection, wordOperationPlugin, nodeIntrinsificationPlugin);
 
-        plugins.setParameterPlugin(nodePlugin);
+        plugins.appendParameterPlugin(nodePlugin);
         plugins.appendNodePlugin(nodePlugin);
         plugins.appendNodePlugin(new MethodHandlePlugin(constantReflection.getMethodHandleAccess()));
-        plugins.setInlineInvokePlugin(new DefaultInlineInvokePlugin(replacements));
+
+        plugins.appendInlineInvokePlugin(replacements);
+        if (InlineDuringParsing.getValue()) {
+            plugins.appendInlineInvokePlugin(new InlineDuringParsingPlugin());
+        }
 
         registerObjectPlugins(invocationPlugins);
         registerClassPlugins(plugins);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Fri May 29 17:01:31 2015 -0700
@@ -95,7 +95,7 @@
         Plugins defaultPlugins = providers.getGraphBuilderPlugins();
         MetaAccessProvider metaAccess = providers.getMetaAccess();
         Plugins plugins = new Plugins(defaultPlugins);
-        plugins.setParameterPlugin(new ConstantBindingParameterPlugin(makeConstArgs(), plugins.getParameterPlugin(), metaAccess, providers.getSnippetReflection()));
+        plugins.prependParameterPlugin(new ConstantBindingParameterPlugin(makeConstArgs(), metaAccess, providers.getSnippetReflection()));
         GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
 
         // Stubs cannot have optimistic assumptions since they have
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Fri May 29 17:01:31 2015 -0700
@@ -109,7 +109,7 @@
         }
     }
 
-    public void initializeForMethodStart(boolean eagerResolve, ParameterPlugin parameterPlugin) {
+    public void initializeForMethodStart(boolean eagerResolve, ParameterPlugin[] parameterPlugins) {
 
         int javaIndex = 0;
         int index = 0;
@@ -117,8 +117,11 @@
             // add the receiver
             FloatingNode receiver = null;
             Stamp receiverStamp = StampFactory.declaredNonNull(method.getDeclaringClass());
-            if (parameterPlugin != null) {
-                receiver = parameterPlugin.interceptParameter(parser, index, receiverStamp);
+            for (ParameterPlugin plugin : parameterPlugins) {
+                receiver = plugin.interceptParameter(parser, index, receiverStamp);
+                if (receiver != null) {
+                    break;
+                }
             }
             if (receiver == null) {
                 receiver = new ParameterNode(javaIndex, receiverStamp);
@@ -143,8 +146,11 @@
                 stamp = StampFactory.forKind(kind);
             }
             FloatingNode param = null;
-            if (parameterPlugin != null) {
-                param = parameterPlugin.interceptParameter(parser, index, stamp);
+            for (ParameterPlugin plugin : parameterPlugins) {
+                param = plugin.interceptParameter(parser, index, stamp);
+                if (param != null) {
+                    break;
+                }
             }
             if (param == null) {
                 param = new ParameterNode(index, stamp);
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri May 29 17:01:31 2015 -0700
@@ -345,7 +345,7 @@
                 BytecodeParser parser = new BytecodeParser(null, metaAccess, method, graphBuilderConfig, optimisticOpts, entryBCI, intrinsicContext);
                 FrameStateBuilder frameState = new FrameStateBuilder(parser, method, graph);
 
-                frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || intrinsicContext != null, graphBuilderConfig.getPlugins().getParameterPlugin());
+                frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || intrinsicContext != null, graphBuilderConfig.getPlugins().getParameterPlugins());
 
                 try (IntrinsicScope s = intrinsicContext != null ? new IntrinsicScope(parser) : null) {
                     parser.build(graph.start(), frameState);
@@ -1401,12 +1401,8 @@
                     lastInstr = beginNode;
                 }
 
-                InlineInvokePlugin plugin = graphBuilderConfig.getPlugins().getInlineInvokePlugin();
-                if (plugin != null) {
-                    if (TraceParserPlugins.getValue()) {
-                        traceWithContext("did not inline %s", targetMethod.format("%h.%n(%p)"));
-                    }
-                    plugin.notifyOfNoninlinedInvoke(this, targetMethod, invoke);
+                for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+                    plugin.notifyNotInlined(this, targetMethod, invoke);
                 }
             }
 
@@ -1497,24 +1493,30 @@
             }
 
             private boolean tryInline(ValueNode[] args, ResolvedJavaMethod targetMethod, JavaType returnType) {
-                InlineInvokePlugin plugin = graphBuilderConfig.getPlugins().getInlineInvokePlugin();
                 boolean canBeInlined = parsingIntrinsic() || targetMethod.canBeInlined();
-                if (plugin == null || !canBeInlined) {
+                if (!canBeInlined) {
                     return false;
                 }
-                InlineInfo inlineInfo = plugin.getInlineInfo(this, targetMethod, args, returnType);
-                if (inlineInfo != null) {
-                    return inline(plugin, targetMethod, inlineInfo.methodToInline, inlineInfo.isIntrinsic, args);
+                for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+                    InlineInfo inlineInfo = plugin.shouldInlineInvoke(this, targetMethod, args, returnType);
+                    if (inlineInfo != null) {
+                        if (inlineInfo.getMethodToInline() == null) {
+                            /* Do not inline, and do not ask the remaining plugins. */
+                            return false;
+                        } else {
+                            return inline(targetMethod, inlineInfo.getMethodToInline(), inlineInfo.isIntrinsic(), args);
+                        }
+                    }
                 }
                 return false;
             }
 
             public void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args) {
-                boolean res = inline(null, targetMethod, substitute, true, args);
+                boolean res = inline(targetMethod, substitute, true, args);
                 assert res : "failed to inline " + substitute;
             }
 
-            private boolean inline(InlineInvokePlugin plugin, ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isIntrinsic, ValueNode[] args) {
+            private boolean inline(ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isIntrinsic, ValueNode[] args) {
                 if (TraceInlineDuringParsing.getValue() || TraceParserPlugins.getValue()) {
                     if (targetMethod.equals(inlinedMethod)) {
                         traceWithContext("inlining call to %s", inlinedMethod.format("%h.%n(%p)"));
@@ -1547,9 +1549,12 @@
                         intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, INLINE_DURING_PARSING);
                     }
                     if (inlinedMethod.hasBytecodes()) {
+                        for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+                            plugin.notifyBeforeInline(targetMethod);
+                        }
                         parseAndInlineCallee(inlinedMethod, args, intrinsic);
-                        if (plugin != null) {
-                            plugin.postInline(inlinedMethod);
+                        for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) {
+                            plugin.notifyAfterInline(targetMethod);
                         }
                     } else {
                         return false;
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PEGraphDecoderTest.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PEGraphDecoderTest.java	Fri May 29 17:01:31 2015 -0700
@@ -109,7 +109,7 @@
 
     class InlineAll implements InlineInvokePlugin {
         @Override
-        public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
+        public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
             return new InlineInfo(method, false);
         }
     }
@@ -124,7 +124,7 @@
             CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(getProviders(), graphBuilderConfig, OptimisticOptimizations.NONE, AllowAssumptions.YES, getTarget().arch);
 
             targetGraph = new StructuredGraph(testMethod, AllowAssumptions.YES);
-            decoder.decode(targetGraph, testMethod, null, null, new InlineAll(), null);
+            decoder.decode(targetGraph, testMethod, null, null, new InlineInvokePlugin[]{new InlineAll()}, null);
             Debug.dump(targetGraph, "Target Graph");
             targetGraph.verify();
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ConstantBindingParameterPlugin.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ConstantBindingParameterPlugin.java	Fri May 29 17:01:31 2015 -0700
@@ -34,19 +34,16 @@
  */
 public class ConstantBindingParameterPlugin implements ParameterPlugin {
     private final Object[] constantArgs;
-    private final ParameterPlugin delegate;
     private final MetaAccessProvider metaAccess;
     private final SnippetReflectionProvider snippetReflection;
 
     /**
      * Creates a plugin that will create {@link ConstantNode}s for each parameter with an index
      * equal to that of a non-null object in {@code constantArgs} (from which the
-     * {@link ConstantNode} is created if it isn't already a {@link ConstantNode}). For all other
-     * parameter indexes, {@code delegate} is applied if it is non-null.
+     * {@link ConstantNode} is created if it isn't already a {@link ConstantNode}).
      */
-    public ConstantBindingParameterPlugin(Object[] constantArgs, ParameterPlugin delegate, MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection) {
+    public ConstantBindingParameterPlugin(Object[] constantArgs, MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection) {
         this.constantArgs = constantArgs;
-        this.delegate = delegate;
         this.metaAccess = metaAccess;
         this.snippetReflection = snippetReflection;
     }
@@ -64,9 +61,6 @@
             }
             return constantNode;
         }
-        if (delegate != null) {
-            return delegate.interceptParameter(b, index, stamp);
-        }
         return null;
     }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultInlineInvokePlugin.java	Fri May 29 22:27:38 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.replacements;
-
-import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
-
-import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.jvmci.meta.*;
-
-public final class DefaultInlineInvokePlugin implements InlineInvokePlugin {
-    private final ReplacementsImpl replacements;
-
-    public DefaultInlineInvokePlugin(ReplacementsImpl replacements) {
-        this.replacements = replacements;
-    }
-
-    public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
-        InlineInfo inlineInfo = replacements.getInlineInfo(b, method, args, returnType);
-        if (inlineInfo == null) {
-            if (InlineDuringParsing.getValue() && method.hasBytecodes() && !method.isSynchronized() && method.getCode().length <= TrivialInliningSize.getValue() &&
-                            b.getDepth() < InlineDuringParsingMaxDepth.getValue()) {
-                return new InlineInfo(method, false);
-            }
-        }
-        return inlineInfo;
-    }
-
-    public void notifyOfNoninlinedInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
-        replacements.notifyOfNoninlinedInvoke(b, method, invoke);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InlineDuringParsingPlugin.java	Fri May 29 17:01:31 2015 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.replacements;
+
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
+
+import com.oracle.graal.graphbuilderconf.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.jvmci.meta.*;
+
+public final class InlineDuringParsingPlugin implements InlineInvokePlugin {
+
+    @Override
+    public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
+        if (method.hasBytecodes() && !method.isSynchronized() && method.getCode().length <= TrivialInliningSize.getValue() && b.getDepth() < InlineDuringParsingMaxDepth.getValue()) {
+            return new InlineInfo(method, false);
+        }
+        return null;
+    }
+}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Fri May 29 17:01:31 2015 -0700
@@ -69,7 +69,7 @@
 
         protected final LoopExplosionPlugin loopExplosionPlugin;
         protected final InvocationPlugins invocationPlugins;
-        protected final InlineInvokePlugin inlineInvokePlugin;
+        protected final InlineInvokePlugin[] inlineInvokePlugins;
         protected final ParameterPlugin parameterPlugin;
         protected final ValueNode[] arguments;
 
@@ -79,7 +79,7 @@
         protected BytecodePosition bytecodePosition;
 
         protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData,
-                        int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin,
+                        int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins, ParameterPlugin parameterPlugin,
                         ValueNode[] arguments) {
             super(targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin));
 
@@ -90,7 +90,7 @@
             this.inliningDepth = inliningDepth;
             this.loopExplosionPlugin = loopExplosionPlugin;
             this.invocationPlugins = invocationPlugins;
-            this.inlineInvokePlugin = inlineInvokePlugin;
+            this.inlineInvokePlugins = inlineInvokePlugins;
             this.parameterPlugin = parameterPlugin;
             this.arguments = arguments;
         }
@@ -298,9 +298,9 @@
         }
     }
 
-    public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin,
+    public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
                     ParameterPlugin parameterPlugin) {
-        PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin,
+        PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugins,
                         parameterPlugin, null);
         decode(methodScope, null);
         cleanupGraph(methodScope, null);
@@ -355,8 +355,8 @@
             return true;
         }
 
-        if (methodScope.inlineInvokePlugin != null) {
-            methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke);
+        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+            plugin.notifyNotInlined(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke);
         }
         return false;
     }
@@ -381,7 +381,7 @@
         invoke.asNode().replaceAtPredecessor(null);
 
         PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin,
-                        methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
+                        methodScope.invocationPlugins, methodScope.inlineInvokePlugins, null, arguments);
         PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor);
         InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext);
 
@@ -407,7 +407,7 @@
     }
 
     protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
-        if (methodScope.inlineInvokePlugin == null || !callTarget.invokeKind().isDirect()) {
+        if (!callTarget.invokeKind().isDirect()) {
             return false;
         }
 
@@ -418,25 +418,38 @@
 
         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
         GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke);
-        InlineInfo inlineInfo = methodScope.inlineInvokePlugin.getInlineInfo(graphBuilderContext, targetMethod, arguments, callTarget.returnType());
-        if (inlineInfo == null) {
-            return false;
+
+        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+            InlineInfo inlineInfo = plugin.shouldInlineInvoke(graphBuilderContext, targetMethod, arguments, callTarget.returnType());
+            if (inlineInfo != null) {
+                if (inlineInfo.getMethodToInline() == null) {
+                    return false;
+                } else {
+                    assert !inlineInfo.isIntrinsic() : "not supported";
+                    return doInline(methodScope, loopScope, invokeData, inlineInfo.getMethodToInline(), arguments);
+                }
+            }
         }
-        assert !inlineInfo.isIntrinsic : "not supported";
+        return false;
+    }
 
-        ResolvedJavaMethod inlineMethod = inlineInfo.methodToInline;
+    protected boolean doInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, ResolvedJavaMethod inlineMethod, ValueNode[] arguments) {
         EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod);
         if (graphToInline == null) {
             return false;
         }
 
+        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+            plugin.notifyBeforeInline(inlineMethod);
+        }
+
         Invoke invoke = invokeData.invoke;
         FixedNode invokeNode = invoke.asNode();
         FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor();
         invokeNode.replaceAtPredecessor(null);
 
         PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1,
-                        methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
+                        methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugins, null, arguments);
         /* Do the actual inlining by decoding the inlineMethod */
         decode(inlineScope, predecessor);
 
@@ -500,7 +513,9 @@
         }
         deleteInvoke(invoke);
 
-        methodScope.inlineInvokePlugin.postInline(inlineMethod);
+        for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
+            plugin.notifyAfterInline(inlineMethod);
+        }
 
         if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
             Debug.dump(methodScope.graph, "Inline finished: " + inlineMethod.getDeclaringClass().getUnqualifiedName() + "." + inlineMethod.getName());
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Fri May 29 17:01:31 2015 -0700
@@ -95,7 +95,8 @@
      * @return an object specifying how {@code method} is to be inlined or null if it should not be
      *         inlined based on substitution related criteria
      */
-    public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
+    @Override
+    public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
         ResolvedJavaMethod subst = getSubstitutionMethod(method);
         if (subst != null) {
             if (b.parsingIntrinsic() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) {
@@ -123,7 +124,8 @@
         return null;
     }
 
-    public void notifyOfNoninlinedInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
+    @Override
+    public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
         if (b.parsingIntrinsic()) {
             IntrinsicContext intrinsic = b.getIntrinsic();
             assert intrinsic.isCallToOriginal(method) : format("All non-recursive calls in the intrinsic %s must be inlined or intrinsified: found call to %s",
@@ -556,7 +558,7 @@
                 Plugins plugins = new Plugins(replacements.graphBuilderPlugins);
                 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
                 if (args != null) {
-                    plugins.setParameterPlugin(new ConstantBindingParameterPlugin(args, plugins.getParameterPlugin(), metaAccess, replacements.snippetReflection));
+                    plugins.prependParameterPlugin(new ConstantBindingParameterPlugin(args, metaAccess, replacements.snippetReflection));
                 }
 
                 IntrinsicContext initialIntrinsicContext = null;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/WordOperationPlugin.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/WordOperationPlugin.java	Fri May 29 17:01:31 2015 -0700
@@ -85,7 +85,7 @@
     }
 
     @Override
-    public void notifyOfNoninlinedInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
+    public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
         if (wordTypes.isWord(invoke.asNode())) {
             invoke.asNode().setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(invoke.asNode())));
         }
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java	Fri May 29 17:01:31 2015 -0700
@@ -140,12 +140,13 @@
         // get UnsafeAccessImpl.unsafeGetInt intrinsified
         TruffleGraphBuilderPlugins.registerUnsafeAccessImplPlugins(conf.getPlugins().getInvocationPlugins(), false);
         // get UnsafeAccess.getInt inlined
-        conf.getPlugins().setInlineInvokePlugin(new InlineEverythingPlugin());
+        conf.getPlugins().appendInlineInvokePlugin(new InlineEverythingPlugin());
         return super.editGraphBuilderConfiguration(conf);
     }
 
     private static final class InlineEverythingPlugin implements InlineInvokePlugin {
-        public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
+        @Override
+        public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
             assert method.hasBytecodes();
             return new InlineInfo(method, false);
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri May 29 17:01:31 2015 -0700
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.truffle;
 
-import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.java.GraphBuilderPhase.Options.*;
 import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
 
 import java.lang.invoke.*;
@@ -158,17 +156,12 @@
         }
 
         @Override
-        public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
-            InlineInfo inlineInfo = replacements.getInlineInfo(builder, original, arguments, returnType);
-            if (inlineInfo != null) {
-                return inlineInfo;
-            }
-
+        public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
             if (original.getAnnotation(TruffleBoundary.class) != null) {
-                return null;
+                return InlineInfo.DO_NOT_INLINE;
             }
             if (replacements.hasSubstitution(original, builder.bci())) {
-                return null;
+                return InlineInfo.DO_NOT_INLINE;
             }
             assert !builder.parsingIntrinsic();
 
@@ -199,7 +192,7 @@
         }
 
         @Override
-        public void postInline(ResolvedJavaMethod inlinedTargetMethod) {
+        public void notifyAfterInline(ResolvedJavaMethod inlinedTargetMethod) {
             if (inlinedTargetMethod.equals(callInlinedMethod)) {
                 inlining.pop();
             }
@@ -211,13 +204,11 @@
         private final ReplacementsImpl replacements;
         private final InvocationPlugins invocationPlugins;
         private final LoopExplosionPlugin loopExplosionPlugin;
-        private final boolean inlineDuringParsing;
 
-        public ParsingInlineInvokePlugin(ReplacementsImpl replacements, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin, boolean inlineDuringParsing) {
+        public ParsingInlineInvokePlugin(ReplacementsImpl replacements, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin) {
             this.replacements = replacements;
             this.invocationPlugins = invocationPlugins;
             this.loopExplosionPlugin = loopExplosionPlugin;
-            this.inlineDuringParsing = inlineDuringParsing;
         }
 
         private boolean hasMethodHandleArgument(ValueNode[] arguments) {
@@ -233,24 +224,19 @@
         }
 
         @Override
-        public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
-            InlineInfo inlineInfo = replacements.getInlineInfo(builder, original, arguments, returnType);
-            if (inlineInfo != null) {
-                return inlineInfo;
-            }
-
+        public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
             if (invocationPlugins.lookupInvocation(original) != null || loopExplosionPlugin.shouldExplodeLoops(original)) {
-                return null;
+                return InlineInfo.DO_NOT_INLINE;
             }
             if (original.getAnnotation(TruffleBoundary.class) != null) {
-                return null;
+                return InlineInfo.DO_NOT_INLINE;
             }
             if (replacements.hasSubstitution(original, builder.bci())) {
-                return null;
+                return InlineInfo.DO_NOT_INLINE;
             }
 
             if (original.equals(callSiteProxyMethod) || original.equals(callDirectMethod)) {
-                return null;
+                return InlineInfo.DO_NOT_INLINE;
             }
             if (hasMethodHandleArgument(arguments)) {
                 /*
@@ -259,9 +245,6 @@
                  */
                 return new InlineInfo(original, false);
             }
-            if (inlineDuringParsing && original.hasBytecodes() && original.getCode().length < TrivialInliningSize.getValue() && builder.getDepth() < InlineDuringParsingMaxDepth.getValue()) {
-                return new InlineInfo(original, false);
-            }
             return null;
         }
     }
@@ -289,18 +272,24 @@
 
         newConfig.setUseProfiling(false);
         Plugins plugins = newConfig.getPlugins();
-        plugins.setParameterPlugin(new InterceptReceiverPlugin(callTarget));
+        plugins.prependParameterPlugin(new InterceptReceiverPlugin(callTarget));
         callTarget.setInlining(new TruffleInlining(callTarget, new DefaultInliningPolicy()));
+        plugins.setLoopExplosionPlugin(new PELoopExplosionPlugin());
 
-        InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), (ReplacementsImpl) providers.getReplacements());
+        ReplacementsImpl replacements = (ReplacementsImpl) providers.getReplacements();
+        plugins.clearInlineInvokePlugins();
+        plugins.appendInlineInvokePlugin(replacements);
+        plugins.appendInlineInvokePlugin(new PEInlineInvokePlugin(callTarget.getInlining(), replacements));
+        HistogramInlineInvokePlugin histogramPlugin = null;
         if (PrintTruffleExpansionHistogram.getValue()) {
-            inlinePlugin = new HistogramInlineInvokePlugin(graph, inlinePlugin);
+            histogramPlugin = new HistogramInlineInvokePlugin(graph);
+            plugins.appendInlineInvokePlugin(histogramPlugin);
         }
-        plugins.setInlineInvokePlugin(inlinePlugin);
-        plugins.setLoopExplosionPlugin(new PELoopExplosionPlugin());
+
         new GraphBuilderPhase.Instance(providers.getMetaAccess(), providers.getStampProvider(), providers.getConstantReflection(), newConfig, TruffleCompiler.Optimizations, null).apply(graph);
+
         if (PrintTruffleExpansionHistogram.getValue()) {
-            ((HistogramInlineInvokePlugin) inlinePlugin).print(callTarget, System.out);
+            histogramPlugin.print(callTarget, System.out);
         }
     }
 
@@ -313,8 +302,11 @@
 
         newConfig.setUseProfiling(false);
         Plugins plugins = newConfig.getPlugins();
-        plugins.setInlineInvokePlugin(new ParsingInlineInvokePlugin((ReplacementsImpl) providers.getReplacements(), parsingInvocationPlugins, loopExplosionPlugin,
-                        !PrintTruffleExpansionHistogram.getValue()));
+        plugins.clearInlineInvokePlugins();
+        plugins.appendInlineInvokePlugin(new ParsingInlineInvokePlugin((ReplacementsImpl) providers.getReplacements(), parsingInvocationPlugins, loopExplosionPlugin));
+        if (!PrintTruffleExpansionHistogram.getValue()) {
+            plugins.appendInlineInvokePlugin(new InlineDuringParsingPlugin());
+        }
 
         return new CachingPEGraphDecoder(providers, newConfig, TruffleCompiler.Optimizations, AllowAssumptions.from(graph.getAssumptions() != null), architecture);
     }
@@ -326,17 +318,24 @@
 
         LoopExplosionPlugin loopExplosionPlugin = new PELoopExplosionPlugin();
         ParameterPlugin parameterPlugin = new InterceptReceiverPlugin(callTarget);
+        InvocationPlugins invocationPlugins = createDecodingInvocationPlugins();
 
-        InvocationPlugins decodingInvocationPlugins = createDecodingInvocationPlugins();
-        InlineInvokePlugin decodingInlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), (ReplacementsImpl) providers.getReplacements());
+        ReplacementsImpl replacements = (ReplacementsImpl) providers.getReplacements();
+        InlineInvokePlugin[] inlineInvokePlugins;
+        InlineInvokePlugin inlineInvokePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), replacements);
+
+        HistogramInlineInvokePlugin histogramPlugin = null;
         if (PrintTruffleExpansionHistogram.getValue()) {
-            decodingInlinePlugin = new HistogramInlineInvokePlugin(graph, decodingInlinePlugin);
+            histogramPlugin = new HistogramInlineInvokePlugin(graph);
+            inlineInvokePlugins = new InlineInvokePlugin[]{replacements, inlineInvokePlugin, histogramPlugin};
+        } else {
+            inlineInvokePlugins = new InlineInvokePlugin[]{replacements, inlineInvokePlugin};
         }
 
-        decoder.decode(graph, graph.method(), loopExplosionPlugin, decodingInvocationPlugins, decodingInlinePlugin, parameterPlugin);
+        decoder.decode(graph, graph.method(), loopExplosionPlugin, invocationPlugins, inlineInvokePlugins, parameterPlugin);
 
         if (PrintTruffleExpansionHistogram.getValue()) {
-            ((HistogramInlineInvokePlugin) decodingInlinePlugin).print(callTarget, System.out);
+            histogramPlugin.print(callTarget, System.out);
         }
     }
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/debug/HistogramInlineInvokePlugin.java	Fri May 29 22:27:38 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/debug/HistogramInlineInvokePlugin.java	Fri May 29 17:01:31 2015 -0700
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.truffle.debug;
 
-import com.oracle.jvmci.meta.JavaType;
-import com.oracle.jvmci.meta.ResolvedJavaMethod;
 import java.io.*;
 import java.util.*;
 
@@ -33,37 +31,32 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.truffle.*;
+import com.oracle.jvmci.meta.*;
 
 public class HistogramInlineInvokePlugin implements InlineInvokePlugin {
 
     private final Map<ResolvedJavaMethod, MethodStatistics> histogram = new HashMap<>();
     private final StructuredGraph graph;
-    private final InlineInvokePlugin delegate;
 
     private HistogramInlineInvokePlugin.MethodStatistic currentStatistic;
 
-    public HistogramInlineInvokePlugin(StructuredGraph graph, InlineInvokePlugin delegate) {
+    public HistogramInlineInvokePlugin(StructuredGraph graph) {
         this.graph = graph;
-        this.delegate = delegate;
+    }
+
+    @Override
+    public void notifyBeforeInline(ResolvedJavaMethod methodToInline) {
+        currentStatistic = new MethodStatistic(currentStatistic, methodToInline, countNodes(), countCalls());
     }
 
-    public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
-        InlineInfo inlineInfo = delegate.getInlineInfo(b, method, args, returnType);
-        if (inlineInfo != null) {
-            currentStatistic = new MethodStatistic(currentStatistic, inlineInfo.methodToInline, countNodes(), countCalls());
-        }
-        return inlineInfo;
-    }
+    @Override
+    public void notifyAfterInline(ResolvedJavaMethod methodToInline) {
+        assert methodToInline.equals(currentStatistic.method);
 
-    public void postInline(ResolvedJavaMethod inlinedTargetMethod) {
-        delegate.postInline(inlinedTargetMethod);
-
-        if (currentStatistic != null) {
-            currentStatistic.applyNodeCountAfter(countNodes());
-            currentStatistic.applyCallsAfter(countCalls());
-            accept(currentStatistic);
-            currentStatistic = currentStatistic.getParent();
-        }
+        currentStatistic.applyNodeCountAfter(countNodes());
+        currentStatistic.applyCallsAfter(countCalls());
+        accept(currentStatistic);
+        currentStatistic = currentStatistic.getParent();
     }
 
     private int countNodes() {