changeset 20929:c617a74a9eab

Merge
author Stefan Anzinger <stefan.anzinger@oracle.com>
date Tue, 14 Apr 2015 14:01:18 +0200
parents d9713313e88c (current diff) cea0b7285190 (diff)
children 143c532a550e
files graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPluginIdHolder.java graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTInstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTInstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultInstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleASTInstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleInstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/StandardSourceTag.java
diffstat 96 files changed, 2330 insertions(+), 1825 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RegisterConfig.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RegisterConfig.java	Tue Apr 14 14:01:18 2015 +0200
@@ -71,15 +71,19 @@
     Register[] getCallingConventionRegisters(Type type, Kind kind);
 
     /**
-     * Gets the set of registers that can be used by the register allocator.
+     * Gets the set of all registers that might be used by the register allocator.
+     *
+     * To get the set of registers the register allocator is allowed to use see
+     * {@link RegisterAllocationConfig#getAllocatableRegisters()}
      */
+    @SuppressWarnings("javadoc")
     Register[] getAllocatableRegisters();
 
     /**
-     * Gets the set of registers that can be used by the register allocator for a value of a
-     * particular kind.
+     * Filters a set of registers and returns only those that can be used by the register allocator
+     * for a value of a particular kind.
      */
-    Register[] getAllocatableRegisters(PlatformKind kind);
+    Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers);
 
     /**
      * Gets the registers whose values must be preserved by a method across any call it makes.
--- a/graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.compiler.amd64.test;
 
+import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static org.junit.Assume.*;
 
 import org.junit.*;
@@ -34,6 +35,7 @@
     @Before
     public void checkAMD64() {
         assumeTrue("skipping AMD64 specific test", getTarget().arch instanceof AMD64);
+        assumeTrue("RegisterPressure is set -> skip", RegisterPressure.getValue() == null);
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/alloc/RegisterAllocationConfig.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015, 2015, 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.compiler.common.alloc;
+
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+
+/**
+ * Configuration for register allocation. This is different to {@link RegisterConfig} as it only
+ * returns registers specified by {@link GraalOptions#RegisterPressure}.
+ */
+public class RegisterAllocationConfig {
+
+    public static final String ALL_REGISTERS = "<all>";
+
+    private static Register findRegister(String name, Register[] all) {
+        for (Register reg : all) {
+            if (reg.name.equals(name)) {
+                return reg;
+            }
+        }
+        throw new IllegalArgumentException("register " + name + " is not allocatable");
+    }
+
+    private static Register[] initAllocatable(Register[] registers) {
+        if (RegisterPressure.getValue() != null && !RegisterPressure.getValue().equals(ALL_REGISTERS)) {
+            String[] names = RegisterPressure.getValue().split(",");
+            Register[] regs = new Register[names.length];
+            for (int i = 0; i < names.length; i++) {
+                regs[i] = findRegister(names[i], registers);
+            }
+            return regs;
+        }
+
+        return registers;
+    }
+
+    private final RegisterConfig registerConfig;
+    private final Map<PlatformKind.Key, Register[]> categorized = new HashMap<>();
+    private Register[] cachedRegisters;
+
+    public RegisterAllocationConfig(RegisterConfig registerConfig) {
+        this.registerConfig = registerConfig;
+    }
+
+    /**
+     * Gets the set of registers that can be used by the register allocator for a value of a
+     * particular kind.
+     */
+    public Register[] getAllocatableRegisters(PlatformKind kind) {
+        PlatformKind.Key key = kind.getKey();
+        if (categorized.containsKey(key)) {
+            Register[] val = categorized.get(key);
+            return val;
+        }
+
+        Register[] ret = registerConfig.filterAllocatableRegisters(kind, getAllocatableRegisters());
+        categorized.put(key, ret);
+        return ret;
+
+    }
+
+    /**
+     * Gets the set of registers that can be used by the register allocator.
+     */
+    public Register[] getAllocatableRegisters() {
+        if (cachedRegisters == null) {
+            cachedRegisters = initAllocatable(registerConfig.getAllocatableRegisters());
+        }
+        assert cachedRegisters != null;
+        return cachedRegisters;
+    }
+
+    public RegisterConfig getRegisterConfig() {
+        return registerConfig;
+    }
+
+}
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/AbstractObjectStamp.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/AbstractObjectStamp.java	Tue Apr 14 14:01:18 2015 +0200
@@ -98,9 +98,6 @@
         if (this == otherStamp) {
             return this;
         }
