changeset 19962:9b669776bf8a

added null checking for the receiver when inlining or applying an InvocationPlugin during graph parsing
author Doug Simon <doug.simon@oracle.com>
date Thu, 19 Mar 2015 22:31:42 +0100
parents 71040f48cc34
children 51cbcbc3eac8
files graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java
diffstat 11 files changed, 359 insertions(+), 230 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java	Thu Mar 19 22:31:42 2015 +0100
@@ -124,17 +124,6 @@
      */
     Replacement getReplacement();
 
-    /**
-     * @see GuardingPiNode#nullCheckedValue(ValueNode)
-     */
-    static ValueNode nullCheckedValue(GraphBuilderContext builder, ValueNode value) {
-        ValueNode nonNullValue = GuardingPiNode.nullCheckedValue(value);
-        if (nonNullValue != value) {
-            builder.append((FixedWithNextNode) nonNullValue);
-        }
-        return nonNullValue;
-    }
-
     boolean eagerResolving();
 
     BailoutException bailout(String string);
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java	Thu Mar 19 22:31:42 2015 +0100
@@ -26,6 +26,7 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
 import com.oracle.graal.nodes.*;
 
 /**
@@ -36,43 +37,43 @@
     /**
      * @see #execute
      */
-    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
-        throw invalidHandler(b, targetMethod);
+    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
+        throw invalidHandler(b, targetMethod, receiver);
     }
 
     /**
      * @see #execute
      */
-    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg) {
-        throw invalidHandler(b, targetMethod, arg);
+    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
+        throw invalidHandler(b, targetMethod, receiver, arg);
     }
 
     /**
      * @see #execute
      */
-    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg1, ValueNode arg2) {
-        throw invalidHandler(b, targetMethod, arg1, arg2);
+    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2) {
+        throw invalidHandler(b, targetMethod, receiver, arg1, arg2);
     }
 
     /**
      * @see #execute
      */
-    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg1, ValueNode arg2, ValueNode arg3) {
-        throw invalidHandler(b, targetMethod, arg1, arg2, arg3);
+    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3) {
+        throw invalidHandler(b, targetMethod, receiver, arg1, arg2, arg3);
     }
 
     /**
      * @see #execute
      */
-    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4) {
-        throw invalidHandler(b, targetMethod, arg1, arg2, arg3, arg4);
+    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4) {
+        throw invalidHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4);
     }
 
     /**
      * @see #execute
      */
