changeset 20111:45d46b136777

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Wed, 01 Apr 2015 16:36:28 +0200
parents 51f2e71b57aa (current diff) 30d6b0cd1394 (diff)
children 5f533a5c2aaf 81d08c81b2a7
files
diffstat 39 files changed, 968 insertions(+), 278 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/EconomyHighTier.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/EconomyHighTier.java	Wed Apr 01 16:36:28 2015 +0200
@@ -36,6 +36,11 @@
         if (ImmutableCode.getValue()) {
             canonicalizer.disableReadCanonicalization();
         }
+
+        if (OptCanonicalizer.getValue()) {
+            appendPhase(canonicalizer);
+        }
+
         appendPhase(new CleanTypeProfileProxyPhase(canonicalizer));
         appendPhase(new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER));
     }
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java	Wed Apr 01 16:36:28 2015 +0200
@@ -213,7 +213,6 @@
 
     protected final MetaAccessProvider metaAccess;
     private final List<MethodInfo> registrations;
-    private final Thread registrationThread;
 
     /**
      * The minimum {@linkplain InvocationPluginIdHolder#getInvocationPluginId() id} for a method
@@ -234,7 +233,6 @@
     private InvocationPlugins parent;
 
     private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) {
-        this.registrationThread = Thread.currentThread();
         this.metaAccess = metaAccess;
         this.registrations = new ArrayList<>(INITIAL_PLUGIN_CAPACITY);
         InvocationPlugins p = parent;
@@ -263,7 +261,6 @@
      * registered for {@code method}.
      */
     public void register(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
-        assert Thread.currentThread() == registrationThread : "invocation plugin registration must be single threaded";
         MethodInfo methodInfo = new MethodInfo(plugin, declaringClass, name, argumentTypes);
         assert Checker.check(this, methodInfo, plugin);
         assert plugins == null : "invocation plugin registration is closed";
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Wed Apr 01 16:36:28 2015 +0200
@@ -31,7 +31,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.api.runtime.*;
-import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.*;
+import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.word.*;
@@ -171,7 +171,7 @@
                 wordTypes = new HotSpotWordTypes(metaAccess, target.wordKind);
             }
             try (InitTimer rt = timer("create GraphBuilderPhase plugins")) {
-                plugins = HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch);
+                plugins = createGraphBuilderPlugins(runtime, target, constantReflection, foreignCalls, metaAccess, snippetReflection, replacements, wordTypes, stampProvider);
                 replacements.setGraphBuilderPlugins(plugins);
             }
             try (InitTimer rt = timer("create Suites provider")) {
@@ -184,6 +184,12 @@
         }
     }
 
+    protected Plugins createGraphBuilderPlugins(HotSpotGraalRuntimeProvider runtime, TargetDescription target, HotSpotConstantReflectionProvider constantReflection,
+                    HotSpotHostForeignCallsProvider foreignCalls, HotSpotMetaAccessProvider metaAccess, HotSpotSnippetReflectionProvider snippetReflection, HotSpotReplacementsImpl replacements,
+                    HotSpotWordTypes wordTypes, HotSpotStampProvider stampProvider) {
+        return HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch);
+    }
+
     protected AMD64HotSpotBackend createBackend(HotSpotGraalRuntimeProvider runtime, HotSpotProviders providers) {
         return new AMD64HotSpotBackend(runtime, providers);
     }