-        if (!isCompatible(otherStamp)) {
-            return StampFactory.illegal(Kind.Illegal);
-        }
         AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
         if (isIllegal()) {
             return other;
@@ -170,9 +167,6 @@
         if (this == otherStamp) {
             return this;
         }
-        if (!isCompatible(otherStamp)) {
-            return StampFactory.illegal(Kind.Illegal);
-        }
         AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
         if (isIllegal()) {
             return this;
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java	Tue Apr 14 14:01:18 2015 +0200
@@ -176,9 +176,6 @@
         if (otherStamp == this) {
             return this;
         }
-        if (!(otherStamp instanceof FloatStamp)) {
-            return StampFactory.illegal(Kind.Illegal);
-        }
         FloatStamp other = (FloatStamp) otherStamp;
         assert getBits() == other.getBits();
         double meetUpperBound = meetBounds(upperBound, other.upperBound, Math::max);
@@ -198,9 +195,6 @@
         if (otherStamp == this) {
             return this;
         }
-        if (!(otherStamp instanceof FloatStamp)) {
-            return StampFactory.illegal(Kind.Illegal);
-        }
         FloatStamp other = (FloatStamp) otherStamp;
         assert getBits() == other.getBits();
         double joinUpperBound = Math.min(upperBound, other.upperBound);
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IllegalStamp.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IllegalStamp.java	Tue Apr 14 14:01:18 2015 +0200
@@ -27,7 +27,9 @@
 import com.oracle.graal.compiler.common.spi.*;
 
 /**
- * This stamp represents the illegal type. Values with this type can not exist at run time.
+ * This stamp represents the type of the {@link Kind#Illegal} value in the second slot of
+ * {@link Kind#Long} and {@link Kind#Double} values. It can only appear in framestates or virtual
+ * objects.
  */
 public final class IllegalStamp extends Stamp {
 
@@ -56,7 +58,8 @@
 
     @Override
     public Stamp constant(Constant c, MetaAccessProvider meta) {
-        throw GraalInternalError.shouldNotReachHere("illegal stamp has no value");
+        assert ((PrimitiveConstant) c).getKind() == Kind.Illegal;
+        return this;
     }
 
     @Override
@@ -66,17 +69,19 @@
 
     @Override
     public Stamp meet(Stamp other) {
+        assert other instanceof IllegalStamp;
         return this;
     }
 
     @Override
     public Stamp join(Stamp other) {
+        assert other instanceof IllegalStamp;
         return this;
     }
 
     @Override
     public boolean isCompatible(Stamp stamp) {
-        return false;
+        return stamp instanceof IllegalStamp;
     }
 
     @Override
@@ -91,6 +96,7 @@
 
     @Override
     public Stamp improveWith(Stamp other) {
+        assert other instanceof IllegalStamp;
         return this;
     }
 
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java	Tue Apr 14 14:01:18 2015 +0200
@@ -253,9 +253,6 @@
         if (otherStamp == this) {
             return this;
         }
-        if (!(otherStamp instanceof IntegerStamp)) {
-            return StampFactory.illegal(Kind.Illegal);
-        }
         IntegerStamp other = (IntegerStamp) otherStamp;
         return createStamp(other, Math.max(upperBound, other.upperBound), Math.min(lowerBound, other.lowerBound), downMask & other.downMask, upMask | other.upMask);
     }
@@ -265,9 +262,6 @@
         if (otherStamp == this) {
             return this;
         }
-        if (!(otherStamp instanceof IntegerStamp)) {
-            return StampFactory.illegal(Kind.Illegal);
-        }
         IntegerStamp other = (IntegerStamp) otherStamp;
         long newDownMask = downMask | other.downMask;
         long newLowerBound = Math.max(lowerBound, other.lowerBound) | newDownMask;
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/StampFactory.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/StampFactory.java	Tue Apr 14 14:01:18 2015 +0200
@@ -76,10 +76,9 @@
         for (Kind k : Kind.values()) {
             if (stampCache[k.ordinal()] != null) {
                 illegalStampCache[k.ordinal()] = stampCache[k.ordinal()].illegal();
-            } else {
-                illegalStampCache[k.ordinal()] = IllegalStamp.getInstance();
             }
         }
+        illegalStampCache[Kind.Illegal.ordinal()] = IllegalStamp.getInstance();
     }
 
     public static Stamp tautology() {
@@ -122,10 +121,6 @@
         return positiveInt;
     }
 
-    public static Stamp illegal() {
-        return illegal(Kind.Illegal);
-    }
-
     public static Stamp illegal(Kind kind) {
         return illegalStampCache[kind.ordinal()];
     }
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/VoidStamp.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/VoidStamp.java	Tue Apr 14 14:01:18 2015 +0200
@@ -46,10 +46,8 @@
 
     @Override
     public Stamp improveWith(Stamp other) {
-        if (other instanceof VoidStamp) {
-            return this;
-        }
-        return StampFactory.illegal(Kind.Illegal);
+        assert other instanceof VoidStamp;
+        return this;
     }
 
     @Override
@@ -74,29 +72,19 @@
 
     @Override
     public Stamp meet(Stamp other) {
-        if (other instanceof IllegalStamp) {
-            return other.join(this);
-        }
-        if (this == other) {
-            return this;
-        }
-        return StampFactory.illegal(Kind.Illegal);
+        assert other instanceof VoidStamp;
+        return this;
     }
 
     @Override
     public Stamp join(Stamp other) {
-        if (other instanceof IllegalStamp) {
-            return other.join(this);
-        }
-        if (this == other) {
-            return this;
-        }
-        return StampFactory.illegal(Kind.Illegal);
+        assert other instanceof VoidStamp;
+        return this;
     }
 
     @Override
     public boolean isCompatible(Stamp stamp) {
-        return this == stamp;
+        return stamp instanceof VoidStamp;
     }
 
     @Override
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/UnsafeArrayTypeWriter.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/UnsafeArrayTypeWriter.java	Tue Apr 14 14:01:18 2015 +0200
@@ -37,12 +37,17 @@
  */
 public class UnsafeArrayTypeWriter implements TypeWriter {
 
-    private static final int CHUNK_SIZE = 4000;
+    private static final int MIN_CHUNK_LENGTH = 200;
+    private static final int MAX_CHUNK_LENGTH = 16000;
 
     static class Chunk {
-        protected final byte[] data = new byte[CHUNK_SIZE];
+        protected final byte[] data;
         protected int size;
         protected Chunk next;
+
+        protected Chunk(int arrayLength) {
+            data = new byte[arrayLength];
+        }
     }
 
     private Chunk firstChunk;
@@ -50,7 +55,7 @@
     private int totalSize;
 
     public UnsafeArrayTypeWriter() {
-        firstChunk = new Chunk();
+        firstChunk = new Chunk(MIN_CHUNK_LENGTH);
         writeChunk = firstChunk;
     }
 
@@ -153,7 +158,7 @@
 
     private long writeOffset(int writeBytes) {
         if (writeChunk.size + writeBytes >= writeChunk.data.length) {
-            Chunk newChunk = new Chunk();
+            Chunk newChunk = new Chunk(Math.min(writeChunk.data.length * 2, MAX_CHUNK_LENGTH));
             writeChunk.next = newChunk;
             writeChunk = newChunk;
         }
--- a/graal/com.oracle.graal.compiler.sparc.test/src/com/oracle/graal/compiler/sparc/test/SPARCAllocatorTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.sparc.test/src/com/oracle/graal/compiler/sparc/test/SPARCAllocatorTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.compiler.sparc.test;
 
+import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static org.junit.Assume.*;
 
 import org.junit.*;
@@ -34,6 +35,7 @@
     @Before
     public void checkSPARC() {
         assumeTrue("skipping SPARC specific test", getTarget().arch instanceof SPARC);
+        assumeTrue("RegisterPressure is set -> skip", RegisterPressure.getValue() == null);
     }
 
     @Test
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -50,7 +50,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.java.*;
 import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.lir.phases.*;
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Apr 14 14:01:18 2015 +0200
@@ -23,6 +23,8 @@
 package com.oracle.graal.compiler;
 
 import static com.oracle.graal.compiler.GraalCompiler.Options.*;
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig.*;
 import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*;
 
 import java.util.*;
@@ -38,6 +40,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.alloc.lsra.*;
 import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.lir.framemap.*;
 import com.oracle.graal.lir.gen.*;
@@ -49,6 +52,7 @@
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.options.*;
+import com.oracle.graal.options.OptionValue.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.schedule.*;
@@ -241,6 +245,22 @@
 
     public static LIRGenerationResult emitLIR(Backend backend, TargetDescription target, SchedulePhase schedule, StructuredGraph graph, Object stub, CallingConvention cc,
                     RegisterConfig registerConfig, LIRSuites lirSuites) {
+        try {
+            return emitLIR0(backend, target, schedule, graph, stub, cc, registerConfig, lirSuites);
+        } catch (OutOfRegistersException e) {
+            if (RegisterPressure.getValue() != null && !RegisterPressure.getValue().equals(ALL_REGISTERS)) {
+                try (OverrideScope s = OptionValue.override(RegisterPressure, ALL_REGISTERS)) {
+                    // retry with default register set
+                    return emitLIR0(backend, target, schedule, graph, stub, cc, registerConfig, lirSuites);
+                }
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    private static LIRGenerationResult emitLIR0(Backend backend, TargetDescription target, SchedulePhase schedule, StructuredGraph graph, Object stub, CallingConvention cc,
+                    RegisterConfig registerConfig, LIRSuites lirSuites) {
         try (Scope ds = Debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start()) {
             List<Block> blocks = schedule.getCFG().getBlocks();
             Block startBlock = schedule.getCFG().getStartBlock();
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Tue Apr 14 14:01:18 2015 +0200
@@ -304,7 +304,7 @@
     }
 
     protected void emitNode(ValueNode node) {
-        if (Debug.isLogEnabled() && node.stamp() instanceof IllegalStamp) {
+        if (Debug.isLogEnabled() && node.stamp().isIllegal()) {
             Debug.log("This node has invalid type, we are emitting dead code(?): %s", node);
         }
         if (node instanceof LIRLowerable) {
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Tue Apr 14 14:01:18 2015 +0200
@@ -433,7 +433,7 @@
             this.movUsageFromEndTo(1);
             return true;
         }
-        for (int i = 0; i < this.extraUsagesCount; ++i) {
+        for (int i = this.extraUsagesCount - 1; i >= 0; i--) {
             if (extraUsages[i] == node) {
                 this.movUsageFromEndTo(i + INLINE_USAGE_COUNT);
                 return true;
@@ -683,12 +683,12 @@
     }
 
     private void unregisterInputs() {
-        for (Node input : inputs()) {
-            removeThisFromUsages(input);
+        acceptInputs((node, input) -> {
+            node.removeThisFromUsages(input);
             if (input.hasNoUsages()) {
-                maybeNotifyZeroUsages(input);
+                node.maybeNotifyZeroUsages(input);
             }
-        }
+        });
     }
 
     public void clearInputs() {
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ForeignCallPlugin.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ForeignCallPlugin.java	Tue Apr 14 14:01:18 2015 +0200
@@ -24,7 +24,7 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java	Tue Apr 14 14:01:18 2015 +0200
@@ -27,7 +27,7 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.*;
 
 /**
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPluginIdHolder.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2015, 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.graphbuilderconf;
-
-import com.oracle.graal.api.meta.*;
-
-/**
- * {@link ResolvedJavaMethod}s that can assign themselves an identifier for use in a side table
- * mapping methods to {@link InvocationPlugin}s.
- */
-public interface InvocationPluginIdHolder extends ResolvedJavaMethod {
-    /**
-     * Sets the unique, positive, non-zero identifier for this method.
-     */
-    void setInvocationPluginId(int id);
-
-    /**
-     * Gets the identifier set by {@link #setInvocationPluginId(int)} or 0 if no
-     * {@link InvocationPlugin} identifier was assigned to this method.
-     */
-    int getInvocationPluginId();
-}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java	Tue Apr 14 14:01:18 2015 +0200
@@ -26,43 +26,19 @@
 
 import java.lang.reflect.*;
 import java.util.*;
-import java.util.stream.*;
 
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.graph.Node;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.iterators.*;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.MethodKey;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
 
 /**
  * Manages a set of {@link InvocationPlugin}s.
  */
 public class InvocationPlugins {
 
-    /**
-     * 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 such a non-static method.
-     */
-    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.
-         */
-        default boolean isConstant() {
-            return false;
-        }
-    }
-
     public static class InvocationPluginReceiver implements Receiver {
         private final GraphBuilderContext parser;
         private ValueNode[] args;
@@ -195,101 +171,16 @@
         }
     }
 
-    static final class MethodInfo {
-        final boolean isStatic;
-        final Class<?> declaringClass;
-        final String name;
-        final Class<?>[] argumentTypes;
-        final InvocationPlugin plugin;
-
-        int id;
-
-        MethodInfo(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
-            this.plugin = plugin;
-            this.isStatic = argumentTypes.length == 0 || argumentTypes[0] != Receiver.class;
-            this.declaringClass = declaringClass;
-            this.name = name;
-            this.argumentTypes = argumentTypes;
-            if (!isStatic) {
-                argumentTypes[0] = declaringClass;
-            }
-            assert resolveJava() != null;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj instanceof MethodInfo) {
-                MethodInfo that = (MethodInfo) obj;
-                boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes);
-                assert !res || this.isStatic == that.isStatic;
-                return res;
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            // Replay compilation mandates use of stable hash codes
-            return declaringClass.getName().hashCode() ^ name.hashCode();
-        }
-
-        ResolvedJavaMethod resolve(MetaAccessProvider metaAccess) {
-            return metaAccess.lookupJavaMethod(resolveJava());
-        }
-
-        Executable resolveJava() {
-            try {
-                Executable res;
-                Class<?>[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length);
-                if (name.equals("<init>")) {
-                    res = declaringClass.getDeclaredConstructor(parameterTypes);
-                } else {
-                    res = declaringClass.getDeclaredMethod(name, parameterTypes);
-                }
-                assert Modifier.isStatic(res.getModifiers()) == isStatic;
-                return res;
-            } catch (NoSuchMethodException | SecurityException e) {
-                throw new GraalInternalError(e);
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('(');
-            for (Class<?> p : argumentTypes) {
-                if (sb.charAt(sb.length() - 1) != '(') {
-                    sb.append(", ");
-                }
-                sb.append(p.getSimpleName());
-            }
-            return sb.append(')').toString();
-        }
-    }
-
-    protected final MetaAccessProvider metaAccess;
-    private final List<MethodInfo> registrations;
-
-    /**
-     * The minimum {@linkplain InvocationPluginIdHolder#getInvocationPluginId() id} for a method
-     * associated with a plugin in {@link #plugins}.
-     */
-    private int minId = Integer.MAX_VALUE;
-
-    /**
-     * Resolved methods to plugins map. The keys (i.e., indexes) are derived from
-     * {@link InvocationPluginIdHolder#getInvocationPluginId()}.
-     */
-    private volatile InvocationPlugin[] plugins;
+    protected final MethodIdMap<InvocationPlugin> plugins;
 
     /**
      * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in
      * this object.
      */
-    private InvocationPlugins parent;
+    protected final InvocationPlugins parent;
 
     private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) {
-        this.metaAccess = metaAccess;
-        this.registrations = new ArrayList<>(INITIAL_PLUGIN_CAPACITY);
+        this.plugins = new MethodIdMap<>(metaAccess);
         InvocationPlugins p = parent;
         // Only adopt a non-empty parent
         while (p != null && p.size() == 0) {
@@ -298,13 +189,11 @@
         this.parent = p;
     }
 
-    private static final int INITIAL_PLUGIN_CAPACITY = 64;
-
     /**
      * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}.
      */
     public InvocationPlugins(InvocationPlugins parent) {
-        this(parent, parent.metaAccess);
+        this(parent, parent.plugins.getMetaAccess());
     }
 
     public InvocationPlugins(MetaAccessProvider metaAccess) {
@@ -316,14 +205,10 @@
      * registered for {@code method}.
      */
     public void register(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
-        MethodInfo methodInfo = new MethodInfo(plugin, declaringClass, name, argumentTypes);
+        MethodKey<InvocationPlugin> methodInfo = plugins.put(plugin, declaringClass, name, argumentTypes);
         assert Checker.check(this, methodInfo, plugin);
-        assert plugins == null : "invocation plugin registration is closed";
-        registrations.add(methodInfo);
     }
 
-    private static int nextInvocationPluginId = 1;
-
     /**
      * Gets the plugin for a given method.
      *
@@ -331,54 +216,14 @@
      * @return the plugin associated with {@code method} or {@code null} if none exists
      */
     public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
-        assert method instanceof InvocationPluginIdHolder;
+        assert method instanceof MethodIdHolder;
         if (parent != null) {
             InvocationPlugin plugin = parent.lookupInvocation(method);
             if (plugin != null) {
                 return plugin;
             }
         }
-        InvocationPluginIdHolder pluggable = (InvocationPluginIdHolder) method;
-        if (plugins == null) {
-            // Must synchronize across all InvocationPlugins objects to ensure thread safe
-            // allocation of InvocationPlugin identifiers
-            synchronized (InvocationPlugins.class) {
-                if (plugins == null) {
-                    if (registrations.isEmpty()) {
-                        plugins = new InvocationPlugin[0];
-                    } else {
-                        int max = Integer.MIN_VALUE;
-                        for (MethodInfo methodInfo : registrations) {
-                            InvocationPluginIdHolder p = (InvocationPluginIdHolder) methodInfo.resolve(metaAccess);
-                            int id = p.getInvocationPluginId();
-                            if (id == 0) {
-                                id = nextInvocationPluginId++;
-                                p.setInvocationPluginId(id);
-                            }
-                            if (id < minId) {
-                                minId = id;
-                            }
-                            if (id > max) {
-                                max = id;
-
-                            }
-                            methodInfo.id = id;
-                        }
-
-                        int length = (max - minId) + 1;
-                        plugins = new InvocationPlugin[length];
-                        for (MethodInfo m : registrations) {
-                            int index = m.id - minId;
-                            plugins[index] = m.plugin;
-                        }
-                    }
-                }
-            }
-        }
-
-        int id = pluggable.getInvocationPluginId();
-        int index = id - minId;
-        return index >= 0 && index < plugins.length ? plugins[index] : null;
+        return plugins.get((MethodIdHolder) method);
     }
 
     /**
@@ -391,7 +236,7 @@
 
     @Override
     public String toString() {
-        return registrations.stream().map(MethodInfo::toString).collect(Collectors.joining(", ")) + " / parent: " + this.parent;
+        return plugins + " / parent: " + this.parent;
     }
 
     private static class Checker {
@@ -420,10 +265,10 @@
             SIGS = sigs.toArray(new Class<?>[sigs.size()][]);
         }
 
-        public static boolean check(InvocationPlugins plugins, MethodInfo method, InvocationPlugin plugin) {
-            InvocationPlugins p = plugins;
+        public static boolean check(InvocationPlugins plugins, MethodKey<InvocationPlugin> method, InvocationPlugin plugin) {
+            InvocationPlugins p = plugins.parent;
             while (p != null) {
-                assert !p.registrations.contains(method) : "a plugin is already registered for " + method;
+                assert !p.plugins.containsKey(method) : "a plugin is already registered for " + method;
                 p = p.parent;
             }
             if (plugin instanceof ForeignCallPlugin) {
@@ -448,12 +293,8 @@
         }
     }
 
-    public MetaAccessProvider getMetaAccess() {
-        return metaAccess;
-    }
-
     public int size() {
-        return registrations.size();
+        return plugins.size();
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodIdHolder.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2015, 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.graphbuilderconf;
+
+import java.util.*;
+import java.util.function.*;
+
+import com.oracle.graal.api.meta.*;
+
+/**
+ * {@link ResolvedJavaMethod}s that can be assigned a globally unique identifier for use as keys in
+ * a {@link MethodIdMap}. This should only be used where the cost of a {@link Map} with
+ * {@link ResolvedJavaMethod}s as keys is too high.
+ */
+public interface MethodIdHolder extends ResolvedJavaMethod {
+    /**
+     * Sets the unique, positive, non-zero identifier for this method.
+     */
+    void setMethodId(int id);
+
+    /**
+     * Gets the identifier set by {@link #setMethodId(int)} or 0 if no identifier was assigned to
+     * this method.
+     */
+    int getMethodId();
+
+    /**
+     * A singleton class for allocating globally unique method identifiers.
+     */
+    static final class MethodIdAllocator {
+
+        /**
+         * Ensures a given method has a unique identifier.
+         */
+        public int assignId(MethodIdHolder holder) {
+            assert Thread.holdsLock(instance) : "must only be called from within MethodIdHolder.allocateIds";
+            int id = holder.getMethodId();
+            if (id == 0) {
+                id = nextId++;
+                holder.setMethodId(id);
+                if (idVerifierMap != null) {
+                    idVerifierMap.put(holder, id);
+                }
+            } else {
+                assert idVerifierMap.get(holder) == id;
+            }
+            return id;
+        }
+
+        private int nextId = 1;
+        private final Map<MethodIdHolder, Integer> idVerifierMap;
+
+        @SuppressWarnings("all")
+        private MethodIdAllocator() {
+            boolean assertionsEnabled = false;
+            assert assertionsEnabled = true;
+            idVerifierMap = assertionsEnabled ? new HashMap<>() : null;
+        }
+
+        /**
+         * Singleton instance.
+         */
+        private static final MethodIdAllocator instance = new MethodIdAllocator();
+    }
+
+    /**
+     * Executes some given code that ensures some set of {@link ResolvedJavaMethod}s have unique ids
+     * {@linkplain MethodIdHolder#setMethodId(int) assigned} to them. The
+     * {@link Consumer#accept(Object)} method of the given object is called under a global lock.
+     */
+    static void assignIds(Consumer<MethodIdAllocator> methodIdConsumer) {
+        synchronized (MethodIdAllocator.instance) {
+            methodIdConsumer.accept(MethodIdAllocator.instance);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodIdMap.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2015, 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.graphbuilderconf;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.graphbuilderconf.MethodIdHolder.MethodIdAllocator;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * A map whose keys are {@link MethodIdHolder}s that doesn't require eager resolution of
+ * {@link ResolvedJavaMethod}s and has retrieval as fast as array indexing. The constraints on using
+ * such a map are:
+ * <ul>
+ * <li>at most one value can be added for any key</li>
+ * <li>no more entries can be added after the first {@linkplain #get(MethodIdHolder) retrieval}</li>
+ * </ul>
+ *
+ * @param <V> the type of the values in the map
+ */
+public class MethodIdMap<V> {
+
+    /**
+     * The receiver in a non-static method. The class literal for this interface must be used with
+     * {@link MethodIdMap#put(Object, Class, String, Class...)} to denote the receiver argument for
+     * such a non-static method.
+     */
+    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.
+         */
+        default boolean isConstant() {
+            return false;
+        }
+    }
+
+    /**
+     * Key for a method.
+     */
+    public static class MethodKey<T> {
+        final boolean isStatic;
+        final Class<?> declaringClass;
+        final String name;
+        final Class<?>[] argumentTypes;
+        final T value;
+        int id;
+
+        MethodKey(T data, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
+            this.value = data;
+            this.isStatic = argumentTypes.length == 0 || argumentTypes[0] != Receiver.class;
+            this.declaringClass = declaringClass;
+            this.name = name;
+            this.argumentTypes = argumentTypes;
+            if (!isStatic) {
+                argumentTypes[0] = declaringClass;
+            }
+            assert resolveJava() != null;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof MethodKey) {
+                MethodKey<?> that = (MethodKey<?>) obj;
+                boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes);
+                assert !res || this.isStatic == that.isStatic;
+                return res;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            // Replay compilation mandates use of stable hash codes
+            return declaringClass.getName().hashCode() ^ name.hashCode();
+        }
+
+        private MethodIdHolder resolve(MetaAccessProvider metaAccess) {
+            return (MethodIdHolder) metaAccess.lookupJavaMethod(resolveJava());
+        }
+
+        private Executable resolveJava() {
+            try {
+                Executable res;
+                Class<?>[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length);
+                if (name.equals("<init>")) {
+                    res = declaringClass.getDeclaredConstructor(parameterTypes);
+                } else {
+                    res = declaringClass.getDeclaredMethod(name, parameterTypes);
+                }
+                assert Modifier.isStatic(res.getModifiers()) == isStatic;
+                return res;
+            } catch (NoSuchMethodException | SecurityException e) {
+                throw new GraalInternalError(e);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('(');
+            for (Class<?> p : argumentTypes) {
+                if (sb.charAt(sb.length() - 1) != '(') {
+                    sb.append(", ");
+                }
+                sb.append(p.getSimpleName());
+            }
+            return sb.append(')').toString();
+        }
+    }
+
+    private final MetaAccessProvider metaAccess;
+
+    /**
+     * Initial list of entries.
+     */
+    private final List<MethodKey<V>> registrations;
+
+    /**
+     * Entry array that is initialized upon first call to {@link #get(MethodIdHolder)}.
+     *
+     * Note: this must be volatile since double-checked locking is used to initialize it
+     */
+    private volatile V[] entries;
+
+    /**
+     * The minimum {@linkplain MethodIdHolder#getMethodId() id} for a key in this map.
+     */
+    private int minId = Integer.MAX_VALUE;
+
+    public MethodIdMap(MetaAccessProvider metaAccess) {
+        this.metaAccess = metaAccess;
+        this.registrations = new ArrayList<>(INITIAL_CAPACITY);
+    }
+
+    private static final int INITIAL_CAPACITY = 64;
+
+    /**
+     * Adds an entry to this map for a specified method.
+     *
+     * @param value value to be associated with the specified method
+     * @param declaringClass the class declaring the method
+     * @param name the name of the method
+     * @param argumentTypes the argument types of the method. Element 0 of this array must be the
+     *            {@link Class} value for {@link Receiver} iff the method is non-static.
+     * @return an object representing the method
+     */
+    public MethodKey<V> put(V value, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
+        MethodKey<V> methodKey = new MethodKey<>(value, declaringClass, name, argumentTypes);
+        assert entries == null : "registration is closed";
+        assert !registrations.contains(methodKey) : "a value is already registered for " + methodKey;
+        registrations.add(methodKey);
+        return methodKey;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected V[] allocateEntries(int length) {
+        return (V[]) new Object[length];
+    }
+
+    /**
+     * Determines if a method denoted by a given {@link MethodKey} is in this map.
+     */
+    public boolean containsKey(MethodKey<V> key) {
+        return registrations.contains(key);
+    }
+
+    public V get(MethodIdHolder method) {
+        if (entries == null) {
+            // 'assignIds' synchronizes on a global lock which ensures thread safe
+            // allocation of identifiers across all MethodIdHolder objects
+            MethodIdHolder.assignIds(new Consumer<MethodIdAllocator>() {
+
+                public void accept(MethodIdAllocator idAllocator) {
+                    if (entries == null) {
+                        if (registrations.isEmpty()) {
+                            entries = allocateEntries(0);
+                        } else {
+                            int max = Integer.MIN_VALUE;
+                            for (MethodKey<V> methodKey : registrations) {
+                                MethodIdHolder m = methodKey.resolve(metaAccess);
+                                int id = idAllocator.assignId(m);
+                                if (id < minId) {
+                                    minId = id;
+                                }
+                                if (id > max) {
+                                    max = id;
+                                }
+                                methodKey.id = id;
+                            }
+
+                            int length = (max - minId) + 1;
+                            entries = allocateEntries(length);
+                            for (MethodKey<V> m : registrations) {
+                                int index = m.id - minId;
+                                entries[index] = m.value;
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        int id = method.getMethodId();
+        int index = id - minId;
+        return index >= 0 && index < entries.length ? entries[index] : null;
+    }
+
+    @Override
+    public String toString() {
+        return registrations.stream().map(MethodKey::toString).collect(Collectors.joining(", "));
+    }
+
+    public MetaAccessProvider getMetaAccess() {
+        return metaAccess;
+    }
+
+    public int size() {
+        return registrations.size();
+    }
+}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodSubstitutionPlugin.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodSubstitutionPlugin.java	Tue Apr 14 14:01:18 2015 +0200
@@ -30,7 +30,7 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.*;
 
 /**
@@ -121,8 +121,8 @@
                 return m;
             }
         }
-        throw new GraalInternalError("No method found in %s compatible with the signature (%s)", declaringClass.getName(), Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect(
-                        Collectors.joining(",")));
+        throw new GraalInternalError("No method found in %s compatible with \"%s(%s)\"", declaringClass.getName(), name, Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect(
+                        Collectors.joining(", ")));
     }
 
     /**
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Tue Apr 14 14:01:18 2015 +0200
@@ -192,7 +192,7 @@
 
     private Register findPollOnReturnScratchRegister() {
         RegisterConfig regConfig = getProviders().getCodeCache().getRegisterConfig();
-        for (Register r : regConfig.getAllocatableRegisters(Kind.Long)) {
+        for (Register r : regConfig.getAllocatableRegisters()) {
             if (!r.equals(regConfig.getReturnRegister(Kind.Long)) && !r.equals(AMD64.rbp)) {
                 return r;
             }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java	Tue Apr 14 14:01:18 2015 +0200
@@ -26,7 +26,6 @@
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 
 import java.util.*;
-import java.util.concurrent.*;
 
 import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
@@ -52,8 +51,6 @@
 
     private final boolean allAllocatableAreCallerSaved;
 
-    private final Map<PlatformKind.Key, Register[]> categorized = new ConcurrentHashMap<>();
-
     private final RegisterAttributes[] attributesMap;
 
     public int getMaximumFrameSize() {
@@ -65,22 +62,15 @@
         return allocatable.clone();
     }
 
-    public Register[] getAllocatableRegisters(PlatformKind kind) {
-        PlatformKind.Key key = kind.getKey();
-        if (categorized.containsKey(key)) {
-            Register[] val = categorized.get(key);
-            return val;
-        }
-
+    public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) {
         ArrayList<Register> list = new ArrayList<>();
-        for (Register reg : getAllocatableRegisters()) {
+        for (Register reg : registers) {
             if (architecture.canStoreValue(reg.getRegisterCategory(), kind)) {
                 list.add(reg);
             }
         }
 
         Register[] ret = list.toArray(new Register[list.size()]);
-        categorized.put(key, ret);
         return ret;
     }
 
@@ -101,15 +91,6 @@
 
     private final CalleeSaveLayout csl;
 
-    private static Register findRegister(String name, Register[] all) {
-        for (Register reg : all) {
-            if (reg.name.equals(name)) {
-                return reg;
-            }
-        }
-        throw new IllegalArgumentException("register " + name + " is not allocatable");
-    }
-
     private static Register[] initAllocatable(boolean reserveForHeapBase) {
         Register[] registers = null;
         // @formatter:off
@@ -127,16 +108,6 @@
                       };
         }
        // @formatter:on
-
-        if (RegisterPressure.getValue() != null) {
-            String[] names = RegisterPressure.getValue().split(",");
-            Register[] regs = new Register[names.length];
-            for (int i = 0; i < names.length; i++) {
-                regs[i] = findRegister(names[i], registers);
-            }
-            return regs;
-        }
-
         return registers;
     }
 
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotRegisterConfig.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotRegisterConfig.java	Tue Apr 14 14:01:18 2015 +0200
@@ -22,11 +22,9 @@
  */
 package com.oracle.graal.hotspot.sparc;
 
-import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static com.oracle.graal.sparc.SPARC.*;
 
 import java.util.*;
-import java.util.concurrent.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.code.CallingConvention.Type;
@@ -43,8 +41,6 @@
 
     private final Register[] allocatable;
 
-    private final Map<PlatformKind, Register[]> categorized = new ConcurrentHashMap<>(20);
-
     private final RegisterAttributes[] attributesMap;
 
     @Override
@@ -52,13 +48,9 @@
         return allocatable.clone();
     }
 
-    public Register[] getAllocatableRegisters(PlatformKind kind) {
-        if (categorized.containsKey(kind)) {
-            return categorized.get(kind);
-        }
-
+    public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) {
         ArrayList<Register> list = new ArrayList<>();
-        for (Register reg : getAllocatableRegisters()) {
+        for (Register reg : registers) {
             if (architecture.canStoreValue(reg.getRegisterCategory(), kind)) {
                 // Special treatment for double precision
                 // TODO: This is wasteful it uses only half of the registers as float.
@@ -77,7 +69,6 @@
         }
 
         Register[] ret = list.toArray(new Register[list.size()]);
-        categorized.put(kind, ret);
         return ret;
     }
 
@@ -111,15 +102,6 @@
 
     private final CalleeSaveLayout csl;
 
-    private static Register findRegister(String name, Register[] all) {
-        for (Register reg : all) {
-            if (reg.name.equals(name)) {
-                return reg;
-            }
-        }
-        throw new IllegalArgumentException("register " + name + " is not allocatable");
-    }
-
     private static Register[] initAllocatable(boolean reserveForHeapBase) {
         Register[] registers = null;
         if (reserveForHeapBase) {
@@ -158,15 +140,6 @@
             // @formatter:on
         }
 
-        if (RegisterPressure.getValue() != null) {
-            String[] names = RegisterPressure.getValue().split(",");
-            Register[] regs = new Register[names.length];
-            for (int i = 0; i < names.length; i++) {
-                regs[i] = findRegister(names[i], registers);
-            }
-            return regs;
-        }
-
         return registers;
     }
 
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -22,7 +22,7 @@
  */
 package com.oracle.graal.hotspot.test;
 
-import static com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext.*;
+import static com.oracle.graal.java.IntrinsicContext.*;
 
 import java.io.*;
 import java.lang.reflect.*;
@@ -39,7 +39,6 @@
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext;
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.*;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Tue Apr 14 14:01:18 2015 +0200
@@ -27,7 +27,6 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.hotspot.replacements.*;
 import com.oracle.graal.hotspot.word.*;
 import com.oracle.graal.phases.util.*;
@@ -68,14 +67,6 @@
             if (!config.useCRC32Intrinsics) {
                 return null;
             }
-        } else if (substituteClass == StringSubstitutions.class) {
-            /*
-             * AMD64's String.equals substitution needs about 8 registers so we better disable the
-             * substitution if there is some register pressure.
-             */
-            if (GraalOptions.RegisterPressure.getValue() != null) {
-                return null;
-            }
         }
         return super.registerMethodSubstitution(cr, originalMethod, substituteMethod);
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Tue Apr 14 14:01:18 2015 +0200
@@ -34,8 +34,8 @@
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.nodes.ClassQueryNode.Query;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java	Tue Apr 14 14:01:18 2015 +0200
@@ -33,6 +33,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.graphbuilderconf.*;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.phases.*;
 import com.oracle.graal.hotspot.replacements.*;
@@ -77,7 +78,7 @@
     @Override
     public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
         if (methodHandleClass == null) {
-            methodHandleClass = metaAccess.lookupJavaType(MethodHandle.class);
+            methodHandleClass = plugins.getMetaAccess().lookupJavaType(MethodHandle.class);
         }
         if (method.getDeclaringClass().equals(methodHandleClass)) {
             HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethodImpl.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethodImpl.java	Tue Apr 14 14:01:18 2015 +0200
@@ -42,7 +42,7 @@
 /**
  * Implementation of {@link JavaMethod} for resolved HotSpot methods.
  */
-public final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, HotSpotProxified, InvocationPluginIdHolder {
+public final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, HotSpotProxified, MethodIdHolder {
 
     private static final long serialVersionUID = -5486975070147586588L;
 
@@ -742,14 +742,14 @@
         return runtime().getCompilerToVM().hasCompiledCodeForOSR(metaspaceMethod, entryBCI, level);
     }
 
-    private int invocationPluginId;
+    private int methodId;
 
-    public void setInvocationPluginId(int id) {
-        assert invocationPluginId == 0;
-        invocationPluginId = id;
+    public void setMethodId(int id) {
+        assert methodId == 0;
+        methodId = id;
     }
 
-    public int getInvocationPluginId() {
-        return invocationPluginId;
+    public int getMethodId() {
+        return methodId;
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/type/MetaspacePointerStamp.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/type/MetaspacePointerStamp.java	Tue Apr 14 14:01:18 2015 +0200
@@ -40,9 +40,7 @@
 
     @Override
     public Stamp meet(Stamp other) {
-        if (!isCompatible(other)) {
-            return StampFactory.illegal();
-        }
+        assert isCompatible(other);
         return this;
     }
 
@@ -53,9 +51,7 @@
 
     @Override
     public Stamp join(Stamp other) {
-        if (!isCompatible(other)) {
-            return StampFactory.illegal();
-        }
+        assert isCompatible(other);
         return this;
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Tue Apr 14 14:01:18 2015 +0200
@@ -32,7 +32,6 @@
 import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.java.AbstractBytecodeParser.ReplacementContext;
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Tue Apr 14 14:01:18 2015 +0200
@@ -20,30 +20,24 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package com.oracle.graal.java;
 
 import static com.oracle.graal.api.code.TypeCheckHints.*;
 import static com.oracle.graal.api.meta.DeoptimizationReason.*;
 import static com.oracle.graal.bytecode.Bytecodes.*;
 import static com.oracle.graal.java.AbstractBytecodeParser.Options.*;
-import static com.oracle.graal.java.HIRFrameStateBuilder.*;
 
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.bytecode.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graphbuilderconf.*;
-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.java.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
@@ -83,153 +77,6 @@
     }
 
     /**
-     * Information about a substitute method being parsed in lieu of an original method. This can
-     * happen when a call to a {@link MethodSubstitution} is encountered or the root of compilation
-     * is a {@link MethodSubstitution} or a snippet.
-     */
-    public static class ReplacementContext implements Replacement {
-        /**
-         * The method being replaced.
-         */
-        final ResolvedJavaMethod method;
-
-        /**
-         * The replacement method.
-         */
-        final ResolvedJavaMethod replacement;
-
-        public ReplacementContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute) {
-            this.method = method;
-            this.replacement = substitute;
-        }
-
-        public ResolvedJavaMethod getOriginalMethod() {
-            return method;
-        }
-
-        public ResolvedJavaMethod getReplacementMethod() {
-            return replacement;
-        }
-
-        public boolean isIntrinsic() {
-            return false;
-        }
-
-        /**
-         * Determines if a call within the compilation scope of a replacement represents a call to
-         * the original method.
-         */
-        public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) {
-            return method.equals(targetMethod) || replacement.equals(targetMethod);
-        }
-
-        IntrinsicContext asIntrinsic() {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "Replacement{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + "}";
-        }
-    }
-
-    /**
-     * Context for a replacement being inlined as a compiler intrinsic. Deoptimization within a
-     * compiler intrinsic must replay the intrinsified call. This context object retains the
-     * information required to build a frame state denoting the JVM state just before the
-     * intrinsified call.
-     */
-    public static class IntrinsicContext extends ReplacementContext {
-
-        /**
-         * BCI denoting an intrinsic is being parsed for inlining after the caller has been parsed.
-         */
-        public static final int POST_PARSE_INLINE_BCI = -1;
-
-        /**
-         * BCI denoting an intrinsic is the compilation root.
-         */
-        public static final int ROOT_COMPILATION_BCI = -2;
-
-        /**
-         * The arguments to the intrinsic.
-         */
-        ValueNode[] args;
-
-        /**
-         * The BCI of the intrinsified invocation, {@link #POST_PARSE_INLINE_BCI} or
-         * {@link #ROOT_COMPILATION_BCI}.
-         */
-        final int bci;
-
-        private FrameState stateBeforeCache;
-
-        public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute, ValueNode[] args, int bci) {
-            super(method, substitute);
-            assert bci != POST_PARSE_INLINE_BCI || args == null;
-            this.args = args;
-            this.bci = bci;
-            assert !isCompilationRoot() || method.hasBytecodes() : "Cannot intrinsic for native or abstract method " + method.format("%H.%n(%p)");
-        }
-
-        @Override
-        public boolean isIntrinsic() {
-            return true;
-        }
-
-        public boolean isPostParseInlined() {
-            return bci == POST_PARSE_INLINE_BCI;
-        }
-
-        public boolean isCompilationRoot() {
-            return bci == ROOT_COMPILATION_BCI;
-        }
-
-        public FrameState getInvokeStateBefore(StructuredGraph graph, BytecodeParser parent) {
-            if (isCompilationRoot()) {
-                int maxLocals = method.getMaxLocals();
-                // The 'args' were initialized based on the intrinsic method but a
-                // frame state's 'locals' needs to have the same length as the frame
-                // state method's 'max_locals'.
-                ValueNode[] locals = maxLocals == args.length ? args : Arrays.copyOf(args, maxLocals);
-                ValueNode[] stack = EMPTY_ARRAY;
-                int stackSize = 0;
-                ValueNode[] locks = EMPTY_ARRAY;
-                List<MonitorIdNode> monitorIds = Collections.emptyList();
-                return graph.add(new FrameState(null, method, 0, locals, stack, stackSize, locks, monitorIds, false, false));
-            } else if (isPostParseInlined()) {
-                return graph.add(new FrameState(BytecodeFrame.BEFORE_BCI));
-            } else {
-                assert !parent.parsingReplacement() || parent.replacementContext instanceof IntrinsicContext;
-                if (stateBeforeCache == null) {
-                    assert stateBeforeCache == null;
-
-                    // Find the non-intrinsic ancestor calling the intrinsified method
-                    BytecodeParser ancestor = parent;
-                    while (ancestor.parsingReplacement()) {
-                        assert ancestor.replacementContext instanceof IntrinsicContext;
-                        ancestor = ancestor.getParent();
-                    }
-                    FrameState stateDuring = ancestor.getFrameState().create(ancestor.bci(), ancestor.getParent(), true);
-                    stateBeforeCache = stateDuring.duplicateModifiedBeforeCall(bci, Kind.Void, args);
-                }
-                return stateBeforeCache;
-            }
-        }
-
-        @Override
-        IntrinsicContext asIntrinsic() {
-            return this;
-        }
-
-        @Override
-        public String toString() {
-            return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + ", bci: " + bci +
-                            (args == null ? "" : ", args: " + Arrays.toString(args)) + (stateBeforeCache == null ? "" : ", stateBefore: " + stateBeforeCache) + "}";
-        }
-    }
-
-    /**
      * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the
      * bytecode instructions as they are parsed.
      */
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Apr 14 14:01:18 2015 +0200
@@ -50,7 +50,6 @@
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo;
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver;
-import com.oracle.graal.java.AbstractBytecodeParser.ReplacementContext;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
 import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
 import com.oracle.graal.nodeinfo.*;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Tue Apr 14 14:01:18 2015 +0200
@@ -32,7 +32,6 @@
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
 import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser;
 import com.oracle.graal.nodeinfo.*;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/IntrinsicContext.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2015, 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.java;
+
+import static com.oracle.graal.java.HIRFrameStateBuilder.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.java.GraphBuilderPhase.Instance.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+
+/**
+ * Context for a replacement being inlined as a compiler intrinsic. Deoptimization within a compiler
+ * intrinsic must replay the intrinsified call. This context object retains the information required
+ * to build a frame state denoting the JVM state just before the intrinsified call.
+ */
+public class IntrinsicContext extends ReplacementContext {
+
+    /**
+     * BCI denoting an intrinsic is being parsed for inlining after the caller has been parsed.
+     */
+    public static final int POST_PARSE_INLINE_BCI = -1;
+
+    /**
+     * BCI denoting an intrinsic is the compilation root.
+     */
+    public static final int ROOT_COMPILATION_BCI = -2;
+
+    /**
+     * The arguments to the intrinsic.
+     */
+    ValueNode[] args;
+
+    /**
+     * The BCI of the intrinsified invocation, {@link #POST_PARSE_INLINE_BCI} or
+     * {@link #ROOT_COMPILATION_BCI}.
+     */
+    final int bci;
+
+    private FrameState stateBeforeCache;
+
+    public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute, ValueNode[] args, int bci) {
+        super(method, substitute);
+        assert bci != POST_PARSE_INLINE_BCI || args == null;
+        this.args = args;
+        this.bci = bci;
+        assert !isCompilationRoot() || method.hasBytecodes() : "Cannot intrinsic for native or abstract method " + method.format("%H.%n(%p)");
+    }
+
+    @Override
+    public boolean isIntrinsic() {
+        return true;
+    }
+
+    public boolean isPostParseInlined() {
+        return bci == POST_PARSE_INLINE_BCI;
+    }
+
+    public boolean isCompilationRoot() {
+        return bci == ROOT_COMPILATION_BCI;
+    }
+
+    public FrameState getInvokeStateBefore(StructuredGraph graph, BytecodeParser parent) {
+        if (isCompilationRoot()) {
+            int maxLocals = method.getMaxLocals();
+            // The 'args' were initialized based on the intrinsic method but a
+            // frame state's 'locals' needs to have the same length as the frame
+            // state method's 'max_locals'.
+            ValueNode[] locals = maxLocals == args.length ? args : Arrays.copyOf(args, maxLocals);
+            ValueNode[] stack = EMPTY_ARRAY;
+            int stackSize = 0;
+            ValueNode[] locks = EMPTY_ARRAY;
+            List<MonitorIdNode> monitorIds = Collections.emptyList();
+            return graph.add(new FrameState(null, method, 0, locals, stack, stackSize, locks, monitorIds, false, false));
+        } else if (isPostParseInlined()) {
+            return graph.add(new FrameState(BytecodeFrame.BEFORE_BCI));
+        } else {
+            assert !parent.parsingReplacement() || parent.replacementContext instanceof IntrinsicContext;
+            if (stateBeforeCache == null) {
+                assert stateBeforeCache == null;
+
+                // Find the non-intrinsic ancestor calling the intrinsified method
+                BytecodeParser ancestor = parent;
+                while (ancestor.parsingReplacement()) {
+                    assert ancestor.replacementContext instanceof IntrinsicContext;
+                    ancestor = ancestor.getParent();
+                }
+                FrameState stateDuring = ancestor.getFrameState().create(ancestor.bci(), ancestor.getParent(), true);
+                stateBeforeCache = stateDuring.duplicateModifiedBeforeCall(bci, Kind.Void, args);
+            }
+            return stateBeforeCache;
+        }
+    }
+
+    @Override
+    IntrinsicContext asIntrinsic() {
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + ", bci: " + bci + (args == null ? "" : ", args: " + Arrays.toString(args)) +
+                        (stateBeforeCache == null ? "" : ", stateBefore: " + stateBeforeCache) + "}";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/ReplacementContext.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, 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.java;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement;
+
+/**
+ * Information about a substitute method being parsed in lieu of an original method. This can happen
+ * when a call to a {@link MethodSubstitution} is encountered or the root of compilation is a
+ * {@link MethodSubstitution} or a snippet.
+ */
+public class ReplacementContext implements Replacement {
+    /**
+     * The method being replaced.
+     */
+    final ResolvedJavaMethod method;
+
+    /**
+     * The replacement method.
+     */
+    final ResolvedJavaMethod replacement;
+
+    public ReplacementContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute) {
+        this.method = method;
+        this.replacement = substitute;
+    }
+
+    public ResolvedJavaMethod getOriginalMethod() {
+        return method;
+    }
+
+    public ResolvedJavaMethod getReplacementMethod() {
+        return replacement;
+    }
+
+    public boolean isIntrinsic() {
+        return false;
+    }
+
+    /**
+     * Determines if a call within the compilation scope of a replacement represents a call to the
+     * original method.
+     */
+    public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) {
+        return method.equals(targetMethod) || replacement.equals(targetMethod);
+    }
+
+    IntrinsicContext asIntrinsic() {
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "Replacement{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + "}";
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Tue Apr 14 14:01:18 2015 +0200
@@ -66,6 +66,7 @@
     final SpillMoveFactory spillMoveFactory;
     final RegisterAttributes[] registerAttributes;
     final Register[] registers;
+    final RegisterAllocationConfig regAllocConfig;
 
     final boolean callKillsRegisters;
 
@@ -163,14 +164,15 @@
      */
     private final int firstVariableNumber;
 
-    LinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory) {
+    LinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig) {
         this.target = target;
         this.res = res;
         this.ir = res.getLIR();
         this.spillMoveFactory = spillMoveFactory;
         this.frameMapBuilder = res.getFrameMapBuilder();
         this.sortedBlocks = ir.linearScanOrder();
-        this.registerAttributes = frameMapBuilder.getRegisterConfig().getAttributesMap();
+        this.registerAttributes = regAllocConfig.getRegisterConfig().getAttributesMap();
+        this.regAllocConfig = regAllocConfig;
 
         this.registers = target.arch.getRegisters();
         this.firstVariableNumber = registers.length;
@@ -178,7 +180,7 @@
 
         // If all allocatable registers are caller saved, then no registers are live across a call
         // site. The register allocator can save time not trying to find a register at a call site.
-        this.callKillsRegisters = this.frameMapBuilder.getRegisterConfig().areAllAllocatableRegistersCallerSaved();
+        this.callKillsRegisters = regAllocConfig.getRegisterConfig().areAllAllocatableRegistersCallerSaved();
     }
 
     int getFirstLirInstructionId(AbstractBlockBase<?> block) {
@@ -1169,7 +1171,7 @@
             };
 
             // create a list with all caller-save registers (cpu, fpu, xmm)
-            Register[] callerSaveRegs = frameMapBuilder.getRegisterConfig().getCallerSaveRegisters();
+            Register[] callerSaveRegs = regAllocConfig.getRegisterConfig().getCallerSaveRegisters();
 
             // iterate all blocks in reverse order
             for (int i = blockCount() - 1; i >= 0; i--) {
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java	Tue Apr 14 14:01:18 2015 +0200
@@ -25,6 +25,7 @@
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
+import com.oracle.graal.compiler.common.alloc.*;
 import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
@@ -34,7 +35,7 @@
 
     @Override
     protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
-        new LinearScan(target, lirGenRes, spillMoveFactory).allocate();
+        new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate();
     }
 
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java	Tue Apr 14 14:01:18 2015 +0200
@@ -713,10 +713,12 @@
                 Debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos);
 
                 if (firstUsage <= interval.from() + 1) {
-                    assert false : "cannot spill interval that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage + ", interval.from()=" + interval.from();
+                    String description = "cannot spill interval that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage + ", interval.from()=" +
+                                    interval.from();
                     // assign a reasonable register and do a bailout in product mode to avoid errors
                     allocator.assignSpillSlot(interval);
-                    throw new BailoutException("LinearScan: no register found");
+                    allocator.printIntervals(description);
+                    throw new OutOfRegistersException("LinearScan: no register found", description);
                 }
 
                 splitAndSpillInterval(interval);
@@ -766,7 +768,7 @@
     }
 
     void initVarsForAlloc(Interval interval) {
-        availableRegs = allocator.frameMapBuilder.getRegisterConfig().getAllocatableRegisters(interval.kind().getPlatformKind());
+        availableRegs = allocator.regAllocConfig.getAllocatableRegisters(interval.kind().getPlatformKind());
     }
 
     static boolean isMove(LIRInstruction op, Interval from, Interval to) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/OutOfRegistersException.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2015, 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.lir.alloc.lsra;
+
+import com.oracle.graal.api.code.*;
+
+public class OutOfRegistersException extends BailoutException {
+
+    private static final long serialVersionUID = -3479786650143432195L;
+
+    private final String description;
+
+    public OutOfRegistersException(String msg) {
+        super(msg);
+        this.description = "";
+    }
+
+    public OutOfRegistersException(Throwable cause, String msg) {
+        super(cause, msg);
+        this.description = "";
+    }
+
+    public OutOfRegistersException(boolean permanent, String msg) {
+        super(permanent, msg);
+        this.description = "";
+    }
+
+    public OutOfRegistersException(String msg, String description) {
+        super(msg);
+        this.description = description;
+    }
+
+    public OutOfRegistersException(Throwable cause, String msg, String description) {
+        super(cause, msg);
+        this.description = description;
+    }
+
+    public OutOfRegistersException(boolean permanent, String msg, String description) {
+        super(permanent, msg);
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/RegisterVerifier.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/RegisterVerifier.java	Tue Apr 14 14:01:18 2015 +0200
@@ -225,7 +225,7 @@
             op.visitEachInput(useConsumer);
             // invalidate all caller save registers at calls
             if (op.destroysCallerSavedRegisters()) {
-                for (Register r : allocator.frameMapBuilder.getRegisterConfig().getCallerSaveRegisters()) {
+                for (Register r : allocator.regAllocConfig.getRegisterConfig().getCallerSaveRegisters()) {
                     statePut(inputState, r.asValue(), null);
                 }
             }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java	Tue Apr 14 14:01:18 2015 +0200
@@ -41,6 +41,12 @@
     private final Assumptions assumptions;
     private final Set<ResolvedJavaMethod> inlinedMethods;
 
+    /**
+     * The "table of contents" of the encoded graph, i.e., the mapping from orderId numbers to the
+     * offset in the encoded byte[] array. Used as a cache during decoding.
+     */
+    protected long[] nodeStartOffsets;
+
     public EncodedGraph(byte[] encoding, long startOffset, Object[] objects, NodeClass<?>[] types, Assumptions assumptions, Set<ResolvedJavaMethod> inlinedMethods) {
         this.encoding = encoding;
         this.startOffset = startOffset;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Tue Apr 14 14:01:18 2015 +0200
@@ -227,11 +227,11 @@
      * the stack.
      */
     public FrameState duplicateModifiedDuringCall(int newBci, Kind popKind) {
-        return duplicateModified(newBci, rethrowException, true, popKind);
+        return duplicateModified(graph(), newBci, rethrowException, true, popKind);
     }
 
     public FrameState duplicateModifiedBeforeCall(int newBci, Kind popKind, ValueNode... pushedValues) {
-        return duplicateModified(newBci, rethrowException, false, popKind, pushedValues);
+        return duplicateModified(graph(), newBci, rethrowException, false, popKind, pushedValues);
     }
 
     /**
@@ -241,7 +241,7 @@
      * followed by a null slot.
      */
     public FrameState duplicateModified(int newBci, boolean newRethrowException, Kind popKind, ValueNode... pushedValues) {
-        return duplicateModified(newBci, newRethrowException, duringCall, popKind, pushedValues);
+        return duplicateModified(graph(), newBci, newRethrowException, duringCall, popKind, pushedValues);
     }
 
     /**
@@ -250,7 +250,7 @@
      */
     public FrameState duplicateModified(Kind popKind, ValueNode pushedValue) {
         assert pushedValue != null && pushedValue.getKind() == popKind;
-        return duplicateModified(bci, rethrowException, duringCall, popKind, pushedValue);
+        return duplicateModified(graph(), bci, rethrowException, duringCall, popKind, pushedValue);
     }
 
     /**
@@ -259,7 +259,7 @@
      * correctly in slot encoding: a long or double will be followed by a null slot. The bci will be
      * changed to newBci.
      */
-    private FrameState duplicateModified(int newBci, boolean newRethrowException, boolean newDuringCall, Kind popKind, ValueNode... pushedValues) {
+    public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean newRethrowException, boolean newDuringCall, Kind popKind, ValueNode... pushedValues) {
         ArrayList<ValueNode> copy;
         if (newRethrowException && !rethrowException && popKind == Kind.Void) {
             assert popKind == Kind.Void;
@@ -285,7 +285,7 @@
         copy.addAll(values.subList(localsSize + stackSize, values.size()));
 
         assert checkStackDepth(bci, stackSize, duringCall, rethrowException, newBci, newStackSize, newDuringCall, newRethrowException);
-        return graph().add(new FrameState(outerFrameState(), method, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings));
+        return graph.add(new FrameState(outerFrameState(), method, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings));
     }
 
     /**
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java	Tue Apr 14 14:01:18 2015 +0200
@@ -26,6 +26,7 @@
 
 import java.util.*;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.util.*;
 import com.oracle.graal.debug.*;
@@ -70,48 +71,38 @@
     protected static class MethodScope {
         /** The target graph where decoded nodes are added to. */
         public final StructuredGraph graph;
-        /** The state of the caller method. Only non-null during method inlining. */
-        public final MethodScope caller;
         /** The encode graph that is decoded. */
         public final EncodedGraph encodedGraph;
         /** Access to the encoded graph. */
         public final TypeReader reader;
-        /**
-         * The "table of contents" of the encoded graph, i.e., the mapping from orderId numbers to
-         * the offset in the encoded byte[] array.
-         */
-        public final long[] nodeStartOffsets;
         /** The kind of loop explosion to be performed during decoding. */
         public final LoopExplosionKind loopExplosion;
 
-        /**
-         * The start node of the decoded graph. This is a temporary node for inlined graphs that
-         * needs to be deleted after inlining.
-         */
-        public StartNode startNode;
         /** All return nodes encountered during decoding. */
         public final List<ReturnNode> returnNodes;
         /** The exception unwind node encountered during decoding, or null. */
         public UnwindNode unwindNode;
 
-        protected MethodScope(StructuredGraph graph, MethodScope caller, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) {
+        protected MethodScope(StructuredGraph graph, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) {
             this.graph = graph;
-            this.caller = caller;
             this.encodedGraph = encodedGraph;
             this.loopExplosion = loopExplosion;
             this.returnNodes = new ArrayList<>();
 
-            reader = new UnsafeArrayTypeReader(encodedGraph.getEncoding(), encodedGraph.getStartOffset());
-            int nodeCount = reader.getUVInt();
-            nodeStartOffsets = new long[nodeCount];
-            for (int i = 0; i < nodeCount; i++) {
-                nodeStartOffsets[i] = encodedGraph.getStartOffset() - reader.getUV();
+            if (encodedGraph != null) {
+                reader = new UnsafeArrayTypeReader(encodedGraph.getEncoding(), encodedGraph.getStartOffset());
+                if (encodedGraph.nodeStartOffsets == null) {
+                    int nodeCount = reader.getUVInt();
+                    long[] nodeStartOffsets = new long[nodeCount];
+                    for (int i = 0; i < nodeCount; i++) {
+                        nodeStartOffsets[i] = encodedGraph.getStartOffset() - reader.getUV();
+                    }
+                    encodedGraph.nodeStartOffsets = nodeStartOffsets;
+                }
+            } else {
+                reader = null;
             }
         }
-
-        public boolean isInlinedMethod() {
-            return caller != null;
-        }
     }
 
     /** Decoding state maintained for each loop in the encoded graph. */
@@ -153,7 +144,7 @@
             this.iterationStates = null;
             this.loopBeginOrderId = -1;
 
-            int nodeCount = methodScope.nodeStartOffsets.length;
+            int nodeCount = methodScope.encodedGraph.nodeStartOffsets.length;
             this.nodesToProcess = new BitSet(nodeCount);
             this.initialCreatedNodes = new Node[nodeCount];
             this.createdNodes = new Node[nodeCount];
@@ -226,22 +217,55 @@
         }
     }
 
+    /**
+     * Additional information encoded for {@link Invoke} nodes to allow method inlining without
+     * decoding the frame state and successors beforehand.
+     */
+    protected static class InvokeData {
+        public final Invoke invoke;
+        public final ResolvedJavaType contextType;
+        public final int invokeOrderId;
+        public final int callTargetOrderId;
+        public final int stateAfterOrderId;
+        public final int nextOrderId;
+
+        public final int nextNextOrderId;
+        public final int exceptionOrderId;
+        public final int exceptionStateOrderId;
+        public final int exceptionNextOrderId;
+
+        protected InvokeData(Invoke invoke, ResolvedJavaType contextType, int invokeOrderId, int callTargetOrderId, int stateAfterOrderId, int nextOrderId, int nextNextOrderId, int exceptionOrderId,
+                        int exceptionStateOrderId, int exceptionNextOrderId) {
+            this.invoke = invoke;
+            this.contextType = contextType;
+            this.invokeOrderId = invokeOrderId;
+            this.callTargetOrderId = callTargetOrderId;
+            this.stateAfterOrderId = stateAfterOrderId;
+            this.nextOrderId = nextOrderId;
+            this.nextNextOrderId = nextNextOrderId;
+            this.exceptionOrderId = exceptionOrderId;
+            this.exceptionStateOrderId = exceptionStateOrderId;
+            this.exceptionNextOrderId = exceptionNextOrderId;
+        }
+    }
+
     public final void decode(StructuredGraph graph, EncodedGraph encodedGraph) {
-        MethodScope methodScope = new MethodScope(graph, null, encodedGraph, LoopExplosionKind.NONE);
-        decode(methodScope);
+        MethodScope methodScope = new MethodScope(graph, encodedGraph, LoopExplosionKind.NONE);
+        decode(methodScope, null);
         cleanupGraph(methodScope);
         methodScope.graph.verify();
     }
 
-    protected final void decode(MethodScope methodScope) {
+    protected final void decode(MethodScope methodScope, FixedWithNextNode startNode) {
         LoopScope loopScope = new LoopScope(methodScope);
-        if (methodScope.isInlinedMethod()) {
-            methodScope.startNode = methodScope.graph.add(new StartNode());
-            methodScope.startNode.setNext(makeStubNode(methodScope, loopScope, GraphEncoder.FIRST_NODE_ORDER_ID));
+        FixedNode firstNode;
+        if (startNode != null) {
+            firstNode = makeStubNode(methodScope, loopScope, GraphEncoder.FIRST_NODE_ORDER_ID);
+            startNode.setNext(firstNode);
             loopScope.nodesToProcess.set(GraphEncoder.FIRST_NODE_ORDER_ID);
         } else {
-            methodScope.startNode = methodScope.graph.start();
-            registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, methodScope.startNode, false, false);
+            firstNode = methodScope.graph.start();
+            registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, firstNode, false, false);
             loopScope.nodesToProcess.set(GraphEncoder.START_NODE_ORDER_ID);
         }
 
@@ -262,7 +286,7 @@
         if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) {
             cleanupGraph(methodScope);
             Debug.dump(methodScope.graph, "Before loop detection");
-            detectLoops(methodScope.graph, methodScope.startNode);
+            detectLoops(methodScope.graph, firstNode);
         }
     }
 
@@ -275,6 +299,18 @@
             return loopScope;
         }
 
+        if ((node instanceof MergeNode || (node instanceof LoopBeginNode && (methodScope.loopExplosion == LoopExplosionKind.FULL_UNROLL || methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE))) &&
+                        ((AbstractMergeNode) node).forwardEndCount() == 1) {
+            AbstractMergeNode merge = (AbstractMergeNode) node;
+            EndNode singleEnd = merge.forwardEndAt(0);
+            FixedNode next = makeStubNode(methodScope, loopScope, nodeOrderId + GraphEncoder.BEGIN_NEXT_ORDER_ID_OFFSET);
+            singleEnd.replaceAtPredecessor(next);
+
+            merge.safeDelete();
+            singleEnd.safeDelete();
+            return loopScope;
+        }
+
         LoopScope successorAddScope = loopScope;
         boolean updatePredecessors = true;
         if (node instanceof LoopExitNode) {
@@ -282,7 +318,7 @@
             updatePredecessors = methodScope.loopExplosion == LoopExplosionKind.NONE;
         }
 
-        methodScope.reader.setByteIndex(methodScope.nodeStartOffsets[nodeOrderId]);
+        methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
         int typeId = methodScope.reader.getUVInt();
         assert node.getNodeClass() == methodScope.encodedGraph.getNodeClasses()[typeId];
         readProperties(methodScope, node);
@@ -299,7 +335,7 @@
             if (methodScope.loopExplosion != LoopExplosionKind.NONE) {
                 handleLoopExplosionProxyNodes(methodScope, loopScope, (LoopExitNode) node, nodeOrderId);
             } else {
-                handleProxyNodes(methodScope, loopScope);
+                handleProxyNodes(methodScope, loopScope, (LoopExitNode) node);
             }
 
         } else if (node instanceof AbstractEndNode) {
@@ -311,7 +347,7 @@
                 phiNodeScope = loopScope.nextIterations.getLast();
             }
 
-            int mergeOrderId = methodScope.reader.getUVInt();
+            int mergeOrderId = readOrderId(methodScope);
             AbstractMergeNode merge = (AbstractMergeNode) lookupNode(phiNodeScope, mergeOrderId);
             if (merge == null) {
                 merge = (AbstractMergeNode) makeStubNode(methodScope, phiNodeScope, mergeOrderId);
@@ -332,7 +368,8 @@
             handlePhiFunctions(methodScope, phiInputScope, phiNodeScope, (AbstractEndNode) node, merge);
 
         } else if (node instanceof Invoke) {
-            simplifyInvoke(methodScope, loopScope, nodeOrderId, (Invoke) node);
+            InvokeData invokeData = readInvokeData(methodScope, nodeOrderId, (Invoke) node);
+            handleInvoke(methodScope, loopScope, invokeData);
 
         } else if (node instanceof ReturnNode) {
             methodScope.returnNodes.add((ReturnNode) node);
@@ -347,15 +384,45 @@
         return resultScope;
     }
 
+    private InvokeData readInvokeData(MethodScope methodScope, int invokeOrderId, Invoke invoke) {
+        ResolvedJavaType contextType = (ResolvedJavaType) readObject(methodScope);
+        int callTargetOrderId = readOrderId(methodScope);
+        int stateAfterOrderId = readOrderId(methodScope);
+        int nextOrderId = readOrderId(methodScope);
+
+        if (invoke instanceof InvokeWithExceptionNode) {
+            int nextNextOrderId = readOrderId(methodScope);
+            int exceptionOrderId = readOrderId(methodScope);
+            int exceptionStateOrderId = readOrderId(methodScope);
+            int exceptionNextOrderId = readOrderId(methodScope);
+            return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, nextNextOrderId, exceptionOrderId, exceptionStateOrderId, exceptionNextOrderId);
+        } else {
+            return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, -1, -1, -1, -1);
+        }
+    }
+
     /**
-     * Hook for subclasses.
-     *
-     * @param methodScope The current method.
-     * @param loopScope The current loop.
-     * @param invokeOrderId The orderId of the method invocation node.
-     * @param invoke The invocation node.
+     * {@link Invoke} nodes do not have the {@link CallTargetNode}, {@link FrameState}, and
+     * successors encoded. Instead, this information is provided separately to allow method inlining
+     * without decoding and adding them to the graph upfront. For non-inlined methods, this method
+     * restores the normal state. Subclasses can override it to perform method inlining.
      */
-    protected void simplifyInvoke(MethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
+    protected void handleInvoke(MethodScope methodScope, LoopScope loopScope, InvokeData invokeData) {
+        assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke";
+        CallTargetNode callTarget = (CallTargetNode) ensureNodeCreated(methodScope, loopScope, invokeData.callTargetOrderId);
+        if (invokeData.invoke instanceof InvokeWithExceptionNode) {
+            ((InvokeWithExceptionNode) invokeData.invoke).setCallTarget(callTarget);
+        } else {
+            ((InvokeNode) invokeData.invoke).setCallTarget(callTarget);
+        }
+
+        assert invokeData.invoke.stateAfter() == null && invokeData.invoke.stateDuring() == null : "FrameState edges are ignored during decoding of Invoke";
+        invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
+
+        invokeData.invoke.setNext(makeStubNode(methodScope, loopScope, invokeData.nextOrderId));
+        if (invokeData.invoke instanceof InvokeWithExceptionNode) {
+            ((InvokeWithExceptionNode) invokeData.invoke).setExceptionEdge((AbstractBeginNode) makeStubNode(methodScope, loopScope, invokeData.exceptionOrderId));
+        }
     }
 
     protected void handleLoopExplosionBegin(MethodScope methodScope, LoopScope loopScope, LoopBeginNode loopBegin) {
@@ -432,10 +499,14 @@
     protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
     }
 
-    protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope) {
+    protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit) {
+        assert loopExit.stateAfter() == null;
+        int stateAfterOrderId = readOrderId(methodScope);
+        loopExit.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, stateAfterOrderId));
+
         int numProxies = methodScope.reader.getUVInt();
         for (int i = 0; i < numProxies; i++) {
-            int proxyOrderId = methodScope.reader.getUVInt();
+            int proxyOrderId = readOrderId(methodScope);
             ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId);
             /*
              * The ProxyNode transports a value from the loop to the outer scope. We therefore
@@ -446,9 +517,11 @@
     }
 
     protected void handleLoopExplosionProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit, int loopExitOrderId) {
+        assert loopExit.stateAfter() == null;
+        int stateAfterOrderId = readOrderId(methodScope);
+
         BeginNode begin = methodScope.graph.add(new BeginNode());
 
-        FrameState loopExitState = loopExit.stateAfter();
         FixedNode loopExitSuccessor = loopExit.next();
         loopExit.replaceAtPredecessor(begin);
 
@@ -457,33 +530,43 @@
          * iterations now take the same loop exit, so we have to introduce a new merge node to
          * handle the merge.
          */
-        MergeNode merge;
-        if (lookupNode(loopScope.outer, loopExitOrderId) == null) {
+        MergeNode merge = null;
+        Node existingExit = lookupNode(loopScope.outer, loopExitOrderId);
+        if (existingExit == null) {
+            /* First loop iteration that exits. No merge necessary yet. */
+            registerNode(loopScope.outer, loopExitOrderId, begin, false, false);
+            begin.setNext(loopExitSuccessor);
+
+        } else if (existingExit instanceof BeginNode) {
+            /* Second loop iteration that exits. Create the merge. */
             merge = methodScope.graph.add(new MergeNode());
-            registerNode(loopScope.outer, loopExitOrderId, merge, false, false);
+            registerNode(loopScope.outer, loopExitOrderId, merge, true, false);
+            /* Add the first iteration. */
+            EndNode firstEnd = methodScope.graph.add(new EndNode());
+            ((BeginNode) existingExit).setNext(firstEnd);
+            merge.addForwardEnd(firstEnd);
             merge.setNext(loopExitSuccessor);
+
         } else {
-            merge = (MergeNode) loopScope.outer.createdNodes[loopExitOrderId];
+            /* Subsequent loop iteration. Merge already created. */
+            merge = (MergeNode) existingExit;
         }
 
-        FrameState oldStateAfter = merge.stateAfter();
-        merge.setStateAfter(loopExitState);
-        if (oldStateAfter != null) {
-            oldStateAfter.safeDelete();
+        if (merge != null) {
+            EndNode end = methodScope.graph.add(new EndNode());
+            begin.setNext(end);
+            merge.addForwardEnd(end);
         }
 
-        EndNode end = methodScope.graph.add(new EndNode());
-        begin.setNext(end);
-        merge.addForwardEnd(end);
-
         /*
          * Possibly create phi nodes for the original proxy nodes that flow out of the loop. Note
          * that we definitely do not need a proxy node itself anymore, since the loop was exploded
          * and is no longer present.
          */
         int numProxies = methodScope.reader.getUVInt();
+        boolean phiCreated = false;
         for (int i = 0; i < numProxies; i++) {
-            int proxyOrderId = methodScope.reader.getUVInt();
+            int proxyOrderId = readOrderId(methodScope);
             ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId);
             ValueNode phiInput = proxy.value();
             ValueNode replacement;
@@ -508,6 +591,7 @@
                 phi.addInput(phiInput);
                 registerNode(loopScope.outer, proxyOrderId, phi, true, false);
                 replacement = phi;
+                phiCreated = true;
 
             } else {
                 /* Phi node has been created before, so just add the new input. */
@@ -519,9 +603,22 @@
             methodScope.graph.replaceFloating(proxy, replacement);
         }
 
+        if (merge != null && (merge.stateAfter() == null || phiCreated)) {
+            FrameState oldStateAfter = merge.stateAfter();
+            registerNode(loopScope.outer, stateAfterOrderId, null, true, true);
+            merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope.outer, stateAfterOrderId));
+            if (oldStateAfter != null) {
+                oldStateAfter.safeDelete();
+            }
+        }
+
         loopExit.safeDelete();
         assert loopExitSuccessor.predecessor() == null;
-        merge.getNodeClass().getSuccessorEdges().update(merge, null, loopExitSuccessor);
+        if (merge != null) {
+            merge.getNodeClass().getSuccessorEdges().update(merge, null, loopExitSuccessor);
+        } else {
+            begin.getNodeClass().getSuccessorEdges().update(begin, null, loopExitSuccessor);
+        }
     }
 
     protected void handlePhiFunctions(MethodScope methodScope, LoopScope phiInputScope, LoopScope phiNodeScope, AbstractEndNode end, AbstractMergeNode merge) {
@@ -555,8 +652,8 @@
         boolean lazyPhi = !(merge instanceof LoopBeginNode) || methodScope.loopExplosion != LoopExplosionKind.NONE;
         int numPhis = methodScope.reader.getUVInt();
         for (int i = 0; i < numPhis; i++) {
-            int phiInputOrderId = methodScope.reader.getUVInt();
-            int phiNodeOrderId = methodScope.reader.getUVInt();
+            int phiInputOrderId = readOrderId(methodScope);
+            int phiNodeOrderId = readOrderId(methodScope);
 
             ValueNode phiInput = (ValueNode) ensureNodeCreated(methodScope, phiInputScope, phiInputOrderId);
 
@@ -588,7 +685,7 @@
     }
 
     protected Node instantiateNode(MethodScope methodScope, int nodeOrderId) {
-        methodScope.reader.setByteIndex(methodScope.nodeStartOffsets[nodeOrderId]);
+        methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
         NodeClass<?> nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()];
         return nodeClass.allocateInstance();
     }
@@ -600,8 +697,7 @@
                 long primitive = methodScope.reader.getSV();
                 fields.setRawPrimitive(node, pos, primitive);
             } else {
-                int objectId = methodScope.reader.getUVInt();
-                Object value = methodScope.encodedGraph.getObjects()[objectId];
+                Object value = readObject(methodScope);
                 fields.set(node, pos, value);
             }
         }
@@ -619,7 +715,7 @@
             if (skipEdge(node, edges, index, true, true)) {
                 continue;
             }
-            int orderId = methodScope.reader.getUVInt();
+            int orderId = readOrderId(methodScope);
             Node value = ensureNodeCreated(methodScope, loopScope, orderId);
             Edges.initializeNode(node, edges.getOffsets(), index, value);
             if (updateUsages && value != null && !value.isDeleted()) {
@@ -636,7 +732,7 @@
                 NodeList<Node> nodeList = new NodeInputList<>(node, size);
                 Edges.initializeList(node, edges.getOffsets(), index, nodeList);
                 for (int idx = 0; idx < size; idx++) {
-                    int orderId = methodScope.reader.getUVInt();
+                    int orderId = readOrderId(methodScope);
                     Node value = ensureNodeCreated(methodScope, loopScope, orderId);
                     nodeList.initialize(idx, value);
                     if (updateUsages && value != null && !value.isDeleted()) {
@@ -656,17 +752,7 @@
             return node;
         }
 
-        long readerByteIndex = methodScope.reader.getByteIndex();
-        node = instantiateNode(methodScope, nodeOrderId);
-        assert !(node instanceof FixedNode);
-
-        /* Read the properties of the node. */
-        readProperties(methodScope, node);
-        /* There must not be any successors to read, since it is a non-fixed node. */
-        assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0;
-        /* Read the inputs of the node, possibly creating them recursively. */
-        makeInputNodes(methodScope, loopScope, node, false);
-        methodScope.reader.setByteIndex(readerByteIndex);
+        node = decodeFloatingNode(methodScope, loopScope, nodeOrderId);
 
         if (node instanceof ProxyNode || node instanceof PhiNode) {
             /*
@@ -688,6 +774,24 @@
     }
 
     /**
+     * Decodes a non-fixed node, but does not do any post-processing and does not register it.
+     */
+    protected Node decodeFloatingNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) {
+        long readerByteIndex = methodScope.reader.getByteIndex();
+        Node node = instantiateNode(methodScope, nodeOrderId);
+        assert !(node instanceof FixedNode);
+
+        /* Read the properties of the node. */
+        readProperties(methodScope, node);
+        /* There must not be any successors to read, since it is a non-fixed node. */
+        assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0;
+        /* Read the inputs of the node, possibly creating them recursively. */
+        makeInputNodes(methodScope, loopScope, node, false);
+        methodScope.reader.setByteIndex(readerByteIndex);
+        return node;
+    }
+
+    /**
      * Hook for subclasses to process a non-fixed node before it is added to the graph.
      *
      * @param methodScope The current method.
@@ -722,7 +826,7 @@
             if (skipEdge(node, edges, index, true, true)) {
                 continue;
             }
-            int orderId = methodScope.reader.getUVInt();
+            int orderId = readOrderId(methodScope);
             Node value = makeStubNode(methodScope, loopScope, orderId);
             Edges.initializeNode(node, edges.getOffsets(), index, value);
             if (updatePredecessors && value != null) {
@@ -738,7 +842,7 @@
                 NodeList<Node> nodeList = new NodeSuccessorList<>(node, size);
                 Edges.initializeList(node, edges.getOffsets(), index, nodeList);
                 for (int idx = 0; idx < size; idx++) {
-                    int orderId = methodScope.reader.getUVInt();
+                    int orderId = readOrderId(methodScope);
                     Node value = makeStubNode(methodScope, loopScope, orderId);
                     nodeList.initialize(idx, value);
                     if (updatePredecessors && value != null) {
@@ -792,6 +896,26 @@
             assert index == edges.getCount() - 1 : "MergeNode has one variable size input (the ends)";
             assert Edges.getNodeList(node, edges.getOffsets(), index) != null : "Input list must have been already created";
             return true;
+
+        } else if (node instanceof LoopExitNode && edges.type() == Edges.Type.Inputs && edges.getType(index) == FrameState.class) {
+            /* The stateAfter of the loop exit is filled manually. */
+            return true;
+
+        } else if (node instanceof Invoke) {
+            assert node instanceof InvokeNode || node instanceof InvokeWithExceptionNode : "The only two Invoke node classes";
+            assert direct : "Invoke and InvokeWithException only have direct successor and input edges";
+            if (edges.type() == Edges.Type.Successors) {
+                assert edges.getCount() == (node instanceof InvokeWithExceptionNode ? 2 : 1) : "InvokeNode has one successor (next); InvokeWithExceptionNode has two successors (next, exceptionEdge)";
+                return true;
+            } else {
+                assert edges.type() == Edges.Type.Inputs;
+                if (edges.getType(index) == CallTargetNode.class) {
+                    return true;
+                } else if (edges.getType(index) == FrameState.class) {
+                    assert edges.get(node, index) == null || edges.get(node, index) == ((Invoke) node).stateAfter() : "Only stateAfter can be a FrameState during encoding";
+                    return true;
+                }
+            }
         }
         return false;
     }
@@ -807,6 +931,14 @@
         loopScope.createdNodes[nodeOrderId] = node;
     }
 
+    protected int readOrderId(MethodScope methodScope) {
+        return methodScope.reader.getUVInt();
+    }
+
+    protected Object readObject(MethodScope methodScope) {
+        return methodScope.encodedGraph.getObjects()[methodScope.reader.getUVInt()];
+    }
+
     /*
      * The following methods are a literal copy from GraphBuilderPhase.
      */
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java	Tue Apr 14 14:01:18 2015 +0200
@@ -29,6 +29,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
+import com.oracle.graal.nodes.java.*;
 
 /**
  * Encodes a {@link StructuredGraph} to a compact byte[] array. All nodes of the graph and edges
@@ -100,6 +101,12 @@
     public static final int FIRST_NODE_ORDER_ID = 2;
 
     /**
+     * The known offset between the orderId of a {@link AbstractBeginNode} and its
+     * {@link AbstractBeginNode#next() successor}.
+     */
+    protected static final int BEGIN_NEXT_ORDER_ID_OFFSET = 1;
+
+    /**
      * Collects all non-primitive data referenced from nodes. The encoding uses an index into an
      * array for decoding. Because of the variable-length encoding, it is beneficial that frequently
      * used objects have the small indices.
@@ -148,6 +155,9 @@
                     objects.addObject(nodeClass.getData().get(node, i));
                 }
             }
+            if (node instanceof Invoke) {
+                objects.addObject(((Invoke) node).getContextType());
+            }
         }
     }
 
@@ -182,7 +192,10 @@
         long[] nodeStartOffsets = new long[nodeCount];
         for (Map.Entry<Node, Integer> entry : nodeOrder.orderIds.entries()) {
             Node node = entry.getKey();
-            nodeStartOffsets[entry.getValue()] = writer.getBytesWritten();
+            Integer orderId = entry.getValue();
+
+            assert !(node instanceof AbstractBeginNode) || nodeOrder.orderIds.get(((AbstractBeginNode) node).next()) == orderId + BEGIN_NEXT_ORDER_ID_OFFSET;
+            nodeStartOffsets[orderId] = writer.getBytesWritten();
 
             /* Write out the type, properties, and edges. */
             NodeClass<?> nodeClass = node.getNodeClass();
@@ -213,11 +226,30 @@
 
             } else if (node instanceof LoopExitNode) {
                 LoopExitNode exit = (LoopExitNode) node;
+                writeOrderId(exit.stateAfter(), nodeOrder);
                 /* Write all proxy nodes of the LoopExitNode. */
                 writer.putUV(exit.proxies().count());
                 for (ProxyNode proxy : exit.proxies()) {
                     writeOrderId(proxy, nodeOrder);
                 }
+
+            } else if (node instanceof Invoke) {
+                Invoke invoke = (Invoke) node;
+                assert invoke.stateDuring() == null : "stateDuring is not used in high-level graphs";
+
+                writeObjectId(invoke.getContextType());
+                writeOrderId(invoke.callTarget(), nodeOrder);
+                writeOrderId(invoke.stateAfter(), nodeOrder);
+                writeOrderId(invoke.next(), nodeOrder);
+                if (invoke instanceof InvokeWithExceptionNode) {
+                    InvokeWithExceptionNode invokeWithExcpetion = (InvokeWithExceptionNode) invoke;
+                    ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExcpetion.exceptionEdge();
+
+                    writeOrderId(invokeWithExcpetion.next().next(), nodeOrder);
+                    writeOrderId(invokeWithExcpetion.exceptionEdge(), nodeOrder);
+                    writeOrderId(exceptionEdge.stateAfter(), nodeOrder);
+                    writeOrderId(exceptionEdge.next(), nodeOrder);
+                }
             }
         }
 
@@ -252,13 +284,8 @@
             FixedNode current = graph.start();
             do {
                 add(current);
-                if (current instanceof InvokeWithExceptionNode) {
-                    /*
-                     * Special handling for invokes: the orderID of the invocation, the regular
-                     * successor, and the exception edge must be consecutive.
-                     */
-                    add(((InvokeWithExceptionNode) current).next());
-                    add(((InvokeWithExceptionNode) current).exceptionEdge());
+                if (current instanceof AbstractBeginNode) {
+                    add(((AbstractBeginNode) current).next());
                 }
 
                 if (current instanceof FixedWithNextNode) {
@@ -308,7 +335,7 @@
                 writer.putSV(primitive);
             } else {
                 Object property = fields.get(node, idx);
-                writer.putUV(objects.getIndex(property));
+                writeObjectId(property);
             }
         }
     }
@@ -343,6 +370,10 @@
         writer.putUV(node == null ? NULL_ORDER_ID : nodeOrder.orderIds.get(node));
     }
 
+    protected void writeObjectId(Object object) {
+        writer.putUV(objects.getIndex(object));
+    }
+
     /**
      * Verification code that checks that the decoding of an encode graph is the same as the
      * original graph.
@@ -365,7 +396,7 @@
 
         pushToWorklist(expectedGraph.start(), actualGraph.start(), nodeMapping, workList);
         while (!workList.isEmpty()) {
-            Pair<Node, Node> pair = workList.pop();
+            Pair<Node, Node> pair = workList.removeFirst();
             Node expectedNode = pair.first;
             Node actualNode = pair.second;
             assert expectedNode.getClass() == actualNode.getClass();
@@ -377,7 +408,7 @@
                 /* The order of the ends can be different, so ignore them. */
                 verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, true);
             } else if (expectedNode instanceof PhiNode) {
-                /* The order of phi inputs can be different, so they are checked manually below. */
+                verifyPhi((PhiNode) expectedNode, (PhiNode) actualNode, nodeMapping, workList);
             } else {
                 verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, false);
             }
@@ -413,20 +444,34 @@
         return true;
     }
 
+    protected static void verifyPhi(PhiNode expectedPhi, PhiNode actualPhi, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) {
+        AbstractMergeNode expectedMergeNode = expectedPhi.merge();
+        AbstractMergeNode actualMergeNode = actualPhi.merge();
+        assert actualMergeNode == nodeMapping.get(expectedMergeNode);
+
+        for (EndNode expectedEndNode : expectedMergeNode.ends) {
+            EndNode actualEndNode = (EndNode) nodeMapping.get(expectedEndNode);
+            if (actualEndNode != null) {
+                ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode);
+                ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode);
+                verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false);
+            }
+        }
+    }
+
     protected static void verifyPhis(AbstractEndNode expectedEndNode, AbstractEndNode actualEndNode, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) {
         AbstractMergeNode expectedMergeNode = expectedEndNode.merge();
         AbstractMergeNode actualMergeNode = (AbstractMergeNode) nodeMapping.get(expectedMergeNode);
-
-        Iterator<PhiNode> actualPhis = actualMergeNode.phis().iterator();
-        for (PhiNode expectedPhi : expectedMergeNode.phis()) {
-            PhiNode actualPhi = actualPhis.next();
-            verifyNodeEqual(expectedPhi, actualPhi, nodeMapping, workList, false);
+        assert actualMergeNode != null;
 
-            ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode);
-            ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode);
-            verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false);
+        for (PhiNode expectedPhi : expectedMergeNode.phis()) {
+            PhiNode actualPhi = (PhiNode) nodeMapping.get(expectedPhi);
+            if (actualPhi != null) {
+                ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode);
+                ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode);
+                verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false);
+            }
         }