-    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5) {
-        throw invalidHandler(b, targetMethod, arg1, arg2, arg3, arg4, arg5);
+    default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5) {
+        throw invalidHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4, arg5);
     }
 
     default ResolvedJavaMethod getSubstitute() {
@@ -84,30 +85,52 @@
      * {@code apply(...)} method that matches the number of arguments.
      *
      * @param targetMethod the method for which plugin is being applied
+     * @param receiver access to the receiver, {@code null} if {@code targetMethod} is static
+     * @param args the remaining arguments
      * @return {@code true} if the plugin handled the invocation of {@code targetMethod}
      *         {@code false} if the graph builder should process the invoke further (e.g., by
      *         inlining it or creating an {@link Invoke} node). A plugin that does not handle an
      *         invocation must not modify the graph being constructed.
      */
-    static boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin plugin, ValueNode[] args) {
-        if (args.length == 0) {
-            return plugin.apply(b, targetMethod);
-        } else if (args.length == 1) {
-            return plugin.apply(b, targetMethod, args[0]);
-        } else if (args.length == 2) {
-            return plugin.apply(b, targetMethod, args[0], args[1]);
-        } else if (args.length == 3) {
-            return plugin.apply(b, targetMethod, args[0], args[1], args[2]);
-        } else if (args.length == 4) {
-            return plugin.apply(b, targetMethod, args[0], args[1], args[2], args[3]);
-        } else if (args.length == 5) {
-            return plugin.apply(b, targetMethod, args[0], args[1], args[2], args[3], args[4]);
+    static boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin plugin, Receiver receiver, ValueNode[] args) {
+        if (receiver != null) {
+            assert !targetMethod.isStatic();
+            assert args.length > 0;
+            if (args.length == 1) {
+                return plugin.apply(b, targetMethod, receiver);
+            } else if (args.length == 2) {
+                return plugin.apply(b, targetMethod, receiver, args[1]);
+            } else if (args.length == 3) {
+                return plugin.apply(b, targetMethod, receiver, args[1], args[2]);
+            } else if (args.length == 4) {
+                return plugin.apply(b, targetMethod, receiver, args[1], args[2], args[3]);
+            } else if (args.length == 5) {
+                return plugin.apply(b, targetMethod, receiver, args[1], args[2], args[3], args[4]);
+            } else {
+                throw plugin.invalidHandler(b, targetMethod, receiver, args);
+            }
         } else {
-            throw plugin.invalidHandler(b, targetMethod, args);
+            assert targetMethod.isStatic();
+            if (args.length == 0) {
+                return plugin.apply(b, targetMethod, null);
+            } else if (args.length == 1) {
+                return plugin.apply(b, targetMethod, null, args[0]);
+            } else if (args.length == 2) {
+                return plugin.apply(b, targetMethod, null, args[0], args[1]);
+            } else if (args.length == 3) {
+                return plugin.apply(b, targetMethod, null, args[0], args[1], args[2]);
+            } else if (args.length == 4) {
+                return plugin.apply(b, targetMethod, null, args[0], args[1], args[2], args[3]);
+            } else if (args.length == 5) {
+                return plugin.apply(b, targetMethod, null, args[0], args[1], args[2], args[3], args[4]);
+            } else {
+                throw plugin.invalidHandler(b, targetMethod, receiver, args);
+            }
+
         }
     }
 
-    default Error invalidHandler(@SuppressWarnings("unused") GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode... args) {
+    default Error invalidHandler(@SuppressWarnings("unused") GraphBuilderContext b, ResolvedJavaMethod targetMethod, @SuppressWarnings("unused") Receiver receiver, ValueNode... args) {
         return new GraalInternalError("Invocation plugin for %s does not handle invocations with %d arguments", targetMethod.format("%H.%n(%p)"), args.length);
     }
 
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java	Thu Mar 19 22:31:42 2015 +0100
@@ -33,6 +33,7 @@
 import com.oracle.graal.graph.Node;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.type.*;
 
 /**
  * Manages a set of {@link InvocationPlugin}s.
@@ -40,14 +41,29 @@
 public class InvocationPlugins {
 
     /**
-     * Sentinel class for use with
+     * Access to the receiver in an {@link InvocationPlugin} for a non-static method. The class
+     * literal for this interface must be used with
      * {@link InvocationPlugins#register(InvocationPlugin, Class, String, Class...)} to denote the
-     * receiver argument for a non-static method.
+     * receiver argument for such a non-static method.
      */
-    public static final class Receiver {
-        private Receiver() {
-            throw GraalInternalError.shouldNotReachHere();
-        }
+    public interface Receiver {
+        /**
+         * Gets the receiver value, null checking it first if necessary.
+         *
+         * @return the receiver value with a {@linkplain StampTool#isPointerNonNull(ValueNode)
+         *         non-null} stamp
+         */
+        ValueNode get();
+
+        /**
+         * Determines if the receiver is constant.
+         */
+        boolean isConstant();
+
+        /**
+         * Determines if the receiver is the null constant.
+         */
+        boolean isNullConstant();
     }
 
     /**
@@ -344,11 +360,12 @@
                     Class<?>[] sig = method.getParameterTypes();
                     assert sig[0] == GraphBuilderContext.class;
                     assert sig[1] == ResolvedJavaMethod.class;
-                    assert Arrays.asList(Arrays.copyOfRange(sig, 2, sig.length)).stream().allMatch(c -> c == ValueNode.class);
-                    while (sigs.size() < sig.length - 1) {
+                    assert sig[2] == Receiver.class;
+                    assert Arrays.asList(Arrays.copyOfRange(sig, 3, sig.length)).stream().allMatch(c -> c == ValueNode.class);
+                    while (sigs.size() < sig.length - 2) {
                         sigs.add(null);
                     }
-                    sigs.set(sig.length - 2, sig);
+                    sigs.set(sig.length - 3, sig);
                 }
             }
             assert sigs.indexOf(null) == -1 : format("need to add an apply() method to %s that takes %d %s arguments ", InvocationPlugin.class.getName(), sigs.indexOf(null),
@@ -362,7 +379,7 @@
                 assert !p.registrations.contains(method) : "a plugin is already registered for " + method;
                 p = p.parent;
             }
-            int arguments = method.argumentTypes.length;
+            int arguments = method.isStatic ? method.argumentTypes.length : method.argumentTypes.length - 1;
             assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, method);
             for (Method m : plugin.getClass().getDeclaredMethods()) {
                 if (m.getName().equals("apply")) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Thu Mar 19 22:31:42 2015 +0100
@@ -22,7 +22,6 @@
  */
 package com.oracle.graal.hotspot.meta;
 
-import static com.oracle.graal.graphbuilderconf.GraphBuilderContext.*;
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 
 import com.oracle.graal.api.code.*;
@@ -30,9 +29,10 @@
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.*;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.replacements.*;
@@ -84,14 +84,15 @@
     private static void registerObjectPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Object.class);
         r.register1("getClass", Receiver.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode rcvr) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
+                ValueNode rcvr = receiver.get();
                 ObjectStamp objectStamp = (ObjectStamp) rcvr.stamp();
                 ValueNode mirror;
                 if (objectStamp.isExactType() && objectStamp.nonNull() && !GraalOptions.ImmutableCode.getValue()) {
                     mirror = b.append(ConstantNode.forConstant(objectStamp.type().getJavaClass(), b.getMetaAccess()));
                 } else {
                     StampProvider stampProvider = b.getStampProvider();
-                    LoadHubNode hub = b.append(new LoadHubNode(stampProvider, nullCheckedValue(b, rcvr)));
+                    LoadHubNode hub = b.append(new LoadHubNode(stampProvider, rcvr));
                     mirror = b.append(new HubGetClassNode(b.getMetaAccess(), hub));
                 }
                 b.push(Kind.Object, mirror);
@@ -103,7 +104,7 @@
     private static void registerSystemPlugins(InvocationPlugins plugins, ForeignCallsProvider foreignCalls) {
         Registration r = new Registration(plugins, System.class);
         r.register0("currentTimeMillis", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 ForeignCallNode foreignCall = new ForeignCallNode(foreignCalls, SystemSubstitutions.JAVA_TIME_MILLIS, StampFactory.forKind(Kind.Long));
                 b.push(Kind.Long, b.append(foreignCall));
                 foreignCall.setStateAfter(b.createStateAfter());
@@ -111,7 +112,7 @@
             }
         });
         r.register0("nanoTime", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 ForeignCallNode foreignCall = new ForeignCallNode(foreignCalls, SystemSubstitutions.JAVA_TIME_NANOS, StampFactory.forKind(Kind.Long));
                 b.push(Kind.Long, b.append(foreignCall));
                 foreignCall.setStateAfter(b.createStateAfter());
@@ -123,7 +124,7 @@
     private static void registerThreadPlugins(InvocationPlugins plugins, MetaAccessProvider metaAccess, WordTypes wordTypes, HotSpotVMConfig config) {
         Registration r = new Registration(plugins, Thread.class);
         r.register0("currentThread", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 CurrentJavaThreadNode thread = b.append(new CurrentJavaThreadNode(wordTypes.getWordKind()));
                 ConstantLocationNode location = b.append(new ConstantLocationNode(JAVA_THREAD_THREAD_OBJECT_LOCATION, config.threadObjectOffset));
                 boolean compressible = false;
@@ -139,9 +140,9 @@
     private static void registerStableOptionPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, StableOptionValue.class);
         r.register1("getValue", Receiver.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode rcvr) {
-                if (rcvr.isConstant() && !rcvr.isNullConstant()) {
-                    Object object = ((HotSpotObjectConstantImpl) rcvr.asConstant()).object();
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
+                if (receiver.isConstant() && !receiver.isNullConstant()) {
+                    Object object = ((HotSpotObjectConstantImpl) receiver.get().asConstant()).object();
                     StableOptionValue<?> option = (StableOptionValue<?>) object;
                     ConstantNode value = b.append(ConstantNode.forConstant(HotSpotObjectConstantImpl.forObject(option.getValue()), b.getMetaAccess()));
                     b.push(Kind.Object, value);
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Thu Mar 19 22:31:42 2015 +0100
@@ -38,10 +38,11 @@
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.GraphBuilderContext.*;
+import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
 import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 
@@ -333,11 +334,8 @@
     protected abstract ValueNode genLoadIndexed(ValueNode index, ValueNode array, Kind kind);
 
     private void genLoadIndexed(Kind kind) {
-
-        emitExplicitExceptions(frameState.peek(1), frameState.peek(0));
-
         ValueNode index = frameState.ipop();
-        ValueNode array = frameState.apop();
+        ValueNode array = emitExplicitExceptions(frameState.apop(), index);
         if (!tryLoadIndexedPlugin(kind, index, array)) {
             frameState.push(kind.getStackKind(), append(genLoadIndexed(array, index, kind)));
         }
@@ -360,11 +358,9 @@
     protected abstract void genStoreIndexed(ValueNode array, ValueNode index, Kind kind, ValueNode value);
 
     private void genStoreIndexed(Kind kind) {
-        emitExplicitExceptions(frameState.peek(2), frameState.peek(1));
-
         ValueNode value = frameState.pop(kind.getStackKind());
         ValueNode index = frameState.ipop();
-        ValueNode array = frameState.apop();
+        ValueNode array = emitExplicitExceptions(frameState.apop(), index);
         genStoreIndexed(array, index, kind, value);
     }
 
@@ -832,10 +828,8 @@
     protected abstract ValueNode genLoadField(ValueNode receiver, ResolvedJavaField field);
 
     private void genGetField(JavaField field) {
-        emitExplicitExceptions(frameState.peek(0), null);
-
         Kind kind = field.getKind();
-        ValueNode receiver = frameState.apop();
+        ValueNode receiver = emitExplicitExceptions(frameState.apop(), null);
         if ((field instanceof ResolvedJavaField) && ((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
             LoadFieldPlugin loadFieldPlugin = this.graphBuilderConfig.getPlugins().getLoadFieldPlugin();
             if (loadFieldPlugin == null || !loadFieldPlugin.apply((GraphBuilderContext) this, receiver, (ResolvedJavaField) field)) {
@@ -846,36 +840,53 @@
         }
     }
 
-    protected abstract void emitNullCheck(ValueNode receiver);
+    /**
+     * Emits control flow to null check a receiver if it's stamp does not indicate it is
+     * {@linkplain StampTool#isPointerNonNull always non-null}.
+     *
+     * @return the receiver with a stamp indicating non-nullness
+     */
+    protected abstract ValueNode emitExplicitNullCheck(ValueNode receiver);
 
-    protected abstract void emitBoundsCheck(ValueNode index, ValueNode length);
+    /**
+     * Emits control flow to check an array index is within bounds of an array's length.
+     *
+     * @param index the index to check
+     * @param length the length of the array being indexed
+     */
+    protected abstract void emitExplicitBoundsCheck(ValueNode index, ValueNode length);
 
     private static final DebugMetric EXPLICIT_EXCEPTIONS = Debug.metric("ExplicitExceptions");
 
     protected abstract ValueNode genArrayLength(ValueNode x);
 
-    protected void emitExplicitExceptions(ValueNode receiver, ValueNode outOfBoundsIndex) {
+    /**
+     * @param receiver the receiver of an object based operation
+     * @param index the index of an array based operation that is to be tested for out of bounds.
+     *            This is null for a non-array operation.
+     * @return the receiver value possibly modified to have a tighter stamp
+     */
+    protected ValueNode emitExplicitExceptions(ValueNode receiver, ValueNode index) {
         assert receiver != null;
         if (graphBuilderConfig.omitAllExceptionEdges() || profilingInfo == null ||
                         (optimisticOpts.useExceptionProbabilityForOperations() && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE && !GraalOptions.StressExplicitExceptionCode.getValue())) {
-            return;
+            return receiver;
         }
 
-        emitNullCheck(receiver);
-        if (outOfBoundsIndex != null) {
-            ValueNode length = append(genArrayLength(receiver));
-            emitBoundsCheck(outOfBoundsIndex, length);
+        ValueNode nonNullReceiver = emitExplicitNullCheck(receiver);
+        if (index != null) {
+            ValueNode length = append(genArrayLength(nonNullReceiver));
+            emitExplicitBoundsCheck(index, length);
         }
         EXPLICIT_EXCEPTIONS.increment();
+        return nonNullReceiver;
     }
 
     protected abstract void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value);
 
     private void genPutField(JavaField field) {
-        emitExplicitExceptions(frameState.peek(1), null);
-
         ValueNode value = frameState.pop(field.getKind().getStackKind());
-        ValueNode receiver = frameState.apop();
+        ValueNode receiver = emitExplicitExceptions(frameState.apop(), null);
         if (field instanceof ResolvedJavaField && ((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
             genStoreField(receiver, (ResolvedJavaField) field, value);
         } else {
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Thu Mar 19 22:31:42 2015 +0100
@@ -27,9 +27,10 @@
 import static com.oracle.graal.bytecode.Bytecodes.*;
 import static com.oracle.graal.compiler.common.GraalInternalError.*;
 import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.graph.iterators.NodePredicates.*;
+import static com.oracle.graal.compiler.common.type.StampFactory.*;
 import static com.oracle.graal.java.AbstractBytecodeParser.Options.*;
 import static com.oracle.graal.nodes.StructuredGraph.*;
+import static com.oracle.graal.nodes.type.StampTool.*;
 import static java.lang.String.*;
 
 import java.util.*;
@@ -49,6 +50,7 @@
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
 import com.oracle.graal.java.AbstractBytecodeParser.ReplacementContext;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
 import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
@@ -233,6 +235,47 @@
             }
         }
 
+        static class InvocationPluginReceiver implements Receiver {
+            final BytecodeParser parser;
+            ValueNode[] args;
+            ValueNode value;
+
+            public InvocationPluginReceiver(BytecodeParser parser) {
+                this.parser = parser;
+            }
+
+            @Override
+            public ValueNode get() {
+                assert args != null : "Cannot get the receiver of a static method";
+                if (value == null) {
+                    value = parser.nullCheckedValue(args[0]);
+                    if (value != args[0]) {
+                        args[0] = value;
+                    }
+                }
+                return value;
+            }
+
+            @Override
+            public boolean isConstant() {
+                return args[0].isConstant();
+            }
+
+            @Override
+            public boolean isNullConstant() {
+                return args[0].isNullConstant();
+            }
+
+            InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) {
+                if (!targetMethod.isStatic()) {
+                    this.args = newArgs;
+                    this.value = null;
+                    return this;
+                }
+                return null;
+            }
+        }
+
         public class BytecodeParser extends AbstractBytecodeParser implements GraphBuilderContext {
 
             private BciBlockMapping blockMap;
@@ -258,6 +301,7 @@
             private Stack<ExplodedLoopContext> explodeLoopsContext;
             private int nextPeelIteration = 1;
             private boolean controlFlowSplit;
+            private final InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(this);
 
             private FixedWithNextNode[] firstInstructionArray;
             private HIRFrameStateBuilder[] entryStateArray;
@@ -385,6 +429,28 @@
                 }
             }
 
+            /**
+             * Gets a version of a given value that has a
+             * {@linkplain StampTool#isPointerNonNull(ValueNode) non-null} stamp.
+             */
+            ValueNode nullCheckedValue(ValueNode value) {
+                if (!StampTool.isPointerNonNull(value.stamp())) {
+                    IsNullNode condition = currentGraph.unique(new IsNullNode(value));
+                    ObjectStamp receiverStamp = (ObjectStamp) value.stamp();
+                    Stamp stamp = receiverStamp.join(objectNonNull());
+                    FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, NullCheckException, InvalidateReprofile, true));
+                    PiNode nonNullReceiver = currentGraph.unique(new PiNode(value, stamp));
+                    nonNullReceiver.setGuard(fixedGuard);
+                    // TODO: Propogating the non-null into the frame state would
+                    // remove subsequent null-checks on the same value. However,
+                    // it currently causes an assertion failure when merging states.
+                    //
+                    // frameState.replace(value, nonNullReceiver);
+                    return nonNullReceiver;
+                }
+                return value;
+            }
+
             private void detectLoops(FixedNode startInstruction) {
                 NodeBitMap visited = currentGraph.createNodeBitMap();
                 NodeBitMap active = currentGraph.createNodeBitMap();
@@ -947,21 +1013,24 @@
             }
 
             @Override
-            protected void emitNullCheck(ValueNode receiver) {
+            protected ValueNode emitExplicitNullCheck(ValueNode receiver) {
                 if (StampTool.isPointerNonNull(receiver.stamp())) {
-                    return;
+                    return receiver;
                 }
                 BytecodeExceptionNode exception = currentGraph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class));
                 AbstractBeginNode falseSucc = currentGraph.add(new BeginNode());
+                PiNode nonNullReceiver = currentGraph.unique(new PiNode(receiver, receiver.stamp().join(objectNonNull())));
+                nonNullReceiver.setGuard(falseSucc);
                 append(new IfNode(currentGraph.unique(new IsNullNode(receiver)), exception, falseSucc, 0.01));
                 lastInstr = falseSucc;
 
                 exception.setStateAfter(createFrameState(bci()));
                 exception.setNext(handleException(exception, bci()));
+                return nonNullReceiver;
             }
 
             @Override
-            protected void emitBoundsCheck(ValueNode index, ValueNode length) {
+            protected void emitExplicitBoundsCheck(ValueNode index, ValueNode length) {
                 AbstractBeginNode trueSucc = currentGraph.add(new BeginNode());
                 BytecodeExceptionNode exception = currentGraph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index));
                 append(new IfNode(currentGraph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99));
@@ -1102,11 +1171,16 @@
                     returnType = returnType.resolve(targetMethod.getDeclaringClass());
                 }
                 if (invokeKind.hasReceiver()) {
-                    emitExplicitExceptions(args[0], null);
+                    args[0] = emitExplicitExceptions(args[0], null);
                     if (invokeKind.isIndirect() && profilingInfo != null && this.optimisticOpts.useTypeCheckHints()) {
                         JavaTypeProfile profile = profilingInfo.getTypeProfile(bci());
                         args[0] = TypeProfileProxyNode.proxify(args[0], profile);
                     }
+
+                    if (args[0].isNullConstant()) {
+                        append(new DeoptimizeNode(InvalidateRecompile, NullCheckException));
+                        return;
+                    }
                 }
 
                 if (tryGenericInvocationPlugin(args, targetMethod)) {
@@ -1176,24 +1250,30 @@
                     this.mark = currentGraph.getMark();
                 }
 
+                String error(String format, Object... a) {
+                    return String.format(format, a) + String.format("%n\tplugin at %s", plugin.getApplySourceLocation(metaAccess));
+                }
+
                 boolean check(boolean pluginResult) {
                     if (pluginResult == true) {
-                        assert beforeStackSize + resultType.getSlotCount() == frameState.stackSize : "plugin manipulated the stack incorrectly " + targetMethod;
+                        assert beforeStackSize + resultType.getSlotCount() == frameState.stackSize : error("plugin manipulated the stack incorrectly");
                         NodeIterable<Node> newNodes = currentGraph.getNewNodes(mark);
-                        assert !needsNullCheck || args[0].usages().filter(isNotA(FrameState.class)).isEmpty() || containsNullCheckOf(newNodes, args[0]) : format(
-                                        "plugin needs to null check the receiver of %s: receiver=%s%n\tplugin at %s", targetMethod.format("%H.%n(%p)"), args[0],
-                                        plugin.getApplySourceLocation(metaAccess));
+                        assert !needsNullCheck || isPointerNonNull(args[0].stamp()) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), args[0]);
                         for (Node n : newNodes) {
                             if (n instanceof StateSplit) {
                                 StateSplit stateSplit = (StateSplit) n;
-                                assert stateSplit.stateAfter() != null : format("%s node added by plugin for %s need to have a non-null frame state: %s%n\tplugin at %s",
-                                                StateSplit.class.getSimpleName(), targetMethod.format("%H.%n(%p)"), stateSplit, plugin.getApplySourceLocation(metaAccess));
+                                assert stateSplit.stateAfter() != null : error("%s node added by plugin for %s need to have a non-null frame state: %s", StateSplit.class.getSimpleName(),
+                                                targetMethod.format("%H.%n(%p)"), stateSplit);
                             }
                         }
-                        graphBuilderConfig.getPlugins().getInvocationPlugins().checkNewNodes(BytecodeParser.this, plugin, newNodes);
+                        try {
+                            graphBuilderConfig.getPlugins().getInvocationPlugins().checkNewNodes(BytecodeParser.this, plugin, newNodes);
+                        } catch (Throwable t) {
+                            throw new AssertionError(error("Error in plugin"), t);
+                        }
                     } else {
-                        assert nodeCount == currentGraph.getNodeCount() : "plugin that returns false must not create new nodes";
-                        assert beforeStackSize == frameState.stackSize : "plugin that returns false must modify the stack";
+                        assert nodeCount == currentGraph.getNodeCount() : error("plugin that returns false must not create new nodes");
+                        assert beforeStackSize == frameState.stackSize : error("plugin that returns false must modify the stack");
                     }
                     return true;
                 }
@@ -1203,7 +1283,7 @@
                 InvocationPlugin plugin = graphBuilderConfig.getPlugins().getInvocationPlugins().lookupInvocation(targetMethod);
                 if (plugin != null) {
                     InvocationPluginAssertions assertions = assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
-                    if (InvocationPlugin.execute(this, targetMethod, plugin, args)) {
+                    if (InvocationPlugin.execute(this, targetMethod, plugin, invocationPluginReceiver.init(targetMethod, args), args)) {
                         assert assertions.check(true);
                         return true;
                     }
@@ -1217,18 +1297,6 @@
                 return plugin != null && plugin.apply(this, targetMethod, args);
             }
 
-            private boolean containsNullCheckOf(NodeIterable<Node> nodes, Node value) {
-                for (Node n : nodes) {
-                    if (n instanceof GuardingPiNode) {
-                        GuardingPiNode pi = (GuardingPiNode) n;
-                        if (pi.condition() instanceof IsNullNode) {
-                            return ((IsNullNode) pi.condition()).getValue() == value;
-                        }
-                    }
-                }
-                return false;
-            }
-
             private boolean tryInline(ValueNode[] args, ResolvedJavaMethod targetMethod, InvokeKind invokeKind, JavaType returnType) {
                 InlineInvokePlugin plugin = graphBuilderConfig.getPlugins().getInlineInvokePlugin();
                 if (plugin == null || !invokeKind.isDirect() || !targetMethod.canBeInlined()) {
@@ -1318,6 +1386,9 @@
             private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, ReplacementContext calleeReplacementContext) {
                 BytecodeParser parser = new BytecodeParser(this, metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, INVOCATION_ENTRY_BCI, calleeReplacementContext);
                 HIRFrameStateBuilder startFrameState = new HIRFrameStateBuilder(parser, targetMethod, currentGraph);
+                if (!targetMethod.isStatic()) {
+                    args[0] = nullCheckedValue(args[0]);
+                }
                 startFrameState.initializeFromArgumentsArray(args);
                 parser.build(this.lastInstr, startFrameState);
 
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Thu Mar 19 22:31:42 2015 +0100
@@ -297,7 +297,7 @@
             ((PhiNode) currentValue).addInput(otherValue);
             return currentValue;
         } else if (currentValue != otherValue) {
-            assert !(block instanceof LoopBeginNode) : "Phi functions for loop headers are create eagerly for changed locals and all stack slots";
+            assert !(block instanceof LoopBeginNode) : String.format("Phi functions for loop headers are create eagerly for changed locals and all stack slots: %s != %s", currentValue, otherValue);
             if (otherValue == null || otherValue.isDeleted() || currentValue.getKind() != otherValue.getKind()) {
                 return null;
             }
@@ -958,4 +958,17 @@
         }
         return false;
     }
+
+    public void replace(ValueNode oldValue, ValueNode newValue) {
+        for (int i = 0; i < locals.length; i++) {
+            if (locals[i] == oldValue) {
+                locals[i] = newValue;
+            }
+        }
+        for (int i = 0; i < stackSize; i++) {
+            if (stack[i] == oldValue) {
+                stack[i] = newValue;
+            }
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java	Thu Mar 19 22:31:42 2015 +0100
@@ -22,10 +22,6 @@
  */
 package com.oracle.graal.nodes;
 
-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.compiler.common.type.*;
 import com.oracle.graal.graph.*;
@@ -70,21 +66,6 @@
         return action;
     }
 
-    /**
-     * Returns a node whose stamp is guaranteed to be {@linkplain StampTool#isPointerNonNull(Stamp)
-     * non-null}. If {@code value} already has such a stamp, then it is returned. Otherwise a fixed
-     * node guarding {@code value} is returned where the guard performs a null check.
-     */
-    public static ValueNode nullCheckedValue(ValueNode value) {
-        ObjectStamp receiverStamp = (ObjectStamp) value.stamp();
-        if (!StampTool.isPointerNonNull(receiverStamp)) {
-            IsNullNode condition = value.graph().unique(new IsNullNode(value));
-            Stamp stamp = receiverStamp.join(objectNonNull());
-            return new GuardingPiNode(value, condition, true, NullCheckException, InvalidateReprofile, stamp);
-        }
-        return value;
-    }
-
     public GuardingPiNode(ValueNode object) {
         this(object, object.graph().unique(new IsNullNode(object)), true, DeoptimizationReason.NullCheckException, DeoptimizationAction.None, object.stamp().join(StampFactory.objectNonNull()));
     }
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java	Thu Mar 19 22:31:42 2015 +0100
@@ -79,6 +79,11 @@
     }
 
     @Test
+    public void testFoo() throws Exception {
+        testGraph("unsafeDirectMemoryRead");
+    }
+
+    @Test
     public void testUnsafeSubstitutions() throws Exception {
         test("unsafeCompareAndSwapInt", unsafe, supply(() -> new Foo()), fooOffset("i"));
 
@@ -108,32 +113,34 @@
         testGraph("unsafeDirectMemoryRead");
         testGraph("unsafeDirectMemoryWrite");
 
-        test("unsafeCompareAndSwapInt", unsafe, supply(() -> new Foo()), fooOffset("i"));
-        test("unsafeCompareAndSwapLong", unsafe, supply(() -> new Foo()), fooOffset("l"));
-        test("unsafeCompareAndSwapObject", unsafe, supply(() -> new Foo()), fooOffset("o"));
+        long address = unsafe.allocateMemory(8 * Kind.values().length);
+        for (Unsafe unsafeArg : new Unsafe[]{unsafe, null}) {
+            test("unsafeCompareAndSwapInt", unsafeArg, supply(() -> new Foo()), fooOffset("i"));
+            test("unsafeCompareAndSwapLong", unsafeArg, supply(() -> new Foo()), fooOffset("l"));
+            test("unsafeCompareAndSwapObject", unsafeArg, supply(() -> new Foo()), fooOffset("o"));
 
-        test("unsafeGetBoolean", unsafe, supply(() -> new Foo()), fooOffset("z"));
-        test("unsafeGetByte", unsafe, supply(() -> new Foo()), fooOffset("b"));
-        test("unsafeGetShort", unsafe, supply(() -> new Foo()), fooOffset("s"));
-        test("unsafeGetChar", unsafe, supply(() -> new Foo()), fooOffset("c"));
-        test("unsafeGetInt", unsafe, supply(() -> new Foo()), fooOffset("i"));
-        test("unsafeGetLong", unsafe, supply(() -> new Foo()), fooOffset("l"));
-        test("unsafeGetFloat", unsafe, supply(() -> new Foo()), fooOffset("f"));
-        test("unsafeGetDouble", unsafe, supply(() -> new Foo()), fooOffset("d"));
-        test("unsafeGetObject", unsafe, supply(() -> new Foo()), fooOffset("o"));
+            test("unsafeGetBoolean", unsafeArg, supply(() -> new Foo()), fooOffset("z"));
+            test("unsafeGetByte", unsafeArg, supply(() -> new Foo()), fooOffset("b"));
+            test("unsafeGetShort", unsafeArg, supply(() -> new Foo()), fooOffset("s"));
+            test("unsafeGetChar", unsafeArg, supply(() -> new Foo()), fooOffset("c"));
+            test("unsafeGetInt", unsafeArg, supply(() -> new Foo()), fooOffset("i"));
+            test("unsafeGetLong", unsafeArg, supply(() -> new Foo()), fooOffset("l"));
+            test("unsafeGetFloat", unsafeArg, supply(() -> new Foo()), fooOffset("f"));
+            test("unsafeGetDouble", unsafeArg, supply(() -> new Foo()), fooOffset("d"));
+            test("unsafeGetObject", unsafeArg, supply(() -> new Foo()), fooOffset("o"));
 
-        test("unsafePutBoolean", unsafe, supply(() -> new Foo()), fooOffset("z"), true);
-        test("unsafePutByte", unsafe, supply(() -> new Foo()), fooOffset("b"), (byte) 87);
-        test("unsafePutShort", unsafe, supply(() -> new Foo()), fooOffset("s"), (short) -93);
-        test("unsafePutChar", unsafe, supply(() -> new Foo()), fooOffset("c"), 'A');
-        test("unsafePutInt", unsafe, supply(() -> new Foo()), fooOffset("i"), 42);
-        test("unsafePutFloat", unsafe, supply(() -> new Foo()), fooOffset("f"), 58.0F);
-        test("unsafePutDouble", unsafe, supply(() -> new Foo()), fooOffset("d"), -28736.243465D);
-        test("unsafePutObject", unsafe, supply(() -> new Foo()), fooOffset("i"), "value1", "value2", "value3");
+            test("unsafePutBoolean", unsafeArg, supply(() -> new Foo()), fooOffset("z"), true);
+            test("unsafePutByte", unsafeArg, supply(() -> new Foo()), fooOffset("b"), (byte) 87);
+            test("unsafePutShort", unsafeArg, supply(() -> new Foo()), fooOffset("s"), (short) -93);
+            test("unsafePutChar", unsafeArg, supply(() -> new Foo()), fooOffset("c"), 'A');
+            test("unsafePutInt", unsafeArg, supply(() -> new Foo()), fooOffset("i"), 42);
+            test("unsafePutFloat", unsafeArg, supply(() -> new Foo()), fooOffset("f"), 58.0F);
+            test("unsafePutDouble", unsafeArg, supply(() -> new Foo()), fooOffset("d"), -28736.243465D);
+            test("unsafePutObject", unsafeArg, supply(() -> new Foo()), fooOffset("i"), "value1", "value2", "value3");
 
-        long address = unsafe.allocateMemory(8 * Kind.values().length);
-        test("unsafeDirectMemoryRead", unsafe, address);
-        test("unsafeDirectMemoryWrite", unsafe, address, 0xCAFEBABEDEADBABEL);
+            test("unsafeDirectMemoryRead", unsafeArg, address);
+            test("unsafeDirectMemoryWrite", unsafeArg, address, 0xCAFEBABEDEADBABEL);
+        }
         unsafe.freeMemory(address);
     }
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Thu Mar 19 22:31:42 2015 +0100
@@ -23,7 +23,6 @@
 package com.oracle.graal.replacements;
 
 import static com.oracle.graal.api.code.MemoryBarriers.*;
-import static com.oracle.graal.graphbuilderconf.GraphBuilderContext.*;
 import static com.oracle.graal.replacements.nodes.MathIntrinsicNode.Operation.*;
 import sun.misc.*;
 
@@ -33,7 +32,8 @@
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.*;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.debug.*;
@@ -105,7 +105,9 @@
         for (Kind kind : new Kind[]{Kind.Int, Kind.Long, Kind.Object}) {
             Class<?> javaClass = kind == Kind.Object ? Object.class : kind.toJavaClass();
             r.register5("compareAndSwap" + kind.name(), Receiver.class, Object.class, long.class, javaClass, javaClass, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode ignoredUnsafe, ValueNode object, ValueNode offset, ValueNode expected, ValueNode x) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode expected, ValueNode x) {
+                    // Emits a null-check for the otherwise unused receiver
+                    unsafe.get();
                     CompareAndSwapNode compareAndSwap = new CompareAndSwapNode(object, offset, expected, x, kind, LocationIdentity.any());
                     b.push(Kind.Boolean.getStackKind(), b.append(compareAndSwap));
                     compareAndSwap.setStateAfter(b.createStateAfter());
@@ -115,7 +117,9 @@
 
             if (getAndSetEnabled(arch)) {
                 r.register4("getAndSet" + kind.name(), Receiver.class, Object.class, long.class, javaClass, new InvocationPlugin() {
-                    public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode ignoredUnsafe, ValueNode object, ValueNode offset, ValueNode value) {
+                    public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode value) {
+                        // Emits a null-check for the otherwise unused receiver
+                        unsafe.get();
                         AtomicReadAndWriteNode atomicReadWrite = new AtomicReadAndWriteNode(object, offset, value, kind, LocationIdentity.any());
                         b.push(kind.getStackKind(), b.append(atomicReadWrite));
                         atomicReadWrite.setStateAfter(b.createStateAfter());
@@ -124,7 +128,9 @@
                 });
                 if (kind != Kind.Object) {
                     r.register4("getAndAdd" + kind.name(), Receiver.class, Object.class, long.class, javaClass, new InvocationPlugin() {
-                        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode ignoredUnsafe, ValueNode object, ValueNode offset, ValueNode delta) {
+                        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode delta) {
+                            // Emits a null-check for the otherwise unused receiver
+                            unsafe.get();
                             AtomicReadAndAddNode atomicReadAdd = new AtomicReadAndAddNode(object, offset, delta, LocationIdentity.any());
                             b.push(kind.getStackKind(), b.append(atomicReadAdd));
                             atomicReadAdd.setStateAfter(b.createStateAfter());
@@ -150,25 +156,25 @@
         Class<?> type = kind.toJavaClass();
         Registration r = new Registration(plugins, declaringClass);
         r.register1("reverseBytes", type, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(kind, b.recursiveAppend(new ReverseBytesNode(value).canonical(null, value)));
                 return true;
             }
         });
         r.register1("bitCount", type, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Int, b.recursiveAppend(new BitCountNode(value).canonical(null, value)));
                 return true;
             }
         });
         r.register2("divideUnsigned", type, type, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode dividend, ValueNode divisor) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
                 b.push(kind, b.recursiveAppend(new UnsignedDivNode(dividend, divisor).canonical(null, dividend, divisor)));
                 return true;
             }
         });
         r.register2("remainderUnsigned", type, type, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode dividend, ValueNode divisor) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
                 b.push(kind, b.recursiveAppend(new UnsignedDivNode(dividend, divisor).canonical(null, dividend, divisor)));
                 return true;
             }
@@ -178,7 +184,7 @@
     private static void registerCharacterPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Character.class);
         r.register1("reverseBytes", char.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 // return (char) (Integer.reverse(i) >> 16);
                 ReverseBytesNode reverse = b.append(new ReverseBytesNode(value));
                 RightShiftNode rightShift = b.append(new RightShiftNode(reverse, b.append(ConstantNode.forInt(16))));
@@ -192,7 +198,7 @@
     private static void registerShortPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Short.class);
         r.register1("reverseBytes", short.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 // return (short) (Integer.reverse(i) >> 16);
                 ReverseBytesNode reverse = b.append(new ReverseBytesNode(value));
                 RightShiftNode rightShift = b.append(new RightShiftNode(reverse, b.append(ConstantNode.forInt(16))));
@@ -206,13 +212,13 @@
     private static void registerFloatPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Float.class);
         r.register1("floatToRawIntBits", float.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Int, b.recursiveAppend(new ReinterpretNode(Kind.Int, value).canonical(null, value)));
                 return true;
             }
         });
         r.register1("intBitsToFloat", int.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Float, b.recursiveAppend(new ReinterpretNode(Kind.Float, value).canonical(null, value)));
                 return true;
             }