@@ -260,15 +266,15 @@
         } else {
             /*
              * System V Application Binary Interface, AMD64 Architecture Processor Supplement
-             * 
+             *
              * Draft Version 0.96
-             * 
+             *
              * http://www.uclibc.org/docs/psABI-x86_64.pdf
-             * 
+             *
              * 3.2.1
-             * 
+             *
              * ...
-             * 
+             *
              * This subsection discusses usage of each register. Registers %rbp, %rbx and %r12
              * through %r15 "belong" to the calling function and the called function is required to
              * preserve their values. In other words, a called function must preserve these
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java	Wed Apr 01 16:36:28 2015 +0200
@@ -27,7 +27,7 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.runtime.*;
-import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.*;
+import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.word.*;
@@ -68,7 +68,7 @@
         HotSpotReplacementsImpl replacements = new HotSpotReplacementsImpl(p, snippetReflection, runtime.getConfig(), target);
         HotSpotDisassemblerProvider disassembler = new HotSpotDisassemblerProvider(runtime);
         HotSpotWordTypes wordTypes = new HotSpotWordTypes(metaAccess, target.wordKind);
-        Plugins plugins = HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch);
+        Plugins plugins = createGraphBuilderPlugins(runtime, target, metaAccess, constantReflection, foreignCalls, stampProvider, snippetReflection, replacements, wordTypes);
         replacements.setGraphBuilderPlugins(plugins);
         HotSpotSuitesProvider suites = createSuites(runtime, plugins);
         HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection,
@@ -77,6 +77,12 @@
         return createBackend(runtime, providers);
     }
 
+    protected Plugins createGraphBuilderPlugins(HotSpotGraalRuntimeProvider runtime, TargetDescription target, HotSpotMetaAccessProvider metaAccess,
+                    HotSpotConstantReflectionProvider constantReflection, HotSpotForeignCallsProvider foreignCalls, HotSpotStampProvider stampProvider,
+                    HotSpotSnippetReflectionProvider snippetReflection, HotSpotReplacementsImpl replacements, HotSpotWordTypes wordTypes) {
+        return HotSpotGraphBuilderPlugins.create(runtime.getConfig(), wordTypes, metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider, replacements, target.arch);
+    }
+
     protected HotSpotSuitesProvider createSuites(HotSpotGraalRuntimeProvider runtime, Plugins plugins) {
         return new HotSpotSuitesProvider(runtime, plugins);
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java	Wed Apr 01 16:36:28 2015 +0200
@@ -47,6 +47,7 @@
 import com.oracle.graal.nodes.HeapAccess.BarrierType;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.word.*;
@@ -124,7 +125,7 @@
             r.register1(query.name(), Receiver.class, new InvocationPlugin() {
                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                     ValueNode javaClass = receiver.get();
-                    ValueNode folded = ClassQueryNode.tryFold(javaClass, query, b.getMetaAccess(), b.getConstantReflection());
+                    ValueNode folded = ClassQueryNode.tryFold(GraphUtil.originalValue(javaClass), query, b.getMetaAccess(), b.getConstantReflection());
                     if (folded != null) {
                         b.addPush(query.returnKind, folded);
                     } else {
@@ -137,7 +138,7 @@
         r.register2("cast", Receiver.class, Object.class, new InvocationPlugin() {
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
                 ValueNode javaClass = receiver.get();
-                ValueNode folded = ClassCastNode.tryFold(javaClass, object, b.getConstantReflection(), b.getAssumptions());
+                ValueNode folded = ClassCastNode.tryFold(GraphUtil.originalValue(javaClass), object, b.getConstantReflection(), b.getAssumptions());
                 if (folded != null) {
                     b.addPush(Kind.Object, folded);
                 } else {
@@ -151,7 +152,7 @@
     private static void registerCallSitePlugins(InvocationPlugins plugins) {
         InvocationPlugin plugin = new InvocationPlugin() {
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
-                ValueNode callSite = receiver.get();
+                ValueNode callSite = GraphUtil.originalValue(receiver.get());
                 ValueNode folded = CallSiteTargetNode.tryFold(callSite, b.getMetaAccess(), b.getAssumptions());
                 if (folded != null) {
                     b.addPush(Kind.Object, folded);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaFieldImpl.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaFieldImpl.java	Wed Apr 01 16:36:28 2015 +0200
@@ -51,7 +51,7 @@
     private final LocationIdentity locationIdentity = new FieldLocationIdentity(this);
 
     public static class FieldLocationIdentity extends LocationIdentity {
-        HotSpotResolvedJavaFieldImpl inner;
+        HotSpotResolvedJavaField inner;
 
         public FieldLocationIdentity(HotSpotResolvedJavaFieldImpl inner) {
             super(false);
@@ -78,7 +78,7 @@
 
         @Override
         public String toString() {
-            return inner.name;
+            return inner.getName();
         }
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassCastNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassCastNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -29,7 +29,6 @@
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.replacements.nodes.*;
 
 /**
@@ -67,9 +66,8 @@
     }
 
     public static ValueNode tryFold(ValueNode javaClass, ValueNode object, ConstantReflectionProvider constantReflection, Assumptions assumptions) {
-        ValueNode value = GraphUtil.originalValue(javaClass);
-        if (value.isConstant()) {
-            ResolvedJavaType type = constantReflection.asJavaType(value.asConstant());
+        if (javaClass != null && javaClass.isConstant()) {
+            ResolvedJavaType type = constantReflection.asJavaType(javaClass.asConstant());
             if (type != null && !type.isPrimitive()) {
                 return CheckCastNode.create(type, object, null, false, assumptions);
             }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassQueryNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ClassQueryNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -30,7 +30,6 @@
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.replacements.nodes.*;
 
 /**
@@ -79,13 +78,12 @@
     }
 
     public static ValueNode tryFold(ValueNode javaClass, Query query, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) {
-        ValueNode value = GraphUtil.originalValue(javaClass);
-        if (value != null && value.isConstant()) {
+        if (javaClass != null && javaClass.isConstant()) {
             if (query.returnKind == Kind.Object) {
                 if (GraalOptions.ImmutableCode.getValue()) {
                     return null;
                 }
-                HotSpotObjectConstant c = (HotSpotObjectConstant) value.asConstant();
+                HotSpotObjectConstant c = (HotSpotObjectConstant) javaClass.asConstant();
                 JavaConstant answer;
                 switch (query) {
                     case getClassLoader0:
@@ -105,7 +103,7 @@
                     return ConstantNode.forConstant(answer, metaAccess);
                 }
             } else {
-                ResolvedJavaType type = constantReflection.asJavaType(value.asConstant());
+                ResolvedJavaType type = constantReflection.asJavaType(javaClass.asConstant());
                 if (type != null) {
                     switch (query) {
                         case isArray:
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ResolvedMethodHandleCallTargetNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ResolvedMethodHandleCallTargetNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.hotspot.nodes;
 
+import static sun.misc.Version.*;
+
 import java.lang.invoke.*;
 
 import com.oracle.graal.api.meta.*;
@@ -35,24 +37,39 @@
 
 /**
  * A call target that replaces itself in the graph when being lowered by restoring the original
- * {@link MethodHandle} invocation target. This is required for when a {@link MethodHandle} call was
- * resolved to a constant target but the target was not inlined. In that case, the original
- * invocation must be restored with all of its original arguments. Why? HotSpot linkage for
- * {@link MethodHandle} intrinsics (see {@code MethodHandles::generate_method_handle_dispatch})
- * expects certain implicit arguments to be on the stack such as the MemberName suffix argument for
- * a call to one of the MethodHandle.linkTo* methods. An
- * {@linkplain MethodHandleNode#tryResolveTargetInvoke resolved} {@link MethodHandle} invocation
- * drops these arguments which means the interpreter won't find them.
+ * {@link MethodHandle} invocation target. Prior to
+ * https://bugs.openjdk.java.net/browse/JDK-8072008, this is required for when a
+ * {@link MethodHandle} call is resolved to a constant target but the target was not inlined. In
+ * that case, the original invocation must be restored with all of its original arguments. Why?
+ * HotSpot linkage for {@link MethodHandle} intrinsics (see
+ * {@code MethodHandles::generate_method_handle_dispatch}) expects certain implicit arguments to be
+ * on the stack such as the MemberName suffix argument for a call to one of the MethodHandle.linkTo*
+ * methods. An {@linkplain MethodHandleNode#tryResolveTargetInvoke resolved} {@link MethodHandle}
+ * invocation drops these arguments which means the interpreter won't find them.
  */
 @NodeInfo
 public final class ResolvedMethodHandleCallTargetNode extends MethodCallTargetNode implements Lowerable {
 
     public static final NodeClass<ResolvedMethodHandleCallTargetNode> TYPE = NodeClass.create(ResolvedMethodHandleCallTargetNode.class);
+
+    /**
+     * Creates a call target for an invocation on a direct target derived by resolving a constant
+     * {@link MethodHandle}.
+     */
+    public static MethodCallTargetNode create(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod,
+                    ValueNode[] originalArguments, JavaType originalReturnType) {
+        if (jdkMajorVersion() >= 1 && jdkMinorVersion() >= 8 && jdkMicroVersion() >= 0 && jdkUpdateVersion() >= 60) {
+            // https://bugs.openjdk.java.net/browse/JDK-8072008 is targeted for 8u60
+            return new MethodCallTargetNode(invokeKind, targetMethod, arguments, returnType);
+        }
+        return new ResolvedMethodHandleCallTargetNode(invokeKind, targetMethod, arguments, returnType, originalTargetMethod, originalArguments, originalReturnType);
+    }
+
     protected final ResolvedJavaMethod originalTargetMethod;
     protected final JavaType originalReturnType;
     @Input NodeInputList<ValueNode> originalArguments;
 
-    public ResolvedMethodHandleCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod,
+    protected ResolvedMethodHandleCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod,
                     ValueNode[] originalArguments, JavaType originalReturnType) {
         super(TYPE, invokeKind, targetMethod, arguments, returnType);
         this.originalTargetMethod = originalTargetMethod;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteTargetNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CallSiteTargetNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -30,7 +30,6 @@
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.replacements.nodes.*;
 
 @NodeInfo
@@ -46,9 +45,8 @@
         return arguments.get(0);
     }
 
-    public static ConstantNode tryFold(ValueNode initialCallSite, MetaAccessProvider metaAccess, Assumptions assumptions) {
-        ValueNode callSite = GraphUtil.originalValue(initialCallSite);
-        if (callSite.isConstant() && !callSite.isNullConstant()) {
+    public static ConstantNode tryFold(ValueNode callSite, MetaAccessProvider metaAccess, Assumptions assumptions) {
+        if (callSite != null && callSite.isConstant() && !callSite.isNullConstant()) {
             HotSpotObjectConstant c = (HotSpotObjectConstant) callSite.asConstant();
             JavaConstant target = c.getCallSiteTarget(assumptions);
             if (target != null) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -256,7 +256,7 @@
                 throw GraalInternalError.shouldNotReachHere();
         }
 
-        MethodCallTargetNode callTarget = new ResolvedMethodHandleCallTargetNode(targetInvokeKind, target, targetArguments, targetReturnType, original, arguments, returnType);
+        MethodCallTargetNode callTarget = ResolvedMethodHandleCallTargetNode.create(targetInvokeKind, target, targetArguments, targetReturnType, original, arguments, returnType);
 
         // The call target can have a different return type than the invoker,
         // e.g. the target returns an Object but the invoker void. In this case
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Wed Apr 01 16:36:28 2015 +0200
@@ -58,6 +58,9 @@
         @Option(help = "Inlines trivial methods during bytecode parsing.", type = OptionType.Expert)
         public static final StableOptionValue<Boolean> InlineDuringParsing = new StableOptionValue<>(false);
 
+        @Option(help = "Inlines intrinsic methods during bytecode parsing.", type = OptionType.Expert)
+        public static final StableOptionValue<Boolean> InlineIntrinsicsDuringParsing = new StableOptionValue<>(true);
+
         @Option(help = "Traces inlining performed during bytecode parsing.", type = OptionType.Debug)
         public static final StableOptionValue<Boolean> TraceInlineDuringParsing = new StableOptionValue<>(false);
 
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Wed Apr 01 16:36:28 2015 +0200
@@ -1745,7 +1745,7 @@
          */
         try (Indent indent = Debug.logAndIndent("LinearScan allocate")) {
 
-            try (Scope s = Debug.scope("LifetimeAnalysis")) {
+            try (Scope s = Debug.scope("LifetimeAnalysis"); DebugCloseable a = Debug.timer("LinearScan_LifetimeAnalysis").start()) {
                 numberInstructions();
                 printLir("Before register allocation", true);
                 computeLocalLiveSets();
@@ -1756,7 +1756,7 @@
                 throw Debug.handle(e);
             }
 
-            try (Scope s = Debug.scope("RegisterAllocation")) {
+            try (Scope s = Debug.scope("RegisterAllocation"); DebugCloseable a = Debug.timer("LinearScan_RegisterAllocation").start()) {
                 printIntervals("Before register allocation");
                 allocateRegisters();
             } catch (Throwable e) {
@@ -1764,14 +1764,14 @@
             }
 
             if (Options.LSRAOptimizeSpillPosition.getValue()) {
-                try (Scope s = Debug.scope("OptimizeSpillPosition")) {
+                try (Scope s = Debug.scope("OptimizeSpillPosition"); DebugCloseable a = Debug.timer("LinearScan_OptimizeSpillPosition").start()) {
                     optimizeSpillPosition();
                 } catch (Throwable e) {
                     throw Debug.handle(e);
                 }
             }
 
-            try (Scope s = Debug.scope("ResolveDataFlow")) {
+            try (Scope s = Debug.scope("ResolveDataFlow"); DebugCloseable a = Debug.timer("LinearScan_ResolveDataFlow").start()) {
                 resolveDataFlow();
             } catch (Throwable e) {
                 throw Debug.handle(e);
@@ -1787,14 +1787,14 @@
                     verify();
                 }
 
-                try (Scope s1 = Debug.scope("EliminateSpillMove")) {
+                try (Scope s1 = Debug.scope("EliminateSpillMove"); DebugCloseable a = Debug.timer("LinearScan_EliminateSpillMove").start()) {
                     eliminateSpillMoves();
                 } catch (Throwable e) {
                     throw Debug.handle(e);
                 }
                 printLir("After spill move elimination", true);
 
-                try (Scope s1 = Debug.scope("AssignLocations")) {
+                try (Scope s1 = Debug.scope("AssignLocations"); DebugCloseable a = Debug.timer("LinearScan_AssignLocations").start()) {
                     assignLocations();
                 } catch (Throwable e) {
                     throw Debug.handle(e);
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationStage.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationStage.java	Wed Apr 01 16:36:28 2015 +0200
@@ -40,8 +40,10 @@
         public static final NestedBooleanOptionValue LIROptRedundantMoveElimination = new NestedBooleanOptionValue(LIROptimization, true);
         @Option(help = "", type = OptionType.Debug)
         public static final NestedBooleanOptionValue LIROptNullCheckOptimizer = new NestedBooleanOptionValue(LIROptimization, true);
-        @Option(help = "", type = OptionType.Debug)
-        public static final OptionValue<Boolean> LIROptMoveProfiling = new OptionValue<>(false);
+        @Option(help = "Enables profiling of move types on LIR level. " +
+                       "Move types are for example stores (register to stack), " +
+                       "constant loads (constant to register) or copies (register to register).", type = OptionType.Debug)
+        public static final OptionValue<Boolean> LIRProfileMoves = new OptionValue<>(false);
         // @formatter:on
     }
 
@@ -58,7 +60,7 @@
         if (Options.LIROptNullCheckOptimizer.getValue()) {
             appendPhase(new NullCheckOptimizer());
         }
-        if (Options.LIROptMoveProfiling.getValue()) {
+        if (Options.LIRProfileMoves.getValue()) {
             appendPhase(new MoveProfiling());
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingReadNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingReadNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -81,4 +81,11 @@
     public FixedAccessNode asFixedNode() {
         return graph().add(new ReadNode(object(), accessLocation(), stamp(), getGuard(), getBarrierType()));
     }
+
+    @Override
+    public boolean verify() {
+        MemoryNode lla = getLastLocationAccess();
+        assert lla != null || getLocationIdentity().isImmutable() : "lastLocationAccess of " + this + " shouldn't be null for mutable location identity " + getLocationIdentity();
+        return super.verify();
+    }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Wed Apr 01 16:36:28 2015 +0200
@@ -289,9 +289,8 @@
                     MemoryAccess access = (MemoryAccess) node;
                     if (access.getLastLocationAccess() == anchor) {
                         MemoryNode lastLocationAccess = state.getLastLocationAccess(access.getLocationIdentity());
-                        if (lastLocationAccess != null) {
-                            access.setLastLocationAccess(lastLocationAccess);
-                        }
+                        assert lastLocationAccess != null;
+                        access.setLastLocationAccess(lastLocationAccess);
                     }
                 }
             }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Wed Apr 01 16:36:28 2015 +0200
@@ -97,7 +97,7 @@
     public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
         ResolvedJavaMethod subst = getMethodSubstitutionMethod(method);
         if (subst != null) {
-            if (b.parsingReplacement() || InlineDuringParsing.getValue()) {
+            if (b.parsingReplacement() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) {
                 // Forced inlining of intrinsics
                 return new InlineInfo(subst, true, true);
             }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -80,7 +80,7 @@
                 if (state1.getVirtualObject() == state2.getVirtualObject()) {
                     // the same virtual objects will always have the same contents
                     tool.replaceWithValue(ConstantNode.forBoolean(true, graph()));
-                } else if (state1.getVirtualObject().entryCount() == state2.getVirtualObject().entryCount()) {
+                } else if (state1.getVirtualObject().entryCount() == state2.getVirtualObject().entryCount() && state1.getState() == EscapeState.Virtual && state2.getState() == EscapeState.Virtual) {
                     int entryCount = state1.getVirtualObject().entryCount();
                     boolean allEqual = true;
                     for (int i = 0; i < entryCount; i++) {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/AssertionNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/AssertionNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -79,8 +79,10 @@
 
     public void generate(NodeLIRBuilderTool generator) {
         assert compileTimeAssertion;
-        if (value.isConstant() && value.asJavaConstant().asInt() == 0) {
-            throw new GraalInternalError("%s: failed compile-time assertion: %s", this, message);
+        if (value.isConstant()) {
+            if (value.asJavaConstant().asInt() == 0) {
+                throw new GraalInternalError("%s: failed compile-time assertion: %s", this, message);
+            }
         } else {
             throw new GraalInternalError("%s: failed compile-time assertion (value %s): %s", this, value, message);
         }
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Wed Apr 01 16:36:28 2015 +0200
@@ -215,11 +215,15 @@
         Runnable r = new Runnable() {
             @Override
             public void run() {
+                boolean success = true;
                 try (Scope s = Debug.scope("Truffle", new TruffleDebugJavaMethod(optimizedCallTarget))) {
                     truffleCompiler.compileMethod(optimizedCallTarget);
-                    optimizedCallTarget.notifyCompilationFinished();
                 } catch (Throwable e) {
                     optimizedCallTarget.notifyCompilationFailed(e);
+                    success = false;
+                } finally {
+                    optimizedCallTarget.notifyCompilationFinished(success);
+
                 }
             }
         };
@@ -248,7 +252,10 @@
         if (codeTask != null && isCompiling(optimizedCallTarget)) {
             this.compilations.remove(optimizedCallTarget);
             boolean result = codeTask.cancel(true);
-            getCompilationNotify().notifyCompilationDequeued(optimizedCallTarget, source, reason);
+            if (result) {
+                optimizedCallTarget.notifyCompilationFinished(false);
+                getCompilationNotify().notifyCompilationDequeued(optimizedCallTarget, source, reason);
+            }
             return result;
         }
         return false;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle.test/sl/TestOSR.sl	Wed Apr 01 16:36:28 2015 +0200
@@ -0,0 +1,13 @@
+function test() {
+  i = 0;
+  sum = 0;  
+  while (i < 300000) { 
+    sum = sum +  i;
+    i = i + 1;  
+  }
+  return sum;
+}
+
+function main() {  
+  test();
+}  
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Wed Apr 01 16:36:28 2015 +0200
@@ -205,6 +205,47 @@
     }
 
     @Test
+    public void constantValueInertToolNodeInstrumentListener() {
+        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() {
+
+            public ToolNode getToolNode(Probe p) {
+                return null;
+            }
+
+        }, null);
+        probe.attach(instrument);
+
+        assertPartialEvalEquals("constant42", root);
+    }
+
+    @Test
+    public void constantValueInertToolNode() {
+        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() {
+
+            public ToolNode getToolNode(Probe p) {
+                return new ToolNode() {
+                };
+            }
+
+        }, null);
+        probe.attach(instrument);
+
+        assertPartialEvalEquals("constant42", root);
+    }
+
+    @Test
     public void instrumentDeopt() {
         final FrameDescriptor fd = new FrameDescriptor();
         final AbstractTestNode result = new ConstantTestNode(42);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java	Wed Apr 01 16:36:28 2015 +0200
@@ -29,7 +29,7 @@
 public class DefaultLoopNodeFactory implements LoopNodeFactory {
 
     public LoopNode create(RepeatingNode repeatingNode) {
-        return new OptimizedLoopNode(repeatingNode);
+        return OptimizedOSRLoopNode.create(repeatingNode);
     }
 
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Apr 01 16:36:28 2015 +0200
@@ -27,7 +27,6 @@
 import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
-import java.util.concurrent.*;
 import java.util.concurrent.atomic.*;
 import java.util.stream.*;
 
@@ -72,6 +71,7 @@
 
     private TruffleInlining inlining;
     private int cachedNonTrivialNodeCount = -1;
+    private boolean compiling;
 
     /**
      * When this call target is inlined, the inlining {@link InstalledCode} registers this
@@ -102,6 +102,10 @@
         this.nodeRewritingAssumption = new CyclicAssumption("nodeRewritingAssumption of " + rootNode.toString());
     }
 
+    public final boolean isCompiling() {
+        return compiling;
+    }
+
     private static RootNode cloneRootNode(RootNode root) {
         if (root == null || !root.isCloningAllowed()) {
             return null;
@@ -174,7 +178,7 @@
     }
 
     @ExplodeLoop
-    private void profileArguments(Object[] args) {
+    void profileArguments(Object[] args) {
         if (profiledArgumentTypesAssumption == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             initializeProfiledArgumentTypes(args);
@@ -272,7 +276,12 @@
         VirtualFrame frame = createFrame(getRootNode().getFrameDescriptor(), args);
         Object result = callProxy(frame);
 
-        // Profile call return type
+        profileReturnType(result);
+
+        return result;
+    }
+
+    void profileReturnType(Object result) {
         if (profiledReturnTypeAssumption == null) {
             if (TruffleReturnTypeSpeculation.getValue()) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
@@ -286,8 +295,6 @@
                 profiledReturnTypeAssumption.invalidate();
             }
         }
-
-        return result;
     }
 
     @Override
@@ -321,25 +328,16 @@
             this.runtime.reinstallStubs();
         } else {
             compilationProfile.reportInterpreterCall();
-            if (compilationPolicy.shouldCompile(compilationProfile, getCompilerOptions())) {
+            if (!isCompiling() && compilationPolicy.shouldCompile(compilationProfile, getCompilerOptions())) {
                 compile();
             }
         }
     }
 
     public void compile() {
-        compile(TruffleBackgroundCompilation.getValue() && !TruffleCompilationExceptionsAreThrown.getValue());
-    }
-
-    public void compile(boolean mayBeAsynchronous) {
-        if (!runtime.isCompiling(this)) {
-            runtime.compile(this, mayBeAsynchronous);
-        } else if (!mayBeAsynchronous && runtime.isCompiling(this)) {
-            try {
-                runtime.waitForCompilation(this, 20000);
-            } catch (ExecutionException | TimeoutException e) {
-                Debug.log(3, e.getMessage());
-            }
+        if (!isCompiling()) {
+            compiling = true;
+            runtime.compile(this, TruffleBackgroundCompilation.getValue() && !TruffleCompilationExceptionsAreThrown.getValue());
         }
     }
 
@@ -363,11 +361,11 @@
         }
     }
 
-    public void notifyCompilationFinished() {
-        // Compilation was successful.
-        if (inlining != null) {
+    public void notifyCompilationFinished(boolean successful) {
+        if (successful && inlining != null) {
             dequeueInlinedCallSites(inlining);
         }
+        compiling = false;
     }
 
     private void dequeueInlinedCallSites(TruffleInlining parentDecision) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -0,0 +1,213 @@
+/*
+ * 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.truffle;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+public final class OptimizedOSRLoopNode extends LoopNode implements ReplaceObserver {
+
+    private static final int OSR_THRESHOLD = TruffleCompilerOptions.TruffleOSRCompilationThreshold.getValue();
+
+    private int interpreterLoopCount;
+    private OptimizedCallTarget compiledTarget;
+
+    @Child private RepeatingNode repeatableNode;
+
+    private OptimizedOSRLoopNode(RepeatingNode repeatableNode) {
+        this.repeatableNode = repeatableNode;
+    }
+
+    @Override
+    public Node copy() {
+        OptimizedOSRLoopNode copy = (OptimizedOSRLoopNode) super.copy();
+        copy.compiledTarget = null;
+        copy.interpreterLoopCount = 0;
+        return copy;
+    }
+
+    @Override
+    public RepeatingNode getRepeatingNode() {
+        return repeatableNode;
+    }
+
+    @Override
+    public void executeLoop(VirtualFrame frame) {
+        if (CompilerDirectives.inInterpreter()) {
+            boolean done = false;
+            while (!done) {
+                if (compiledTarget == null) {
+                    done = profilingLoop(frame);
+                } else {
+                    done = compilingLoop(frame);
+                }
+            }
+        } else {
+            while (repeatableNode.executeRepeating(frame)) {
+                if (CompilerDirectives.inInterpreter()) {
+                    // compiled method got invalidated. We might need OSR again.
+                    executeLoop(frame);
+                    return;
+                }
+            }
+        }
+    }
+
+    private boolean profilingLoop(VirtualFrame frame) {
+        int overflowLoopCount = Integer.MAX_VALUE - OSR_THRESHOLD + interpreterLoopCount;
+        try {
+            while (repeatableNode.executeRepeating(frame)) {
+                try {
+                    overflowLoopCount = Math.incrementExact(overflowLoopCount);
+                } catch (ArithmeticException e) {
+                    compileLoop(frame);
+                    return false;
+                }
+            }
+        } finally {
+            reportLoopCount(overflowLoopCount - Integer.MAX_VALUE + OSR_THRESHOLD - interpreterLoopCount);
+        }
+        return true;
+    }
+
+    private boolean compilingLoop(VirtualFrame frame) {
+        int iterations = 0;
+        try {
+            do {
+                OptimizedCallTarget target = compiledTarget;
+                if (target == null) {
+                    return false;
+                } else if (target.isValid()) {
+                    Object result = target.callDirect(new Object[]{frame});
+                    if (result == Boolean.TRUE) {
+                        // loop is done. No further repetitions necessary.
+                        return true;
+                    } else {
+                        invalidate(this, "OSR compilation got invalidated");
+                        return false;
+                    }
+                } else if (!target.isCompiling()) {
+                    invalidate(this, "OSR compilation failed or cancelled");
+                    return false;
+                }
+                iterations++;
+            } while (repeatableNode.executeRepeating(frame));
+        } finally {
+            reportLoopCount(iterations);
+        }
+        return true;
+    }
+
+    private void compileLoop(VirtualFrame frame) {
+        atomic(new Runnable() {
+            public void run() {
+                /*
+                 * Compilations need to run atomically as they may be scheduled by multiple threads
+                 * at the same time. This strategy lets the first thread win. Later threads will not
+                 * issue compiles.
+                 */
+                if (compiledTarget == null) {
+                    compiledTarget = compileImpl(frame);
+                    if (compiledTarget == null) {
+                        interpreterLoopCount = 0;
+                    }
+                }
+            }
+        });
+    }
+
+    private OptimizedCallTarget compileImpl(VirtualFrame frame) {
+        Node parent = getParent();
+        OptimizedCallTarget target = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(new OSRRootNode(this));
+        // to avoid a deopt on first call we provide some profiling information
+        target.profileReturnType(Boolean.TRUE);
+        target.profileReturnType(Boolean.FALSE);
+        target.profileArguments(new Object[]{frame});
+        // let the old parent re-adopt the children
+        parent.adoptChildren();
+        target.compile();
+        return target;
+    }
+
+    private void reportLoopCount(int reportIterations) {
+        if (reportIterations != 0) {
+            interpreterLoopCount += reportIterations;
+            getRootNode().reportLoopCount(reportIterations);
+        }
+    }
+
+    public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
+        invalidate(newNode, reason);
+    }
+
+    private void invalidate(Object source, CharSequence reason) {
+        OptimizedCallTarget target = this.compiledTarget;
+        if (target != null) {
+            target.invalidate(source, reason);
+            compiledTarget = null;
+            interpreterLoopCount = 0;
+        }
+    }
+
+    public static LoopNode create(RepeatingNode repeat) {
+        if (TruffleCompilerOptions.TruffleOSR.getValue()) {
+            return new OptimizedOSRLoopNode(repeat);
+        } else {
+            return new OptimizedLoopNode(repeat);
+        }
+    }
+
+    private static class OSRRootNode extends RootNode {
+
+        @Child private OptimizedOSRLoopNode loopNode;
+
+        public OSRRootNode(OptimizedOSRLoopNode loop) {
+            super(loop.getSourceSection(), loop.getRootNode().getFrameDescriptor());
+            this.loopNode = loop;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            VirtualFrame parentFrame = (VirtualFrame) frame.getArguments()[0];
+            while (loopNode.getRepeatingNode().executeRepeating(parentFrame)) {
+                if (CompilerDirectives.inInterpreter()) {
+                    return Boolean.FALSE;
+                }
+            }
+            return Boolean.TRUE;
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return loopNode.getRepeatingNode().toString() + "<OSR>";
+        }
+
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Wed Apr 01 16:36:28 2015 +0200
@@ -98,6 +98,12 @@
     @Option(help = "Experimental. New splitting only: Split everything aggressively. ", type = OptionType.Debug)
     public static final OptionValue<Boolean> TruffleSplittingAggressive = new OptionValue<>(false);
 
+    @Option(help = "Enable on stack replacement for Truffle loops.", type = OptionType.Debug)
+    public static final OptionValue<Boolean> TruffleOSR = new OptionValue<>(true);
+
+    @Option(help = "Number of loop iterations until on-stack-replacement compilation is triggered.", type = OptionType.Debug)
+    public static final OptionValue<Integer> TruffleOSRCompilationThreshold = new OptionValue<>(10000);
+
     @Option(help = "Disable call target splitting if tree size exceeds this limit", type = OptionType.Debug)
     public static final OptionValue<Integer> TruffleSplittingMaxCalleeSize = new OptionValue<>(100);
 
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Wed Apr 01 16:36:28 2015 +0200
@@ -35,6 +35,11 @@
 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.impl.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
 
 /**
  * <h3>AST Instrumentation</h3>
@@ -339,142 +344,6 @@
 
     }
 
-    private abstract class TestLanguageNode extends Node {
-        public abstract Object execute(VirtualFrame vFrame);
-
-        @Override
-        public boolean isInstrumentable() {
-            return true;
-        }
-
-        @Override
-        public WrapperNode createWrapperNode() {
-            return new TestLanguageWrapperNode(this);
-        }
-    }
-
-    @NodeInfo(cost = NodeCost.NONE)
-    private class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode {
-        @Child private TestLanguageNode child;
-        @Child private ProbeNode probeNode;
-
-        public TestLanguageWrapperNode(TestLanguageNode child) {
-            assert !(child instanceof TestLanguageWrapperNode);
-            this.child = child;
-        }
-
-        @Override
-        public String instrumentationInfo() {
-            return "Wrapper node for testing";
-        }
-
-        @Override
-        public boolean isInstrumentable() {
-            return false;
-        }
-
-        @Override
-        public void insertProbe(ProbeNode newProbeNode) {
-            this.probeNode = newProbeNode;
-        }
-
-        @Override
-        public Probe getProbe() {
-            return probeNode.getProbe();
-        }
-
-        @Override
-        public Node getChild() {
-            return child;
-        }
-
-        @Override
-        public Object execute(VirtualFrame vFrame) {
-            probeNode.enter(child, vFrame);
-            Object result;
-
-            try {
-                result = child.execute(vFrame);
-                probeNode.returnValue(child, vFrame, result);
-            } catch (KillException e) {
-                throw (e);
-            } catch (Exception e) {
-                probeNode.returnExceptional(child, vFrame, e);
-                throw (e);
-            }
-
-            return result;
-        }
-    }
-
-    /**
-     * A simple node for our test language to store a value.
-     */
-    private class TestValueNode extends TestLanguageNode {
-        private final int value;
-
-        public TestValueNode(int value) {
-            this.value = value;
-        }
-
-        @Override
-        public Object execute(VirtualFrame vFrame) {
-            return new Integer(this.value);
-        }
-    }
-
-    /**
-     * A node for our test language that adds up two {@link TestValueNode}s.
-     */
-    private class TestAdditionNode extends TestLanguageNode {
-        @Child private TestLanguageNode leftChild;
-        @Child private TestLanguageNode rightChild;
-
-        public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) {
-            this.leftChild = insert(leftChild);
-            this.rightChild = insert(rightChild);
-        }
-
-        @Override
-        public Object execute(VirtualFrame vFrame) {
-            return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue());
-        }
-    }
-
-    /**
-     * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST
-     * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
-     * completes an AST. The root nodes serves as our entry point into a program.
-     */
-    private class TestRootNode extends RootNode {
-        @Child private TestLanguageNode body;
-
-        /**
-         * This constructor emulates the global machinery that applies registered probers to every
-         * newly created AST. Global registry is not used, since that would interfere with other
-         * tests run in the same environment.
-         */
-        public TestRootNode(TestLanguageNode body) {
-            super(null);
-            this.body = body;
-        }
-
-        @Override
-        public Object execute(VirtualFrame vFrame) {
-            return body.execute(vFrame);
-        }
-
-        @Override
-        public boolean isCloningAllowed() {
-            return true;
-        }
-
-        @Override
-        public void applyInstrumentation() {
-            Probe.applyASTProbers(body);
-        }
-    }
-
     private interface TestCounter {
 
         int enterCount();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Wed Apr 01 16:36:28 2015 +0200
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ *
+ * 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 com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Tests instrumentation where a client can attach a node that gets attached into the AST.
+ */
+class InstrumentationTestNodes {
+
+    abstract static class TestLanguageNode extends Node {
+        public abstract Object execute(VirtualFrame vFrame);
+
+        @Override
+        public boolean isInstrumentable() {
+            return true;
+        }
+
+        @Override
+        public WrapperNode createWrapperNode() {
+            return new TestLanguageWrapperNode(this);
+        }
+    }
+
+    @NodeInfo(cost = NodeCost.NONE)
+    static class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode {
+        @Child private TestLanguageNode child;
+        @Child private ProbeNode probeNode;
+
+        public TestLanguageWrapperNode(TestLanguageNode child) {
+            assert !(child instanceof TestLanguageWrapperNode);
+            this.child = child;
+        }
+
+        @Override
+        public String instrumentationInfo() {
+            return "Wrapper node for testing";
+        }
+
+        @Override
+        public boolean isInstrumentable() {
+            return false;
+        }
+
+        @Override
+        public void insertProbe(ProbeNode newProbeNode) {
+            this.probeNode = newProbeNode;
+        }
+
+        @Override
+        public Probe getProbe() {
+            return probeNode.getProbe();
+        }
+
+        @Override
+        public Node getChild() {
+            return child;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            probeNode.enter(child, vFrame);
+            Object result;
+
+            try {
+                result = child.execute(vFrame);
+                probeNode.returnValue(child, vFrame, result);
+            } catch (KillException e) {
+                throw (e);
+            } catch (Exception e) {
+                probeNode.returnExceptional(child, vFrame, e);
+                throw (e);
+            }
+
+            return result;
+        }
+    }
+
+    /**
+     * A simple node for our test language to store a value.
+     */
+    static class TestValueNode extends TestLanguageNode {
+        private final int value;
+
+        public TestValueNode(int value) {
+            this.value = value;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return new Integer(this.value);
+        }
+    }
+
+    /**
+     * A node for our test language that adds up two {@link TestValueNode}s.
+     */
+    static class TestAdditionNode extends TestLanguageNode {
+        @Child private TestLanguageNode leftChild;
+        @Child private TestLanguageNode rightChild;
+
+        public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) {
+            this.leftChild = insert(leftChild);
+            this.rightChild = insert(rightChild);
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue());
+        }
+    }
+
+    /**
+     * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST
+     * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
+     * completes an AST. The root nodes serves as our entry point into a program.
+     */
+    static class TestRootNode extends RootNode {
+        @Child private TestLanguageNode body;
+
+        /**
+         * This constructor emulates the global machinery that applies registered probers to every
+         * newly created AST. Global registry is not used, since that would interfere with other
+         * tests run in the same environment.
+         */
+        public TestRootNode(TestLanguageNode body) {
+            super(null);
+            this.body = body;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return body.execute(vFrame);
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return true;
+        }
+
+        @Override
+        public void applyInstrumentation() {
+            Probe.applyASTProbers(body);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java	Wed Apr 01 16:36:28 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.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/tools/TestNodes.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TestNodes.java	Wed Apr 01 16:36:28 2015 +0200
@@ -32,7 +32,7 @@
 /**
  * Nodes and an {@linkplain CallTarget executable ASTs} for testing.
  */
-public class TestNodes {
+class TestNodes {
 
     /**
      * A fake source used for testing: empty line 1, expression on line 2.
@@ -44,7 +44,7 @@
     /**
      * An executable addition expression that evaluates to 13.
      */
-    public static CallTarget createExpr13TestCallTarget() {
+    static CallTarget createExpr13TestCallTarget() {
         final RootNode rootNode = createExpr13TestRootNode();
         return Truffle.getRuntime().createCallTarget(rootNode);
     }
@@ -52,7 +52,7 @@
     /**
      * Root holding an addition expression that evaluates to 13.
      */
-    public static RootNode createExpr13TestRootNode() {
+    static RootNode createExpr13TestRootNode() {
         final TestLanguageNode ast = createExpr13AST();
         final TestRootNode rootNode = new TestRootNode(ast);
         rootNode.adoptChildren();
@@ -62,7 +62,7 @@
     /**
      * Addition expression that evaluates to 13, with faked source attribution.
      */
-    public static TestLanguageNode createExpr13AST() {
+    static TestLanguageNode createExpr13AST() {
         final SourceSection leftSourceSection = expr13Source.createSection("left", 1, 1);
         final TestValueNode leftValueNode = new TestValueNode(6, leftSourceSection);
         final SourceSection rightSourceSection = expr13Source.createSection("right", 3, 1);
@@ -71,7 +71,7 @@
         return new TestAddNode(leftValueNode, rightValueNode, exprSourceSection);
     }
 
-    public abstract static class TestLanguageNode extends Node {
+    abstract static class TestLanguageNode extends Node {
         public abstract Object execute(VirtualFrame frame);
 
         public TestLanguageNode() {
@@ -93,7 +93,7 @@
     }
 
     @NodeInfo(cost = NodeCost.NONE)
-    public static class TestWrapperNode extends TestLanguageNode implements WrapperNode {
+    static class TestWrapperNode extends TestLanguageNode implements WrapperNode {
         @Child private TestLanguageNode child;
         @Child private ProbeNode probeNode;
 
@@ -151,7 +151,7 @@
      * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
      * completes an AST. The root nodes serves as our entry point into a program.
      */
-    public static class TestRootNode extends RootNode {
+    static class TestRootNode extends RootNode {
         @Child private TestLanguageNode body;
 
         /**
@@ -180,7 +180,7 @@
         }
     }
 
-    public static class TestValueNode extends TestLanguageNode {
+    static class TestValueNode extends TestLanguageNode {
         private final int value;
 
         public TestValueNode(int value) {
@@ -198,7 +198,7 @@
         }
     }
 
-    public static class TestAddNode extends TestLanguageNode {
+    static class TestAddNode extends TestLanguageNode {
         @Child private TestLanguageNode leftChild;
         @Child private TestLanguageNode rightChild;
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Wed Apr 01 16:36:28 2015 +0200
@@ -194,6 +194,19 @@
         return new ASTInstrument(astListener, 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.
+     *
+     * @param toolNodeListener a listener for the tool that can request an AST fragment
+     * @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);
+    }
+
     // TODO (mlvdv) experimental
     /**
      * For implementation testing.
@@ -278,7 +291,7 @@
             if (instrumentNode != null) {
                 if (instrumentNode.getInstrument() == this) {
                     // Found the match at the head of the chain
-                    return instrumentNode.nextInstrument;
+                    return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
                 found = instrumentNode.removeFromChain(BasicInstrument.this);
@@ -298,29 +311,29 @@
 
             public void enter(Node node, VirtualFrame vFrame) {
                 BasicInstrument.this.instrumentListener.enter(BasicInstrument.this.probe);
-                if (nextInstrument != null) {
-                    nextInstrument.enter(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
                 BasicInstrument.this.instrumentListener.returnVoid(BasicInstrument.this.probe);
-                if (nextInstrument != null) {
-                    nextInstrument.returnVoid(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
                 BasicInstrument.this.instrumentListener.returnValue(BasicInstrument.this.probe, result);
-                if (nextInstrument != null) {
-                    nextInstrument.returnValue(node, vFrame, 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);
-                if (nextInstrument != null) {
-                    nextInstrument.returnExceptional(node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
@@ -362,7 +375,7 @@
             if (instrumentNode != null) {
                 if (instrumentNode.getInstrument() == this) {
                     // Found the match at the head of the chain
-                    return instrumentNode.nextInstrument;
+                    return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
                 found = instrumentNode.removeFromChain(ASTInstrument.this);
@@ -382,29 +395,29 @@
 
             public void enter(Node node, VirtualFrame vFrame) {
                 ASTInstrument.this.astListener.enter(ASTInstrument.this.probe, node, vFrame);
-                if (nextInstrument != null) {
-                    nextInstrument.enter(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);
-                if (nextInstrument != null) {
-                    nextInstrument.returnVoid(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);
-                if (nextInstrument != null) {
-                    nextInstrument.returnValue(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);
-                if (nextInstrument != null) {
-                    nextInstrument.returnExceptional(node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
@@ -416,6 +429,104 @@
 
     }
 
+    /**
+     * An instrument that propagates events to an instance of {@link ASTInstrumentListener}.
+     */
+    private static final class ToolNodeInstrument extends Instrument {
+
+        /**
+         * Tool-supplied listener for AST events.
+         */
+        private final ToolNodeInstrumentListener toolNodeListener;
+
+        private ToolNodeInstrument(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.toolNodeListener = toolNodeListener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new ToolInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(ToolNodeInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class ToolInstrumentNode extends AbstractInstrumentNode {
+
+            @Child ToolNode toolNode;
+
+            private ToolInstrumentNode(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;
+                        adoptChildren();
+                        ToolNodeInstrument.this.probe.invalidateProbeUnchanged();
+                    }
+                }
+                if (toolNode != null) {
+                    toolNode.enter(node, vFrame);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                if (toolNode != null) {
+                    toolNode.returnVoid(node, vFrame);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                if (toolNode != null) {
+                    toolNode.returnValue(node, vFrame, result);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                if (toolNode != null) {
+                    toolNode.returnExceptional(node, vFrame, exception);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : toolNodeListener.getClass().getSimpleName();
+            }
+        }
+
+    }
+
     public interface TruffleOptListener {
         void notifyIsCompiled(boolean isCompiled);
     }
@@ -440,7 +551,7 @@
             if (instrumentNode != null) {
                 if (instrumentNode.getInstrument() == this) {
                     // Found the match at the head of the chain
-                    return instrumentNode.nextInstrument;
+                    return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
                 found = instrumentNode.removeFromChain(TruffleOptInstrument.this);
@@ -466,26 +577,26 @@
                     this.isCompiled = CompilerDirectives.inCompiledCode();
                     TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled);
                 }
-                if (nextInstrument != null) {
-                    nextInstrument.enter(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                if (nextInstrument != null) {
-                    nextInstrument.returnVoid(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                if (nextInstrument != null) {
-                    nextInstrument.returnValue(node, vFrame, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                if (nextInstrument != null) {
-                    nextInstrument.returnExceptional(node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
@@ -500,10 +611,10 @@
     @NodeInfo(cost = NodeCost.NONE)
     abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode {
 
-        @Child protected AbstractInstrumentNode nextInstrument;
+        @Child protected AbstractInstrumentNode nextInstrumentNode;
 
         protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) {
-            this.nextInstrument = nextNode;
+            this.nextInstrumentNode = nextNode;
         }
 
         @Override
@@ -527,21 +638,21 @@
          */
         private boolean removeFromChain(Instrument instrument) {
             assert getInstrument() != instrument;
-            if (nextInstrument == null) {
+            if (nextInstrumentNode == null) {
                 return false;
             }
-            if (nextInstrument.getInstrument() == instrument) {
+            if (nextInstrumentNode.getInstrument() == instrument) {
                 // Next is the one to remove
-                if (nextInstrument.nextInstrument == null) {
+                if (nextInstrumentNode.nextInstrumentNode == null) {
                     // Next is at the tail; just forget
-                    nextInstrument = null;
+                    nextInstrumentNode = null;
                 } else {
                     // Replace next with its successor
-                    nextInstrument.replace(nextInstrument.nextInstrument);
+                    nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode);
                 }
                 return true;
             }
-            return nextInstrument.removeFromChain(instrument);
+            return nextInstrumentNode.removeFromChain(instrument);
         }
 
         protected String getInstrumentInfo() {
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Wed Apr 01 16:36:28 2015 +0200
@@ -336,7 +336,7 @@
         }
     }
 
-    private void invalidateProbeUnchanged() {
+    void invalidateProbeUnchanged() {
         probeStateUnchangedCyclic.invalidate();
     }
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -129,7 +129,7 @@
     /**
      * First {@link AbstractInstrumentNode} node in chain; {@code null} of no instruments in chain.
      */
-    @Child protected AbstractInstrumentNode firstInstrument;
+    @Child protected AbstractInstrumentNode firstInstrumentNode;
 
     @Override
     public boolean isInstrumentable() {
@@ -156,29 +156,29 @@
         if (trap != null) {
             trap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize());
         }
-        if (firstInstrument != null) {
-            firstInstrument.enter(node, vFrame);
+        if (firstInstrumentNode != null) {
+            firstInstrumentNode.enter(node, vFrame);
         }
     }
 
     public void returnVoid(Node node, VirtualFrame vFrame) {
         this.probe.checkProbeUnchanged();
-        if (firstInstrument != null) {
-            firstInstrument.returnVoid(node, vFrame);
+        if (firstInstrumentNode != null) {
+            firstInstrumentNode.returnVoid(node, vFrame);
         }
     }
 
     public void returnValue(Node node, VirtualFrame vFrame, Object result) {
         this.probe.checkProbeUnchanged();
-        if (firstInstrument != null) {
-            firstInstrument.returnValue(node, vFrame, result);
+        if (firstInstrumentNode != null) {
+            firstInstrumentNode.returnValue(node, vFrame, result);
         }
     }
 
     public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
         this.probe.checkProbeUnchanged();
-        if (firstInstrument != null) {
-            firstInstrument.returnExceptional(node, vFrame, exception);
+        if (firstInstrumentNode != null) {
+            firstInstrumentNode.returnExceptional(node, vFrame, exception);
         }
     }
 
@@ -194,7 +194,7 @@
         assert instrument.getProbe() == probe;
         // The existing chain of nodes may be empty
         // Attach the modified chain.
-        firstInstrument = insert(instrument.addToChain(firstInstrument));
+        firstInstrumentNode = insert(instrument.addToChain(firstInstrumentNode));
     }
 
     /**
@@ -205,11 +205,11 @@
     @TruffleBoundary
     void removeInstrument(Instrument instrument) {
         assert instrument.getProbe() == probe;
-        final AbstractInstrumentNode modifiedChain = instrument.removeFromChain(firstInstrument);
+        final AbstractInstrumentNode modifiedChain = instrument.removeFromChain(firstInstrumentNode);
         if (modifiedChain == null) {
-            firstInstrument = null;
+            firstInstrumentNode = null;
         } else {
-            firstInstrument = insert(modifiedChain);
+            firstInstrumentNode = insert(modifiedChain);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java	Wed Apr 01 16:36:28 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 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;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java	Wed Apr 01 16:36:28 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 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.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -64,7 +64,7 @@
      * @param node the node to format.
      * @return a formatted source section string
      */
-    private static String formatSourceSection(Node node) {
+    public static String formatSourceSection(Node node) {
         if (node == null) {
             return "<unknown>";
         }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLRepeatingNode.java	Wed Apr 01 16:36:15 2015 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLRepeatingNode.java	Wed Apr 01 16:36:28 2015 +0200
@@ -93,4 +93,9 @@
         }
     }
 
+    @Override
+    public String toString() {
+        return SLStatementNode.formatSourceSection(this);
+    }
+
 }
--- a/mx/mx_graal.py	Wed Apr 01 16:36:15 2015 +0200
+++ b/mx/mx_graal.py	Wed Apr 01 16:36:28 2015 +0200
@@ -83,6 +83,9 @@
 
 _minVersion = mx.VersionSpec('1.8')
 
+# max version (first _unsupported_ version)
+_untilVersion = mx.VersionSpec('1.8.0_40')
+
 class JDKDeployedDist:
     def __init__(self, name, isExtension):
         self.name = name
@@ -2527,7 +2530,9 @@
 def mx_post_parse_cmd_line(opts):  #
     # TODO _minVersion check could probably be part of a Suite in mx?
     if mx.java().version < _minVersion:
-        mx.abort('Requires Java version ' + str(_minVersion) + ' or greater, got version ' + str(mx.java().version))
+        mx.abort('Requires Java version ' + str(_minVersion) + ' or greater for JAVA_HOME, got version ' + str(mx.java().version))
+    if mx.java().version >= _untilVersion:
+        mx.abort('Requires Java version strictly before ' + str(_untilVersion) + ' for JAVA_HOME, got version ' + str(mx.java().version))
 
     if _vmSourcesAvailable:
         if hasattr(opts, 'vm') and opts.vm is not None:
--- a/mxtool/mx.py	Wed Apr 01 16:36:15 2015 +0200
+++ b/mxtool/mx.py	Wed Apr 01 16:36:28 2015 +0200
@@ -1660,6 +1660,8 @@
     elif get_os() == 'linux':
         base = '/usr/lib/jvm'
         candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))]
+        base = '/usr/java'
+        candidateJdks += [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))]
     elif get_os() == 'solaris':
         base = '/usr/jdk/instances'
         candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))]
@@ -5331,7 +5333,7 @@
     if opts.extra_java_homes:
         for java_home in opts.extra_java_homes.split(os.pathsep):
             extraJdk = JavaConfig(java_home, opts.java_dbg_port)
-            if extraJdk > defaultJdk:
+            if extraJdk.javaCompliance > defaultJdk.javaCompliance:
                 abort('Secondary JDK ' + extraJdk.jdk + ' has higher compliance level than default JDK ' + defaultJdk.jdk)
             _java_homes.append(extraJdk)