-        assert !actualPhis.hasNext();
     }
 
     private static void verifyNodesEqual(NodeIterable<Node> expectedNodes, NodeIterable<Node> actualNodes, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList, boolean ignoreEndNode) {
@@ -453,7 +498,12 @@
 
     protected static void pushToWorklist(Node expectedNode, Node actualNode, NodeMap<Node> nodeMapping, Deque<Pair<Node, Node>> workList) {
         nodeMapping.set(expectedNode, actualNode);
-        workList.push(new Pair<>(expectedNode, actualNode));
+        if (expectedNode instanceof AbstractEndNode) {
+            /* To ensure phi nodes have been added, we handle everything before block ends. */
+            workList.addLast(new Pair<>(expectedNode, actualNode));
+        } else {
+            workList.addFirst(new Pair<>(expectedNode, actualNode));
+        }
     }
 }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -107,7 +107,7 @@
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
-        if (stamp() == StampFactory.illegal(object.getKind())) {
+        if (stamp().isIllegal()) {
             // The guard always fails
             return new DeoptimizeNode(action, reason);
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -140,6 +140,15 @@
         return super.verify();
     }
 
+    public void eliminateNegation() {
+        AbstractBeginNode oldTrueSuccessor = trueSuccessor;
+        AbstractBeginNode oldFalseSuccessor = falseSuccessor;
+        trueSuccessor = oldFalseSuccessor;
+        falseSuccessor = oldTrueSuccessor;
+        trueSuccessorProbability = 1 - trueSuccessorProbability;
+        setCondition(((LogicNegationNode) condition).getValue());
+    }
+
     @Override
     public void simplify(SimplifierTool tool) {
         if (trueSuccessor().next() instanceof DeoptimizeNode) {
@@ -155,15 +164,7 @@
         }
 
         if (condition() instanceof LogicNegationNode) {
-            AbstractBeginNode trueSucc = trueSuccessor();
-            AbstractBeginNode falseSucc = falseSuccessor();
-            setTrueSuccessor(null);
-            setFalseSuccessor(null);
-            LogicNegationNode negation = (LogicNegationNode) condition();
-            IfNode newIfNode = graph().add(new IfNode(negation.getValue(), falseSucc, trueSucc, 1 - trueSuccessorProbability));
-            predecessor().replaceFirstSuccessor(this, newIfNode);
-            GraphUtil.killWithUnusedFloatingInputs(this);
-            return;
+            eliminateNegation();
         }
         if (condition() instanceof LogicConstantNode) {
             LogicConstantNode c = (LogicConstantNode) condition();
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -64,6 +64,11 @@
         return callTarget;
     }
 
+    void setCallTarget(CallTargetNode callTarget) {
+        updateUsages(this.callTarget, callTarget);
+        this.callTarget = callTarget;
+    }
+
     @Override
     public boolean isPolymorphic() {
         return polymorphic;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -81,6 +81,11 @@
         return callTarget;
     }
 
+    void setCallTarget(CallTargetNode callTarget) {
+        updateUsages(this.callTarget, callTarget);
+        this.callTarget = callTarget;
+    }
+
     public MethodCallTargetNode methodCallTarget() {
         return (MethodCallTargetNode) callTarget;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.nodes.calc;
 
+import java.util.*;
+
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.type.*;
@@ -43,8 +45,16 @@
 
     protected final FloatConvert op;
 
+    private static final EnumMap<FloatConvert, SerializableUnaryFunction<FloatConvertOp>> getOps;
+    static {
+        getOps = new EnumMap<>(FloatConvert.class);
+        for (FloatConvert op : FloatConvert.values()) {
+            getOps.put(op, table -> table.getFloatConvert(op));
+        }
+    }
+
     public FloatConvertNode(FloatConvert op, ValueNode input) {
-        super(TYPE, table -> table.getFloatConvert(op), input);
+        super(TYPE, getOps.get(op), input);
         this.op = op;
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -101,7 +101,7 @@
              * This really represent a misuse of LoadMethod since we're loading from a class which
              * isn't known to implement the original method but for now at least fold it away.
              */
-            return ConstantNode.forConstant(JavaConstant.NULL_POINTER, null);
+            return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, null);
         } else {
             return ConstantNode.forConstant(stamp(), newMethod.getEncoding(), tool.getMetaAccess());
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -116,7 +116,7 @@
         Stamp newStamp = StampFactory.declaredTrusted(type).improveWith(object().stamp());
         ValueNode condition;
         ValueNode theValue = object;
-        if (newStamp instanceof IllegalStamp) {
+        if (newStamp.isIllegal()) {
             // This is a check cast that will always fail
             condition = LogicConstantNode.contradiction(graph());
             newStamp = StampFactory.declaredTrusted(type);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Tue Apr 14 14:01:18 2015 +0200
@@ -150,11 +150,11 @@
 
     public static void killWithUnusedFloatingInputs(Node node) {
         node.safeDelete();
-        for (Node in : node.inputs()) {
+        node.acceptInputs((n, in) -> {
             if (in.isAlive() && in.hasNoUsages() && !(in instanceof FixedNode)) {
                 killWithUnusedFloatingInputs(in);
             }
-        }
+        });
     }
 
     public static void removeFixedWithUnusedInputs(FixedWithNextNode fixed) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Tue Apr 14 14:01:18 2015 +0200
@@ -306,7 +306,7 @@
             if (callerLockDepth != 0) {
                 for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.TYPE)) {
                     MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original);
-                    processMonitorId(invoke, monitor);
+                    processMonitorId(invoke.stateAfter(), monitor);
                 }
             }
         } else {
@@ -432,8 +432,7 @@
         return pos;
     }
 
-    public static void processMonitorId(Invoke invoke, MonitorIdNode monitorIdNode) {
-        FrameState stateAfter = invoke.stateAfter();
+    public static void processMonitorId(FrameState stateAfter, MonitorIdNode monitorIdNode) {
         if (stateAfter != null) {
             int callerLockDepth = stateAfter.nestedLockDepth();
             monitorIdNode.setLockDepth(monitorIdNode.getLockDepth() + callerLockDepth);
@@ -579,26 +578,43 @@
     }
 
     public static ValueNode mergeReturns(AbstractMergeNode merge, List<? extends ReturnNode> returnNodes, List<Node> canonicalizedNodes) {
+        ValueNode singleReturnValue = null;
         PhiNode returnValuePhi = null;
-
         for (ReturnNode returnNode : returnNodes) {
-            // create and wire up a new EndNode
-            EndNode endNode = merge.graph().add(new EndNode());
-            merge.addForwardEnd(endNode);
+            if (returnNode.result() != null) {
+                if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == returnNode.result())) {
+                    /* Only one return value, so no need yet for a phi node. */
+                    singleReturnValue = returnNode.result();
 
-            if (returnNode.result() != null) {
-                if (returnValuePhi == null) {
+                } else if (returnValuePhi == null) {
+                    /* Found a second return value, so create phi node. */
                     returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge));
                     if (canonicalizedNodes != null) {
                         canonicalizedNodes.add(returnValuePhi);
                     }
+                    for (int i = 0; i < merge.forwardEndCount(); i++) {
+                        returnValuePhi.addInput(singleReturnValue);
+                    }
+                    returnValuePhi.addInput(returnNode.result());
+
+                } else {
+                    /* Multiple return values, just add to existing phi node. */
+                    returnValuePhi.addInput(returnNode.result());
                 }
-                returnValuePhi.addInput(returnNode.result());
             }
+
+            // create and wire up a new EndNode
+            EndNode endNode = merge.graph().add(new EndNode());
+            merge.addForwardEnd(endNode);
             returnNode.replaceAndDelete(endNode);
-
         }
-        return returnValuePhi;
+
+        if (returnValuePhi != null) {
+            assert returnValuePhi.verify();
+            return returnValuePhi;
+        } else {
+            return singleReturnValue;
+        }
     }
 
     private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map<Node, Node> duplicates) {
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/InferStamps.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/InferStamps.java	Tue Apr 14 14:01:18 2015 +0200
@@ -88,7 +88,7 @@
         for (Node n : graph.getNodes()) {
             if (n instanceof ValuePhiNode) {
                 ValueNode node = (ValueNode) n;
-                assert !(node.stamp() instanceof IllegalStamp) : "Stamp is illegal after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion).";
+                assert node.stamp().isLegal() : "Stamp is illegal after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion).";
             }
         }
         return true;
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64GraphBuilderPlugins.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64GraphBuilderPlugins.java	Tue Apr 14 14:01:18 2015 +0200
@@ -29,10 +29,10 @@
 import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
-import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.replacements.*;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Tue Apr 14 14:01:18 2015 +0200
@@ -30,7 +30,6 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
-import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext;
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java	Tue Apr 14 14:01:18 2015 +0200
@@ -27,7 +27,7 @@
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Tue Apr 14 14:01:18 2015 +0200
@@ -47,8 +47,6 @@
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement;
-import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext;
-import com.oracle.graal.java.AbstractBytecodeParser.ReplacementContext;
 import com.oracle.graal.java.*;
 import com.oracle.graal.java.GraphBuilderPhase.Instance;
 import com.oracle.graal.nodes.*;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Tue Apr 14 14:01:18 2015 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.replacements;
 
 import static com.oracle.graal.api.code.MemoryBarriers.*;
-import static com.oracle.graal.compiler.common.GraalOptions.*;
 
 import java.lang.reflect.*;
 import java.util.*;
@@ -38,8 +37,8 @@
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graphbuilderconf.*;
-import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver;
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.debug.*;
@@ -97,23 +96,17 @@
     }
 
     private static void registerStringPlugins(InvocationPlugins plugins) {
-        /*
-         * AMD64's String.equals substitution needs about 8 registers so we disable it if there is
-         * some artificial register pressure.
-         */
-        if (RegisterPressure.getValue() == null) {
-            Registration r = new Registration(plugins, String.class);
-            r.registerMethodSubstitution(StringSubstitutions.class, "equals", Receiver.class, Object.class);
+        Registration r = new Registration(plugins, String.class);
+        r.registerMethodSubstitution(StringSubstitutions.class, "equals", Receiver.class, Object.class);
 
-            r = new Registration(plugins, StringSubstitutions.class);
-            r.register1("getValue", String.class, new InvocationPlugin() {
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                    ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD);
-                    b.addPush(new LoadFieldNode(value, field));
-                    return true;
-                }
-            });
-        }
+        r = new Registration(plugins, StringSubstitutions.class);
+        r.register1("getValue", String.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
+                ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD);
+                b.addPush(new LoadFieldNode(value, field));
+                return true;
+            }
+        });
     }
 
     private static void registerArraysPlugins(InvocationPlugins plugins) {
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -67,7 +67,7 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument");
+        Instrument instrument = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument");
         probe.attach(instrument);
         assertPartialEvalEquals("constant42", root);
     }