@@ -222,13 +228,13 @@
     private static void registerDoublePlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Double.class);
         r.register1("doubleToRawLongBits", double.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Long, b.recursiveAppend(new ReinterpretNode(Kind.Long, value).canonical(null, value)));
                 return true;
             }
         });
         r.register1("longBitsToDouble", long.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Double, b.recursiveAppend(new ReinterpretNode(Kind.Double, value).canonical(null, value)));
                 return true;
             }
@@ -238,32 +244,32 @@
     private static void registerMathPlugins(Architecture arch, InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Math.class);
         r.register1("abs", Float.TYPE, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Float, b.recursiveAppend(new AbsNode(value).canonical(null, value)));
                 return true;
             }
         });
         r.register1("abs", Double.TYPE, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Double, b.recursiveAppend(new AbsNode(value).canonical(null, value)));
                 return true;
             }
         });
         r.register1("sqrt", Double.TYPE, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.push(Kind.Double, b.recursiveAppend(new SqrtNode(value).canonical(null, value)));
                 return true;
             }
         });
         if (getAndSetEnabled(arch)) {
             r.register1("log", Double.TYPE, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                     b.push(Kind.Double, b.recursiveAppend(MathIntrinsicNode.create(value, LOG)));
                     return true;
                 }
             });
             r.register1("log10", Double.TYPE, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                     b.push(Kind.Double, b.recursiveAppend(MathIntrinsicNode.create(value, LOG10)));
                     return true;
                 }