@@ -79,7 +79,7 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument");
+        Instrument instrument = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument");
         probe.attach(instrument);
         assertPartialEvalEquals("constant42", root);
     }
@@ -91,7 +91,7 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument");
+        Instrument instrument = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument");
         probe.attach(instrument);
         instrument.dispose();
         assertPartialEvalEquals("constant42", root);
@@ -104,7 +104,7 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument");
+        Instrument instrument = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument");
         probe.attach(instrument);
         instrument.dispose();
         assertPartialEvalEquals("constant42", root);
@@ -117,9 +117,9 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument1 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 1");
+        Instrument instrument1 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 1");
         probe.attach(instrument1);
-        Instrument instrument2 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 2");
+        Instrument instrument2 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 2");
         probe.attach(instrument2);
         assertPartialEvalEquals("constant42", root);
     }
@@ -131,9 +131,9 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument1 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 1");
+        Instrument instrument1 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 1");
         probe.attach(instrument1);
-        Instrument instrument2 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 2");
+        Instrument instrument2 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 2");
         probe.attach(instrument2);
         assertPartialEvalEquals("constant42", root);
     }
@@ -145,11 +145,11 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument1 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 1");
+        Instrument instrument1 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 1");
         probe.attach(instrument1);
-        Instrument instrument2 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 2");
+        Instrument instrument2 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 2");
         probe.attach(instrument2);
-        Instrument instrument3 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 3");
+        Instrument instrument3 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 3");
         probe.attach(instrument3);
         assertPartialEvalEquals("constant42", root);
     }
@@ -161,11 +161,11 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument1 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 1");
+        Instrument instrument1 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 1");
         probe.attach(instrument1);
-        Instrument instrument2 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 2");
+        Instrument instrument2 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 2");
         probe.attach(instrument2);
-        Instrument instrument3 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 3");
+        Instrument instrument3 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 3");
         probe.attach(instrument3);
         assertPartialEvalEquals("constant42", root);
     }
@@ -177,11 +177,11 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument1 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 1");
+        Instrument instrument1 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 1");
         probe.attach(instrument1);
-        Instrument instrument2 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 2");
+        Instrument instrument2 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 2");
         probe.attach(instrument2);
-        Instrument instrument3 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 3");
+        Instrument instrument3 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 3");
         probe.attach(instrument3);
         instrument2.dispose();
         assertPartialEvalEquals("constant42", root);
@@ -194,54 +194,56 @@
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        Instrument instrument1 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 1");
+        Instrument instrument1 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 1");
         probe.attach(instrument1);
-        Instrument instrument2 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 2");
+        Instrument instrument2 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 2");
         probe.attach(instrument2);
-        Instrument instrument3 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 3");
+        Instrument instrument3 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 3");
         probe.attach(instrument3);
         instrument2.dispose();
         assertPartialEvalEquals("constant42", root);
     }
 
     @Test
-    public void constantValueInertToolNodeInstrumentListener() {
+    public void constantValueInertSpliceInstrumentListener() {
         FrameDescriptor fd = new FrameDescriptor();
         AbstractTestNode result = new ConstantTestNode(42);
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        // A listener that could insert a "tool node" into the AST, but which never does.
-        Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() {
+        // A listener that could insert a SplicedNode into the AST, but which never does.
+        Instrument instrument = Instrument.create(new SpliceInstrumentListener() {
 
-            public ToolNode getToolNode(Probe p) {
+            public SplicedNode getSpliceNode(Probe p) {
                 return null;
             }
 
         }, null);
         probe.attach(instrument);
 
+        // It all gets compiled away
         assertPartialEvalEquals("constant42", root);
     }
 
     @Test
-    public void constantValueInertToolNode() {
+    public void constantValueInertSplicedNode() {
         FrameDescriptor fd = new FrameDescriptor();
         AbstractTestNode result = new ConstantTestNode(42);
         RootTestNode root = new RootTestNode(fd, "constantValue", result);
         root.adoptChildren();
         Probe probe = result.probe();
-        // A listener that inserts a "tool node" with empty methods into the AST.
-        Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() {
+        // A listener that inserts a SplicedNode with empty methods into the AST.
+        Instrument instrument = Instrument.create(new SpliceInstrumentListener() {
 
-            public ToolNode getToolNode(Probe p) {
-                return new ToolNode() {
+            public SplicedNode getSpliceNode(Probe p) {
+                return new SplicedNode() {
                 };
             }
 
         }, null);
         probe.attach(instrument);
 
+        // It all gets compiled away.
         assertPartialEvalEquals("constant42", root);
     }
 
@@ -280,7 +282,7 @@
             Assert.assertEquals(0, count[0]);           // Didn't count anything
 
             // Add a counting instrument; this changes the "Probe state" and should cause a deopt
-            final Instrument countingInstrument = Instrument.create(new DefaultInstrumentListener() {
+            final Instrument countingInstrument = Instrument.create(new DefaultSimpleInstrumentListener() {
 
                 @Override
                 public void enter(Probe p) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java	Tue Apr 14 14:01:18 2015 +0200
@@ -29,13 +29,14 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.debug.*;
+import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo;
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver;
 import com.oracle.graal.java.*;
+import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.extended.*;
@@ -63,8 +64,11 @@
     protected final StampProvider stampProvider;
 
     protected class PEMethodScope extends MethodScope {
+        /** The state of the caller method. Only non-null during method inlining. */
+        protected final PEMethodScope caller;
+        protected final LoopScope callerLoopScope;
         protected final ResolvedJavaMethod method;
-        protected final Invoke invoke;
+        protected final InvokeData invokeData;
         protected final int inliningDepth;
 
         protected final LoopExplosionPlugin loopExplosionPlugin;
@@ -73,15 +77,20 @@
         protected final ParameterPlugin parameterPlugin;
         protected final ValueNode[] arguments;
 
-        protected FrameState outerFrameState;
+        protected FrameState outerState;
+        protected FrameState exceptionState;
+        protected ExceptionPlaceholderNode exceptionPlaceholderNode;
         protected BytecodePosition bytecodePosition;
 
-        protected PEMethodScope(StructuredGraph targetGraph, MethodScope caller, EncodedGraph encodedGraph, ResolvedJavaMethod method, Invoke invoke, int inliningDepth,
-                        LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin, ValueNode[] arguments) {
-            super(targetGraph, caller, encodedGraph, loopExplosionKind(method, loopExplosionPlugin));
+        protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData,
+                        int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin,
+                        ValueNode[] arguments) {
+            super(targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin));
 
+            this.caller = caller;
+            this.callerLoopScope = callerLoopScope;
             this.method = method;
-            this.invoke = invoke;
+            this.invokeData = invokeData;
             this.inliningDepth = inliningDepth;
             this.loopExplosionPlugin = loopExplosionPlugin;
             this.invocationPlugins = invocationPlugins;
@@ -89,6 +98,10 @@
             this.parameterPlugin = parameterPlugin;
             this.arguments = arguments;
         }
+
+        public boolean isInlinedMethod() {
+            return caller != null;
+        }
     }
 
     protected class PECanonicalizerTool implements CanonicalizerTool {
@@ -114,7 +127,7 @@
     }
 
     protected class PENonAppendGraphBuilderContext implements GraphBuilderContext {
-        private final PEMethodScope methodScope;
+        protected final PEMethodScope methodScope;
 
         public PENonAppendGraphBuilderContext(PEMethodScope methodScope) {
             this.methodScope = methodScope;
@@ -212,13 +225,11 @@
     }
 
     protected class PEAppendGraphBuilderContext extends PENonAppendGraphBuilderContext {
-        protected final Invoke invoke;
         protected FixedWithNextNode lastInstr;
         protected ValueNode pushedNode;
 
-        public PEAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke, FixedWithNextNode lastInstr) {
+        public PEAppendGraphBuilderContext(PEMethodScope methodScope, FixedWithNextNode lastInstr) {
             super(methodScope);
-            this.invoke = invoke;
             this.lastInstr = lastInstr;
         }
 
@@ -232,7 +243,9 @@
 
         @Override
         public FrameState createStateAfter() {
-            return invoke.stateAfter().duplicate();
+            Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
+            getGraph().add(stateAfter);
+            return (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter);
         }
 
         @Override
@@ -274,6 +287,15 @@
         }
     }
 
+    @NodeInfo
+    static class ExceptionPlaceholderNode extends ValueNode {
+        public static final NodeClass<ExceptionPlaceholderNode> TYPE = NodeClass.create(ExceptionPlaceholderNode.class);
+
+        public ExceptionPlaceholderNode() {
+            super(TYPE, StampFactory.object());
+        }
+    }
+
     public PEGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, StampProvider stampProvider) {
         this.metaAccess = metaAccess;
         this.constantReflection = constantReflection;
@@ -294,8 +316,9 @@
 
     public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin,
                     ParameterPlugin parameterPlugin) {
-        PEMethodScope methodScope = new PEMethodScope(targetGraph, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin, parameterPlugin, null);
-        decode(methodScope);
+        PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin,
+                        parameterPlugin, null);
+        decode(methodScope, null);
         cleanupGraph(methodScope);
         methodScope.graph.verify();
     }
@@ -310,8 +333,6 @@
     protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
-        Debug.dump(methodScope.graph, "Loop iteration " + loopScope.loopIteration);
-
         if (loopScope.loopIteration > MaximumLoopExplosionCount.getValue()) {
             String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?";
             if (FailedLoopExplosionIsFatal.getValue()) {
@@ -323,39 +344,52 @@
     }
 
     @Override
-    protected void simplifyInvoke(MethodScope s, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
-        if (!(invoke.callTarget() instanceof MethodCallTargetNode)) {
-            return;
-        }
+    protected void handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) {
         PEMethodScope methodScope = (PEMethodScope) s;
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+
+        /*
+         * Decode the call target, but do not add it to the graph yet. This avoids adding usages for
+         * all the arguments, which are expensive to remove again when we can inline the method.
+         */
+        assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke";
+        CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId);
+        if (!(callTarget instanceof MethodCallTargetNode) || !trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget)) {
 
+            /* We know that we need an invoke, so now we can add the call target to the graph. */
+            methodScope.graph.add(callTarget);
+            registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false);
+            super.handleInvoke(methodScope, loopScope, invokeData);
+        }
+    }
+
+    protected boolean trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
         // attempt to devirtualize the call
-        ResolvedJavaType contextType = (invoke.stateAfter() == null && invoke.stateDuring() == null) ? null : invoke.getContextType();
-        ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), contextType);
+        ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), invokeData.contextType);
         if (specialCallTarget != null) {
             callTarget.setTargetMethod(specialCallTarget);
             callTarget.setInvokeKind(InvokeKind.Special);
         }
 
-        if (tryInvocationPlugin(methodScope, loopScope, invokeOrderId, invoke)) {
-            return;
+        if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) {
+            return true;
         }
-        if (tryInline(methodScope, loopScope, invokeOrderId, invoke)) {
-            return;
+        if (tryInline(methodScope, loopScope, invokeData, callTarget)) {
+            return true;
         }
 
         if (methodScope.inlineInvokePlugin != null) {
-            methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope), callTarget.targetMethod(), invoke);
+            methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope), callTarget.targetMethod(), invokeData.invoke);
         }
+        return false;
     }
 
-    protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
+    protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
         if (methodScope.invocationPlugins == null) {
             return false;
         }
 
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+        Invoke invoke = invokeData.invoke;
+
         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
         InvocationPlugin invocationPlugin = methodScope.invocationPlugins.lookupInvocation(targetMethod);
         if (invocationPlugin == null) {
@@ -364,52 +398,41 @@
 
         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
         FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor();
-        FixedNode invokeNext = invoke.next();
-        AbstractBeginNode invokeException = null;
-        if (invoke instanceof InvokeWithExceptionNode) {
-            invokeException = ((InvokeWithExceptionNode) invoke).exceptionEdge();
-        }
 
+        /* Remove invoke from graph so that invocation plugin can append nodes to the predecessor. */
         invoke.asNode().replaceAtPredecessor(null);
 
-        PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, invoke, invokePredecessor);
+        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin,
+                        methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
+        PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor);
         InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext);
+
         if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) {
 
             if (graphBuilderContext.lastInstr != null) {
-                registerNode(loopScope, invokeOrderId, graphBuilderContext.pushedNode, true, true);
+                registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true);
                 invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode);
+                graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData));
             } else {
                 assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?";
                 invoke.asNode().replaceAtUsages(null);
             }
 
             deleteInvoke(invoke);
-            if (invokeException != null) {
-                invokeException.safeDelete();
-            }
-
-            if (graphBuilderContext.lastInstr != null) {
-                graphBuilderContext.lastInstr.setNext(invokeNext);
-            } else {
-                invokeNext.replaceAtPredecessor(null);
-                invokeNext.safeDelete();
-            }
             return true;
 
         } else {
-            /* Restore original state: invoke is in Graph. */
+            /* Intrinsification failed, restore original state: invoke is in Graph. */
             invokePredecessor.setNext(invoke.asNode());
             return false;
         }
     }
 
-    protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) {
-        if (methodScope.inlineInvokePlugin == null || !invoke.getInvokeKind().isDirect()) {
+    protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
+        if (methodScope.inlineInvokePlugin == null || !callTarget.invokeKind().isDirect()) {
             return false;
         }
 
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
         if (!targetMethod.canBeInlined()) {
             return false;
@@ -429,50 +452,72 @@
             return false;
         }
 
-        int exceptionObjectOrderId = -1;
-        if (invoke instanceof InvokeWithExceptionNode) {
-            /*
-             * We need to have the regular next node (usually a KillingBeginNode) and the exception
-             * edge node (always an ExceptionObjectNode) fully decoded, because both can be changed
-             * or replaced as part of the inlining process. The GraphEncoder adds these two
-             * successors in a known order (first the regular next node, then the exception edge)
-             * that we can rely on here.
-             */
-            assert ((InvokeWithExceptionNode) invoke).next().next() == null;
-            processNextNode(methodScope, loopScope);
-            assert ((InvokeWithExceptionNode) invoke).next().next() != null;
+        Invoke invoke = invokeData.invoke;
+        FixedNode invokeNode = invoke.asNode();
+        FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor();
+        invokeNode.replaceAtPredecessor(null);
 
-            assert ((InvokeWithExceptionNode) invoke).exceptionEdge().next() == null;
-            exceptionObjectOrderId = loopScope.nodesToProcess.nextSetBit(0);
-            processNextNode(methodScope, loopScope);
-            assert ((InvokeWithExceptionNode) invoke).exceptionEdge().next() != null;
-        }
-
-        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, graphToInline, inlineMethod, invoke, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin,
-                        methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
+        PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1,
+                        methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments);
         /* Do the actual inlining by decoding the inlineMethod */
-        decode(inlineScope);
+        decode(inlineScope, predecessor);
 
         ValueNode exceptionValue = null;
         if (inlineScope.unwindNode != null) {
             exceptionValue = inlineScope.unwindNode.exception();
         }
+        UnwindNode unwindNode = inlineScope.unwindNode;
 
-        FixedNode firstInlinedNode = inlineScope.startNode.next();
-        /* The StartNode was only necessary as a placeholder during decoding. */
-        inlineScope.startNode.safeDelete();
+        if (invoke instanceof InvokeWithExceptionNode) {
+            InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
+            assert invokeWithException.next() == null;
+            assert invokeWithException.exceptionEdge() == null;
+
+            if (unwindNode != null) {
+                assert unwindNode.predecessor() != null;
+                Node n = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId);
+                unwindNode.replaceAndDelete(n);
+            }
+
+        } else {
+            if (unwindNode != null && !unwindNode.isDeleted()) {
+                DeoptimizeNode deoptimizeNode = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
+                unwindNode.replaceAndDelete(deoptimizeNode);
+            }
+        }
+
+        assert invoke.next() == null;
 
-        assert inlineScope.startNode.stateAfter() == null;
-        ValueNode returnValue = InliningUtil.finishInlining(invoke, methodScope.graph, firstInlinedNode, inlineScope.returnNodes, inlineScope.unwindNode, inlineScope.encodedGraph.getAssumptions(),
-                        inlineScope.encodedGraph.getInlinedMethods(), null);
+        ValueNode returnValue;
+        List<ReturnNode> returnNodes = inlineScope.returnNodes;
+        if (!returnNodes.isEmpty()) {
+            FixedNode n;
+            n = nodeAfterInvoke(methodScope, loopScope, invokeData);
+            if (returnNodes.size() == 1) {
+                ReturnNode returnNode = returnNodes.get(0);
+                returnValue = returnNode.result();
+                returnNode.replaceAndDelete(n);
+            } else {
+                AbstractMergeNode merge = methodScope.graph.add(new MergeNode());
+                merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
+                returnValue = InliningUtil.mergeReturns(merge, returnNodes, null);
+                merge.setNext(n);
+            }
+        } else {
+            returnValue = null;
+        }
+        invokeNode.replaceAtUsages(returnValue);
 
         /*
          * Usage the handles that we have on the return value and the exception to update the
          * orderId->Node table.
          */
-        registerNode(loopScope, invokeOrderId, returnValue, true, true);
+        registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true);
         if (invoke instanceof InvokeWithExceptionNode) {
-            registerNode(loopScope, exceptionObjectOrderId, exceptionValue, true, true);
+            registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true);
+        }
+        if (inlineScope.exceptionPlaceholderNode != null) {
+            inlineScope.exceptionPlaceholderNode.replaceAndDelete(exceptionValue);
         }
         deleteInvoke(invoke);
 
@@ -480,6 +525,16 @@
         return true;
     }
 
+    public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData) {
+        FixedNode n;
+        if (invokeData.invoke instanceof InvokeWithExceptionNode) {
+            n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId);
+        } else {
+            n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId);
+        }
+        return n;
+    }
+
     private static void deleteInvoke(Invoke invoke) {
         /*
          * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can
@@ -487,7 +542,7 @@
          */
         FrameState frameState = invoke.stateAfter();
         invoke.asNode().safeDelete();
-        invoke.callTarget().safeDelete();
+        assert invoke.callTarget() == null : "must not have been added to the graph yet";
         if (frameState != null && frameState.hasNoUsages()) {
             frameState.safeDelete();
         }
@@ -499,15 +554,20 @@
     protected void simplifyFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
-        if (node instanceof IfNode && ((IfNode) node).condition() instanceof LogicConstantNode) {
+        if (node instanceof IfNode) {
             IfNode ifNode = (IfNode) node;
-            boolean condition = ((LogicConstantNode) ifNode.condition()).getValue();
-            AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition);
-            AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition);
+            if (ifNode.condition() instanceof LogicNegationNode) {
+                ifNode.eliminateNegation();
+            }
+            if (ifNode.condition() instanceof LogicConstantNode) {
+                boolean condition = ((LogicConstantNode) ifNode.condition()).getValue();
+                AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition);
+                AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition);
 
-            methodScope.graph.removeSplit(ifNode, survivingSuccessor);
-            assert deadSuccessor.next() == null : "must not be parsed yet";
-            deadSuccessor.safeDelete();
+                methodScope.graph.removeSplit(ifNode, survivingSuccessor);
+                assert deadSuccessor.next() == null : "must not be parsed yet";
+                deadSuccessor.safeDelete();
+            }
 
         } else if (node instanceof IntegerSwitchNode && ((IntegerSwitchNode) node).value().isConstant()) {
             IntegerSwitchNode switchNode = (IntegerSwitchNode) node;
@@ -587,31 +647,68 @@
         return node;
     }
 
+    protected void ensureOuterStateDecoded(PEMethodScope methodScope) {
+        if (methodScope.outerState == null && methodScope.caller != null) {
+            FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter();
+            if (stateAtReturn == null) {
+                stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
+            }
+
+            Kind invokeReturnKind = methodScope.invokeData.invoke.asNode().getKind();
+            FrameState outerState = stateAtReturn.duplicateModified(methodScope.graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind);
+
+            if (methodScope.caller != null) {
+                ensureOuterStateDecoded(methodScope.caller);
+                outerState.setOuterFrameState(methodScope.caller.outerState);
+            }
+            methodScope.outerState = outerState;
+        }
+    }
+
+    protected void ensureStateAfterDecoded(PEMethodScope methodScope) {
+        if (methodScope.invokeData.invoke.stateAfter() == null) {
+            methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId));
+        }
+    }
+
+    protected void ensureExceptionStateDecoded(PEMethodScope methodScope) {
+        if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) {
+            ensureStateAfterDecoded(methodScope);
+
+            assert methodScope.exceptionPlaceholderNode == null;
+            methodScope.exceptionPlaceholderNode = methodScope.graph.add(new ExceptionPlaceholderNode());
+            registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false);
+            FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId);
+
+            if (methodScope.caller != null) {
+                ensureOuterStateDecoded(methodScope.caller);
+                exceptionState.setOuterFrameState(methodScope.caller.outerState);
+            }
+            methodScope.exceptionState = exceptionState;
+        }
+    }
+
     @Override
     protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
         if (methodScope.isInlinedMethod()) {
             if (node instanceof SimpleInfopointNode) {
-                methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition);
+                methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition);
                 return node;
 
             } else if (node instanceof FrameState) {
-                FrameState stateAtExceptionEdge = null;
-                if (methodScope.invoke instanceof InvokeWithExceptionNode) {
-                    InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) methodScope.invoke);
-                    ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
-                    stateAtExceptionEdge = obj.stateAfter();
+                FrameState frameState = (FrameState) node;
+
+                ensureOuterStateDecoded(methodScope);
+                if (frameState.bci < 0) {
+                    ensureExceptionStateDecoded(methodScope);
                 }
-                if (methodScope.outerFrameState == null) {
-                    FrameState stateAtReturn = methodScope.invoke.stateAfter();
-                    Kind invokeReturnKind = methodScope.invoke.asNode().getKind();
-                    methodScope.outerFrameState = stateAtReturn.duplicateModifiedDuringCall(methodScope.invoke.bci(), invokeReturnKind);
-                }
-                return InliningUtil.processFrameState((FrameState) node, methodScope.invoke, methodScope.method, stateAtExceptionEdge, methodScope.outerFrameState, true);
+                return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, methodScope.method, methodScope.exceptionState, methodScope.outerState, true);
 
             } else if (node instanceof MonitorIdNode) {
-                InliningUtil.processMonitorId(methodScope.invoke, (MonitorIdNode) node);
+                ensureOuterStateDecoded(methodScope);
+                InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node);
                 return node;
             }
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue Apr 14 14:01:18 2015 +0200
@@ -42,7 +42,6 @@
 import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