@@ -278,7 +284,7 @@
             this.condition = condition;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
             // the mirroring and negation operations get the condition into canonical form
             boolean mirror = condition.canonicalMirror();
             boolean negate = condition.canonicalNegate();
@@ -312,25 +318,25 @@
         r.register2("belowOrEqual", int.class, int.class, new UnsignedMathPlugin(Condition.BE));
         r.register2("belowOrEqual", long.class, long.class, new UnsignedMathPlugin(Condition.BE));
         r.register2("divide", int.class, int.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                 b.push(Kind.Int, b.recursiveAppend(new UnsignedDivNode(x, y).canonical(null, x, y)));
                 return true;
             }
         });
         r.register2("divide", long.class, long.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                 b.push(Kind.Long, b.recursiveAppend(new UnsignedDivNode(x, y).canonical(null, x, y)));
                 return true;
             }
         });
         r.register2("remainder", int.class, int.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                 b.push(Kind.Int, b.recursiveAppend(new UnsignedRemNode(x, y).canonical(null, x, y)));
                 return true;
             }
         });
         r.register2("remainder", long.class, long.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                 b.push(Kind.Long, b.recursiveAppend(new UnsignedRemNode(x, y).canonical(null, x, y)));
                 return true;
             }
@@ -349,7 +355,8 @@
     private static void registerObjectPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Object.class);
         r.register1("<init>", Receiver.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode object) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