@@ -50,6 +49,7 @@
 import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
+import com.oracle.graal.replacements.*;
 import com.oracle.graal.truffle.debug.*;
 import com.oracle.graal.truffle.nodes.*;
 import com.oracle.graal.truffle.nodes.asserts.*;
@@ -164,28 +164,78 @@
 
     private class PEInlineInvokePlugin implements InlineInvokePlugin {
 
-        private final boolean duringParsing;
         private Deque<TruffleInlining> inlining;
         private OptimizedDirectCallNode lastDirectCallNode;
-        private final Replacements replacements;
+        private final ReplacementsImpl replacements;
 
-        private final InvocationPlugins invocationPlugins;
-        private final LoopExplosionPlugin loopExplosionPlugin;
-
-        public PEInlineInvokePlugin(TruffleInlining inlining, Replacements replacements, boolean duringParsing, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin) {
+        public PEInlineInvokePlugin(TruffleInlining inlining, ReplacementsImpl replacements) {
             this.inlining = new ArrayDeque<>();
             this.inlining.push(inlining);
             this.replacements = replacements;
-            this.duringParsing = duringParsing;
+        }
+
+        @Override
+        public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
+            InlineInfo inlineInfo = replacements.getInlineInfo(builder, original, arguments, returnType);
+            if (inlineInfo != null) {
+                return inlineInfo;
+            }
+
+            if (original.getAnnotation(TruffleBoundary.class) != null) {
+                return null;
+            }
+            assert !replacements.hasSubstitution(original) : "Replacements must have been processed by now";
+            assert !builder.parsingReplacement();
+
+            if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
+                if (original.equals(callSiteProxyMethod)) {
+                    ValueNode arg1 = arguments[0];
+                    if (!arg1.isConstant()) {
+                        GraalInternalError.shouldNotReachHere("The direct call node does not resolve to a constant!");
+                    }
+
+                    Object callNode = snippetReflection.asObject(Object.class, (JavaConstant) arg1.asConstant());
+                    if (callNode instanceof OptimizedDirectCallNode) {
+                        OptimizedDirectCallNode directCallNode = (OptimizedDirectCallNode) callNode;
+                        lastDirectCallNode = directCallNode;
+                    }
+                } else if (original.equals(callDirectMethod)) {
+                    TruffleInliningDecision decision = getDecision(inlining.peek(), lastDirectCallNode);
+                    lastDirectCallNode = null;
+                    if (decision != null && decision.isInline()) {
+                        inlining.push(decision);
+                        builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption()));
+                        return new InlineInfo(callInlinedMethod, false, false);
+                    }
+                }
+            }
+
+            return new InlineInfo(original, false, false);
+        }
+
+        @Override
+        public void postInline(ResolvedJavaMethod inlinedTargetMethod) {
+            if (inlinedTargetMethod.equals(callInlinedMethod)) {
+                inlining.pop();
+            }
+        }
+    }
+
+    private class ParsingInlineInvokePlugin implements InlineInvokePlugin {
+
+        private final ReplacementsImpl replacements;
+        private final InvocationPlugins invocationPlugins;
+        private final LoopExplosionPlugin loopExplosionPlugin;
+        private final boolean inlineDuringParsing;
+
+        public ParsingInlineInvokePlugin(ReplacementsImpl replacements, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin, boolean inlineDuringParsing) {
+            this.replacements = replacements;
             this.invocationPlugins = invocationPlugins;
             this.loopExplosionPlugin = loopExplosionPlugin;
+            this.inlineDuringParsing = inlineDuringParsing;
         }
 
         private boolean hasMethodHandleArgument(ValueNode[] arguments) {
-            /*
-             * We want to process invokes that have a constant MethodHandle parameter. And the
-             * method must be statically bound, otherwise we do not have a single target method.
-             */
             for (ValueNode argument : arguments) {
                 if (argument.isConstant()) {
                     JavaConstant constant = argument.asJavaConstant();
@@ -197,58 +247,35 @@
             return false;
         }
 
+        @Override
         public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) {
-            if (duringParsing && (invocationPlugins.lookupInvocation(original) != null || loopExplosionPlugin.shouldExplodeLoops(original))) {
+            InlineInfo inlineInfo = replacements.getInlineInfo(builder, original, arguments, returnType);
+            if (inlineInfo != null) {
+                return inlineInfo;
+            }
+
+            if (invocationPlugins.lookupInvocation(original) != null || loopExplosionPlugin.shouldExplodeLoops(original)) {
                 return null;
             }
-
             if (original.getAnnotation(TruffleBoundary.class) != null) {
                 return null;
             }
-            if (replacements != null && replacements.hasSubstitution(original)) {
+            assert !replacements.hasSubstitution(original) : "Replacements must have been processed by now";
+
+            if (original.equals(callSiteProxyMethod) || original.equals(callDirectMethod)) {
                 return null;
             }
-            assert !builder.parsingReplacement();
-            if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
-                if (original.equals(callSiteProxyMethod)) {
-                    if (duringParsing) {
-                        return null;
-                    }
-                    ValueNode arg1 = arguments[0];
-                    if (!arg1.isConstant()) {
-                        GraalInternalError.shouldNotReachHere("The direct call node does not resolve to a constant!");
-                    }
-
-                    Object callNode = snippetReflection.asObject(Object.class, (JavaConstant) arg1.asConstant());
-                    if (callNode instanceof OptimizedDirectCallNode) {
-                        OptimizedDirectCallNode directCallNode = (OptimizedDirectCallNode) callNode;
-                        lastDirectCallNode = directCallNode;
-                    }
-                } else if (original.equals(callDirectMethod)) {
-                    if (duringParsing) {
-                        return null;
-                    }
-                    TruffleInliningDecision decision = getDecision(inlining.peek(), lastDirectCallNode);
-                    lastDirectCallNode = null;
-                    if (decision != null && decision.isInline()) {
-                        inlining.push(decision);
-                        builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption()));
-                        return new InlineInfo(callInlinedMethod, false, false);
-                    }
-                }
+            if (hasMethodHandleArgument(arguments)) {
+                /*
+                 * We want to inline invokes that have a constant MethodHandle parameter to remove
+                 * invokedynamic related calls as early as possible.
+                 */
+                return new InlineInfo(original, false, false);
             }
-
-            if (duringParsing && (!original.hasBytecodes() || original.getCode().length >= TrivialInliningSize.getValue() || builder.getDepth() >= InlineDuringParsingMaxDepth.getValue()) &&
-                            !hasMethodHandleArgument(arguments)) {
-                return null;
+            if (inlineDuringParsing && original.hasBytecodes() && original.getCode().length < TrivialInliningSize.getValue() && builder.getDepth() < InlineDuringParsingMaxDepth.getValue()) {
+                return new InlineInfo(original, false, false);
             }
-            return new InlineInfo(original, false, false);
-        }
-
-        public void postInline(ResolvedJavaMethod inlinedTargetMethod) {
-            if (inlinedTargetMethod.equals(callInlinedMethod)) {
-                inlining.pop();
-            }
+            return null;
         }
     }
 
@@ -279,7 +306,7 @@
         plugins.setParameterPlugin(new InterceptReceiverPlugin(callTarget));
         callTarget.setInlining(new TruffleInlining(callTarget, new DefaultInliningPolicy()));
 
-        InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), false, null, null);
+        InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), (ReplacementsImpl) providers.getReplacements());
         if (PrintTruffleExpansionHistogram.getValue()) {
             inlinePlugin = new HistogramInlineInvokePlugin(graph, inlinePlugin);
         }
@@ -303,15 +330,16 @@
         newConfig.setUseProfiling(false);
         Plugins plugins = newConfig.getPlugins();
         plugins.setLoadFieldPlugin(new InterceptLoadFieldPlugin());
-        plugins.setInlineInvokePlugin(new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), true, parsingInvocationPlugins, loopExplosionPlugin));
+        plugins.setInlineInvokePlugin(new ParsingInlineInvokePlugin((ReplacementsImpl) providers.getReplacements(), parsingInvocationPlugins, loopExplosionPlugin,
+                        !PrintTruffleExpansionHistogram.getValue()));
 
         CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(providers, newConfig, AllowAssumptions.from(graph.getAssumptions() != null));
 
         ParameterPlugin parameterPlugin = new InterceptReceiverPlugin(callTarget);
 
-        InvocationPlugins decodingInvocationPlugins = new InvocationPlugins(providers.getMetaAccess());
+        InvocationPlugins decodingInvocationPlugins = new InvocationPlugins(parsingInvocationPlugins.getParent());
         TruffleGraphBuilderPlugins.registerInvocationPlugins(providers.getMetaAccess(), decodingInvocationPlugins, false, snippetReflection);
-        InlineInvokePlugin decodingInlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), false, decodingInvocationPlugins, loopExplosionPlugin);
+        InlineInvokePlugin decodingInlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), (ReplacementsImpl) providers.getReplacements());
         if (PrintTruffleExpansionHistogram.getValue()) {
             decodingInlinePlugin = new HistogramInlineInvokePlugin(graph, decodingInlinePlugin);
         }
@@ -431,7 +459,9 @@
         if (!TruffleCompilerOptions.TruffleInlineAcrossTruffleBoundary.getValue()) {
             // Do not inline across Truffle boundaries.
             for (MethodCallTargetNode mct : graph.getNodes(MethodCallTargetNode.TYPE)) {
-                mct.invoke().setUseForInlining(false);
+                if (mct.targetMethod().getAnnotation(TruffleBoundary.class) != null) {
+                    mct.invoke().setUseForInlining(false);
+                }
             }
         }
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Tue Apr 14 14:01:18 2015 +0200
@@ -32,8 +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.Receiver;
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
+import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -157,7 +157,6 @@
 
         // Check that the "probed" AST still executes correctly
         assertEquals(13, callTarget1.call());
-
     }
 
     @Test
@@ -178,11 +177,10 @@
 
         // Check instrumentation with the simplest kind of counters.
         // They should all be removed when the check is finished.
-        checkCounters(probe, callTarget, rootNode, new TestInstrumentCounter(), new TestInstrumentCounter(), new TestInstrumentCounter());
+        checkCounters(probe, callTarget, rootNode, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter());
 
         // Now try with the more complex flavor of listener
-        checkCounters(probe, callTarget, rootNode, new TestASTInstrumentCounter(), new TestASTInstrumentCounter(), new TestASTInstrumentCounter());
-
+        checkCounters(probe, callTarget, rootNode, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter());
     }
 
     private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) {
@@ -280,12 +278,10 @@
         assertEquals(counterB.leaveCount(), 8);
         assertEquals(counterC.enterCount(), 2);
         assertEquals(counterC.leaveCount(), 2);
-
     }
 
     @Test
     public void testTagging() {
-
         // Applies appropriate tags
         final TestASTProber astProber = new TestASTProber();
         Probe.registerASTProber(astProber);
@@ -341,7 +337,6 @@
         assertEquals(valueCounter.count, 2);
 
         Probe.unregisterASTProber(astProber);
-
     }
 
     private interface TestCounter {
@@ -353,92 +348,107 @@
         void attach(Probe probe);
 
         void dispose();
-
     }
 
     /**
-     * A counter for the number of times execution enters and leaves a probed AST node, using the
-     * simplest kind of listener.
+     * A counter for the number of times execution enters and leaves a probed AST node.
      */
-    private class TestInstrumentCounter implements TestCounter {
+    private class TestSimpleInstrumentCounter implements TestCounter {
 
         public int enterCount = 0;
         public int leaveCount = 0;
         public final Instrument instrument;
 
-        public TestInstrumentCounter() {
+        public TestSimpleInstrumentCounter() {
             this.instrument = Instrument.create(new SimpleInstrumentListener() {
 
-                @Override
                 public void enter(Probe probe) {
                     enterCount++;
                 }
 
-                @Override
-                public void returnAny(Probe probe) {
+                public void returnVoid(Probe probe) {
+                    leaveCount++;
+                }
+
+                public void returnValue(Probe probe, Object result) {
+                    leaveCount++;
+                }
+
+                public void returnExceptional(Probe probe, Exception exception) {
                     leaveCount++;
                 }
 
             }, "Instrumentation Test Counter");
-
         }
 
+        @Override
         public int enterCount() {
             return enterCount;
         }
 
+        @Override
         public int leaveCount() {
             return leaveCount;
         }
 
+        @Override
         public void attach(Probe probe) {
             probe.attach(instrument);
         }
 
+        @Override
         public void dispose() {
             instrument.dispose();
         }
     }
 
     /**
-     * A counter for the number of times execution enters and leaves a probed AST node, using the
-     * simplest kind of listener.
+     * A counter for the number of times execution enters and leaves a probed AST node.
      */
-    private class TestASTInstrumentCounter implements TestCounter {
+    private class TestStandardInstrumentCounter implements TestCounter {
 
         public int enterCount = 0;
         public int leaveCount = 0;
         public final Instrument instrument;
 
-        public TestASTInstrumentCounter() {
-            this.instrument = Instrument.create(new SimpleASTInstrumentListener() {
+        public TestStandardInstrumentCounter() {
+            this.instrument = Instrument.create(new StandardInstrumentListener() {
 
-                @Override
                 public void enter(Probe probe, Node node, VirtualFrame vFrame) {
                     enterCount++;
                 }
 
-                @Override
-                public void returnAny(Probe probe, Node node, VirtualFrame vFrame) {
+                public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
+                    leaveCount++;
+                }
+
+                public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
+                    leaveCount++;
+                }
+
+                public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
                     leaveCount++;
                 }
 
             }, "Instrumentation Test Counter");
-
         }
 
+        @Override
         public int enterCount() {
             return enterCount;
         }
 
+        @Override
         public int leaveCount() {
             return leaveCount;
         }
 
+        @Override
         public void attach(Probe probe) {
             probe.attach(instrument);
         }
 
+        @Override
         public void dispose() {
             instrument.dispose();
         }
@@ -475,7 +485,7 @@
     /**
      * Counts the number of "enter" events at probed nodes using the simplest AST listener.
      */
-    static final class TestInstrumentListener extends DefaultInstrumentListener {
+    static final class TestSimpleInstrumentListener extends DefaultSimpleInstrumentListener {
 
         public int counter = 0;
 
@@ -483,13 +493,12 @@
         public void enter(Probe probe) {
             counter++;
         }
-
     }
 
     /**
      * Counts the number of "enter" events at probed nodes using the AST listener.
      */
-    static final class TestASTInstrumentListener extends DefaultASTInstrumentListener {
+    static final class TestASTInstrumentListener extends DefaultStandardInstrumentListener {
 
         public int counter = 0;
 
@@ -497,7 +506,6 @@
         public void enter(Probe probe, Node node, VirtualFrame vFrame) {
             counter++;
         }
-
     }
 
     /**
@@ -514,7 +522,7 @@
             // where we want to count executions.
             // it will get copied when ASTs cloned, so
             // keep the count in this outer class.
-            probe.attach(Instrument.create(new DefaultInstrumentListener() {
+            probe.attach(Instrument.create(new DefaultSimpleInstrumentListener() {
 
                 @Override
                 public void enter(Probe p) {
@@ -539,5 +547,4 @@
             tagCount++;
         }
     }
-
 }
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Tue Apr 14 14:01:18 2015 +0200
@@ -86,7 +86,6 @@
         public Object execute(VirtualFrame vFrame) {
             probeNode.enter(child, vFrame);
             Object result;
-
             try {
                 result = child.execute(vFrame);
                 probeNode.returnValue(child, vFrame, result);
@@ -96,7 +95,6 @@
                 probeNode.returnExceptional(child, vFrame, e);
                 throw (e);
             }
-
             return result;
         }
     }
@@ -169,4 +167,17 @@
         }
     }
 
+    static class TestSplicedCounterNode extends SplicedNode {
+
+        private long count;
+
+        @Override
+        public void enter(Node node, VirtualFrame vFrame) {
+            count++;
+        }
+
+        public long getCount() {
+            return count;
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015, 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.truffle.api.test.instrument;
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestSplicedCounterNode;
+
+/**
+ * Tests the kind of instrumentation where a client can provide an AST fragment to be
+ * <em>spliced</em> directly into the AST.
+ */
+public class SpliceInstrumentTest {
+
+    @Test
+    public void testSpliceInstrumentListener() {
+        // Create a simple addition AST
+        final TruffleRuntime runtime = Truffle.getRuntime();
+        final TestValueNode leftValueNode = new TestValueNode(6);
+        final TestValueNode rightValueNode = new TestValueNode(7);
+        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
+        final TestRootNode rootNode = new TestRootNode(addNode);
+        final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
+
+        // Ensure it executes correctly
+        assertEquals(13, callTarget1.call());
+
+        // Probe the addition node
+        final Probe probe = addNode.probe();
+
+        assertEquals(13, callTarget1.call());
+
+        // Attach a null listener; it never actually attaches a node.
+        final Instrument instrument = Instrument.create(new SpliceInstrumentListener() {
+
+            public SplicedNode getSpliceNode(Probe p) {
+                return null;
+            }
+
+        }, null);
+        probe.attach(instrument);
+
+        assertEquals(13, callTarget1.call());
+
+        final TestSplicedCounterNode counter = new TestSplicedCounterNode();
+
+        // Attach a listener that splices an execution counter into the AST.
+        probe.attach(Instrument.create(new SpliceInstrumentListener() {
+
+            public SplicedNode getSpliceNode(Probe p) {
+                return counter;
+            }
+
+        }, null));
+        assertEquals(0, counter.getCount());
+
+        assertEquals(13, callTarget1.call());
+
+        assertEquals(1, counter.getCount());
+
+    }
+
+}
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2015, 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.truffle.api.test.instrument;
-
-import static org.junit.Assert.*;
-
-import org.junit.*;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
-
-/**
- * Tests instrumentation where a client can attach a node that gets attached into the AST.
- */
-public class ToolNodeInstrumentationTest {
-
-    @Test
-    public void testToolNodeListener() {
-        // Create a simple addition AST
-        final TruffleRuntime runtime = Truffle.getRuntime();
-        final TestValueNode leftValueNode = new TestValueNode(6);
-        final TestValueNode rightValueNode = new TestValueNode(7);
-        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
-        final TestRootNode rootNode = new TestRootNode(addNode);
-        final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
-
-        // Ensure it executes correctly
-        assertEquals(13, callTarget1.call());
-
-        // Probe the addition node
-        final Probe probe = addNode.probe();
-
-        assertEquals(13, callTarget1.call());
-
-        // Attach a listener that never actually attaches a node.
-        final Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() {
-
-            public ToolNode getToolNode(Probe p) {
-                return null;
-            }
-
-        }, null);
-        probe.attach(instrument);
-
-        assertEquals(13, callTarget1.call());
-
-        final int[] count = new int[1];
-
-        // Attach a listener that never actually attaches a node.
-        probe.attach(Instrument.create(new ToolNodeInstrumentListener() {
-
-            public ToolNode getToolNode(Probe p) {
-                return new ToolNode() {
-
-                    @Override
-                    public void enter(Node node, VirtualFrame vFrame) {
-                        count[0] = count[0] + 1;
-                    }
-                };
-            }
-
-        }, null));
-        assertEquals(0, count[0]);
-
-        assertEquals(13, callTarget1.call());
-
-        assertEquals(1, count[0]);
-
-    }
-
-}
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java	Tue Apr 14 14:01:18 2015 +0200
@@ -54,9 +54,9 @@
         // Initially has only the default tag
         assertEquals(source.getSourceTags().size(), 1);
 
-        assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL));
-        assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL));
-        assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source));
+        assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL));
+        assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL));
+        assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source));
 
         assertFalse(source.isTaggedAs(testTag));
         assertEquals(Source.findSourcesTaggedAs(testTag).size(), 0);
@@ -67,9 +67,9 @@
         // Now there are exactly two tags
         assertEquals(source.getSourceTags().size(), 2);
 
-        assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL));
-        assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL));
-        assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source));
+        assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL));
+        assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL));
+        assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source));
 
         assertTrue(source.getSourceTags().contains(testTag));
         assertTrue(source.isTaggedAs(testTag));
@@ -82,9 +82,9 @@
         // Nothing has changed
         assertEquals(source.getSourceTags().size(), 2);
 
-        assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL));
-        assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL));
-        assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source));
+        assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL));
+        assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL));
+        assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source));
 
         assertTrue(source.getSourceTags().contains(testTag));
         assertTrue(source.isTaggedAs(testTag));
@@ -135,7 +135,7 @@
         assertEquals(newSource[0], source);
         assertEquals(newTagEvents[0], 1);
         assertEquals(taggedSource[0], source);
-        assertEquals(newTag[0], StandardSourceTag.FROM_LITERAL);
+        assertEquals(newTag[0], Source.Tags.FROM_LITERAL);
 
         // reset
         newSource[0] = null;
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTInstrumentListener.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument;
-
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * A listener of Truffle AST runtime execution events that can collect information, examine the
- * execution state at a particular node, and possibly intervene on behalf of an external tool.
- */
-public interface ASTInstrumentListener {
-
-    /**
-     * Receive notification that an AST node's execute method is about to be called.
-     */
-    void enter(Probe probe, Node node, VirtualFrame vFrame);
-
-    /**
-     * Receive notification that an AST Node's {@code void}-valued execute method has just returned.
-     */
-    void returnVoid(Probe probe, Node node, VirtualFrame vFrame);
-
-    /**
-     * Receive notification that an AST Node's execute method has just returned a value (boxed if
-     * primitive).
-     */
-    void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result);
-
-    /**
-     * Receive notification that an AST Node's execute method has just thrown an exception.
-     */
-    void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue Apr 14 14:01:18 2015 +0200
@@ -25,145 +25,77 @@
 package com.oracle.truffle.api.instrument;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