+                ValueNode object = receiver.get();
                 if (RegisterFinalizerNode.mayHaveFinalizer(object, b.getAssumptions())) {
                     RegisterFinalizerNode registerFinalizer = new RegisterFinalizerNode(object);
                     b.append(registerFinalizer);
@@ -363,24 +370,23 @@
     private static void registerClassPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, Class.class);
         r.register2("isInstance", Receiver.class, Object.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode type, ValueNode object) {
-                ValueNode nullCheckedType = nullCheckedValue(b, type);
-                LogicNode condition = b.append(InstanceOfDynamicNode.create(b.getConstantReflection(), nullCheckedType, object));
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode object) {
+                LogicNode condition = b.append(InstanceOfDynamicNode.create(b.getConstantReflection(), type.get(), object));
                 b.push(Kind.Boolean.getStackKind(), b.recursiveAppend(new ConditionalNode(condition).canonical(null)));
                 return true;
             }
         });
         r.register2("isAssignableFrom", Receiver.class, Class.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode type, ValueNode otherType) {
-                ClassIsAssignableFromNode condition = b.recursiveAppend(new ClassIsAssignableFromNode(nullCheckedValue(b, type), otherType));
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode otherType) {
+                ClassIsAssignableFromNode condition = b.recursiveAppend(new ClassIsAssignableFromNode(type.get(), otherType));
                 b.push(Kind.Boolean.getStackKind(), b.recursiveAppend(new ConditionalNode(condition).canonical(null)));
                 return true;
             }
         });
         r.register2("cast", Receiver.class, Object.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode rcvr, ValueNode object) {
-                if (rcvr.isConstant() && !rcvr.isNullConstant()) {
-                    ResolvedJavaType type = b.getConstantReflection().asJavaType(rcvr.asConstant());
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
+                if (receiver.isConstant() && !receiver.isNullConstant()) {
+                    ResolvedJavaType type = b.getConstantReflection().asJavaType(receiver.get().asConstant());
                     if (type != null && !type.isPrimitive()) {
                         b.push(Kind.Object, b.append(CheckCastNode.create(type, object, null, false, b.getAssumptions())));
                         return true;
@@ -402,7 +408,7 @@
         Registration r = new Registration(plugins, Edges.class);
         for (Class<?> c : new Class<?>[]{Node.class, NodeList.class}) {
             r.register2("get" + c.getSimpleName() + "Unsafe", Node.class, long.class, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode node, ValueNode offset) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset) {
                     ValueNode value = b.append(new UnsafeLoadNode(node, offset, Kind.Object, LocationIdentity.any()));
                     boolean exactType = false;
                     boolean nonNull = false;
@@ -411,7 +417,7 @@
                 }
             });
             r.register3("put" + c.getSimpleName() + "Unsafe", Node.class, long.class, c, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode node, ValueNode offset, ValueNode value) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset, ValueNode value) {
                     UnsafeStoreNode unsafeStore = new UnsafeStoreNode(node, offset, value, Kind.Object, LocationIdentity.any());
                     b.append(unsafeStore);
                     unsafeStore.setStateAfter(b.createStateAfter());
@@ -429,7 +435,7 @@
             this.kind = kind;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
             if (b.parsingReplacement()) {
                 ResolvedJavaMethod rootMethod = b.getRootMethod();
                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
@@ -456,7 +462,7 @@
             this.kind = kind;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
             if (b.parsingReplacement()) {
                 ResolvedJavaMethod rootMethod = b.getRootMethod();
                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
@@ -465,7 +471,7 @@
                     return false;
                 }
             }
-            ValueNode valueNode = UnboxNode.create(b.getMetaAccess(), b.getConstantReflection(), nullCheckedValue(b, value), kind);
+            ValueNode valueNode = UnboxNode.create(b.getMetaAccess(), b.getConstantReflection(), receiver.get(), kind);
             b.push(kind.getStackKind(), b.append(valueNode));
             return true;
         }
@@ -486,12 +492,16 @@
             this.isVolatile = isVolatile;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode ignoredUnsafe, ValueNode address) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address) {
+            // Emits a null-check for the otherwise unused receiver
+            unsafe.get();
             b.push(returnKind.getStackKind(), b.append(new DirectReadNode(address, returnKind)));
             return true;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode ignoredUnsafe, ValueNode object, ValueNode offset) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset) {
+            // Emits a null-check for the otherwise unused receiver
+            unsafe.get();
             if (isVolatile) {
                 b.append(new MembarNode(JMM_PRE_VOLATILE_READ));
             }
@@ -513,12 +523,16 @@
             this.isVolatile = isVolatile;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode ignoredUnsafe, ValueNode address, ValueNode value) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address, ValueNode value) {
+            // Emits a null-check for the otherwise unused receiver
+            unsafe.get();
             b.append(new DirectStoreNode(address, value, kind));
             return true;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode ignoredUnsafe, ValueNode object, ValueNode offset, ValueNode value) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode value) {
+            // Emits a null-check for the otherwise unused receiver
+            unsafe.get();
             if (isVolatile) {
                 b.append(new MembarNode(JMM_PRE_VOLATILE_WRITE));
             }
@@ -535,42 +549,42 @@
     private static void registerGraalDirectivesPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, GraalDirectives.class);
         r.register0("deoptimize", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.append(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
                 return true;
             }
         });
 
         r.register0("deoptimizeAndInvalidate", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.append(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
                 return true;
             }
         });
 
         r.register0("inCompiledCode", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.push(Kind.Int, b.append(ConstantNode.forInt(1)));
                 return true;
             }
         });
 
         r.register0("controlFlowAnchor", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.append(new ControlFlowAnchorNode());
                 return true;
             }
         });
 
         r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode probability, ValueNode condition) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) {
                 b.push(Kind.Int, b.append(new BranchProbabilityNode(probability, condition)));
                 return true;
             }
         });
 
         InvocationPlugin blackholePlugin = new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.append(new BlackholeNode(value));
                 return true;
             }
@@ -583,7 +597,7 @@
 
                 final Kind stackKind = kind.getStackKind();
                 r.register1("opaque", javaClass, new InvocationPlugin() {
-                    public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+                    public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                         b.push(stackKind, b.append(new OpaqueNode(value)));
                         return true;
                     }
@@ -594,7 +608,8 @@
 
     private static void registerJMHBlackholePlugins(InvocationPlugins plugins) {
         InvocationPlugin blackholePlugin = new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode blackhole, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver blackhole, ValueNode value) {
+                blackhole.get();
                 b.append(new BlackholeNode(value));
                 return true;
             }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Thu Mar 19 19:27:25 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Thu Mar 19 22:31:42 2015 +0100
@@ -32,7 +32,8 @@
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.*;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
@@ -68,9 +69,9 @@
     public static void registerOptimizedAssumptionPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, OptimizedAssumption.class);
         r.register1("isValid", Receiver.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg) {
-                if (arg.isConstant()) {
-                    Constant constant = arg.asConstant();
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
+                if (receiver.isConstant()) {
+                    Constant constant = receiver.get().asConstant();
                     OptimizedAssumption assumption = b.getSnippetReflection().asObject(OptimizedAssumption.class, (JavaConstant) constant);
                     b.push(Kind.Boolean.getStackKind(), b.append(ConstantNode.forBoolean(assumption.isValid())));
                     if (assumption.isValid()) {
@@ -89,31 +90,31 @@
         for (Kind kind : new Kind[]{Kind.Int, Kind.Long}) {
             Class<?> type = kind.toJavaClass();
             r.register2("addExact", type, type, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                     b.push(kind.getStackKind(), b.append(new IntegerAddExactNode(x, y)));
                     return true;
                 }
             });
             r.register2("subtractExact", type, type, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                     b.push(kind.getStackKind(), b.append(new IntegerSubExactNode(x, y)));
                     return true;
                 }
             });
             r.register2("multiplyExact", type, type, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                     b.push(kind.getStackKind(), b.append(new IntegerMulExactNode(x, y)));
                     return true;
                 }
             });
             r.register2("multiplyHigh", type, type, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                     b.push(kind.getStackKind(), b.append(new IntegerMulHighNode(x, y)));
                     return true;
                 }
             });
             r.register2("multiplyHighUnsigned", type, type, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode x, ValueNode y) {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
                     b.push(kind.getStackKind(), b.append(new UnsignedMulHighNode(x, y)));
                     return true;
                 }