-import com.oracle.truffle.api.instrument.impl.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 
-// TODO (mlvdv) migrate some of this to external documentation.
-// TODO (mlvdv) move all this to a factory implemented in .impl (together with Probe),
-// then break out some of the nested classes into package privates.
+// TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
+// TODO (mlvdv) migrate factory into .impl (together with Probe)? break out nested classes?
+
 /**
- * A dynamically added/removed binding between a {@link Probe}, which provides notification of
- * <em>execution events</em> taking place at a {@link Node} in a Guest Language (GL) Truffle AST,
- * and a <em>listener</em>, which consumes notifications on behalf of an external tool. There are at
- * present two kinds of listeners that be used:
- * <ol>
- * <li>{@link InstrumentListener} is the simplest and is intended for tools that require no access
- * to the <em>internal execution state</em> of the Truffle execution, only that execution has passed
- * through a particular location in the program. Information about that location is made available
- * via the {@link Probe} argument in notification methods, including the {@linkplain SourceSection
- * source location} of the node and any {@linkplain SyntaxTag tags} that have been applied to the
- * node.</li>
- * <li>{@link ASTInstrumentListener} reports the same events and {@link Probe} argument, but
- * additionally provides access to the execution state via the explicit {@link Node} and
- * {@link Frame} at the current execution site.</li>
- * </ol>
- * <p>
- * <h4>Summary: How to "instrument" an AST location:</h4>
- * <p>
+ * A <em>binding</em> between:
  * <ol>
- * <li>Create an implementation of a <em>listener</em> interface.</li>
- * <li>Create an Instrument via factory methods
- * {@link Instrument#create(InstrumentListener, String)} or
- * {@link Instrument#create(ASTInstrumentListener, String)}.</li>
- * <li>"Attach" the Instrument to a Probe via {@link Probe#attach(Instrument)}, at which point event
- * notifications begin to arrive at the listener.</li>
- * <li>When no longer needed, "detach" the Instrument via {@link ASTInstrument#dispose()}, at which
- * point event notifications to the listener cease, and the Instrument becomes unusable.</li>
- * </ol>
- * <p>
- * <h4>Options for creating listeners:</h4>
- * <p>
- * <ol>
- * <li>Implement one of the <em>listener interfaces</em>: {@link InstrumentListener} or
- * {@link ASTInstrumentListener} . Their event handling methods account for both the entry into an
- * AST node (about to call) and three possible kinds of <em>execution return</em> from an AST node.</li>
- * <li>Extend one of the <em>helper implementations</em>: {@link DefaultInstrumentListener} or
- * {@link DefaultASTInstrumentListener}. These provide no-op implementation of every listener
- * method, so only the methods of interest need to be overridden.</li>
- * <li>Extend one of the <em>helper implementations</em>: {@link SimpleInstrumentListener} or
- * {@link SimpleASTInstrumentListener}. These re-route all <em>execution returns</em> to a single
- * method, ignoring return values, so only two methods (for "enter" and "return") will notify all
- * events.</li>
+ * <li>A {@link Probe}: a source of <em>execution events</em> taking place at a program location in
+ * an executing Truffle AST, and</li>
+ * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client.
  * </ol>
  * <p>
- * <h4>General guidelines for {@link ASTInstrumentListener} implementation:</h4>
+ * Client-oriented documentation for the use of Instruments is available online at <a
+ * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events"
+ * >https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
  * <p>
- * Unlike the listener interface {@link InstrumentListener}, which isolates implementations
- * completely from Truffle internals (and is thus <em>Truffle-safe</em>), implementations of
- * {@link ASTInstrumentListener} can interact directly with (and potentially affect) Truffle
- * execution in general and Truffle optimization in particular. For example, it is possible to
- * implement a debugger with this interface.
- * </p>
+ * The implementation of Instruments is complicated by the requirement that Truffle be able to clone
+ * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to
+ * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes.
  * <p>
- * As a consequence, implementations of {@link ASTInstrumentListener} effectively become part of the
- * Truffle execution and must be coded according to general guidelines for Truffle implementations.
- * For example:
- * <ul>
- * <li>Do not store {@link Frame} or {@link Node} references in fields.</li>
- * <li>Prefer {@code final} fields and (where performance is important) short methods.</li>
- * <li>If needed, pass along the {@link VirtualFrame} reference from an event notification as far as
- * possible through code that is expected to be inlined, since this incurs no runtime overhead. When
- * access to frame data is needed, substitute a more expensive {@linkplain Frame#materialize()
- * materialized} representation of the frame.</li>
- * <li>If a listener calls back to its tool during event handling, and if performance is an issue,
- * then this should be through a final "callback" field in the instrument, and the called methods
- * should be minimal.</li>
- * <li>On the other hand, implementations should prevent Truffle from inlining beyond a reasonable
- * point with the method annotation {@link TruffleBoundary}.</li>
- * <li>The implicit "outer" pointer in a non-static inner class is a useful (and
- * Truffle-optimizable) way to implement callbacks to owner tools.</li>
- * <li>Primitive-valued return events are boxed for event notification, but Truffle will eliminate
- * the boxing if they are cast back to their primitive values quickly (in particular before crossing
- * any {@link TruffleBoundary} annotations).
- * </ul>
+ * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is encouraged
+ * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than
+ * the properties associated with a Probe: it's {@link SourceSection} and any associated instances
+ * of {@link SyntaxTag}.
  * <p>
- * <h4>Allowing for AST cloning:</h4>
+ * AST cloning is <em>not transparent</em> to clients that use the
+ * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance
+ * (and thus the AST instance) where the event takes place.
  * <p>
- * Truffle routinely <em>clones</em> ASTs, which has consequences for implementations of
- * {@link ASTInstrumentListener} (but not for implementations of {@link InstrumentListener}, from
- * which cloning is hidden).
- * <ul>
- * <li>Even though a {@link Probe} is uniquely associated with a particular location in the
- * executing Guest Language program, execution events at that location will in general be
- * implemented by different {@link Node} instances, i.e. <em>clones</em> of the originally probed
- * node.</li>
- * <li>Because of <em>cloning</em> the {@link Node} supplied with notifications to a particular
- * listener will vary, but because they all represent the same GL program location the events should
- * be treated as equivalent for most purposes.</li>
- * </ul>
- * <p>
- * <h4>Access to execution state via {@link ASTInstrumentListener}:</h4>
+ * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4>
  * <p>
  * <ul>
- * <li>Notification arguments provide primary access to the GL program's execution states:
- * <ul>
- * <li>{@link Node}: the concrete node (in one of the AST's clones) from which the event originated.
+ * <li>A new Instrument is created in permanent association with a client-provided
+ * <em>listener.</em></li>
+ *
+ * <li>Multiple Instruments may share a single listener.</li>
+ *
+ * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a
+ * Probe, at which time the Instrument begins routing execution events from the Probe's AST location
+ * to the Instrument's listener.</li>
+ *
+ * <li>Neither Instruments nor Probes are {@link Node}s.</li>
+ *
+ * <li>A Probe has a single source-based location in an AST, but manages a separate
+ * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li>
+ * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned
+ * along with the rest of the AST.</li>
+ *
+ * <li>When a new Instrument (for example an instance of {@link SimpleInstrument} is attached to a
+ * Probe, the Instrument inserts a new instance of its private Node type,
+ * {@link SimpleInstrument.SimpleInstrumentNode}, into <em>each of the instrument chains</em>
+ * managed by the Probe, i.e. one node instance per existing clone of the AST.</li>
+ *
+ * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the
+ * Instrument's private Node type will be cloned along with the rest of the the AST.</li>
+ * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the
+ * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener.
  * </li>
- * <li>{@link VirtualFrame}: the current execution frame.
- * </ul>
- * <li>Truffle global information is available, for example the execution
- * {@linkplain TruffleRuntime#iterateFrames(FrameInstanceVisitor) stack}.</li>
- * <li>Additional API access to execution state may be added in the future.</li>
+ *
+ * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the
+ * Instrument searches every instrument chain associated with the Probe and removes the instance of
+ * its private Node type.</li>
+ *
+ * <li>Attaching and disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of the
+ * AST.</li>
+ *
  * </ul>
- * <p>
- * <h4>Activating and deactivating Instruments:</h4>
- * <p>
- * Instruments are <em>single-use</em>:
- * <ul>
- * <li>An instrument becomes active only when <em>attached</em> to a Probe via
- * {@link Probe#attach(Instrument)}, and it may only be attached to a single Probe. It is a runtime
- * error to attempt attaching a previously attached instrument.</li>
- * <li>Attaching an instrument modifies every existing clone of the AST to which it is being
- * attached, which can trigger deoptimization.</li>
- * <li>The method {@link Instrument#dispose()} makes an instrument inactive by removing it from the
- * Probe to which it was attached and rendering it permanently inert.</li>
- * <li>Disposal removes the implementation of an instrument from all ASTs to which it was attached,
- * which can trigger deoptimization.</li>
- * </ul>
- * <p>
- * <h4>Sharing listeners:</h4>
- * <p>
- * Although an Instrument may only be attached to a single Probe, a listener can be shared among
- * multiple Instruments. This can be useful for observing events that might happen at different
- * locations in a single AST, for example all assignments to a particular variable. In this case a
- * new Instrument would be created and attached at each assignment node, but all the Instruments
- * would be created with the same listener.
- * <p>
- * <strong>Disclaimer:</strong> experimental; under development.
  *
  * @see Probe
  * @see TruffleEvents
@@ -177,34 +109,35 @@
      * @param instrumentInfo optional description of the instrument's role, useful for debugging.
      * @return a new instrument, ready for attachment at a probe
      */
-    public static Instrument create(InstrumentListener listener, String instrumentInfo) {
-        return new BasicInstrument(listener, instrumentInfo);
+    public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) {
+        return new SimpleInstrument(listener, instrumentInfo);
     }
 
     /**
      * Creates an instrument that will route execution events to a listener, along with access to
      * internal execution state.
      *
-     * @param astListener a listener for event generated by the instrument that provides access to
-     *            internal execution state
+     * @param standardListener a listener for event generated by the instrument that provides access
+     *            to internal execution state
      * @param instrumentInfo optional description of the instrument's role, useful for debugging.
      * @return a new instrument, ready for attachment at a probe
      */
-    public static Instrument create(ASTInstrumentListener astListener, String instrumentInfo) {
-        return new ASTInstrument(astListener, instrumentInfo);
+    public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) {
+        return new StandardInstrument(standardListener, instrumentInfo);
     }
 
     /**
      * Creates an instrument that, when executed the first time in any particular AST location,
-     * invites the tool to provide an AST fragment for attachment/adoption into the running AST.
+     * invites the tool to provide an AST fragment for <em>splicing</em> directly into the running
+     * AST.
      *
-     * @param toolNodeListener a listener for the tool that can request an AST fragment
+     * @param spliceListener a callback to the client that requests an AST node to be splice.
      * @param instrumentInfo instrumentInfo optional description of the instrument's role, useful
      *            for debugging.
      * @return a new instrument, ready for attachment at a probe.
      */
-    public static Instrument create(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) {
-        return new ToolNodeInstrument(toolNodeListener, instrumentInfo);
+    public static Instrument create(SpliceInstrumentListener spliceListener, String instrumentInfo) {
+        return new SpliceInstrument(spliceListener, instrumentInfo);
     }
 
     // TODO (mlvdv) experimental
@@ -232,8 +165,7 @@
     }
 
     /**
-     * Removes this instrument (and any clones) from the probe to which it attached and renders the
-     * instrument inert.
+     * Removes this instrument from the probe to which it attached and renders the instrument inert.
      *
      * @throws IllegalStateException if this instrument has already been disposed
      */
@@ -266,23 +198,28 @@
     abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode);
 
     /**
-     * An instrument that propagates events to an instance of {@link InstrumentListener}.
+     * Removes this instrument from an instrument chain.
      */
-    private static final class BasicInstrument extends Instrument {
+    abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode);
+
+    /**
+     * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}.
+     */
+    private static final class SimpleInstrument extends Instrument {
 
         /**
          * Tool-supplied listener for events.
          */
-        private final InstrumentListener instrumentListener;
+        private final SimpleInstrumentListener simpleListener;
 
-        private BasicInstrument(InstrumentListener basicListener, String instrumentInfo) {
+        private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) {
             super(instrumentInfo);
-            this.instrumentListener = basicListener;
+            this.simpleListener = simpleListener;
         }
 
         @Override
         AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new BasicInstrumentNode(nextNode);
+            return new SimpleInstrumentNode(nextNode);
         }
 
         @Override
@@ -294,7 +231,7 @@
                     return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(BasicInstrument.this);
+                found = instrumentNode.removeFromChain(SimpleInstrument.this);
             }
             if (!found) {
                 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
@@ -303,35 +240,35 @@
         }
 
         @NodeInfo(cost = NodeCost.NONE)
-        private final class BasicInstrumentNode extends AbstractInstrumentNode {
+        private final class SimpleInstrumentNode extends AbstractInstrumentNode {
 
-            private BasicInstrumentNode(AbstractInstrumentNode nextNode) {
+            private SimpleInstrumentNode(AbstractInstrumentNode nextNode) {
                 super(nextNode);
             }
 
             public void enter(Node node, VirtualFrame vFrame) {
-                BasicInstrument.this.instrumentListener.enter(BasicInstrument.this.probe);
+                SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                BasicInstrument.this.instrumentListener.returnVoid(BasicInstrument.this.probe);
+                SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                BasicInstrument.this.instrumentListener.returnValue(BasicInstrument.this.probe, result);
+                SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                BasicInstrument.this.instrumentListener.returnExceptional(BasicInstrument.this.probe, exception);
+                SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
@@ -339,34 +276,29 @@
 
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
-                return info != null ? info : instrumentListener.getClass().getSimpleName();
+                return info != null ? info : simpleListener.getClass().getSimpleName();
             }
         }
     }
 
     /**
-     * Removes this instrument from an instrument chain.
+     * An instrument that propagates events to an instance of {@link StandardInstrumentListener}.
      */
-    abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode);
-
-    /**
-     * An instrument that propagates events to an instance of {@link ASTInstrumentListener}.
-     */
-    private static final class ASTInstrument extends Instrument {
+    private static final class StandardInstrument extends Instrument {
 
         /**
          * Tool-supplied listener for AST events.
          */
-        private final ASTInstrumentListener astListener;
+        private final StandardInstrumentListener standardListener;
 
-        private ASTInstrument(ASTInstrumentListener astListener, String instrumentInfo) {
+        private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) {
             super(instrumentInfo);
-            this.astListener = astListener;
+            this.standardListener = standardListener;
         }
 
         @Override
         AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new ASTInstrumentNode(nextNode);
+            return new StandardInstrumentNode(nextNode);
         }
 
         @Override
@@ -378,7 +310,7 @@
                     return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(ASTInstrument.this);
+                found = instrumentNode.removeFromChain(StandardInstrument.this);
             }
             if (!found) {
                 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
@@ -387,35 +319,35 @@
         }
 
         @NodeInfo(cost = NodeCost.NONE)