@@ -124,47 +125,47 @@
     public static void registerCompilerDirectivesPlugins(InvocationPlugins plugins) {
         Registration r = new Registration(plugins, CompilerDirectives.class);
         r.register0("inInterpreter", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.push(Kind.Boolean.getStackKind(), b.append(ConstantNode.forBoolean(false)));
                 return true;
             }
         });
         r.register0("inCompiledCode", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.push(Kind.Boolean.getStackKind(), b.append(ConstantNode.forBoolean(true)));
                 return true;
             }
         });
         r.register0("transferToInterpreter", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.append(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
                 return true;
             }
         });
         r.register0("transferToInterpreterAndInvalidate", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 b.append(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
                 return true;
             }
         });
         r.register1("interpreterOnly", Runnable.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
                 return true;
             }
         });
         r.register1("interpreterOnly", Callable.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode arg) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
                 return true;
             }
         });
         r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode probability, ValueNode condition) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) {
                 b.push(Kind.Boolean.getStackKind(), b.append(new BranchProbabilityNode(probability, condition)));
                 return true;
             }
         });
         r.register1("bailout", String.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode message) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
                 if (message.isConstant()) {
                     throw b.bailout(message.asConstant().toValueString());
                 }
@@ -172,7 +173,7 @@
             }
         });
         r.register1("isCompilationConstant", Object.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 if ((value instanceof BoxNode ? ((BoxNode) value).getValue() : value).isConstant()) {
                     b.push(Kind.Boolean.getStackKind(), b.append(ConstantNode.forBoolean(true)));
                 } else {
@@ -182,7 +183,7 @@
             }
         });
         r.register1("materialize", Object.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 b.append(new ForceMaterializeNode(value));
                 return true;
             }
@@ -190,7 +191,7 @@
 
         r = new Registration(plugins, CompilerAsserts.class);
         r.register1("partialEvaluationConstant", Object.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode value) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
                 ValueNode curValue = value;
                 if (curValue instanceof BoxNode) {
                     BoxNode boxNode = (BoxNode) curValue;
@@ -215,7 +216,7 @@
             }
         });
         r.register1("neverPartOfCompilation", String.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode message) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
                 if (message.isConstant()) {
                     String messageString = message.asConstant().toValueString();
                     b.append(new NeverPartOfCompilationNode(messageString));
@@ -229,14 +230,14 @@
     public static void registerOptimizedCallTargetPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins) {
         Registration r = new Registration(plugins, OptimizedCallTarget.class);
         r.register2("createFrame", FrameDescriptor.class, Object[].class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode descriptor, ValueNode args) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode descriptor, ValueNode args) {
                 Class<?> frameClass = TruffleCompilerOptions.TruffleUseFrameWithoutBoxing.getValue() ? FrameWithoutBoxing.class : FrameWithBoxing.class;
                 b.push(Kind.Object, b.append(new NewFrameNode(StampFactory.exactNonNull(metaAccess.lookupJavaType(frameClass)), descriptor, args)));
                 return true;
             }
         });
         r.register2("castArrayFixedLength", Object[].class, int.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode args, ValueNode length) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode args, ValueNode length) {
                 b.push(Kind.Object, b.append(new PiArrayNode(args, length, args.stamp())));
                 return true;
             }
@@ -264,8 +265,8 @@
 
     private static void registerMaterialize(Registration r) {
         r.register1("materialize", Receiver.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode frame) {
-                b.push(Kind.Object, b.append(new MaterializeFrameNode(GraphBuilderContext.nullCheckedValue(b, frame))));
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frame) {
+                b.push(Kind.Object, b.append(new MaterializeFrameNode(frame.get())));
                 return true;
             }
         });
@@ -273,7 +274,7 @@
 
     private static void registerUnsafeCast(Registration r) {
         r.register4("unsafeCast", Object.class, Class.class, boolean.class, boolean.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode object, ValueNode clazz, ValueNode condition, ValueNode nonNull) {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode clazz, ValueNode condition, ValueNode nonNull) {
                 if (clazz.isConstant() && nonNull.isConstant()) {
                     ConstantReflectionProvider constantReflection = b.getConstantReflection();
                     ResolvedJavaType javaType = constantReflection.asJavaType(clazz.asConstant());
@@ -331,7 +332,7 @@
             this.returnKind = returnKind;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode object, ValueNode offset, ValueNode condition, ValueNode location) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode condition, ValueNode location) {
             if (location.isConstant()) {
                 LocationIdentity locationIdentity;
                 if (location.isNullConstant()) {
@@ -356,7 +357,7 @@
             this.kind = kind;
         }
 
-        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode object, ValueNode offset, ValueNode value, ValueNode location) {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode value, ValueNode location) {
             ValueNode locationArgument = location;
             if (locationArgument.isConstant()) {
                 LocationIdentity locationIdentity;