-        private final class ASTInstrumentNode extends AbstractInstrumentNode {
+        private final class StandardInstrumentNode extends AbstractInstrumentNode {
 
-            private ASTInstrumentNode(AbstractInstrumentNode nextNode) {
+            private StandardInstrumentNode(AbstractInstrumentNode nextNode) {
                 super(nextNode);
             }
 
             public void enter(Node node, VirtualFrame vFrame) {
-                ASTInstrument.this.astListener.enter(ASTInstrument.this.probe, node, vFrame);
+                standardListener.enter(StandardInstrument.this.probe, node, vFrame);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                ASTInstrument.this.astListener.returnVoid(ASTInstrument.this.probe, node, vFrame);
+                standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                ASTInstrument.this.astListener.returnValue(ASTInstrument.this.probe, node, vFrame, result);
+                standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                ASTInstrument.this.astListener.returnExceptional(ASTInstrument.this.probe, node, vFrame, exception);
+                standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
@@ -423,30 +355,31 @@
 
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
-                return info != null ? info : astListener.getClass().getSimpleName();
+                return info != null ? info : standardListener.getClass().getSimpleName();
             }
         }
-
     }
 
+    // TODO (mlvdv) EXPERIMENTAL- UNDER DEVELOPMENT
     /**
-     * An instrument that propagates events to an instance of {@link ASTInstrumentListener}.
+     * An instrument that allows clients to "splice" an AST fragment directly into a Probe's
+     * <em>instrumentation chain</em>, and thus directly into the executing Truffle AST.
      */
-    private static final class ToolNodeInstrument extends Instrument {
+    private static final class SpliceInstrument extends Instrument {
 
         /**
          * Tool-supplied listener for AST events.
          */
-        private final ToolNodeInstrumentListener toolNodeListener;
+        private final SpliceInstrumentListener spliceListener;
 
-        private ToolNodeInstrument(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) {
+        private SpliceInstrument(SpliceInstrumentListener spliceListener, String instrumentInfo) {
             super(instrumentInfo);
-            this.toolNodeListener = toolNodeListener;
+            this.spliceListener = spliceListener;
         }
 
         @Override
         AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new ToolInstrumentNode(nextNode);
+            return new SpliceInstrumentNode(nextNode);
         }
 
         @Override
@@ -458,7 +391,7 @@
                     return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(ToolNodeInstrument.this);
+                found = instrumentNode.removeFromChain(SpliceInstrument.this);
             }
             if (!found) {
                 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
@@ -467,25 +400,25 @@
         }
 
         @NodeInfo(cost = NodeCost.NONE)
-        private final class ToolInstrumentNode extends AbstractInstrumentNode {
+        private final class SpliceInstrumentNode extends AbstractInstrumentNode {
 
-            @Child ToolNode toolNode;
+            @Child SplicedNode splicedNode;
 
-            private ToolInstrumentNode(AbstractInstrumentNode nextNode) {
+            private SpliceInstrumentNode(AbstractInstrumentNode nextNode) {
                 super(nextNode);
             }
 
             public void enter(Node node, VirtualFrame vFrame) {
-                if (toolNode == null) {
-                    final ToolNode newToolNode = ToolNodeInstrument.this.toolNodeListener.getToolNode(ToolNodeInstrument.this.probe);
-                    if (newToolNode != null) {
-                        toolNode = newToolNode;
+                if (splicedNode == null) {
+                    final SplicedNode newSplicedNode = SpliceInstrument.this.spliceListener.getSpliceNode(SpliceInstrument.this.probe);
+                    if (newSplicedNode != null) {
+                        splicedNode = newSplicedNode;
                         adoptChildren();
-                        ToolNodeInstrument.this.probe.invalidateProbeUnchanged();
+                        SpliceInstrument.this.probe.invalidateProbeUnchanged();
                     }
                 }
-                if (toolNode != null) {
-                    toolNode.enter(node, vFrame);
+                if (splicedNode != null) {
+                    splicedNode.enter(node, vFrame);
                 }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.enter(node, vFrame);
@@ -493,8 +426,8 @@
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                if (toolNode != null) {
-                    toolNode.returnVoid(node, vFrame);
+                if (splicedNode != null) {
+                    splicedNode.returnVoid(node, vFrame);
                 }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
@@ -502,8 +435,8 @@
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                if (toolNode != null) {
-                    toolNode.returnValue(node, vFrame, result);
+                if (splicedNode != null) {
+                    splicedNode.returnValue(node, vFrame, result);
                 }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
@@ -511,8 +444,8 @@
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                if (toolNode != null) {
-                    toolNode.returnExceptional(node, vFrame, exception);
+                if (splicedNode != null) {
+                    splicedNode.returnExceptional(node, vFrame, exception);
                 }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
@@ -521,10 +454,9 @@
 
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
-                return info != null ? info : toolNodeListener.getClass().getSimpleName();
+                return info != null ? info : spliceListener.getClass().getSimpleName();
             }
         }
-
     }
 
     public interface TruffleOptListener {
@@ -605,7 +537,6 @@
                 return info != null ? info : toolOptListener.getClass().getSimpleName();
             }
         }
-
     }
 
     @NodeInfo(cost = NodeCost.NONE)
@@ -658,7 +589,5 @@
         protected String getInstrumentInfo() {
             return Instrument.this.instrumentInfo;
         }
-
     }
-
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentListener.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument;
-
-/**
- * A listener of Truffle execution events that can collect information on behalf of an external
- * tool. Contextual information about the source of the event, if not stored in the implementation
- * of the listener, can be obtained via access to the {@link Probe} that generates the event.
- */
-public interface InstrumentListener {
-
-    /**
-     * Receive notification that an AST node's execute method is about to be called.
-     */
-    void enter(Probe probe);
-
-    /**
-     * Receive notification that an AST Node's {@code void}-valued execute method has just returned.
-     */
-    void returnVoid(Probe probe);
-
-    /**
-     * Receive notification that an AST Node's execute method has just returned a value (boxed if
-     * primitive).
-     */
-    void returnValue(Probe probe, Object result);
-
-    /**
-     * Receive notification that an AST Node's execute method has just thrown an exception.
-     */
-    void returnExceptional(Probe probe, Exception exception);
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -28,8 +28,9 @@
 import com.oracle.truffle.api.nodes.*;
 
 /**
- * A marker interface for Truffle {@linkplain Node nodes} that support <em>Instrumentation</em> and
- * are should not be part of any Guest Language execution semantics.
+ * A marker interface for Truffle {@linkplain Node nodes} that internally implement the
+ * <em>Instrumentation Framework</em>. Such nodes should not be part of any Guest Language execution
+ * semantics, and should in general not be visible to ordinary Instrumentation clients.
  */
 public interface InstrumentationNode {
 
@@ -39,7 +40,8 @@
     String instrumentationInfo();
 
     /**
-     * Events at a Truffle node that get propagated through the Instrumentation Framework.
+     * Events that propagate through the {@linkplain InstrumentationNode implementation nodes} of
+     * the Instrumentation Framework, not visible in this form to Instrumentation clients.
      */
     interface TruffleEvents {
 
@@ -62,6 +64,5 @@
          * An AST Node's execute method has just thrown an exception.
          */
         void returnExceptional(Node node, VirtualFrame vFrame, Exception exception);
-
     }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Tue Apr 14 14:01:18 2015 +0200
@@ -34,38 +34,62 @@
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.utilities.*;
 
-//TODO (mlvdv) migrate some of this to external documentation.
+//TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
+
 /**
- * A binding between a particular <em>location</em> in the Truffle AST representation of a running
- * Guest Language (GL) program (i.e. a {@link Node}) and a dynamically managed collection of
- * "attached" {@linkplain Instrument instrumentation} for use by external tools. The instrumentation
- * is intended to persist at the location, even if the specific node instance is
- * {@linkplain Node#replace(Node) replaced}.
+ * A <em>binding</em> between:
+ * <ol>
+ * <li>A program location in an executing Truffle AST (defined by a {@link SourceSection}), and</li>
+ * <li>A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that
+ * receive event notifications on behalf of external clients.</li>
+ * </ol>
  * <p>
- * The effect of a binding is to intercept {@linkplain TruffleEvents execution events} arriving at
- * the node and notify each attached {@link Instrument} before execution is allowed to proceed to
- * the child.
+ * Client-oriented documentation for the use of Probes is available online at <a
+ * HREF="https://wiki.openjdk.java.net/display/Graal/Finding+Probes"
+ * >https://wiki.openjdk.java.net/display/Graal/Finding+Probes</a>
  * <p>
- * A Probe is "inserted" into a GL node via a call to {@link Node#probe()}. No more than one Probe
- * can be inserted at a node.
+ * <h4>Implementation notes:</h4>
  * <p>
- * The "probing" of a Truffle AST must be done after it is complete (i.e. with parent pointers
- * correctly assigned), but before any executions. This is done by creating an instance of
- * {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}, after which it
- * will be automatically applied to newly created ASTs.
- * <p>
- * Each Probe may also have assigned to it any number of {@link SyntaxTag}s, for example identifying
- * a node as a {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. Tags can be queried by tools to
- * configure behavior relevant to each probed node.
- * <p>
- * Instrumentation is implemented by modifying ASTs, both by inserting nodes into each AST at probed
- * locations and by attaching additional nodes that implement dynamically attached instruments.
- * Attached instrumentation code become, in effect, part of the GL program, and is subject to the
- * same levels of optimization as other GL code. This implementation accounts properly for the fact
- * that Truffle frequently <em>clones</em> ASTs, along with any attached instrumentation nodes. A
- * {@link Probe}, along with attached {@link Instrument}s, represents a <em>logical</em> binding
- * with a source code location, producing event notifications that are (mostly) independent of which
- * AST clone is executing.
+ * <ul>
+ * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a
+ * particular {@link SourceSection}, even though:
+ * <ul>
+ * <li>that location is represented in an AST as a {@link Node}, which might be replaced through
+ * optimizations such as specialization, and</li>
+ * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by multiple
+ * Nodes in multiple ASTs.</li>
+ * </ul>
+ * </li>
+ *
+ * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events}
+ * arriving at the "probed" AST node and notify each attached {@link Instrument} before execution is
+ * allowed to proceed to the child and again after execution completes.</li>
+ *
+ * <li>A Probe is "inserted" into a GL node via a call to {@link Node#probe()}. No more than one
+ * Probe can be inserted at a node; a redundant call returns the existing Probe<./li>
+ *
+ * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers
+ * correctly assigned), but before any cloning or executions. This is done by creating an instance
+ * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}, after which it
+ * will be applied automatically to every newly created AST.</li>
+ *
+ * <li>An AST node becomes <em>probed</em> by insertion of a {@link ProbeNode.WrapperNode} into the
+ * AST, together with an associated {@link ProbeNode} that routes events to all the
+ * {@linkplain Instrument Instruments} attached to its <em>instrument chain</em>.</li>
+ *
+ * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well,
+ * together with their attached instrument chains. The {@link Probe} instance intercepts cloning
+ * events and keeps track of all copies.</li>
+ *
+ * <li>All attached {@link InstrumentationNode}s effectively become part of the running program:
+ * <ul>
+ * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle optimization.</li>
+ * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with any
+ * Truffle optimizations.</li>
+ * </ul>
+ * </li>
+ *
+ * </ul>
  *
  * @see Instrument
  * @see ASTProber
@@ -173,6 +197,7 @@
         return taggedProbes;
     }
 
+    // TODO (mlvdv) can this be generalized to permit multiple traps without a performance hit?
     /**
      * Sets the current "tag trap"; there can be no more than one set at a time.
      * <ul>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.instrument;
+
+import com.oracle.truffle.api.source.*;
+
+/**
+ * A receiver of Truffle execution events that can act on behalf of an external client.
+ * <p>
+ * The {@link Probe} instance provides access to the {@link SourceSection} associated with the
+ * event, as well as any {@link SyntaxTag}s that have been applied at that program's location.
+ * <p>
+ * This is the simplest kind of listener, suitable for clients that need no other information about
+ * the program's execution state at the time of the event. Clients that require access to the AST
+ * execution state should use {@link StandardInstrumentListener}.
+ * <p>
+ * Clients are free, of course, to record additional information in the listener implementation that
+ * carries additional information about the context and reason for the particular {@link Instrument}
+ * that is to be created from the listener.
+ */
+public interface SimpleInstrumentListener {
+
+    /**
+     * Receive notification that a program location is about to be executed.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void enter(Probe probe);
+
+    /**
+     * Receive notification that a program location's {@code void}-valued execution has just
+     * completed.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void returnVoid(Probe probe);
+
+    /**
+     * Receive notification that a program location's execution has just completed and returned a
+     * value (boxed if primitive).
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void returnValue(Probe probe, Object result);
+
+    /**
+     * Receive notification that a program location's execution has just thrown an exception.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void returnExceptional(Probe probe, Exception exception);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SpliceInstrumentListener.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.instrument;
+
+/**
+ * Instrument listener for a tool that works by providing an AST to be <em>spliced</em> directly
+ * into the AST.
+ */
+public interface SpliceInstrumentListener {
+
+    /**
+     * Receive notification that a probed AST node to which the {@link Instrument} is attached is
+     * about to be executed for the first time. This is a lazy opportunity for the tool to
+     * optionally add the root of a newly created AST fragment that will be <em>spliced</em>
+     * directly into the executing AST. The new AST fragment will immediately begin receiving
+     * {@link InstrumentationNode.TruffleEvents}, beginning with the current execution event.
+     * <p>
+     * AST fragments must be written to Truffle conventions. Some of these conventions are
+     * especially important if the fragment is to be fully optimized along with it's new parent AST.
+     * <p>
+     * If this method returns {@code null} then it will be called again the next time the probed
+     * node is about to be executed.
+     * <p>
+     * In some situations, this method will be called more than once for a particular Probe, and a
+     * new instance must be supplied each time. Each instance will be attached at the equivalent
+     * location in clones of the AST, and so should be behave as if equivalent for most purposes.
+     * <p>
+     * In some situations the AST fragment supplied by this method maybe cloned for attachment to
+     * equivalent locations in cloned AST, so care should be taken about any state local to each
+     * instance of the AST fragment.
+     *
+     * @see Instrument
+     */
+    SplicedNode getSpliceNode(Probe probe);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SplicedNode.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.instrument;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Root of a client-provided AST fragment that can be spliced directly into an executing AST via
+ * {@link Instrument#create(SpliceInstrumentListener, String)}.
+ * <p>
+ * <strong>Note:</strong> Instances of this class will in some situations be cloned by the
+ * instrumentation platform for attachment at equivalent locations in cloned ASTs.
+ */
+public abstract class SplicedNode extends Node implements InstrumentationNode.TruffleEvents, InstrumentationNode {
+
+    public void enter(Node node, VirtualFrame vFrame) {
+    }
+
+    public void returnVoid(Node node, VirtualFrame vFrame) {
+    }
+
+    public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+    }
+
+    public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+    }
+
+    public String instrumentationInfo() {
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.instrument;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
+
+/**
+ * A receiver of Truffle execution events that can act on behalf of an external client.
+ * <p>
+ * The {@link Probe} argument provides access to the {@link SourceSection} associated with the
+ * event, as well as any {@link SyntaxTag}s that have been applied at that AST node.
+ * <p>
+ * This listener is designed for clients that also require access to the AST execution state at the
+ * time of the event. Clients that do not require access to the AST execution state should use the
+ * {@link SimpleInstrumentListener}.
+ * <p>
+ * Clients are free, of course, to record additional information in the listener implementation that
+ * carries additional information about the context and reason for the particular {@link Instrument}
+ * that is to be created from the listener.
+ */
+public interface StandardInstrumentListener {
+
+    /**
+     * Receive notification that an AST node's execute method is about to be called.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void enter(Probe probe, Node node, VirtualFrame vFrame);
+
+    /**
+     * Receive notification that an AST Node's {@code void}-valued execute method has just returned.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void returnVoid(Probe probe, Node node, VirtualFrame vFrame);
+
+    /**
+     * Receive notification that an AST Node's execute method has just returned a value (boxed if
+     * primitive).
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result);
+
+    /**
+     * Receive notification that an AST Node's execute method has just thrown an exception.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception);
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument;
-
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * Root of a tool-provided AST fragment that can be attached directly into an executing AST via
- * {@link Instrument#create(ToolNodeInstrumentListener, String)}.
- * <p>
- * <strong>Note:</strong> Instances of this class will in some situations be cloned by the
- * instrumentation platform for attachment at equivalent locations in cloned parent ASTs.
- */
-public abstract class ToolNode extends Node implements InstrumentationNode.TruffleEvents, InstrumentationNode {
-
-    public void enter(Node node, VirtualFrame vFrame) {
-    }
-
-    public void returnVoid(Node node, VirtualFrame vFrame) {
-    }
-
-    public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-    }
-
-    public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-    }
-
-    public String instrumentationInfo() {
-        return null;
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument;
-
-/**
- * Instrument listener for a tool that works by providing an AST to be attached/adopted directly
- * into the AST.
- */
-public interface ToolNodeInstrumentListener {
-
-    /**
-     * Receive notification that a probed AST node to which the {@link Instrument} is attached is
-     * about to be executed for the first time. This is a lazy opportunity for the tool to
-     * optionally add the root of a newly created AST fragment that will be attached/adopted
-     * directly into the executing AST. The new AST fragment will immediately begin receiving
-     * {@link InstrumentationNode.TruffleEvents}, beginning with the current execution event.
-     * <p>
-     * AST fragments must be written to Truffle conventions. Some of these conventions are
-     * especially important if the fragment is to be fully optimized along with it's new parent AST.
-     * <p>
-     * If this method returns {@code null} then it will be called again the next time the probed
-     * node is about to be executed.
-     * <p>
-     * In some situations, this method will be called more than once for a particular Probe, and a
-     * new instance must be supplied each time. Each instance will be attached at the equivalent
-     * location in clones of the AST, and so should be behave as if equivalent for most purposes.
-     * <p>
-     * In some situations the AST fragment supplied by this method maybe cloned for attachment to
-     * equivalent locations in cloned AST, so care should be taken about any state local to each
-     * instance of the AST fragment.
-     *
-     * @see Instrument
-     */
-    ToolNode getToolNode(Probe probe);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTInstrumentListener.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument.impl;
-
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * A listener for AST {@linkplain ASTInstrumentListener execution events} that provides a no-op
- * implementation of every event.
- */
-public class DefaultASTInstrumentListener implements ASTInstrumentListener {
-
-    public void enter(Probe probe, Node node, VirtualFrame vFrame) {
-    }
-
-    public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
-    }
-
-    public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
-    }
-
-    public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultInstrumentListener.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument.impl;
-
-import com.oracle.truffle.api.instrument.*;
-
-/**
- * A listener for Truffle execution events that provides a no-op implementation of every event.
- */
-public class DefaultInstrumentListener implements InstrumentListener {
-
-    public void enter(Probe probe) {
-    }
-
-    public void returnVoid(Probe probe) {
-    }
-
-    public void returnValue(Probe probe, Object result) {
-    }
-
-    public void returnExceptional(Probe probe, Exception exception) {
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.instrument.impl;
+
+import com.oracle.truffle.api.instrument.*;
+
+/**
+ * A listener for Truffle execution events that provides a no-op implementation of every event.
+ */
+public class DefaultSimpleInstrumentListener implements SimpleInstrumentListener {
+
+    public void enter(Probe probe) {
+    }
+
+    public void returnVoid(Probe probe) {
+    }
+
+    public void returnValue(Probe probe, Object result) {
+    }
+
+    public void returnExceptional(Probe probe, Exception exception) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java	Tue Apr 14 14:01:18 2015 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.instrument.impl;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * A listener for AST {@linkplain StandardInstrumentListener execution events} that provides a no-op
+ * implementation of every event.
+ */
+public class DefaultStandardInstrumentListener implements StandardInstrumentListener {
+
+    public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+    }
+
+    public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
+    }
+
+    public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
+    }
+
+    public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleASTInstrumentListener.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument.impl;
-
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * An abstract listener for AST {@linkplain ASTInstrumentListener execution events} that ignores
- * return values and supports handling all events by overriding only two methods:
- * <ul>
- * <li>{@link #enter(Probe, Node, VirtualFrame)}, and</li>
- * <li>{@link #returnAny(Probe, Node, VirtualFrame)}.</li>
- * </ul>
- */
-public abstract class SimpleASTInstrumentListener implements ASTInstrumentListener {
-
-    public void enter(Probe probe, Node node, VirtualFrame vFrame) {
-    }
-
-    /**
-     * Receive notification that one of an AST Node's execute methods has just returned by any
-     * means: with or without a return value (ignored) or via exception (ignored).
-     *
-     * @param probe where the event originated
-     * @param node specific node of the event
-     * @param vFrame
-     */
-    protected void returnAny(Probe probe, Node node, VirtualFrame vFrame) {
-    }
-
-    public final void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
-        returnAny(probe, node, vFrame);
-    }
-
-    public final void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
-        returnAny(probe, node, vFrame);
-    }
-
-    public final void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception e) {
-        returnAny(probe, node, vFrame);
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleInstrumentListener.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument.impl;
-
-import com.oracle.truffle.api.instrument.*;
-
-/**
- * An abstract listener for Truffle {@linkplain InstrumentListener execution events} that ignores
- * return values and supports handling all events by overriding only two methods:
- * <ul>
- * <li>{@link #enter(Probe)}, and</li>
- * <li>{@link #returnAny(Probe)}.</li>
- * </ul>
- */
-public abstract class SimpleInstrumentListener implements InstrumentListener {
-
-    public void enter(Probe probe) {
-    }
-
-    /**
-     * Receive notification that an execute method has just returned by any means: with or without a
-     * return value (ignored) or via exception (ignored).
-     *
-     * @param probe
-     */
-    protected void returnAny(Probe probe) {
-    }
-
-    public final void returnVoid(Probe probe) {
-        returnAny(probe);
-    }
-
-    public final void returnValue(Probe probe, Object result) {
-        returnAny(probe);
-    }
-
-    public final void returnExceptional(Probe probe, Exception e) {
-        returnAny(probe);
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Tue Apr 14 14:01:18 2015 +0200
@@ -78,6 +78,51 @@
     // TODO (mlvdv) consider canonicalizing and reusing SourceSection instances
     // TOOD (mlvdv) connect SourceSections into a spatial tree for fast geometric lookup
 
+    public enum Tags implements SourceTag {
+
+        /**
+         * From bytes.
+         */
+        FROM_BYTES("bytes", "read from bytes"),
+
+        /**
+         * Read from a file.
+         */
+        FROM_FILE("file", "read from a file"),
+
+        /**
+         * From literal text.
+         */
+        FROM_LITERAL("literal", "from literal text"),
+
+        /**
+         * From a {@linkplain java.io.Reader Reader}.
+         */
+        FROM_READER("reader", "read from a Java Reader"),
+
+        /**
+         * Read from a URL.
+         */
+        FROM_URL("URL", "read from a URL");
+
+        private final String name;
+        private final String description;
+
+        private Tags(String name, String description) {
+            this.name = name;
+            this.description = description;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+    }
+
     /**
      * All Sources that have been created.
      */
@@ -119,7 +164,7 @@
         if (reset) {
             source.reset();
         }
-        notifyNewSource(source).tagAs(StandardSourceTag.FROM_FILE);
+        notifyNewSource(source).tagAs(Tags.FROM_FILE);
         return source;
     }
 
@@ -161,7 +206,7 @@
                 filePathToSource.put(path, new WeakReference<>(source));
             }
         }
-        notifyNewSource(source).tagAs(StandardSourceTag.FROM_FILE);
+        notifyNewSource(source).tagAs(Tags.FROM_FILE);
         return source;
     }
 
@@ -176,7 +221,7 @@
     public static Source fromText(CharSequence chars, String description) {
         assert chars != null;
         final LiteralSource source = new LiteralSource(description, chars.toString());
-        notifyNewSource(source).tagAs(StandardSourceTag.FROM_LITERAL);
+        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
         return source;
     }
 
@@ -190,7 +235,7 @@
      */
     public static Source fromURL(URL url, String description) throws IOException {
         final URLSource source = URLSource.get(url, description);
-        notifyNewSource(source).tagAs(StandardSourceTag.FROM_URL);
+        notifyNewSource(source).tagAs(Tags.FROM_URL);
         return source;
     }
 
@@ -204,7 +249,7 @@
      */
     public static Source fromReader(Reader reader, String description) throws IOException {
         final LiteralSource source = new LiteralSource(description, read(reader));
-        notifyNewSource(source).tagAs(StandardSourceTag.FROM_READER);
+        notifyNewSource(source).tagAs(Tags.FROM_READER);
         return source;
     }
 
@@ -237,7 +282,7 @@
      */
     public static Source fromBytes(byte[] bytes, int byteIndex, int length, String description, BytesDecoder decoder) {
         final BytesSource source = new BytesSource(description, bytes, byteIndex, length, decoder);
-        notifyNewSource(source).tagAs(StandardSourceTag.FROM_BYTES);
+        notifyNewSource(source).tagAs(Tags.FROM_BYTES);
         return source;
     }
 
@@ -252,7 +297,7 @@
     public static Source asPseudoFile(CharSequence chars, String pseudoFileName) {
         final Source source = new LiteralSource(pseudoFileName, chars.toString());
         filePathToSource.put(pseudoFileName, new WeakReference<>(source));
-        notifyNewSource(source).tagAs(StandardSourceTag.FROM_LITERAL);
+        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
         return source;
     }
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceTag.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceTag.java	Tue Apr 14 14:01:18 2015 +0200
@@ -27,16 +27,14 @@
 /**
  * Categorical information (best implemented as enums} about particular sources of Guest Language
  * code that can be useful to configure behavior of both the language runtime and external tools.
- * These might include {@linkplain StandardSourceTag standard tags} noting, for example, whether the
+ * These might include {@linkplain Source.Tags standard tags} noting, for example, whether the
  * source was read from a file and whether it should be considered library code.
  * <p>
- * An untagged {@link Source} should by default be considered application code.
- * <p>
  * The need for additional tags is likely to arise, in some cases because of issue specific to a
  * Guest Language, but also for help configuring the behavior of particular tools.
  *
  * @see Source
- * @see StandardSourceTag
+ * @see Source.Tags
  */
 public interface SourceTag {
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/StandardSourceTag.java	Tue Apr 14 13:37:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.source;
-
-/**
- * A general set of "properties" or "categories" that might be usefully attached to a particular
- * source of code, both for use by the language runtime and by external tools. This set of tags
- * includes some intended to be applied by default by {@link Source} factory methods or other
- * services built into the Truffle platform.
- * <p>
- * The need for additional tags is likely to arise, in some cases because of issue specific to a
- * Guest Language, but also for help configuring the behavior of particular tools.
- *
- * @see Source
- */
-public enum StandardSourceTag implements SourceTag {
-
-    /**
-     * Builtin.
-     */
-    BUILTIN("builtin", "implementation of language builtins"),
-
-    /**
-     * From bytes.
-     */
-    FROM_BYTES("bytes", "read from bytes"),
-
-    /**
-     * Read from a file.
-     */
-    FROM_FILE("file", "read from a file"),
-
-    /**
-     * From literal text.
-     */
-    FROM_LITERAL("literal", "from literal text"),
-
-    /**
-     * From a {@linkplain java.io.Reader Reader}.
-     */
-    FROM_READER("reader", "read from a Java Reader"),
-
-    /**
-     * Read from a URL.
-     */
-    FROM_URL("URL", "read from a URL"),
-
-    /**
-     * Treat as LIBRARY code.
-     */
-    LIBRARY("library", "library code");
-
-    private final String name;
-    private final String description;
-
-    private StandardSourceTag(String name, String description) {
-        this.name = name;
-        this.description = description;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java	Tue Apr 14 14:01:18 2015 +0200
@@ -35,10 +35,13 @@
 
 /**
  * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes that
- * hold a specified {@linkplain SyntaxTag tag}, tabulated by source and line number associated with
- * each node. Tags are presumed to be applied external to the tool. If no tag is specified,
- * {@linkplain StandardSyntaxTag#STATEMENT STATEMENT} is used, corresponding to conventional
- * behavior for code coverage tools.
+ * hold a specified {@linkplain SyntaxTag syntax tag}, tabulated by source and line number
+ * associated with each node. Syntax tags are presumed to be applied external to the tool. If no tag
+ * is specified, {@linkplain StandardSyntaxTag#STATEMENT STATEMENT} is used, corresponding to
+ * conventional behavior for code coverage tools.
+ * <p>
+ * No counts will be kept for execution in sources that hold the {@link SourceTag}
+ * {@link Tags#NO_COVERAGE}.
  * <p>
  * <b>Tool Life Cycle</b>
  * <p>
@@ -48,7 +51,7 @@
  * <p>
  * <ul>
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
- * to produce the event {@link InstrumentListener#enter(Probe)};</li>
+ * to produce the event {@link SimpleInstrumentListener#enter(Probe)};</li>
  * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
  * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
@@ -70,6 +73,30 @@
  */
 public final class CoverageTracker extends InstrumentationTool {
 
+    public enum Tags implements SourceTag {
+
+        /**
+         * Report no counts for sources holding this tag.
+         */
+        NO_COVERAGE("No Coverage", "Coverage Tracker will igore");
+
+        private final String name;
+        private final String description;
+
+        private Tags(String name, String description) {
+            this.name = name;
+            this.description = description;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+
     /** Counting data. */
     private final Map<LineLocation, CoverageRecord> coverageMap = new HashMap<>();
 
@@ -226,7 +253,7 @@
      * A listener for events at each instrumented AST location. This listener counts
      * "execution calls" to the instrumented node.
      */
-    private final class CoverageRecord extends DefaultInstrumentListener {
+    private final class CoverageRecord extends DefaultSimpleInstrumentListener {
 
         private final SourceSection srcSection; // The text of the code being counted
         private Instrument instrument;  // The attached Instrument, in case need to remove.
@@ -264,7 +291,7 @@
                 final SourceSection srcSection = probe.getProbedSourceSection();
                 if (srcSection == null) {
                     // TODO (mlvdv) report this?
-                } else {
+                } else if (!srcSection.getSource().isTaggedAs(Tags.NO_COVERAGE)) {
                     // Get the source line where the
                     final LineLocation lineLocation = srcSection.getLineLocation();
                     CoverageRecord record = coverageMap.get(lineLocation);
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java	Tue Apr 14 14:01:18 2015 +0200
@@ -49,7 +49,7 @@
  * <p>
  * <ul>
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
- * to produce the event {@link ASTInstrumentListener#enter(Probe, Node, VirtualFrame)};</li>
+ * to produce the event {@link StandardInstrumentListener#enter(Probe, Node, VirtualFrame)};</li>
  * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
  * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
@@ -95,7 +95,7 @@
      * Listener for events at instrumented nodes. Counts are maintained in a shared table, so the
      * listener is stateless and can be shared by every {@link Instrument}.
      */
-    private final ASTInstrumentListener instrumentListener = new DefaultASTInstrumentListener() {
+    private final StandardInstrumentListener instrumentListener = new DefaultStandardInstrumentListener() {
         @Override
         public void enter(Probe probe, Node node, VirtualFrame vFrame) {
             if (isEnabled()) {
--- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Tue Apr 14 13:37:47 2015 +0200
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Tue Apr 14 14:01:18 2015 +0200
@@ -274,7 +274,7 @@
      * attached at {@link SLWriteLocalVariableNode}, but provides no guards to protect it from being
      * attached elsewhere.
      */
-    public final class SLPrintAssigmentValueListener extends DefaultInstrumentListener {
+    public final class SLPrintAssigmentValueListener extends DefaultSimpleInstrumentListener {
 
         private PrintStream output;
 
--- a/mx/mx_graal.py	Tue Apr 14 13:37:47 2015 +0200
+++ b/mx/mx_graal.py	Tue Apr 14 14:01:18 2015 +0200
@@ -1587,7 +1587,7 @@
         for vmbuild in ['product', 'fastdebug']:
             for theVm in ['client', 'server']:
                 if not isVMSupported(theVm):
-                    mx.log('The' + theVm + ' VM is not supported on this platform')
+                    mx.log('The ' + theVm + ' VM is not supported on this platform')
                     continue
                 with VM(theVm, vmbuild):
                     with Task('DaCapo_pmd:' + theVm + ':' + vmbuild, tasks) as t:
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Tue Apr 14 13:37:47 2015 +0200
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Tue Apr 14 14:01:18 2015 +0200
@@ -154,7 +154,7 @@
     }
   }
 
-  return (jbyteArray) JNIHandles::make_local(reconstituted_code);
+  return (jbyteArray) JNIHandles::make_local(THREAD, reconstituted_code);
 C2V_END
 
 C2V_VMENTRY(jint, exceptionTableLength, (JNIEnv *, jobject, jlong metaspace_method))
@@ -576,7 +576,7 @@
   }
 
   Handle result = java_lang_String::create_from_platform_dependent_str(st.as_string(), CHECK_NULL);
-  return JNIHandles::make_local(result());
+  return JNIHandles::make_local(THREAD, result());
 C2V_END
 
 C2V_VMENTRY(jobject, getStackTraceElement, (JNIEnv*, jobject, jlong metaspace_method, int bci))
@@ -585,7 +585,7 @@
 
   methodHandle method = asMethod(metaspace_method);
   oop element = java_lang_StackTraceElement::create(method, bci, CHECK_NULL);
-  return JNIHandles::make_local(element);
+  return JNIHandles::make_local(THREAD, element);
 C2V_END
 
 C2V_VMENTRY(jobject, executeCompiledMethodVarargs, (JNIEnv*, jobject, jobject args, jobject hotspotInstalledCode))
@@ -609,7 +609,7 @@
   if (jap.get_ret_type() == T_VOID) {
     return NULL;
   } else if (jap.get_ret_type() == T_OBJECT || jap.get_ret_type() == T_ARRAY) {
-    return JNIHandles::make_local((oop) result.get_jobject());
+    return JNIHandles::make_local(THREAD, (oop) result.get_jobject());
   } else {
     jvalue *value = (jvalue *) result.get_value_addr();
     // Narrow the value down if required (Important on big endian machines)
@@ -628,7 +628,7 @@
        break;
      }
     oop o = java_lang_boxing_object::create(jap.get_ret_type(), value, CHECK_NULL);
-    return JNIHandles::make_local(o);
+    return JNIHandles::make_local(THREAD, o);
   }
 C2V_END
 
@@ -656,7 +656,7 @@
     i += 2;
   }
 
-  return (jlongArray) JNIHandles::make_local(result);
+  return (jlongArray) JNIHandles::make_local(THREAD, result);
 C2V_END
 
 C2V_VMENTRY(jlong, getLocalVariableTableStart, (JNIEnv *, jobject, jlong metaspace_method))
@@ -709,9 +709,9 @@
   InstalledCode::set_address(hotspotInstalledCode, 0);
 C2V_END
 
-C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv*, jobject, jlong metaspace_klass))
+C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv* env, jobject, jlong metaspace_klass))
   Klass* klass = asKlass(metaspace_klass);
-  return JNIHandles::make_local(klass->java_mirror());
+  return JNIHandles::make_local(THREAD, klass->java_mirror());
 C2V_END
 
 C2V_VMENTRY(jlong, readUnsafeKlassPointer, (JNIEnv*, jobject, jobject o))
@@ -722,13 +722,13 @@
 
 C2V_VMENTRY(jobject, readUncompressedOop, (JNIEnv*, jobject, jlong addr))
   oop ret = oopDesc::load_decode_heap_oop((oop*)(address)addr);
-  return JNIHandles::make_local(ret);
+  return JNIHandles::make_local(THREAD, ret);
 C2V_END
 
 C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv*, jobject))
   typeArrayOop arrayOop = oopFactory::new_longArray(GraalCounterSize, CHECK_NULL);
   JavaThread::collect_counters(arrayOop);
-  return (jlongArray) JNIHandles::make_local(arrayOop);
+  return (jlongArray) JNIHandles::make_local(THREAD, arrayOop);
 C2V_END
 
 C2V_VMENTRY(int, allocateCompileId, (JNIEnv*, jobject, jlong metaspace_method, int entry_bci))
--- a/src/share/vm/graal/graalRuntime.cpp	Tue Apr 14 13:37:47 2015 +0200
+++ b/src/share/vm/graal/graalRuntime.cpp	Tue Apr 14 14:01:18 2015 +0200
@@ -647,7 +647,7 @@
   TempNewSymbol sig = SymbolTable::new_symbol("()Lcom/oracle/truffle/api/TruffleRuntime;", CHECK_NULL);
   JavaValue result(T_OBJECT);
   JavaCalls::call_static(&result, klass, makeInstance, sig, CHECK_NULL);
-  return JNIHandles::make_local((oop) result.get_jobject());
+  return JNIHandles::make_local(THREAD, (oop) result.get_jobject());
 JVM_END
 
 // private static NativeFunctionInterfaceRuntime.createInterface()
@@ -660,7 +660,7 @@
   TempNewSymbol sig = SymbolTable::new_symbol("()Lcom/oracle/nfi/api/NativeFunctionInterface;", CHECK_NULL);
   JavaValue result(T_OBJECT);
   JavaCalls::call_static(&result, klass, makeInstance, sig, CHECK_NULL);
-  return JNIHandles::make_local((oop) result.get_jobject());
+  return JNIHandles::make_local(THREAD, (oop) result.get_jobject());
 JVM_END
 
 void GraalRuntime::check_generated_sources_sha1(TRAPS) {