changeset 21214:bfb6e742ad0a

Merge
author Stefan Anzinger <stefan.anzinger@oracle.com>
date Mon, 04 May 2015 19:34:51 +0200
parents 0028ab94d268 (current diff) fbe449ca9707 (diff)
children e11eb6ec180e
files graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/PhiResolver.java graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_instanceof01.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/PhiResolver.java graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SpliceInstrumentListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SplicedNode.java
diffstat 61 files changed, 1657 insertions(+), 945 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Mon May 04 19:12:50 2015 +0200
+++ b/CHANGELOG.md	Mon May 04 19:34:51 2015 +0200
@@ -4,15 +4,18 @@
 
 
 ## `tip`
+...
+
+## Version 0.7
+29-Apr-2015, [Repository Revision](http://hg.openjdk.java.net/graal/graal/shortlog/graal-0.7)
 ### Graal
 * By default the Graal code is now only compiled by C1 which should improve application start-up.
 * Merged with jdk8u40-b25.
 * The Graal class loader now loads all lib/graal/graal*.jar jars.
-* Fast Graal services (see com.oracle.graal.api.runtime.Service) are now looked up using service files in lib/graal/services
+* Fast Graal services (see com.oracle.graal.api.runtime.Service) are now looked up using service files in lib/graal/services.
 * Add utilities ModifiersProvider#isConcrete, ResolvedJavaMethod#hasBytecodes, ResolvedJavaMethod#hasReceiver to Graal API.
 * Add `GraalDirectives` API, containing methods to influence compiler behavior for unittests and microbenchmarks.
 * Introduce `LIRSuites`, an extensible configuration for the low-level compiler pipeline.
-* ...
 
 ### Truffle
 * New, faster partial evaluation (no more TruffleCache).
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java	Mon May 04 19:34:51 2015 +0200
@@ -124,6 +124,27 @@
     }
 
     /**
+     * Merge the types of the inputs. The result will have the {@link PlatformKind} of one of the
+     * inputs. If all inputs are values (references), the result is a value (reference). Otherwise,
+     * the result is a derived reference.
+     *
+     * This method should be used to construct the result {@link LIRKind} of merge operation that do
+     * not modify values (e.g. phis).
+     */
+    public static LIRKind merge(Value... inputs) {
+        assert inputs.length > 0;
+        for (Value input : inputs) {
+            LIRKind kind = input.getLIRKind();
+            if (kind.isDerivedReference()) {
+                return kind;
+            }
+        }
+
+        // all inputs are values or references, just return one of them
+        return inputs[0].getLIRKind();
+    }
+
+    /**
      * Create a new {@link LIRKind} with the same reference information and a new
      * {@linkplain #getPlatformKind platform kind}. If the new kind is a longer vector than this,
      * the new elements are marked as untracked values.
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Mon May 04 19:34:51 2015 +0200
@@ -32,6 +32,8 @@
 import static com.oracle.graal.lir.amd64.AMD64Arithmetic.*;
 import static com.oracle.graal.lir.amd64.AMD64MathIntrinsicOp.IntrinsicOpcode.*;
 
+import java.util.*;
+
 import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
@@ -61,12 +63,14 @@
 import com.oracle.graal.lir.amd64.AMD64ControlFlow.ReturnOp;
 import com.oracle.graal.lir.amd64.AMD64ControlFlow.StrategySwitchOp;
 import com.oracle.graal.lir.amd64.AMD64ControlFlow.TableSwitchOp;
+import com.oracle.graal.lir.amd64.AMD64Move.AMD64StackMove;
 import com.oracle.graal.lir.amd64.AMD64Move.LeaDataOp;
 import com.oracle.graal.lir.amd64.AMD64Move.LeaOp;
 import com.oracle.graal.lir.amd64.AMD64Move.MembarOp;
 import com.oracle.graal.lir.amd64.AMD64Move.MoveFromRegOp;
 import com.oracle.graal.lir.amd64.AMD64Move.MoveToRegOp;
 import com.oracle.graal.lir.amd64.AMD64Move.StackLeaOp;
+import com.oracle.graal.lir.framemap.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.phases.util.*;
 
@@ -76,13 +80,56 @@
 public abstract class AMD64LIRGenerator extends LIRGenerator implements AMD64ArithmeticLIRGenerator {
 
     private static final RegisterValue RCX_I = AMD64.rcx.asValue(LIRKind.value(Kind.Int));
+    private AMD64SpillMoveFactory moveFactory;
+
+    private static class RegisterBackupPair {
+        public final Register register;
+        public final StackSlotValue backupSlot;
+
+        RegisterBackupPair(Register register, StackSlotValue backupSlot) {
+            this.register = register;
+            this.backupSlot = backupSlot;
+        }
+    }
 
     private class AMD64SpillMoveFactory implements LIRGeneratorTool.SpillMoveFactory {
+        private Map<PlatformKind.Key, RegisterBackupPair> categorized;
 
         @Override
         public LIRInstruction createMove(AllocatableValue result, Value input) {
             return AMD64LIRGenerator.this.createMove(result, input);
         }
+
+        @Override
+        public LIRInstruction createStackMove(AllocatableValue result, Value input) {
+            RegisterBackupPair backup = getScratchRegister(input.getPlatformKind());
+            return new AMD64StackMove(result, input, backup.register, backup.backupSlot);
+        }
+
+        private RegisterBackupPair getScratchRegister(PlatformKind kind) {
+            PlatformKind.Key key = kind.getKey();
+            if (categorized == null) {
+                categorized = new HashMap<>();
+            } else if (categorized.containsKey(key)) {
+                return categorized.get(key);
+            }
+
+            FrameMapBuilder frameMapBuilder = getResult().getFrameMapBuilder();
+            RegisterConfig registerConfig = frameMapBuilder.getRegisterConfig();
+
+            Register[] availableRegister = registerConfig.filterAllocatableRegisters(kind, registerConfig.getAllocatableRegisters());
+            assert availableRegister != null && availableRegister.length > 1;
+            Register scratchRegister = availableRegister[0];
+
+            Architecture arch = frameMapBuilder.getCodeCache().getTarget().arch;
+            LIRKind largestKind = LIRKind.value(arch.getLargestStorableKind(scratchRegister.getRegisterCategory()));
+            VirtualStackSlot backupSlot = frameMapBuilder.allocateSpillSlot(largestKind);
+
+            RegisterBackupPair value = new RegisterBackupPair(scratchRegister, backupSlot);
+            categorized.put(key, value);
+
+            return value;
+        }
     }
 
     public AMD64LIRGenerator(LIRKindTool lirKindTool, Providers providers, CallingConvention cc, LIRGenerationResult lirGenRes) {
@@ -90,7 +137,10 @@
     }
 
     public SpillMoveFactory getSpillMoveFactory() {
-        return new AMD64SpillMoveFactory();
+        if (moveFactory == null) {
+            moveFactory = new AMD64SpillMoveFactory();
+        }
+        return moveFactory;
     }
 
     @Override
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Mon May 04 19:34:51 2015 +0200
@@ -70,6 +70,7 @@
 public abstract class SPARCLIRGenerator extends LIRGenerator {
 
     private StackSlotValue tmpStackSlot;
+    private SPARCSpillMoveFactory moveFactory;
 
     private class SPARCSpillMoveFactory implements LIRGeneratorTool.SpillMoveFactory {
 
@@ -84,7 +85,10 @@
     }
 
     public SpillMoveFactory getSpillMoveFactory() {
-        return new SPARCSpillMoveFactory();
+        if (moveFactory == null) {
+            moveFactory = new SPARCSpillMoveFactory();
+        }
+        return moveFactory;
     }
 
     @Override
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Mon May 04 19:34:51 2015 +0200
@@ -127,6 +127,10 @@
     protected static void breakpoint() {
     }
 
+    @SuppressWarnings("unused")
+    protected static void breakpoint(int arg0) {
+    }
+
     protected Suites createSuites() {
         Suites ret = backend.getSuites().createSuites();
         ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(CleanTypeProfileProxyPhase.class);
@@ -852,6 +856,12 @@
                 return true;
             }
         }, GraalCompilerTest.class, "breakpoint");
+        invocationPlugins.register(new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg0) {
+                b.add(new BreakpointNode(arg0));
+                return true;
+            }
+        }, GraalCompilerTest.class, "breakpoint", int.class);
         return conf;
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GuardEliminationCornerCasesTest.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GuardEliminationCornerCasesTest.java	Mon May 04 19:34:51 2015 +0200
@@ -62,11 +62,11 @@
                     if (b instanceof C) {
                         return 1;
                     } else {
-                        GraalDirectives.deoptimize();
+                        GraalDirectives.deoptimizeAndInvalidate();
                     }
                 }
             } else {
-                GraalDirectives.deoptimize();
+                GraalDirectives.deoptimizeAndInvalidate();
             }
         }
         return 0;
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Mon May 04 19:34:51 2015 +0200
@@ -293,7 +293,11 @@
 
             // LIR generation
             LIRGenerationContext context = new LIRGenerationContext(lirGen, nodeLirGen, graph, schedule);
-            new LIRGenerationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context);
+            try (Scope s = Debug.scope("LIRGeneration", nodeLirGen, lir)) {
+                new LIRGenerationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context);
+            } catch (Throwable e) {
+                throw Debug.handle(e);
+            }
 
             try (Scope s = Debug.scope("LIRStages", nodeLirGen, lir)) {
                 return emitLowLevel(target, codeEmittingOrder, linearScanOrder, lirGenRes, lirGen, lirSuites);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Mon May 04 19:34:51 2015 +0200
@@ -45,6 +45,7 @@
 import com.oracle.graal.lir.debug.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.gen.LIRGenerator.Options;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.BlockScope;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.cfg.*;
@@ -192,71 +193,71 @@
     }
 
     public void doBlock(Block block, StructuredGraph graph, BlockMap<List<Node>> blockMap) {
-        gen.doBlockStart(block);
+        try (BlockScope blockScope = gen.getBlockScope(block)) {
 
-        if (block == gen.getResult().getLIR().getControlFlowGraph().getStartBlock()) {
-            assert block.getPredecessorCount() == 0;
-            emitPrologue(graph);
-        } else {
-            assert block.getPredecessorCount() > 0;
-        }
+            if (block == gen.getResult().getLIR().getControlFlowGraph().getStartBlock()) {
+                assert block.getPredecessorCount() == 0;
+                emitPrologue(graph);
+            } else {
+                assert block.getPredecessorCount() > 0;
+            }
 
-        List<Node> nodes = blockMap.get(block);
+            List<Node> nodes = blockMap.get(block);
 
-        // Allow NodeLIRBuilder subclass to specialize code generation of any interesting groups
-        // of instructions
-        matchComplexExpressions(nodes);
+            // Allow NodeLIRBuilder subclass to specialize code generation of any interesting groups
+            // of instructions
+            matchComplexExpressions(nodes);
 
-        for (int i = 0; i < nodes.size(); i++) {
-            Node node = nodes.get(i);
-            if (node instanceof ValueNode) {
-                ValueNode valueNode = (ValueNode) node;
-                if (Options.TraceLIRGeneratorLevel.getValue() >= 3) {
-                    TTY.println("LIRGen for " + valueNode);
-                }
-                Value operand = getOperand(valueNode);
-                if (operand == null) {
-                    if (!peephole(valueNode)) {
-                        try {
-                            doRoot(valueNode);
-                        } catch (GraalInternalError e) {
-                            throw GraalGraphInternalError.transformAndAddContext(e, valueNode);
-                        } catch (Throwable e) {
-                            throw new GraalGraphInternalError(e).addContext(valueNode);
+            for (int i = 0; i < nodes.size(); i++) {
+                Node node = nodes.get(i);
+                if (node instanceof ValueNode) {
+                    ValueNode valueNode = (ValueNode) node;
+                    if (Options.TraceLIRGeneratorLevel.getValue() >= 3) {
+                        TTY.println("LIRGen for " + valueNode);
+                    }
+                    Value operand = getOperand(valueNode);
+                    if (operand == null) {
+                        if (!peephole(valueNode)) {
+                            try {
+                                doRoot(valueNode);
+                            } catch (GraalInternalError e) {
+                                throw GraalGraphInternalError.transformAndAddContext(e, valueNode);
+                            } catch (Throwable e) {
+                                throw new GraalGraphInternalError(e).addContext(valueNode);
+                            }
                         }
+                    } else if (ComplexMatchValue.INTERIOR_MATCH.equals(operand)) {
+                        // Doesn't need to be evaluated
+                        Debug.log("interior match for %s", valueNode);
+                    } else if (operand instanceof ComplexMatchValue) {
+                        Debug.log("complex match for %s", valueNode);
+                        ComplexMatchValue match = (ComplexMatchValue) operand;
+                        operand = match.evaluate(this);
+                        if (operand != null) {
+                            setResult(valueNode, operand);
+                        }
+                    } else {
+                        // There can be cases in which the result of an instruction is already set
+                        // before by other instructions.
                     }
-                } else if (ComplexMatchValue.INTERIOR_MATCH.equals(operand)) {
-                    // Doesn't need to be evaluated
-                    Debug.log("interior match for %s", valueNode);
-                } else if (operand instanceof ComplexMatchValue) {
-                    Debug.log("complex match for %s", valueNode);
-                    ComplexMatchValue match = (ComplexMatchValue) operand;
-                    operand = match.evaluate(this);
-                    if (operand != null) {
-                        setResult(valueNode, operand);
-                    }
-                } else {
-                    // There can be cases in which the result of an instruction is already set
-                    // before by other instructions.
                 }
             }
-        }
 
-        if (!gen.hasBlockEnd(block)) {
-            NodeClassIterable successors = block.getEndNode().successors();
-            assert successors.count() == block.getSuccessorCount();
-            if (block.getSuccessorCount() != 1) {
-                /*
-                 * If we have more than one successor, we cannot just use the first one. Since
-                 * successors are unordered, this would be a random choice.
-                 */
-                throw new GraalInternalError("Block without BlockEndOp: " + block.getEndNode());
+            if (!gen.hasBlockEnd(block)) {
+                NodeClassIterable successors = block.getEndNode().successors();
+                assert successors.count() == block.getSuccessorCount();
+                if (block.getSuccessorCount() != 1) {
+                    /*
+                     * If we have more than one successor, we cannot just use the first one. Since
+                     * successors are unordered, this would be a random choice.
+                     */
+                    throw new GraalInternalError("Block without BlockEndOp: " + block.getEndNode());
+                }
+                gen.emitJump(getLIRBlock((FixedNode) successors.first()));
             }
-            gen.emitJump(getLIRBlock((FixedNode) successors.first()));
+
+            assert verifyBlock(gen.getResult().getLIR(), block);
         }
-
-        assert verifyBlock(gen.getResult().getLIR(), block);
-        gen.doBlockEnd(block);
     }
 
     protected void matchComplexExpressions(List<Node> nodes) {
@@ -345,7 +346,9 @@
 
     @Override
     public void visitEndNode(AbstractEndNode end) {
-        moveToPhi(end.merge(), end);
+        AbstractMergeNode merge = end.merge();
+        moveToPhi(merge, end);
+        append(newJumpOp(getLIRBlock(merge)));
     }
 
     /**
@@ -359,7 +362,7 @@
         if (Options.TraceLIRGeneratorLevel.getValue() >= 1) {
             TTY.println("MOVE TO PHI from " + pred + " to " + merge);
         }
-        PhiResolver resolver = new PhiResolver(gen);
+        PhiResolver resolver = PhiResolver.create(gen);
         for (PhiNode phi : merge.phis()) {
             if (phi instanceof ValuePhiNode) {
                 ValueNode curVal = phi.valueAt(pred);
@@ -367,8 +370,6 @@
             }
         }
         resolver.dispose();
-
-        append(newJumpOp(getLIRBlock(merge)));
     }
 
     protected JumpOp newJumpOp(LabelRef ref) {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/PhiResolver.java	Mon May 04 19:12:50 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +0,0 @@
-/*
- * Copyright (c) 2009, 2014, 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.gen;
-
-import static com.oracle.graal.api.code.ValueUtil.*;
-import static com.oracle.graal.api.meta.Value.*;
-import static com.oracle.graal.lir.LIRValueUtil.*;
-
-import java.util.*;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.lir.gen.*;
-import com.oracle.graal.nodes.*;
-
-/**
- * Converts {@link ValuePhiNode} instructions into moves.
- *
- * Resolves cycles:
- *
- * <pre>
- *
- *  r1 := r2  becomes  temp := r1
- *  r2 := r1           r1 := r2
- *                     r2 := temp
- * </pre>
- *
- * and orders moves:
- *
- * <pre>
- *  r2 := r3  becomes  r1 := r2
- *  r1 := r2           r2 := r3
- * </pre>
- */
-public class PhiResolver {
-
-    /**
-     * Tracks a data flow dependency between a source operand and any number of the destination
-     * operands.
-     */
-    static class PhiResolverNode {
-
-        /**
-         * A source operand whose value flows into the {@linkplain #destinations destination}
-         * operands.
-         */
-        final Value operand;
-
-        /**
-         * The operands whose values are defined by the {@linkplain #operand source} operand.
-         */
-        final ArrayList<PhiResolverNode> destinations;
-
-        /**
-         * Denotes if a move instruction has already been emitted to initialize the value of
-         * {@link #operand}.
-         */
-        boolean assigned;
-
-        /**
-         * Specifies if this operand been visited for the purpose of emitting a move instruction.
-         */
-        boolean visited;
-
-        /**
-         * Specifies if this is the initial definition in data flow path for a given value.
-         */
-        boolean startNode;
-
-        PhiResolverNode(Value operand) {
-            this.operand = operand;
-            destinations = new ArrayList<>(4);
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder buf = new StringBuilder(operand.toString());
-            if (!destinations.isEmpty()) {
-                buf.append(" ->");
-                for (PhiResolverNode node : destinations) {
-                    buf.append(' ').append(node.operand);
-                }
-            }
-            return buf.toString();
-        }
-    }
-
-    private final LIRGeneratorTool gen;
-
-    /**
-     * The operand loop header phi for the operand currently being process in {@link #dispose()}.
-     */
-    private PhiResolverNode loop;
-
-    private Value temp;
-
-    private final ArrayList<PhiResolverNode> variableOperands = new ArrayList<>(3);
-    private final ArrayList<PhiResolverNode> otherOperands = new ArrayList<>(3);
-
-    /**
-     * Maps operands to nodes.
-     */
-    private final HashMap<Value, PhiResolverNode> operandToNodeMap = CollectionsFactory.newMap();
-
-    public PhiResolver(LIRGeneratorTool gen) {
-        this.gen = gen;
-        temp = ILLEGAL;
-    }
-
-    public void dispose() {
-        // resolve any cycles in moves from and to variables
-        for (int i = variableOperands.size() - 1; i >= 0; i--) {
-            PhiResolverNode node = variableOperands.get(i);
-            if (!node.visited) {
-                loop = null;
-                move(node, null);
-                node.startNode = true;
-                assert isIllegal(temp) : "moveTempTo() call missing";
-            }
-        }
-
-        // generate move for move from non variable to arbitrary destination
-        for (int i = otherOperands.size() - 1; i >= 0; i--) {
-            PhiResolverNode node = otherOperands.get(i);
-            for (int j = node.destinations.size() - 1; j >= 0; j--) {
-                emitMove(node.destinations.get(j).operand, node.operand);
-            }
-        }
-    }
-
-    public void move(Value dest, Value src) {
-        assert isVariable(dest) : "destination must be virtual";
-        // tty.print("move "); src.print(); tty.print(" to "); dest.print(); tty.cr();
-        assert isLegal(src) : "source for phi move is illegal";
-        assert isLegal(dest) : "destination for phi move is illegal";
-        PhiResolverNode srcNode = sourceNode(src);
-        PhiResolverNode destNode = destinationNode(dest);
-        srcNode.destinations.add(destNode);
-    }
-
-    private PhiResolverNode createNode(Value operand, boolean source) {
-        PhiResolverNode node;
-        if (isVariable(operand)) {
-            node = operandToNodeMap.get(operand);
-            assert node == null || node.operand.equals(operand);
-            if (node == null) {
-                node = new PhiResolverNode(operand);
-                operandToNodeMap.put(operand, node);
-            }
-            // Make sure that all variables show up in the list when
-            // they are used as the source of a move.
-            if (source) {
-                if (!variableOperands.contains(node)) {
-                    variableOperands.add(node);
-                }
-            }
-        } else {
-            assert source;
-            node = new PhiResolverNode(operand);
-            otherOperands.add(node);
-        }
-        return node;
-    }
-
-    private PhiResolverNode destinationNode(Value opr) {
-        return createNode(opr, false);
-    }
-
-    private void emitMove(Value dest, Value src) {
-        assert isLegal(src);
-        assert isLegal(dest);
-        gen.emitMove((AllocatableValue) dest, src);
-    }
-
-    // Traverse assignment graph in depth first order and generate moves in post order
-    // ie. two assignments: b := c, a := b start with node c:
-    // Call graph: move(c, NULL) -> move(b, c) -> move(a, b)
-    // Generates moves in this order: move b to a and move c to b
-    // ie. cycle a := b, b := a start with node a
-    // Call graph: move(a, NULL) -> move(b, a) -> move(a, b)
-    // Generates moves in this order: move b to temp, move a to b, move temp to a
-    private void move(PhiResolverNode dest, PhiResolverNode src) {
-        if (!dest.visited) {
-            dest.visited = true;
-            for (int i = dest.destinations.size() - 1; i >= 0; i--) {
-                move(dest.destinations.get(i), dest);
-            }
-        } else if (!dest.startNode) {
-            // cycle in graph detected
-            assert loop == null : "only one loop valid!";
-            loop = dest;
-            moveToTemp(src.operand);
-            return;
-        } // else dest is a start node
-
-        if (!dest.assigned) {
-            if (loop == dest) {
-                moveTempTo(dest.operand);
-                dest.assigned = true;
-            } else if (src != null) {
-                emitMove(dest.operand, src.operand);
-                dest.assigned = true;
-            }
-        }
-    }
-
-    private void moveTempTo(Value dest) {
-        assert isLegal(temp);
-        emitMove(dest, temp);
-        temp = ILLEGAL;
-    }
-
-    private void moveToTemp(Value src) {
-        assert isIllegal(temp);
-        temp = gen.newVariable(src.getLIRKind());
-        emitMove(temp, src);
-    }
-
-    private PhiResolverNode sourceNode(Value opr) {
-        return createNode(opr, true);
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostForeignCallsProvider.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostForeignCallsProvider.java	Mon May 04 19:34:51 2015 +0200
@@ -194,14 +194,16 @@
         linkForeignCall(providers, LOG_PRINTF, c.logPrintfAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(providers, LOG_OBJECT, c.logObjectAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(providers, LOG_PRIMITIVE, c.logPrimitiveAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
-        linkForeignCall(providers, THREAD_IS_INTERRUPTED, c.threadIsInterruptedAddress, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any());
         linkForeignCall(providers, VM_ERROR, c.vmErrorAddress, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(providers, OSR_MIGRATION_END, c.osrMigrationEndAddress, DONT_PREPEND_THREAD, LEAF_NOFP, NOT_REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(providers, G1WBPRECALL, c.writeBarrierPreAddress, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(providers, G1WBPOSTCALL, c.writeBarrierPostAddress, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(providers, VALIDATE_OBJECT, c.validateObject, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS);
 
-        linkForeignCall(providers, TEST_DEOPTIMIZE_CALL_INT, c.testDeoptimizeCallInt, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any());
+        // Cannot be a leaf as VM acquires Thread_lock which requires thread_in_vm state
+        linkForeignCall(providers, THREAD_IS_INTERRUPTED, c.threadIsInterruptedAddress, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any());
+
+        linkForeignCall(providers, TEST_DEOPTIMIZE_CALL_INT, c.testDeoptimizeCallInt, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, any());
 
         // sometimes the same function is used for different kinds of arraycopy so check for
         // duplicates using a map.
--- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_instanceof01.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_instanceof01.java	Mon May 04 19:34:51 2015 +0200
@@ -24,14 +24,10 @@
 
 import org.junit.*;
 
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.phases.*;
 import com.oracle.graal.jtt.*;
-import com.oracle.graal.nodes.*;
 import com.oracle.graal.options.*;
-import com.oracle.graal.options.OptionValue.*;
+import com.oracle.graal.options.OptionValue.OverrideScope;
 import com.oracle.graal.phases.tiers.*;
 
 /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/Loop09_2.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2009, 2012, 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.jtt.loop;
+
+import org.junit.*;
+
+import com.oracle.graal.jtt.*;
+
+/*
+ */
+public class Loop09_2 extends JTTTest {
+
+    private static int cnt;
+
+    public static int test(int arg) {
+        cnt = 0;
+        int count = arg;
+        for (int i = 0; i < arg; i++) {
+            count++;
+            foo();
+        }
+        return count - cnt;
+    }
+
+    static void foo() {
+        cnt++;
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        runTest("test", 0);
+    }
+
+    @Test
+    public void run1() throws Throwable {
+        runTest("test", 10);
+    }
+
+    @Test
+    public void run2() throws Throwable {
+        runTest("test", 25);
+    }
+
+}
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java	Mon May 04 19:34:51 2015 +0200
@@ -108,6 +108,47 @@
         }
     }
 
+    @Opcode("STACKMOVE")
+    public static final class AMD64StackMove extends AMD64LIRInstruction implements MoveOp {
+        public static final LIRInstructionClass<AMD64StackMove> TYPE = LIRInstructionClass.create(AMD64StackMove.class);
+
+        @Def({STACK}) protected AllocatableValue result;
+        @Use({STACK, HINT}) protected Value input;
+        @Alive({OperandFlag.STACK, OperandFlag.UNINITIALIZED}) private StackSlotValue backupSlot;
+
+        private Register scratch;
+
+        public AMD64StackMove(AllocatableValue result, Value input, Register scratch, StackSlotValue backupSlot) {
+            super(TYPE);
+            this.result = result;
+            this.input = input;
+            this.backupSlot = backupSlot;
+            this.scratch = scratch;
+        }
+
+        @Override
+        public Value getInput() {
+            return input;
+        }
+
+        @Override
+        public AllocatableValue getResult() {
+            return result;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
+            // backup scratch register
+            move(backupSlot.getKind(), crb, masm, backupSlot, scratch.asValue(backupSlot.getLIRKind()));
+            // move stack slot
+            move(getInput().getKind(), crb, masm, scratch.asValue(getInput().getLIRKind()), getInput());
+            move(getResult().getKind(), crb, masm, getResult(), scratch.asValue(getResult().getLIRKind()));
+            // restore scratch register
+            move(backupSlot.getKind(), crb, masm, scratch.asValue(backupSlot.getLIRKind()), backupSlot);
+
+        }
+    }
+
     public static final class LeaOp extends AMD64LIRInstruction {
         public static final LIRInstructionClass<LeaOp> TYPE = LIRInstructionClass.create(LeaOp.class);
 
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Mon May 04 19:34:51 2015 +0200
@@ -235,4 +235,33 @@
             // do nothing, just keep value alive until at least here
         }
     }
+
+    public static final class StackMove extends LIRInstruction implements MoveOp {
+        public static final LIRInstructionClass<StackMove> TYPE = LIRInstructionClass.create(StackMove.class);
+
+        @Def({STACK, HINT}) protected AllocatableValue result;
+        @Use({STACK}) protected Value input;
+
+        public StackMove(AllocatableValue result, Value input) {
+            super(TYPE);
+            this.result = result;
+            this.input = input;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb) {
+            throw new GraalInternalError(this + " should have been removed");
+        }
+
+        @Override
+        public Value getInput() {
+            return input;
+        }
+
+        @Override
+        public AllocatableValue getResult() {
+            return result;
+        }
+    }
+
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Mon May 04 19:34:51 2015 +0200
@@ -200,6 +200,10 @@
         return spillMoveFactory;
     }
 
+    protected MoveResolver createMoveResolver() {
+        return new MoveResolver(this);
+    }
+
     public static boolean isVariableOrRegister(Value value) {
         return isVariable(value) || isRegister(value);
     }
@@ -554,9 +558,7 @@
                             // move target is a stack slot that is always correct, so eliminate
                             // instruction
                             if (Debug.isLogEnabled()) {
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult()));
-                                }
+                                Debug.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult()));
                             }
                             // null-instructions are deleted by assignRegNum
                             instructions.set(j, null);
@@ -688,7 +690,7 @@
 
         // iterate all blocks
         for (final AbstractBlockBase<?> block : sortedBlocks) {
-            try (Indent indent = Debug.logAndIndent("compute local live sets for block %d", block.getId())) {
+            try (Indent indent = Debug.logAndIndent("compute local live sets for block %s", block)) {
 
                 final BitSet liveGen = new BitSet(liveSize);
                 final BitSet liveKill = new BitSet(liveSize);
@@ -702,7 +704,7 @@
                         if (!liveKill.get(operandNum)) {
                             liveGen.set(operandNum);
                             if (Debug.isLogEnabled()) {
-                                Debug.log("liveGen for operand %d", operandNum);
+                                Debug.log("liveGen for operand %d(%s)", operandNum, operand);
                             }
                         }
                         if (block.getLoop() != null) {
@@ -720,7 +722,7 @@
                         if (!liveKill.get(operandNum)) {
                             liveGen.set(operandNum);
                             if (Debug.isLogEnabled()) {
-                                Debug.log("liveGen in state for operand %d", operandNum);
+                                Debug.log("liveGen in state for operand %d(%s)", operandNum, operand);
                             }
                         }
                     }
@@ -730,7 +732,7 @@
                         int varNum = operandNumber(operand);
                         liveKill.set(varNum);
                         if (Debug.isLogEnabled()) {
-                            Debug.log("liveKill for operand %d", varNum);
+                            Debug.log("liveKill for operand %d(%s)", varNum, operand);
                         }
                         if (block.getLoop() != null) {
                             intervalInLoop.setBit(varNum, block.getLoop().getIndex());
@@ -1521,7 +1523,7 @@
         try (Indent indent = Debug.logAndIndent("resolve data flow")) {
 
             int numBlocks = blockCount();
-            MoveResolver moveResolver = new MoveResolver(this);
+            MoveResolver moveResolver = createMoveResolver();
             BitSet blockCompleted = new BitSet(numBlocks);
             BitSet alreadyResolved = new BitSet(numBlocks);
 
@@ -1613,14 +1615,17 @@
             if (DetailedAsserts.getValue()) {
                 AbstractBlockBase<?> block = blockForId(opId);
                 if (block.getSuccessorCount() <= 1 && opId == getLastLirInstructionId(block)) {
-                    // check if spill moves could have been appended at the end of this block, but
-                    // before the branch instruction. So the split child information for this branch
-                    // would
-                    // be incorrect.
+                    /*
+                     * Check if spill moves could have been appended at the end of this block, but
+                     * before the branch instruction. So the split child information for this branch
+                     * would be incorrect.
+                     */
                     LIRInstruction instr = ir.getLIRforBlock(block).get(ir.getLIRforBlock(block).size() - 1);
                     if (instr instanceof StandardOp.JumpOp) {
                         if (blockData.get(block).liveOut.get(operandNumber(operand))) {
-                            assert false : "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow)";
+                            assert false : String.format(
+                                            "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow) block=%s, instruction=%s, operand=%s",
+                                            block, instr, operand);
                         }
                     }
                 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java	Mon May 04 19:34:51 2015 +0200
@@ -74,7 +74,7 @@
     LinearScanWalker(LinearScan allocator, Interval unhandledFixedFirst, Interval unhandledAnyFirst) {
         super(allocator, unhandledFixedFirst, unhandledAnyFirst);
 
-        moveResolver = new MoveResolver(allocator);
+        moveResolver = allocator.createMoveResolver();
         spillIntervals = Util.uncheckedCast(new List[allocator.registers.length]);
         for (int i = 0; i < allocator.registers.length; i++) {
             spillIntervals[i] = EMPTY_LIST;
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java	Mon May 04 19:34:51 2015 +0200
@@ -29,6 +29,7 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.lir.*;
 
@@ -47,13 +48,18 @@
     private boolean multipleReadsAllowed;
     private final int[] registerBlocked;
 
-    private int registerBlocked(int reg) {
-        return registerBlocked[reg];
+    private void setValueBlocked(Value location, int direction) {
+        assert direction == 1 || direction == -1 : "out of bounds";
+        if (isRegister(location)) {
+            registerBlocked[asRegister(location).number] += direction;
+        }
     }
 
-    private void setRegisterBlocked(int reg, int direction) {
-        assert direction == 1 || direction == -1 : "out of bounds";
-        registerBlocked[reg] += direction;
+    private int valueBlocked(Value location) {
+        if (isRegister(location)) {
+            return registerBlocked[asRegister(location).number];
+        }
+        throw GraalInternalError.shouldNotReachHere("unhandled value " + location);
     }
 
     void setMultipleReadsAllowed() {
@@ -80,7 +86,7 @@
     boolean checkEmpty() {
         assert mappingFrom.size() == 0 && mappingFromOpr.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing";
         for (int i = 0; i < allocator.registers.length; i++) {
-            assert registerBlocked(i) == 0 : "register map must be empty before and after processing";
+            assert registerBlocked[i] == 0 : "register map must be empty before and after processing";
         }
         assert !multipleReadsAllowed : "must have default value";
         return true;
@@ -149,9 +155,9 @@
     private void blockRegisters(Interval interval) {
         Value location = interval.location();
         if (isRegister(location)) {
-            int reg = asRegister(location).number;
-            assert multipleReadsAllowed || registerBlocked(reg) == 0 : "register already marked as used";
-            setRegisterBlocked(reg, 1);
+            assert multipleReadsAllowed || valueBlocked(location) == 0 : "register already marked as used";
+            int direction = 1;
+            setValueBlocked(location, direction);
         }
     }
 
@@ -159,9 +165,8 @@
     private void unblockRegisters(Interval interval) {
         Value location = interval.location();
         if (isRegister(location)) {
-            int reg = asRegister(location).number;
-            assert registerBlocked(reg) > 0 : "register already marked as unused";
-            setRegisterBlocked(reg, -1);
+            assert valueBlocked(location) > 0 : "register already marked as unused";
+            setValueBlocked(location, -1);
         }
     }
 
@@ -174,7 +179,7 @@
 
         Value reg = to.location();
         if (isRegister(reg)) {
-            if (registerBlocked(asRegister(reg).number) > 1 || (registerBlocked(asRegister(reg).number) == 1 && !reg.equals(fromReg))) {
+            if (valueBlocked(reg) > 1 || (valueBlocked(reg) == 1 && !reg.equals(fromReg))) {
                 return false;
             }
         }
@@ -350,7 +355,8 @@
         }
 
         assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval;
-        assert fromInterval.kind().equals(toInterval.kind());
+        assert fromInterval.kind().equals(toInterval.kind()) || (fromInterval.kind().getPlatformKind().equals(toInterval.kind().getPlatformKind()) && toInterval.kind().isDerivedReference()) : String.format(
+                        "Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, toInterval);
         mappingFrom.add(fromInterval);
         mappingFromOpr.add(Value.ILLEGAL);
         mappingTo.add(toInterval);
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java	Mon May 04 19:34:51 2015 +0200
@@ -183,7 +183,7 @@
             TTY.println();
         }
         assert LIRVerifier.verify(op);
-        res.getLIR().getLIRforBlock(currentBlock).add(op);
+        res.getLIR().getLIRforBlock(getCurrentBlock()).add(op);
         return op;
     }
 
@@ -195,35 +195,55 @@
         return ops.get(ops.size() - 1) instanceof BlockEndOp;
     }
 
-    public final void doBlockStart(AbstractBlockBase<?> block) {
-        if (Options.PrintIRWithLIR.getValue()) {
-            TTY.print(block.toString());
+    private final class BlockScopeImpl extends BlockScope {
+
+        private BlockScopeImpl(AbstractBlockBase<?> block) {
+            currentBlock = block;
+        }
+
+        private void doBlockStart() {
+            if (Options.PrintIRWithLIR.getValue()) {
+                TTY.print(currentBlock.toString());
+            }
+
+            // set up the list of LIR instructions
+            assert res.getLIR().getLIRforBlock(currentBlock) == null : "LIR list already computed for this block";
+            res.getLIR().setLIRforBlock(currentBlock, new ArrayList<LIRInstruction>());
+
+            append(new LabelOp(new Label(currentBlock.getId()), currentBlock.isAligned()));
+
+            if (Options.TraceLIRGeneratorLevel.getValue() >= 1) {
+                TTY.println("BEGIN Generating LIR for block B" + currentBlock.getId());
+            }
         }
 
-        currentBlock = block;
+        private void doBlockEnd() {
+            if (Options.TraceLIRGeneratorLevel.getValue() >= 1) {
+                TTY.println("END Generating LIR for block B" + currentBlock.getId());
+            }
 
-        // set up the list of LIR instructions
-        assert res.getLIR().getLIRforBlock(block) == null : "LIR list already computed for this block";
-        res.getLIR().setLIRforBlock(block, new ArrayList<LIRInstruction>());
+            if (Options.PrintIRWithLIR.getValue()) {
+                TTY.println();
+            }
+            currentBlock = null;
+        }
 
-        append(new LabelOp(new Label(block.getId()), block.isAligned()));
+        @Override
+        public AbstractBlockBase<?> getCurrentBlock() {
+            return currentBlock;
+        }
 
-        if (Options.TraceLIRGeneratorLevel.getValue() >= 1) {
-            TTY.println("BEGIN Generating LIR for block B" + block.getId());
+        @Override
+        public void close() {
+            doBlockEnd();
         }
+
     }
 
-    public final void doBlockEnd(AbstractBlockBase<?> block) {
-
-        if (Options.TraceLIRGeneratorLevel.getValue() >= 1) {
-            TTY.println("END Generating LIR for block B" + block.getId());
-        }
-
-        currentBlock = null;
-
-        if (Options.PrintIRWithLIR.getValue()) {
-            TTY.println();
-        }
+    public final BlockScope getBlockScope(AbstractBlockBase<?> block) {
+        BlockScopeImpl blockScope = new BlockScopeImpl(block);
+        blockScope.doBlockStart();
+        return blockScope;
     }
 
     public void emitIncomingValues(Value[] params) {
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java	Mon May 04 19:34:51 2015 +0200
@@ -29,12 +29,25 @@
 import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.compiler.common.spi.*;
 import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.StandardOp.StackMove;
 
 public interface LIRGeneratorTool extends ArithmeticLIRGenerator, BenchmarkCounterFactory {
 
     public interface SpillMoveFactory {
 
         LIRInstruction createMove(AllocatableValue result, Value input);
+
+        default LIRInstruction createStackMove(AllocatableValue result, Value input) {
+            return new StackMove(result, input);
+        }
+    }
+
+    public abstract class BlockScope implements AutoCloseable {
+
+        public abstract AbstractBlockBase<?> getCurrentBlock();
+
+        public abstract void close();
+
     }
 
     CodeGenProviders getProviders();
@@ -55,9 +68,7 @@
 
     SpillMoveFactory getSpillMoveFactory();
 
-    void doBlockStart(AbstractBlockBase<?> block);
-
-    void doBlockEnd(AbstractBlockBase<?> block);
+    BlockScope getBlockScope(AbstractBlockBase<?> block);
 
     Value emitLoadConstant(LIRKind kind, Constant constant);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/PhiResolver.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2009, 2014, 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.gen;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.api.meta.Value.*;
+import static com.oracle.graal.lir.LIRValueUtil.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.*;
+
+/**
+ * Converts phi instructions into moves.
+ *
+ * Resolves cycles:
+ *
+ * <pre>
+ *
+ *  r1 := r2  becomes  temp := r1
+ *  r2 := r1           r1 := r2
+ *                     r2 := temp
+ * </pre>
+ *
+ * and orders moves:
+ *
+ * <pre>
+ *  r2 := r3  becomes  r1 := r2
+ *  r1 := r2           r2 := r3
+ * </pre>
+ */
+public class PhiResolver {
+
+    /**
+     * Tracks a data flow dependency between a source operand and any number of the destination
+     * operands.
+     */
+    static class PhiResolverNode {
+
+        /**
+         * A source operand whose value flows into the {@linkplain #destinations destination}
+         * operands.
+         */
+        final Value operand;
+
+        /**
+         * The operands whose values are defined by the {@linkplain #operand source} operand.
+         */
+        final ArrayList<PhiResolverNode> destinations;
+
+        /**
+         * Denotes if a move instruction has already been emitted to initialize the value of
+         * {@link #operand}.
+         */
+        boolean assigned;
+
+        /**
+         * Specifies if this operand been visited for the purpose of emitting a move instruction.
+         */
+        boolean visited;
+
+        /**
+         * Specifies if this is the initial definition in data flow path for a given value.
+         */
+        boolean startNode;
+
+        PhiResolverNode(Value operand) {
+            this.operand = operand;
+            destinations = new ArrayList<>(4);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder buf = new StringBuilder(operand.toString());
+            if (!destinations.isEmpty()) {
+                buf.append(" ->");
+                for (PhiResolverNode node : destinations) {
+                    buf.append(' ').append(node.operand);
+                }
+            }
+            return buf.toString();
+        }
+    }
+
+    private final LIRGeneratorTool gen;
+    private final SpillMoveFactory moveFactory;
+    private final LIRInsertionBuffer buffer;
+    private final int insertBefore;
+
+    /**
+     * The operand loop header phi for the operand currently being process in {@link #dispose()}.
+     */
+    private PhiResolverNode loop;
+
+    private Value temp;
+
+    private final ArrayList<PhiResolverNode> variableOperands = new ArrayList<>(3);
+    private final ArrayList<PhiResolverNode> otherOperands = new ArrayList<>(3);
+
+    /**
+     * Maps operands to nodes.
+     */
+    private final HashMap<Value, PhiResolverNode> operandToNodeMap = CollectionsFactory.newMap();
+
+    public static PhiResolver create(LIRGeneratorTool gen) {
+        AbstractBlockBase<?> block = gen.getCurrentBlock();
+        assert block != null;
+        List<LIRInstruction> instructions = gen.getResult().getLIR().getLIRforBlock(block);
+
+        return new PhiResolver(gen, new LIRInsertionBuffer(), instructions, instructions.size());
+    }
+
+    public static PhiResolver create(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
+        return new PhiResolver(gen, buffer, instructions, insertBefore);
+    }
+
+    protected PhiResolver(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
+        this.gen = gen;
+        moveFactory = gen.getSpillMoveFactory();
+        temp = ILLEGAL;
+
+        this.buffer = buffer;
+        this.buffer.init(instructions);
+        this.insertBefore = insertBefore;
+
+    }
+
+    public void dispose() {
+        // resolve any cycles in moves from and to variables
+        for (int i = variableOperands.size() - 1; i >= 0; i--) {
+            PhiResolverNode node = variableOperands.get(i);
+            if (!node.visited) {
+                loop = null;
+                move(node, null);
+                node.startNode = true;
+                assert isIllegal(temp) : "moveTempTo() call missing";
+            }
+        }
+
+        // generate move for move from non variable to arbitrary destination
+        for (int i = otherOperands.size() - 1; i >= 0; i--) {
+            PhiResolverNode node = otherOperands.get(i);
+            for (int j = node.destinations.size() - 1; j >= 0; j--) {
+                emitMove(node.destinations.get(j).operand, node.operand);
+            }
+        }
+        buffer.finish();
+    }
+
+    public void move(Value dest, Value src) {
+        assert isVariable(dest) : "destination must be virtual";
+        // tty.print("move "); src.print(); tty.print(" to "); dest.print(); tty.cr();
+        assert isLegal(src) : "source for phi move is illegal";
+        assert isLegal(dest) : "destination for phi move is illegal";
+        PhiResolverNode srcNode = sourceNode(src);
+        PhiResolverNode destNode = destinationNode(dest);
+        srcNode.destinations.add(destNode);
+    }
+
+    private PhiResolverNode createNode(Value operand, boolean source) {
+        PhiResolverNode node;
+        if (isVariable(operand)) {
+            node = operandToNodeMap.get(operand);
+            assert node == null || node.operand.equals(operand);
+            if (node == null) {
+                node = new PhiResolverNode(operand);
+                operandToNodeMap.put(operand, node);
+            }
+            // Make sure that all variables show up in the list when
+            // they are used as the source of a move.
+            if (source) {
+                if (!variableOperands.contains(node)) {
+                    variableOperands.add(node);
+                }
+            }
+        } else {
+            assert source;
+            node = new PhiResolverNode(operand);
+            otherOperands.add(node);
+        }
+        return node;
+    }
+
+    private PhiResolverNode destinationNode(Value opr) {
+        return createNode(opr, false);
+    }
+
+    private void emitMove(Value dest, Value src) {
+        assert isLegal(src);
+        assert isLegal(dest);
+        LIRInstruction move = moveFactory.createMove((AllocatableValue) dest, src);
+        buffer.append(insertBefore, move);
+    }
+
+    // Traverse assignment graph in depth first order and generate moves in post order
+    // ie. two assignments: b := c, a := b start with node c:
+    // Call graph: move(c, NULL) -> move(b, c) -> move(a, b)
+    // Generates moves in this order: move b to a and move c to b
+    // ie. cycle a := b, b := a start with node a
+    // Call graph: move(a, NULL) -> move(b, a) -> move(a, b)
+    // Generates moves in this order: move b to temp, move a to b, move temp to a
+    private void move(PhiResolverNode dest, PhiResolverNode src) {
+        if (!dest.visited) {
+            dest.visited = true;
+            for (int i = dest.destinations.size() - 1; i >= 0; i--) {
+                move(dest.destinations.get(i), dest);
+            }
+        } else if (!dest.startNode) {
+            // cycle in graph detected
+            assert loop == null : "only one loop valid!";
+            loop = dest;
+            moveToTemp(src.operand);
+            return;
+        } // else dest is a start node
+
+        if (!dest.assigned) {
+            if (loop == dest) {
+                moveTempTo(dest.operand);
+                dest.assigned = true;
+            } else if (src != null) {
+                emitMove(dest.operand, src.operand);
+                dest.assigned = true;
+            }
+        }
+    }
+
+    private void moveTempTo(Value dest) {
+        assert isLegal(temp);
+        emitMove(dest, temp);
+        temp = ILLEGAL;
+    }
+
+    private void moveToTemp(Value src) {
+        assert isIllegal(temp);
+        temp = gen.newVariable(src.getLIRKind());
+        emitMove(temp, src);
+    }
+
+    private PhiResolverNode sourceNode(Value opr) {
+        return createNode(opr, true);
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/FixPointIntervalBuilder.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/FixPointIntervalBuilder.java	Mon May 04 19:34:51 2015 +0200
@@ -189,6 +189,7 @@
                 if (isVirtualStackSlot(operand)) {
                     VirtualStackSlot vslot = asVirtualStackSlot(operand);
                     addUse(vslot, inst, flags);
+                    addRegisterHint(inst, vslot, mode, flags, false);
                     usePos.add(inst);
                     Debug.log("set operand: %s", operand);
                     currentSet.set(vslot.getId());
@@ -201,6 +202,7 @@
                 if (isVirtualStackSlot(operand)) {
                     VirtualStackSlot vslot = asVirtualStackSlot(operand);
                     addDef(vslot, inst);
+                    addRegisterHint(inst, vslot, mode, flags, true);
                     usePos.add(inst);
                     Debug.log("clear operand: %s", operand);
                     currentSet.clear(vslot.getId());
@@ -229,6 +231,32 @@
             interval.addFrom(inst.id());
         }
 
+        void addRegisterHint(final LIRInstruction op, VirtualStackSlot targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
+            if (flags.contains(OperandFlag.HINT)) {
+
+                op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> {
+                    if (isStackSlotValue(registerHint)) {
+                        assert isVirtualStackSlot(registerHint) : "Hint is not a VirtualStackSlot: " + registerHint;
+                        StackInterval from = getOrCreateInterval((VirtualStackSlot) registerHint);
+                        StackInterval to = getOrCreateInterval(targetValue);
+
+                        /* hints always point from def to use */
+                        if (hintAtDef) {
+                            to.setLocationHint(from);
+                        } else {
+                            from.setLocationHint(to);
+                        }
+                        if (Debug.isLogEnabled()) {
+                            Debug.log("operation %s at opId %d: added hint from interval %d to %d", op, op.id(), from, to);
+                        }
+
+                        return registerHint;
+                    }
+                    return null;
+                });
+            }
+        }
+
     }
 
     private StackInterval get(VirtualStackSlot stackSlot) {
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java	Mon May 04 19:34:51 2015 +0200
@@ -30,6 +30,7 @@
     private static final int INVALID_START = Integer.MAX_VALUE;
     private static final int INVALID_END = Integer.MIN_VALUE;
     private final VirtualStackSlot operand;
+    private StackInterval hint;
     private final LIRKind kind;
     private int from = INVALID_START;
     private int to = INVALID_END;
@@ -98,7 +99,15 @@
 
     @Override
     public String toString() {
-        return String.format("SI[%d-%d] k=%s o=%s l=%s", from, to, kind, operand, location);
+        return String.format("SI[%d-%d] k=%s o=%s l=%s h=%s", from, to, kind, operand, location, hint.getOperand());
+    }
+
+    public void setLocationHint(StackInterval locationHint) {
+        hint = locationHint;
+    }
+
+    public StackInterval locationHint() {
+        return hint;
     }
 
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java	Mon May 04 19:34:51 2015 +0200
@@ -134,6 +134,9 @@
     }
 
     private void visitDeoptBegin(AbstractBeginNode deoptBegin, DeoptimizationAction deoptAction, DeoptimizationReason deoptReason, StructuredGraph graph) {
+        if (!deoptAction.doesInvalidateCompilation()) {
+            return;
+        }
         if (deoptBegin instanceof AbstractMergeNode) {
             AbstractMergeNode mergeNode = (AbstractMergeNode) deoptBegin;
             Debug.log("Visiting %s", mergeNode);
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java	Mon May 04 19:34:51 2015 +0200
@@ -576,7 +576,8 @@
             out.printf("\"[%s|%c]\"", interval.getOperand(), interval.getOperand().getKind().getTypeChar());
         }
 
-        out.printf("%s -1 ", interval.getOperand());
+        StackInterval hint = interval.locationHint();
+        out.printf("%s %s ", interval.getOperand(), hint != null ? hint.getOperand() : -1);
 
         out.printf("[%d, %d[", interval.from(), interval.to());
 
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Mon May 04 19:34:51 2015 +0200
@@ -205,46 +205,53 @@
     }
 
     @Test
-    public void constantValueInertSpliceInstrumentListener() {
+    public void constantValueInertToolEvalNodeFactory() {
         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 SplicedNode into the AST, but which never does.
-        Instrument instrument = Instrument.create(new SpliceInstrumentListener() {
+        Probe testProbe = result.probe();
+        // A factory that could insert a ToolEvalNode into the AST, but which never does.
+        Instrument instrument = Instrument.create(new ToolEvalNodeFactory() {
 
-            public SplicedNode getSpliceNode(Probe p) {
+            public ToolEvalNode createToolEvalNode(Probe probe, Node node) {
                 return null;
             }
-
         }, null);
-        probe.attach(instrument);
+        testProbe.attach(instrument);
 
         // It all gets compiled away
         assertPartialEvalEquals("constant42", root);
     }
 
     @Test
-    public void constantValueInertSplicedNode() {
+    public void constantValueInertToolEvalNode() {
         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 SplicedNode with empty methods into the AST.
-        Instrument instrument = Instrument.create(new SpliceInstrumentListener() {
+        AbstractTestNode resultTestNode = new ConstantTestNode(42);
+        RootTestNode rootTestNode = new RootTestNode(fd, "constantValue", resultTestNode);
+        rootTestNode.adoptChildren();
+        Probe testProbe = resultTestNode.probe();
+        // A factory that inserts a ToolEvalNode with empty methods into the instrumentation chain.
+        Instrument instrument = Instrument.create(new ToolEvalNodeFactory() {
 
-            public SplicedNode getSpliceNode(Probe p) {
-                return new SplicedNode() {
+            public ToolEvalNode createToolEvalNode(Probe probe, Node node) {
+                return new ToolEvalNode() {
+
+                    public String instrumentationInfo() {
+                        return null;
+                    }
+
+                    @Override
+                    public Object executeToolEvalNode(Node n, VirtualFrame frame) {
+                        return null;
+                    }
                 };
             }
-
         }, null);
-        probe.attach(instrument);
+        testProbe.attach(instrument);
 
         // It all gets compiled away.
-        assertPartialEvalEquals("constant42", root);
+        assertPartialEvalEquals("constant42", rootTestNode);
     }
 
     @Test
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithBoxing.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithBoxing.java	Mon May 04 19:34:51 2015 +0200
@@ -22,11 +22,8 @@
  */
 package com.oracle.graal.truffle;
 
-import java.lang.reflect.*;
 import java.util.*;
 
-import sun.misc.*;
-
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 
@@ -236,70 +233,4 @@
     static <T> T unsafeCast(Object value, Class<T> type, boolean condition, boolean nonNull) {
         return (T) value;
     }
-
-    @SuppressWarnings("unused")
-    static int unsafeGetInt(Object receiver, long offset, boolean condition, Object locationIdentity) {
-        return UNSAFE.getInt(receiver, offset);
-    }
-
-    @SuppressWarnings("unused")
-    static long unsafeGetLong(Object receiver, long offset, boolean condition, Object locationIdentity) {
-        return UNSAFE.getLong(receiver, offset);
-    }
-
-    @SuppressWarnings("unused")
-    static float unsafeGetFloat(Object receiver, long offset, boolean condition, Object locationIdentity) {
-        return UNSAFE.getFloat(receiver, offset);
-    }
-
-    @SuppressWarnings("unused")
-    static double unsafeGetDouble(Object receiver, long offset, boolean condition, Object locationIdentity) {
-        return UNSAFE.getDouble(receiver, offset);
-    }
-
-    @SuppressWarnings("unused")
-    static Object unsafeGetObject(Object receiver, long offset, boolean condition, Object locationIdentity) {
-        return UNSAFE.getObject(receiver, offset);
-    }
-
-    @SuppressWarnings("unused")
-    static void unsafePutInt(Object receiver, long offset, int value, Object locationIdentity) {
-        UNSAFE.putInt(receiver, offset, value);
-    }
-
-    @SuppressWarnings("unused")
-    static void unsafePutLong(Object receiver, long offset, long value, Object locationIdentity) {
-        UNSAFE.putLong(receiver, offset, value);
-    }
-
-    @SuppressWarnings("unused")
-    static void unsafePutFloat(Object receiver, long offset, float value, Object locationIdentity) {
-        UNSAFE.putFloat(receiver, offset, value);
-    }
-
-    @SuppressWarnings("unused")
-    static void unsafePutDouble(Object receiver, long offset, double value, Object locationIdentity) {
-        UNSAFE.putDouble(receiver, offset, value);
-    }
-
-    @SuppressWarnings("unused")
-    static void unsafePutObject(Object receiver, long offset, Object value, Object locationIdentity) {
-        UNSAFE.putObject(receiver, offset, value);
-    }
-
-    private static final Unsafe UNSAFE = getUnsafe();
-
-    private static Unsafe getUnsafe() {
-        try {
-            return Unsafe.getUnsafe();
-        } catch (SecurityException e) {
-        }
-        try {
-            Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
-            theUnsafeInstance.setAccessible(true);
-            return (Unsafe) theUnsafeInstance.get(Unsafe.class);
-        } catch (Exception e) {
-            throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e);
-        }
-    }
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java	Mon May 04 19:34:51 2015 +0200
@@ -104,7 +104,8 @@
     }
 
     private Object getObjectUnsafe(int slotIndex, FrameSlot slot) {
-        return unsafeGetObject(getLocals(), Unsafe.ARRAY_OBJECT_BASE_OFFSET + slotIndex * (long) Unsafe.ARRAY_OBJECT_INDEX_SCALE, this.getTags()[slotIndex] == FrameSlotKind.Object.tag, slot);
+        boolean condition = this.getTags()[slotIndex] == OBJECT_TAG;
+        return unsafeGetObject(getLocals(), Unsafe.ARRAY_OBJECT_BASE_OFFSET + slotIndex * (long) Unsafe.ARRAY_OBJECT_INDEX_SCALE, condition, slot);
     }
 
     @Override
@@ -127,7 +128,7 @@
 
     private byte getByteUnsafe(int slotIndex, FrameSlot slot) {
         long offset = getPrimitiveOffset(slotIndex);
-        boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Byte.tag;
+        boolean condition = this.getTags()[slotIndex] == BYTE_TAG;
         return (byte) unsafeGetInt(getPrimitiveLocals(), offset, condition, slot);
     }
 
@@ -152,7 +153,7 @@
 
     private boolean getBooleanUnsafe(int slotIndex, FrameSlot slot) {
         long offset = getPrimitiveOffset(slotIndex);
-        boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Boolean.tag;
+        boolean condition = this.getTags()[slotIndex] == BOOLEAN_TAG;
         return unsafeGetInt(getPrimitiveLocals(), offset, condition, slot) != 0;
     }
 
@@ -177,7 +178,7 @@
 
     private float getFloatUnsafe(int slotIndex, FrameSlot slot) {
         long offset = getPrimitiveOffset(slotIndex);
-        boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Float.tag;
+        boolean condition = this.getTags()[slotIndex] == FLOAT_TAG;
         return unsafeGetFloat(getPrimitiveLocals(), offset, condition, slot);
     }
 
@@ -202,7 +203,7 @@
 
     private long getLongUnsafe(int slotIndex, FrameSlot slot) {
         long offset = getPrimitiveOffset(slotIndex);
-        boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Long.tag;
+        boolean condition = this.getTags()[slotIndex] == LONG_TAG;
         return unsafeGetLong(getPrimitiveLocals(), offset, condition, slot);
     }
 
@@ -227,7 +228,7 @@
 
     private int getIntUnsafe(int slotIndex, FrameSlot slot) {
         long offset = getPrimitiveOffset(slotIndex);
-        boolean condition = this.getTags()[slot.getIndex()] == FrameSlotKind.Int.tag;
+        boolean condition = this.getTags()[slot.getIndex()] == INT_TAG;
         return unsafeGetInt(getPrimitiveLocals(), offset, condition, slot);
     }
 
@@ -252,7 +253,7 @@
 
     private double getDoubleUnsafe(int slotIndex, FrameSlot slot) {
         long offset = getPrimitiveOffset(slotIndex);
-        boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Double.tag;
+        boolean condition = this.getTags()[slotIndex] == DOUBLE_TAG;
         return unsafeGetDouble(getPrimitiveLocals(), offset, condition, slot);
     }
 
@@ -306,20 +307,20 @@
             resize();
         }
         byte tag = getTags()[slotIndex];
-        if (tag == FrameSlotKind.Boolean.tag) {
+        if (tag == BOOLEAN_TAG) {
             return getBooleanUnsafe(slotIndex, slot);
-        } else if (tag == FrameSlotKind.Byte.tag) {
+        } else if (tag == BYTE_TAG) {
             return getByteUnsafe(slotIndex, slot);
-        } else if (tag == FrameSlotKind.Int.tag) {
+        } else if (tag == INT_TAG) {
             return getIntUnsafe(slotIndex, slot);
-        } else if (tag == FrameSlotKind.Double.tag) {
+        } else if (tag == DOUBLE_TAG) {
             return getDoubleUnsafe(slotIndex, slot);
-        } else if (tag == FrameSlotKind.Long.tag) {
+        } else if (tag == LONG_TAG) {
             return getLongUnsafe(slotIndex, slot);
-        } else if (tag == FrameSlotKind.Float.tag) {
+        } else if (tag == FLOAT_TAG) {
             return getFloatUnsafe(slotIndex, slot);
         } else {
-            assert tag == FrameSlotKind.Object.tag;
+            assert tag == OBJECT_TAG;
             return getObjectUnsafe(slotIndex, slot);
         }
     }
@@ -348,37 +349,37 @@
 
     @Override
     public boolean isObject(FrameSlot slot) {
-        return getTag(slot) == FrameSlotKind.Object.tag;
+        return getTag(slot) == OBJECT_TAG;
     }
 
     @Override
     public boolean isByte(FrameSlot slot) {
-        return getTag(slot) == FrameSlotKind.Byte.tag;
+        return getTag(slot) == BYTE_TAG;
     }
 
     @Override
     public boolean isBoolean(FrameSlot slot) {
-        return getTag(slot) == FrameSlotKind.Boolean.tag;
+        return getTag(slot) == BOOLEAN_TAG;
     }
 
     @Override
     public boolean isInt(FrameSlot slot) {
-        return getTag(slot) == FrameSlotKind.Int.tag;
+        return getTag(slot) == INT_TAG;
     }
 
     @Override
     public boolean isLong(FrameSlot slot) {
-        return getTag(slot) == FrameSlotKind.Long.tag;
+        return getTag(slot) == LONG_TAG;
     }
 
     @Override
     public boolean isFloat(FrameSlot slot) {
-        return getTag(slot) == FrameSlotKind.Float.tag;
+        return getTag(slot) == FLOAT_TAG;
     }
 
     @Override
     public boolean isDouble(FrameSlot slot) {
-        return getTag(slot) == FrameSlotKind.Double.tag;
+        return getTag(slot) == DOUBLE_TAG;
     }
 
     @SuppressWarnings({"unchecked", "unused"})
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Mon May 04 19:34:51 2015 +0200
@@ -458,7 +458,7 @@
     }
 
     @Override
-    public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
+    public boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
         CompilerAsserts.neverPartOfCompilation();
         if (isValid()) {
             invalidate(newNode, reason);
@@ -470,6 +470,7 @@
         if (cancelInstalledTask(newNode, reason)) {
             compilationProfile.reportInvalidated();
         }
+        return false;
     }
 
     public void accept(NodeVisitor visitor, boolean includeInlinedNodes) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java	Mon May 04 19:34:51 2015 +0200
@@ -157,8 +157,9 @@
         }
     }
 
-    public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
+    public boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
         invalidate(newNode, reason);
+        return false;
     }
 
     private void invalidate(Object source, CharSequence reason) {
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Mon May 04 19:34:51 2015 +0200
@@ -167,17 +167,22 @@
         }
     }
 
-    static class TestSplicedCounterNode extends SplicedNode {
+    static class TestToolEvalCounterNode extends ToolEvalNode {
 
         private long count;
 
         @Override
-        public void enter(Node node, VirtualFrame vFrame) {
+        public Object executeToolEvalNode(Node node, VirtualFrame vFrame) {
             count++;
+            return null;
         }
 
         public long getCount() {
             return count;
         }
+
+        public String instrumentationInfo() {
+            return null;
+        }
     }
 }
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java	Mon May 04 19:12:50 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +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.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());
-
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolEvalInstrumentTest.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,89 @@
+/*
+ * 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.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;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestToolEvalCounterNode;
+
+/**
+ * Tests the kind of instrumentation where a client can provide an AST fragment to be
+ * <em>spliced</em> directly into the AST.
+ */
+public class ToolEvalInstrumentTest {
+
+    @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 ToolEvalNodeFactory() {
+
+            public ToolEvalNode createToolEvalNode(Probe p, Node n) {
+                return null;
+            }
+        }, null);
+        probe.attach(instrument);
+
+        assertEquals(13, callTarget1.call());
+
+        final TestToolEvalCounterNode counter = new TestToolEvalCounterNode();
+
+        // Attach a listener that splices an execution counter into the AST.
+        probe.attach(Instrument.create(new ToolEvalNodeFactory() {
+
+            public ToolEvalNode createToolEvalNode(Probe p, Node n) {
+                return counter;
+            }
+        }, null));
+        assertEquals(0, counter.getCount());
+
+        assertEquals(13, callTarget1.call());
+
+        assertEquals(1, counter.getCount());
+
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java	Mon May 04 19:34:51 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -31,5 +31,10 @@
  */
 public interface ReplaceObserver {
 
-    void nodeReplaced(Node oldNode, Node newNode, CharSequence reason);
+    /**
+     * Returns <code>true</code> if the event is consumed and no parent nodes should be notified by
+     * for replaces. Returns <code>false</code> if the parent {@link Node} or {@link CallTarget}
+     * should get notified.
+     */
+    boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason);
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/Frame.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/Frame.java	Mon May 04 19:34:51 2015 +0200
@@ -167,8 +167,7 @@
 
     /**
      * Materializes this frame, which allows it to be stored in a field or cast to
-     * {@link java.lang.Object}. The frame however looses the ability to be packed or to access the
-     * caller frame.
+     * {@link java.lang.Object}.
      *
      * @return the new materialized frame
      */
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/MaterializedFrame.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/MaterializedFrame.java	Mon May 04 19:34:51 2015 +0200
@@ -27,9 +27,7 @@
 /**
  * Represents a materialized frame containing values of local variables of the guest language. It
  * can be created using the {@link VirtualFrame#materialize()} method. Instances of this type are
- * the only frame instances that may be stored in fields or cast to {@link java.lang.Object}. In
- * contrast to a {@link VirtualFrame}, a {@link MaterializedFrame} can no longer be packed and it
- * also does not provide access to the caller frame.
+ * the only frame instances that may be stored in fields or cast to {@link java.lang.Object}.
  */
 public interface MaterializedFrame extends Frame {
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Mon May 04 19:34:51 2015 +0200
@@ -103,10 +103,11 @@
 public abstract class Instrument {
 
     /**
-     * Creates an instrument that will route execution events to a listener.
+     * Creates a <em>Simple Instrument</em>: this Instrument routes execution events to a
+     * client-provided listener.
      *
-     * @param listener a minimal listener for event generated by the instrument.
-     * @param instrumentInfo optional description of the instrument's role, useful for debugging.
+     * @param listener a listener for execution events
+     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
      * @return a new instrument, ready for attachment at a probe
      */
     public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) {
@@ -114,12 +115,11 @@
     }
 
     /**
-     * Creates an instrument that will route execution events to a listener, along with access to
-     * internal execution state.
+     * Creates a <em>Standard Instrument</em>: this Instrument routes execution events, together
+     * with access to Truffle execution state, to a client-provided listener.
      *
-     * @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.
+     * @param standardListener a listener for execution events and execution state
+     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
      * @return a new instrument, ready for attachment at a probe
      */
     public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) {
@@ -127,17 +127,16 @@
     }
 
     /**
-     * Creates an instrument that, when executed the first time in any particular AST location,
-     * invites the tool to provide an AST fragment for <em>splicing</em> directly into the running
-     * AST.
+     * Creates a <em>Tool Eval Instrument</em>: this Instrument executes efficiently, subject to
+     * full Truffle optimization, a client-provided AST fragment every time the Probed node is
+     * entered.
      *
-     * @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.
+     * @param toolEvalNodeFactory provider of AST fragments on behalf of the client
+     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
+     * @return a new instrument, ready for attachment at a probe
      */
-    public static Instrument create(SpliceInstrumentListener spliceListener, String instrumentInfo) {
-        return new SpliceInstrument(spliceListener, instrumentInfo);
+    public static Instrument create(ToolEvalNodeFactory toolEvalNodeFactory, String instrumentInfo) {
+        return new ToolEvalInstrument(toolEvalNodeFactory, instrumentInfo);
     }
 
     // TODO (mlvdv) experimental
@@ -165,7 +164,16 @@
     }
 
     /**
-     * Removes this instrument from the probe to which it attached and renders the instrument inert.
+     * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not
+     * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}.
+     */
+    public Probe getProbe() {
+        return probe;
+    }
+
+    /**
+     * Removes this Instrument from the Probe to which it attached and renders this Instrument
+     * inert.
      *
      * @throws IllegalStateException if this instrument has already been disposed
      */
@@ -176,14 +184,14 @@
         if (probe != null) {
             // It's attached
             probe.disposeInstrument(this);
+            probe = null;
         }
         this.isDisposed = true;
     }
 
-    Probe getProbe() {
-        return probe;
-    }
-
+    /**
+     * For internal implementation only.
+     */
     void setAttachedTo(Probe probe) {
         this.probe = probe;
     }
@@ -239,6 +247,9 @@
             return instrumentNode;
         }
 
+        /**
+         * Node that implements a {@link SimpleInstrument} in a particular AST.
+         */
         @NodeInfo(cost = NodeCost.NONE)
         private final class SimpleInstrumentNode extends AbstractInstrumentNode {
 
@@ -318,6 +329,9 @@
             return instrumentNode;
         }
 
+        /**
+         * Node that implements a {@link StandardInstrument} in a particular AST.
+         */
         @NodeInfo(cost = NodeCost.NONE)
         private final class StandardInstrumentNode extends AbstractInstrumentNode {
 
@@ -360,26 +374,27 @@
         }
     }
 
-    // TODO (mlvdv) EXPERIMENTAL- UNDER DEVELOPMENT
     /**
-     * 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.
+     * An instrument that allows clients to provide an AST fragment to be executed directly from
+     * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle
+     * AST with potential for full optimization.
      */
-    private static final class SpliceInstrument extends Instrument {
+    private static final class ToolEvalInstrument extends Instrument {
 
         /**
-         * Tool-supplied listener for AST events.
+         * Client-provided supplier of new node instances to attach.
          */
-        private final SpliceInstrumentListener spliceListener;
+        private final ToolEvalNodeFactory toolEvalNodeFactory;
 
-        private SpliceInstrument(SpliceInstrumentListener spliceListener, String instrumentInfo) {
+        private ToolEvalInstrument(ToolEvalNodeFactory toolEvalNodeFactory, String instrumentInfo) {
             super(instrumentInfo);
-            this.spliceListener = spliceListener;
+            this.toolEvalNodeFactory = toolEvalNodeFactory;
+
         }
 
         @Override
         AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new SpliceInstrumentNode(nextNode);
+            return new ToolEvalNodeInstrumentNode(nextNode);
         }
 
         @Override
@@ -391,7 +406,7 @@
                     return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(SpliceInstrument.this);
+                found = instrumentNode.removeFromChain(ToolEvalInstrument.this);
             }
             if (!found) {
                 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
@@ -399,26 +414,30 @@
             return instrumentNode;
         }
 
+        /**
+         * Node that implements a {@link ToolEvalInstrument} in a particular AST.
+         */
         @NodeInfo(cost = NodeCost.NONE)
-        private final class SpliceInstrumentNode extends AbstractInstrumentNode {
+        private final class ToolEvalNodeInstrumentNode extends AbstractInstrumentNode {
 
-            @Child SplicedNode splicedNode;
+            @Child ToolEvalNode toolEvalNode;
 
-            private SpliceInstrumentNode(AbstractInstrumentNode nextNode) {
+            private ToolEvalNodeInstrumentNode(AbstractInstrumentNode nextNode) {
                 super(nextNode);
             }
 
             public void enter(Node node, VirtualFrame vFrame) {
-                if (splicedNode == null) {
-                    final SplicedNode newSplicedNode = SpliceInstrument.this.spliceListener.getSpliceNode(SpliceInstrument.this.probe);
-                    if (newSplicedNode != null) {
-                        splicedNode = newSplicedNode;
+                if (toolEvalNode == null) {
+                    final ToolEvalNode newToolEvalNodeNode = ToolEvalInstrument.this.toolEvalNodeFactory.createToolEvalNode(ToolEvalInstrument.this.probe, node);
+                    if (newToolEvalNodeNode != null) {
+                        toolEvalNode = newToolEvalNodeNode;
                         adoptChildren();
-                        SpliceInstrument.this.probe.invalidateProbeUnchanged();
+                        ToolEvalInstrument.this.probe.invalidateProbeUnchanged();
                     }
                 }
-                if (splicedNode != null) {
-                    splicedNode.enter(node, vFrame);
+                if (toolEvalNode != null) {
+                    // TODO (mlvdv) should report exception ; non-trivial architectural change
+                    toolEvalNode.executeToolEvalNode(node, vFrame);
                 }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.enter(node, vFrame);
@@ -426,27 +445,18 @@
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                if (splicedNode != null) {
-                    splicedNode.returnVoid(node, vFrame);
-                }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                if (splicedNode != null) {
-                    splicedNode.returnValue(node, vFrame, result);
-                }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                if (splicedNode != null) {
-                    splicedNode.returnExceptional(node, vFrame, exception);
-                }
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
@@ -454,7 +464,7 @@
 
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
-                return info != null ? info : spliceListener.getClass().getSimpleName();
+                return info != null ? info : toolEvalNodeFactory.getClass().getSimpleName();
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+public class InstrumentationException extends Exception {
+
+    public InstrumentationException(RuntimeException ex) {
+        super(ex);
+    }
+
+    private static final long serialVersionUID = 447857066220935502L;
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Mon May 04 19:34:51 2015 +0200
@@ -200,6 +200,13 @@
     }
 
     /**
+     * Gets the guest-language AST node to which this Probe is attached.
+     */
+    Node getProbedNode() {
+        return ((WrapperNode) this.getParent()).getChild();
+    }
+
+    /**
      * Adds an {@link AbstractInstrumentNode} to this chain.
      */
     @TruffleBoundary
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SpliceInstrumentListener.java	Mon May 04 19:12:50 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 <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);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SplicedNode.java	Mon May 04 19:12:50 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 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/ToolEvalNode.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,51 @@
+/*
+ * 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 executed efficiently, subject to full Truffle
+ * optimization, by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool Eval
+ * Instrument}.
+ *
+ * @see Instrument
+ * @see ToolEvalNodeFactory
+ * @see ToolEvalResultListener
+ */
+public abstract class ToolEvalNode extends Node implements InstrumentationNode {
+
+    /**
+     * Executes this AST fragment on behalf of a client {@link Instrument}, just before the
+     * guest-language AST node to which the {@link Probe} holding the Instrument is executed.
+     *
+     * @param node the guest-language AST node to which the host Instrument's Probe is attached
+     * @param vFrame execution frame at the guest-language AST node
+     * @return the result of this AST fragment's execution
+     */
+    public abstract Object executeToolEvalNode(Node node, VirtualFrame vFrame);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalNodeFactory.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,51 @@
+/*
+ * 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.nodes.*;
+
+/**
+ * Creator of {@linkplain ToolEvalNode AST fragments} suitable for efficient execution, subject to
+ * full Truffle optimization, by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool
+ * Eval Instrument}.
+ *
+ * @see Instrument
+ * @see ToolEvalNode
+ */
+public interface ToolEvalNodeFactory {
+
+    /**
+     * Provider of {@linkplain ToolEvalNode AST fragment} instances for efficient execution via
+     * instrumentation, subject to full Truffle optimization, at a {@linkplain Probe Probed} site in
+     * a guest-language AST.
+     *
+     * @param probe the Probe to which the Instrument requesting the AST fragment is attached
+     * @param node the guest-language AST location that is the context in which the requesting
+     *            Instrument must execute the AST fragment.
+     * @return a newly created AST fragment suitable for execution, via instrumentation, in the
+     *         execution context of the specified guest-language AST site.
+     */
+    ToolEvalNode createToolEvalNode(Probe probe, Node node);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalResultListener.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,64 @@
+/*
+ * 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.*;
+
+/**
+ * Listener for receiving the result a client-provided {@linkplain ToolEvalNode AST fragment}, when
+ * executed by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool Eval Instrument}.
+ *
+ * @see Instrument
+ * @see ToolEvalNode
+ * @see ToolEvalNodeFactory
+ */
+public interface ToolEvalResultListener {
+
+    /**
+     * Notifies listener that a client-provided {@linkplain ToolEvalNode AST fragment} has been
+     * executed by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool Eval
+     * Instrument} with the specified result, possibly {@code null}.
+     *
+     * @param node the guest-language AST node to which the host Instrument's {@link Probe} is
+     *            attached
+     * @param vFrame execution frame at the guest-language AST node
+     * @param result the result of this AST fragment's execution
+     */
+    void notifyToolEvalResult(Node node, VirtualFrame vFrame, Object result);
+
+    /**
+     * Notifies listener that execution of client-provided {@linkplain ToolEvalNode AST fragment}
+     * filed during execution by a @linkplain Instrument#create(ToolEvalNodeFactory, String) Tool
+     * Eval Instrument}.
+     *
+     * @param node the guest-language AST node to which the host Instrument's {@link Probe} is
+     *            attached
+     * @param vFrame execution frame at the guest-language AST node
+     * @param ex the exception
+     */
+    void notifyToolEvalFailure(Node node, VirtualFrame vFrame, RuntimeException ex);
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon May 04 19:34:51 2015 +0200
@@ -299,14 +299,18 @@
     private void reportReplace(Node oldNode, Node newNode, CharSequence reason) {
         Node node = this;
         while (node != null) {
+            boolean consumed = false;
             if (node instanceof ReplaceObserver) {
-                ((ReplaceObserver) node).nodeReplaced(oldNode, newNode, reason);
+                consumed = ((ReplaceObserver) node).nodeReplaced(oldNode, newNode, reason);
             } else if (node instanceof RootNode) {
                 CallTarget target = ((RootNode) node).getCallTarget();
                 if (target instanceof ReplaceObserver) {
-                    ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason);
+                    consumed = ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason);
                 }
             }
+            if (consumed) {
+                break;
+            }
             node = node.getParent();
         }
         if (TruffleOptions.TraceRewrites) {
--- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/ForeignAccessArguments.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/ForeignAccessArguments.java	Mon May 04 19:34:51 2015 +0200
@@ -54,6 +54,10 @@
         return copyOfRange(arguments, RUNTIME_ARGUMENT_COUNT, arguments.length);
     }
 
+    public static Object[] extractUserArguments(int skip, Object[] arguments) {
+        return copyOfRange(arguments, RUNTIME_ARGUMENT_COUNT + skip, arguments.length);
+    }
+
     public static int getUserArgumentCount(Object[] arguments) {
         return arguments.length - RUNTIME_ARGUMENT_COUNT;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleGlobalScope.java	Mon May 04 19:34:51 2015 +0200
@@ -0,0 +1,38 @@
+/*
+ * 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.interop;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
+
+public interface TruffleGlobalScope {
+    void exportTruffleObject(Object identifier, TruffleObject object);
+
+    FrameSlot getFrameSlot(Object identifier);
+
+    TruffleObject getTruffleObject(FrameSlot slot);
+
+    boolean contains(Object identifier);
+}
--- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleLanguage.java	Mon May 04 19:12:50 2015 +0200
+++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleLanguage.java	Mon May 04 19:34:51 2015 +0200
@@ -29,12 +29,12 @@
 public interface TruffleLanguage {
     String getLanguageName();
 
-    TruffleObject getLanguageContext();
-
-    void setGlobalContext(TruffleObject context);
-
-    boolean hasGlobalProperty(String name);
-
     Object run(String[] arguments);
 
+    void setTruffleGlobalScope(TruffleGlobalScope globalScope);
+
+    TruffleObject getLanguageGlobal();
+
+    boolean isObjectOfLanguage(TruffleObject object);
+
 }
--- a/mx/mx_graal.py	Mon May 04 19:12:50 2015 +0200
+++ b/mx/mx_graal.py	Mon May 04 19:34:51 2015 +0200
@@ -118,7 +118,7 @@
         return _vm
     vm = mx.get_env('DEFAULT_VM')
     if vm is None:
-        if not sys.stdout.isatty():
+        if not mx.is_interactive():
             mx.abort('Need to specify VM with --vm option or DEFAULT_VM environment variable')
         envPath = join(_graal_home, 'mx', 'env')
         mx.log('Please select the VM to be executed from the following: ')
@@ -392,7 +392,7 @@
     if not vm:
         vm = _get_vm()
     mx.log('The ' + bld + ' ' + vm + ' VM has not been created')
-    if sys.stdout.isatty():
+    if mx.is_interactive():
         if mx.ask_yes_no('Build it now', 'y'):
             with VM(vm, bld):
                 build([])
@@ -1714,9 +1714,8 @@
 
     with Task('CleanAndBuildIdealGraphVisualizer', tasks) as t:
         if t and platform.processor() != 'sparc':
-            env = _igvFallbackJDK(os.environ)
             buildxml = mx._cygpathU2W(join(_graal_home, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'build.xml'))
-            mx.run(['ant', '-f', buildxml, '-q', 'clean', 'build'], env=env)
+            mx.run(['ant', '-f', buildxml, '-q', 'clean', 'build'], env=_igvBuildEnv())
 
     # Prevent Graal modifications from breaking the standard builds
     if args.buildNonGraal:
@@ -1758,6 +1757,11 @@
     if args.task_filter:
         Task.filters = args.task_filter.split(',')
 
+    # Force
+    if not mx._opts.strict_compliance:
+        mx.log("[gate] foring strict compliance")
+        mx._opts.strict_compliance = True
+
     tasks = []
     total = Task('Gate')
     try:
@@ -1861,38 +1865,34 @@
 
     dacapo(['100', 'eclipse', '-esa'])
 
-def _igvFallbackJDK(env):
+def _igvJdk():
     v8u20 = mx.VersionSpec("1.8.0_20")
     v8u40 = mx.VersionSpec("1.8.0_40")
     v8 = mx.VersionSpec("1.8")
-    igvHomes = [h for h in mx._java_homes if h.version >= v8 and (h.version < v8u20 or h.version >= v8u40)]
-    defaultJava = mx.java()
-    if defaultJava not in igvHomes:
-        if not igvHomes:
-            mx.abort("No JDK available for building IGV. Must have JDK >= 1.8 and < 1.8.0u20 or >= 1.8.0u40 in JAVA_HOME or EXTRA_JAVA_HOMES")
-        env = dict(env)
-        fallbackJDK = igvHomes[0]
-        mx.logv("1.8.0_20 has a known javac bug (JDK-8043926), thus falling back to " + str(fallbackJDK.version))
-        env['JAVA_HOME'] = str(fallbackJDK.jdk)
+    def _igvJdkVersionCheck(version):
+        return version >= v8 and (version < v8u20 or version >= v8u40)
+    return mx.java_version(_igvJdkVersionCheck, versionDescription='>= 1.8 and < 1.8.0u20 or >= 1.8.0u40').jdk
+
+def _igvBuildEnv():
+        # When the http_proxy environment variable is set, convert it to the proxy settings that ant needs
+    env = dict(os.environ)
+    proxy = os.environ.get('http_proxy')
+    if not (proxy is None) and len(proxy) > 0:
+        if '://' in proxy:
+            # Remove the http:// prefix (or any other protocol prefix)
+            proxy = proxy.split('://', 1)[1]
+        # Separate proxy server name and port number
+        proxyName, proxyPort = proxy.split(':', 1)
+        proxyEnv = '-DproxyHost="' + proxyName + '" -DproxyPort=' + proxyPort
+        env['ANT_OPTS'] = proxyEnv
+
+    env['JAVA_HOME'] = _igvJdk()
     return env
 
 def igv(args):
     """run the Ideal Graph Visualizer"""
     logFile = '.ideal_graph_visualizer.log'
     with open(join(_graal_home, logFile), 'w') as fp:
-        # When the http_proxy environment variable is set, convert it to the proxy settings that ant needs
-        env = dict(os.environ)
-        proxy = os.environ.get('http_proxy')
-        if not (proxy is None) and len(proxy) > 0:
-            if '://' in proxy:
-                # Remove the http:// prefix (or any other protocol prefix)
-                proxy = proxy.split('://', 1)[1]
-            # Separate proxy server name and port number
-            proxyName, proxyPort = proxy.split(':', 1)
-            proxyEnv = '-DproxyHost="' + proxyName + '" -DproxyPort=' + proxyPort
-            env['ANT_OPTS'] = proxyEnv
-        env = _igvFallbackJDK(env)
-
         mx.logv('[Ideal Graph Visualizer log is in ' + fp.name + ']')
         nbplatform = join(_graal_home, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'nbplatform')
 
@@ -1914,6 +1914,8 @@
 
         if not exists(nbplatform):
             mx.logv('[This execution may take a while as the NetBeans platform needs to be downloaded]')
+
+        env = _igvBuildEnv()
         # make the jar for Batik 1.7 available.
         env['IGV_BATIK_JAR'] = mx.library('BATIK').get_path(True)
         if mx.run(['ant', '-f', mx._cygpathU2W(join(_graal_home, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'build.xml')), '-l', mx._cygpathU2W(fp.name), 'run'], env=env, nonZeroIsFatal=False):
@@ -2571,7 +2573,7 @@
     findbugsResults = join(_graal_home, 'findbugs.results')
 
     cmd = ['-jar', mx._cygpathU2W(findbugsJar), '-textui', '-low', '-maxRank', '15']
-    if sys.stdout.isatty():
+    if mx.is_interactive():
         cmd.append('-progress')
     cmd = cmd + ['-auxclasspath', mx._separatedCygpathU2W(mx.classpath([d.name for d in _jdkDeployedDists] + [p.name for p in nonTestProjects])), '-output', mx._cygpathU2W(findbugsResults), '-exitcode'] + args + outputDirs
     exitcode = mx.run_java(cmd, nonZeroIsFatal=False, javaConfig=mx.java(javaCompliance))
--- a/mxtool/.pylintrc	Mon May 04 19:12:50 2015 +0200
+++ b/mxtool/.pylintrc	Mon May 04 19:34:51 2015 +0200
@@ -44,7 +44,8 @@
         too-many-lines,missing-docstring,no-init,no-self-use,too-many-statements,
         too-many-locals,too-few-public-methods,too-many-instance-attributes,
         too-many-arguments,too-many-branches,too-many-public-methods,
-        multiple-statements,abstract-method,F0401,no-member,non-parent-init-called
+        multiple-statements,abstract-method,F0401,no-member,non-parent-init-called,
+        maybe-no-member
 
 # F0401: http://www.logilab.org/ticket/9386
 
--- a/mxtool/mx.py	Mon May 04 19:12:50 2015 +0200
+++ b/mxtool/mx.py	Mon May 04 19:34:51 2015 +0200
@@ -47,6 +47,14 @@
 from argparse import ArgumentParser, REMAINDER
 from os.path import join, basename, dirname, exists, getmtime, isabs, expandvars, isdir, isfile
 
+try:
+    # needed to work around https://bugs.python.org/issue1927
+    import readline
+    #then make pylint happy..
+    readline.get_line_buffer()
+except ImportError:
+    pass
+
 # Support for Python 2.6
 def check_output(*popenargs, **kwargs):
     process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
@@ -94,7 +102,8 @@
 _primary_suite_path = None
 _primary_suite = None
 _opts = None
-_java_homes = None
+_extra_java_homes = []
+_default_java_home = None
 _warn = False
 
 """
@@ -213,10 +222,6 @@
                             if p.javaCompliance > self.javaCompliance:
                                 abort("Compliance level doesn't match: Distribution {0} requires {1}, but {2} is {3}.".format(self.name, self.javaCompliance, p.name, p.javaCompliance))
 
-                        # skip a  Java project if its Java compliance level is "higher" than the configured JDK
-                        jdk = java(p.javaCompliance)
-                        assert jdk
-
                         logv('[' + self.path + ': adding project ' + p.name + ']')
                         outputDir = p.output_dir()
                         for root, _, files in os.walk(outputDir):
@@ -306,14 +311,6 @@
         self.definedAnnotationProcessors = None
         self.definedAnnotationProcessorsDist = None
 
-
-        # Verify that a JDK exists for this project if its compliance level is
-        # less than the compliance level of the default JDK
-        jdk = java(self.javaCompliance)
-        if jdk is None and self.javaCompliance < java().javaCompliance:
-            abort('Cannot find ' + str(self.javaCompliance) + ' JDK required by ' + name + '. ' +
-                  'Specify it with --extra-java-homes option or EXTRA_JAVA_HOMES environment variable.')
-
         # Create directories for projects that don't yet exist
         if not exists(d):
             os.mkdir(d)
@@ -755,6 +752,7 @@
         sha1path = path + '.sha1'
 
         includedInJDK = getattr(self, 'includedInJDK', None)
+        # TODO since we don't know which JDK will be used, this check is dubious
         if includedInJDK and java().javaCompliance >= JavaCompliance(includedInJDK):
             return None
 
@@ -1230,7 +1228,7 @@
                         del _libs[d.name]
                         self.libs.remove(d)
             elif d.isProject():
-                if java(d.javaCompliance) is None:
+                if java(d.javaCompliance, cancel='some projects will be omitted which may result in errrors') is None:
                     logv('[omitting project {0} as Java compliance {1} cannot be satisfied by configured JDKs]'.format(d, d.javaCompliance))
                     del _projects[d.name]
                     self.projects.remove(d)
@@ -1656,68 +1654,6 @@
         p.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors)
     return deps
 
-def _handle_lookup_java_home(jdk):
-    return _handle_lookup_jdk(jdk, 'JAVA_HOME', '--java-home', False)
-
-def _handle_lookup_extra_java_homes(jdk):
-    return _handle_lookup_jdk(jdk, 'EXTRA_JAVA_HOMES', '--extra-java-homes', True)
-
-def _handle_lookup_jdk(jdk, varName, flagName, allowMultiple):
-    if jdk != None and jdk != '':
-        return jdk
-    jdk = os.environ.get(varName)
-    if jdk != None and jdk != '':
-        return jdk
-
-    if not sys.stdout.isatty():
-        abort('Could not find bootstrap {0}. Use {1} option or ensure {2} environment variable is set.'.format(varName, flagName, varName))
-
-    candidateJdks = []
-    if get_os() == 'darwin':
-        base = '/Library/Java/JavaVirtualMachines'
-        if exists(base):
-            candidateJdks = [join(base, n, 'Contents/Home') for n in os.listdir(base) if exists(join(base, n, 'Contents/Home'))]
-    elif get_os() == 'linux':
-        base = '/usr/lib/jvm'
-        if exists(base):
-            candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))]
-        base = '/usr/java'
-        if exists(base):
-            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'
-        if exists(base):
-            candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))]
-    elif get_os() == 'windows':
-        base = r'C:\Program Files\Java'
-        if exists(base):
-            candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, r'jre\lib\rt.jar'))]
-
-    javaHome = None
-    if len(candidateJdks) != 0:
-        log('Missing value for {0}.'.format(varName))
-        javaHome = select_items(candidateJdks + ['<other>'], allowMultiple=allowMultiple)
-        if javaHome == '<other>':
-            javaHome = None
-        if javaHome != None and allowMultiple:
-            javaHome = os.pathsep.join(javaHome)
-
-    while javaHome is None:
-        javaHome = raw_input('Enter path of JDK for {0}: '.format(varName))
-        rtJarPath = join(javaHome, 'jre', 'lib', 'rt.jar')
-        if not exists(rtJarPath):
-            log('Does not appear to be a valid JDK as ' + rtJarPath + ' does not exist')
-            javaHome = None
-        else:
-            break
-
-    envPath = join(_primary_suite.mxDir, 'env')
-    if ask_yes_no('Persist this setting by adding "{0}={1}" to {2}'.format(varName, javaHome, envPath), 'y'):
-        with open(envPath, 'a') as fp:
-            print >> fp, varName + '=' + javaHome
-
-    return javaHome
-
 class ArgParser(ArgumentParser):
     # Override parent to append the list of available commands
     def format_help(self):
@@ -1744,6 +1680,7 @@
         self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~'))
         self.add_argument('--java-home', help='primary JDK directory (must be JDK 7 or later)', metavar='<path>')
         self.add_argument('--extra-java-homes', help='secondary JDK directories separated by "' + os.pathsep + '"', metavar='<path>')
+        self.add_argument('--strict-compliance', action='store_true', dest='strict_compliance', help='Projects of a certain compliance will only be built with a JDK of this exact compliance', default=False)
         self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='<name>', default=[])
         self.add_argument('--kill-with-sigquit', action='store_true', dest='killwithsigquit', help='send sigquit first before killing child processes')
         if get_os() != 'windows':
@@ -1769,15 +1706,16 @@
         if opts.very_verbose:
             opts.verbose = True
 
-        opts.java_home = _handle_lookup_java_home(opts.java_home)
-        opts.extra_java_homes = _handle_lookup_extra_java_homes(opts.extra_java_homes)
-
         if opts.user_home is None or opts.user_home == '':
             abort('Could not find user home. Use --user-home option or ensure HOME environment variable is set.')
 
-        os.environ['JAVA_HOME'] = opts.java_home
+        if opts.java_home:
+            os.environ['JAVA_HOME'] = opts.java_home
         os.environ['HOME'] = opts.user_home
 
+        if os.environ.get('STRICT_COMPLIANCE'):
+            _opts.strict_compliance = True
+
         opts.ignored_projects = opts.ignored_projects + os.environ.get('IGNORED_PROJECTS', '').split(',')
 
         commandAndArgs = opts.__dict__.pop('commandAndArgs')
@@ -1796,19 +1734,231 @@
         msg += ' {0:<20} {1}\n'.format(cmd, doc.split('\n', 1)[0])
     return msg + '\n'
 
-def java(requiredCompliance=None):
+_canceled_java_requests = set()
+
+def java(requiredCompliance=None, purpose=None, cancel=None):
     """
     Get a JavaConfig object containing Java commands launch details.
     If requiredCompliance is None, the compliance level specified by --java-home/JAVA_HOME
     is returned. Otherwise, the JavaConfig exactly matching requiredCompliance is returned
     or None if there is no exact match.
     """
-    assert _java_homes
+
+    global _default_java_home
+    if cancel and (requiredCompliance, purpose) in _canceled_java_requests:
+        return None
+
     if not requiredCompliance:
-        return _java_homes[0]
-    for java in _java_homes:
-        if java.javaCompliance == requiredCompliance:
+        if not _default_java_home:
+            _default_java_home = _find_jdk(purpose=purpose, cancel=cancel)
+            if not _default_java_home:
+                assert cancel
+                _canceled_java_requests.add((requiredCompliance, purpose))
+        return _default_java_home
+
+    if _opts.strict_compliance:
+        complianceCheck = requiredCompliance.exactMatch
+        desc = str(requiredCompliance)
+    else:
+        compVersion = VersionSpec(str(requiredCompliance))
+        complianceCheck = lambda version: version >= compVersion
+        desc = '>=' + str(requiredCompliance)
+
+    for java in _extra_java_homes:
+        if complianceCheck(java.version):
+            return java
+
+    jdk = _find_jdk(versionCheck=complianceCheck, versionDescription=desc, purpose=purpose, cancel=cancel)
+    if jdk:
+        assert jdk not in _extra_java_homes
+        _extra_java_homes.append(jdk)
+    else:
+        assert cancel
+        _canceled_java_requests.add((requiredCompliance, purpose))
+    return jdk
+
+def java_version(versionCheck, versionDescription=None, purpose=None):
+    if _default_java_home and versionCheck(_default_java_home.version):
+        return _default_java_home
+    for java in _extra_java_homes:
+        if versionCheck(java.version):
             return java
+    jdk = _find_jdk(versionCheck, versionDescription, purpose)
+    assert jdk not in _extra_java_homes
+    _extra_java_homes.append(jdk)
+    return jdk
+
+def _find_jdk(versionCheck=None, versionDescription=None, purpose=None, cancel=None):
+    if not versionCheck:
+        versionCheck = lambda v: True
+    assert not versionDescription or versionCheck
+    if not versionCheck and not purpose:
+        isDefaultJdk = True
+    else:
+        isDefaultJdk = False
+
+    candidateJdks = []
+    source = ''
+    if _opts.java_home:
+        candidateJdks.append(_opts.java_home)
+        source = '--java-home'
+    elif os.environ.get('JAVA_HOME'):
+        candidateJdks.append(os.environ.get('JAVA_HOME'))
+        source = 'JAVA_HOME'
+
+    result = _find_jdk_in_candidates(candidateJdks, versionCheck, warn=True, source=source)
+    if result:
+        return result
+
+    candidateJdks = []
+
+    if _opts.extra_java_homes:
+        candidateJdks += _opts.extra_java_homes.split(os.pathsep)
+        source = '--extra-java-homes'
+    elif os.environ.get('EXTRA_JAVA_HOMES'):
+        candidateJdks += os.environ.get('EXTRA_JAVA_HOMES').split(os.pathsep)
+        source = 'EXTRA_JAVA_HOMES'
+
+    result = _find_jdk_in_candidates(candidateJdks, versionCheck, warn=True, source=source)
+    if not result:
+        candidateJdks = []
+        source = ''
+
+        if get_os() == 'darwin':
+            base = '/Library/Java/JavaVirtualMachines'
+            if exists(base):
+                candidateJdks = [join(base, n, 'Contents/Home') for n in os.listdir(base)]
+        elif get_os() == 'linux':
+            base = '/usr/lib/jvm'
+            if exists(base):
+                candidateJdks = [join(base, n) for n in os.listdir(base)]
+            base = '/usr/java'
+            if exists(base):
+                candidateJdks += [join(base, n) for n in os.listdir(base)]
+        elif get_os() == 'solaris':
+            base = '/usr/jdk/instances'
+            if exists(base):
+                candidateJdks = [join(base, n) for n in os.listdir(base)]
+        elif get_os() == 'windows':
+            base = r'C:\Program Files\Java'
+            if exists(base):
+                candidateJdks = [join(base, n) for n in os.listdir(base)]
+
+        configs = _filtered_jdk_configs(candidateJdks, versionCheck)
+    else:
+        if not isDefaultJdk:
+            return result
+        configs = [result]
+
+    if len(configs) > 1:
+        if not is_interactive():
+            msg = "Multiple possible choices for a JDK"
+            if purpose:
+                msg += ' for' + purpose
+            msg += ': '
+            if versionDescription:
+                msg += '(' + versionDescription + ')'
+            selected = configs[0]
+            msg += ". Selecting " + str(selected)
+            log(msg)
+        else:
+            msg = 'Please select a '
+            if isDefaultJdk:
+                msg += 'default '
+            msg += 'JDK'
+            if purpose:
+                msg += ' for' + purpose
+            msg += ': '
+            if versionDescription:
+                msg += '(' + versionDescription + ')'
+            log(msg)
+            choices = configs + ['<other>']
+            if cancel:
+                choices.append('Cancel (' + cancel + ')')
+            selected = select_items(choices, allowMultiple=False)
+            if isinstance(selected, types.StringTypes) and selected == '<other>':
+                selected = None
+            if isinstance(selected, types.StringTypes) and selected == 'Cancel (' + cancel + ')':
+                return None
+    elif len(configs) == 1:
+        selected = configs[0]
+        msg = 'Selected ' + str(selected) + ' as '
+        if isDefaultJdk:
+            msg += 'default'
+        msg += 'JDK'
+        if versionDescription:
+            msg = msg + ' ' + versionDescription
+        if purpose:
+            msg += ' for' + purpose
+        log(msg)
+    else:
+        msg = 'Could not find any JDK'
+        if purpose:
+            msg += ' for' + purpose
+        msg += ' '
+        if versionDescription:
+            msg = msg + '(' + versionDescription + ')'
+        log(msg)
+        selected = None
+
+    while not selected:
+        jdkLocation = raw_input('Enter path of JDK: ')
+        selected = _find_jdk_in_candidates([jdkLocation], versionCheck, warn=True)
+
+    varName = 'JAVA_HOME' if isDefaultJdk else 'EXTRA_JAVA_HOMES'
+    allowMultiple = not isDefaultJdk
+    envPath = join(_primary_suite.mxDir, 'env')
+    if is_interactive() and ask_yes_no('Persist this setting by adding "{0}={1}" to {2}'.format(varName, selected.jdk, envPath), 'y'):
+        envLines = []
+        with open(envPath) as fp:
+            append = True
+            for line in fp:
+                if line.rstrip().startswith(varName):
+                    _, currentValue = line.split('=', 1)
+                    currentValue = currentValue.strip()
+                    if not allowMultiple and currentValue:
+                        if not ask_yes_no('{0} is already set to {1}, overwrite with {2}?'.format(varName, currentValue, selected.jdk), 'n'):
+                            return selected
+                        else:
+                            line = varName + '=' + selected.jdk + os.linesep
+                    else:
+                        line = line.rstrip()
+                        if currentValue:
+                            line += os.pathsep
+                        line += selected.jdk + os.linesep
+                    append = False
+                envLines.append(line)
+        if append:
+            envLines.append(varName + '=' + selected.jdk)
+
+        with open(envPath, 'w') as fp:
+            for line in envLines:
+                fp.write(line)
+
+    if varName == 'JAVA_HOME':
+        os.environ['JAVA_HOME'] = selected.jdk
+
+    return selected
+
+def is_interactive():
+    return sys.__stdin__.isatty()
+
+def _filtered_jdk_configs(candidates, versionCheck, warn=False, source=None):
+    filtered = []
+    for candidate in candidates:
+        try:
+            config = JavaConfig(candidate)
+            if versionCheck(config.version):
+                filtered.append(config)
+        except JavaConfigException as e:
+            if warn:
+                log('Path in ' + source + "' is not pointing to a JDK (" + e.message + ")")
+    return filtered
+
+def _find_jdk_in_candidates(candidates, versionCheck, warn=False, source=None):
+    filtered = _filtered_jdk_configs(candidates, versionCheck, warn, source)
+    if filtered:
+        return filtered[0]
     return None
 
 
@@ -2079,6 +2229,10 @@
     def __hash__(self):
         return self.value.__hash__()
 
+    def exactMatch(self, version):
+        assert isinstance(version, VersionSpec)
+        return len(version.parts) > 1 and version.parts[0] == 1 and version.parts[1] == self.value
+
 """
 A version specification as defined in JSR-56
 """
@@ -2102,13 +2256,16 @@
         return os.pathsep.join([path for path in _separatedCygpathW2U(paths).split(os.pathsep) if exists(path)])
     return None
 
+class JavaConfigException(Exception):
+    def __init__(self, value):
+        Exception.__init__(self, value)
+
 """
 A JavaConfig object encapsulates info on how Java commands are run.
 """
 class JavaConfig:
-    def __init__(self, java_home, java_dbg_port):
+    def __init__(self, java_home):
         self.jdk = java_home
-        self.debug_port = java_dbg_port
         self.jar = exe_suffix(join(self.jdk, 'bin', 'jar'))
         self.java = exe_suffix(join(self.jdk, 'bin', 'java'))
         self.javac = exe_suffix(join(self.jdk, 'bin', 'javac'))
@@ -2122,7 +2279,7 @@
         self._endorseddirs = None
 
         if not exists(self.java):
-            abort('Java launcher does not exist: ' + self.java)
+            raise JavaConfigException('Java launcher does not exist: ' + self.java)
 
         def delAtAndSplit(s):
             return shlex.split(s.lstrip('@'))
@@ -2139,8 +2296,7 @@
             try:
                 output = subprocess.check_output([self.java, '-version'], stderr=subprocess.STDOUT)
             except subprocess.CalledProcessError as e:
-                print e.output
-                abort(e.returncode)
+                raise JavaConfigException(e.returncode + " :" + e.output)
 
         def _checkOutput(out):
             return 'version' in out
@@ -2156,8 +2312,8 @@
         self.version = VersionSpec(version.split()[2].strip('"'))
         self.javaCompliance = JavaCompliance(self.version.versionString)
 
-        if self.debug_port is not None:
-            self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(self.debug_port)]
+        if _opts.java_dbg_port is not None:
+            self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(_opts.java_dbg_port)]
 
     def _init_classpaths(self):
         if not self._classpaths_initialized:
@@ -2180,7 +2336,7 @@
             self._classpaths_initialized = True
 
     def __repr__(self):
-        return "JavaConfig(" + str(self.jdk) + ", " + str(self.debug_port) + ")"
+        return "JavaConfig(" + str(self.jdk) + ")"
 
     def __str__(self):
         return "Java " + str(self.version) + " (" + str(self.javaCompliance) + ") from " + str(self.jdk)
@@ -2475,8 +2631,8 @@
                     self.logCompilation('javac' if not args.alt_javac else args.alt_javac)
                     javacCmd = [javac, '-g', '-J-Xmx1g', '-source', compliance, '-target', compliance, '-classpath', cp, '-d', outputDir]
                     jdk.javacLibOptions(javacCmd)
-                    if jdk.debug_port is not None:
-                        javacCmd += ['-J-Xdebug', '-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(jdk.debug_port)]
+                    if _opts.java_dbg_port is not None:
+                        javacCmd += ['-J-Xdebug', '-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(_opts.java_dbg_port)]
                     javacCmd += processorArgs
                     javacCmd += ['@' + _cygpathU2W(argfile.name)]
 
@@ -2636,7 +2792,6 @@
         # skip building this Java project if its Java compliance level is "higher" than the configured JDK
         requiredCompliance = p.javaCompliance if p.javaCompliance else JavaCompliance(args.compliance) if args.compliance else None
         jdk = java(requiredCompliance)
-        assert jdk
 
         outputDir = p.output_dir()
 
@@ -2940,16 +3095,33 @@
         def __init__(self, settingsDir, javaCompliance):
             self.path = join(settingsDir, 'org.eclipse.jdt.core.prefs')
             self.javaCompliance = javaCompliance
-            self.javafiles = list()
             with open(join(settingsDir, 'org.eclipse.jdt.ui.prefs')) as fp:
                 jdtUiPrefs = fp.read()
             self.removeTrailingWhitespace = 'sp_cleanup.remove_trailing_whitespaces_all=true' in jdtUiPrefs
             if self.removeTrailingWhitespace:
                 assert 'sp_cleanup.remove_trailing_whitespaces=true' in jdtUiPrefs and 'sp_cleanup.remove_trailing_whitespaces_ignore_empty=false' in jdtUiPrefs
-
-        def settings(self):
+            self.cachedHash = None
+
+        def __hash__(self):
+            if not self.cachedHash:
+                with open(self.path) as fp:
+                    self.cachedHash = (fp.read(), self.javaCompliance, self.removeTrailingWhitespace).__hash__()
+            return self.cachedHash
+
+        def __eq__(self, other):
+            if not isinstance(other, Batch):
+                return False
+            if self.removeTrailingWhitespace != other.removeTrailingWhitespace:
+                return False
+            if self.javaCompliance != other.javaCompliance:
+                return False
+            if self.path == other.path:
+                return True
             with open(self.path) as fp:
-                return fp.read() + java(self.javaCompliance).java + str(self.removeTrailingWhitespace)
+                with open(other.path) as ofp:
+                    if fp.read() != ofp.read():
+                        return False
+            return True
 
     class FileInfo:
         def __init__(self, path):
@@ -2992,22 +3164,22 @@
             if _opts.verbose:
                 log('[no Eclipse Code Formatter preferences at {0} - skipping]'.format(batch.path))
             continue
-
+        javafiles = []
         for sourceDir in sourceDirs:
             for root, _, files in os.walk(sourceDir):
                 for f in [join(root, name) for name in files if name.endswith('.java')]:
-                    batch.javafiles.append(FileInfo(f))
-        if len(batch.javafiles) == 0:
+                    javafiles.append(FileInfo(f))
+        if len(javafiles) == 0:
             logv('[no Java sources in {0} - skipping]'.format(p.name))
             continue
 
-        res = batches.setdefault(batch.settings(), batch)
-        if res is not batch:
-            res.javafiles = res.javafiles + batch.javafiles
+        res = batches.setdefault(batch, javafiles)
+        if res is not javafiles:
+            res.extend(javafiles)
 
     log("we have: " + str(len(batches)) + " batches")
-    for batch in batches.itervalues():
-        for chunk in _chunk_files_for_command_line(batch.javafiles, pathFunction=lambda f: f.path):
+    for batch, javafiles in batches.iteritems():
+        for chunk in _chunk_files_for_command_line(javafiles, pathFunction=lambda f: f.path):
             run([args.eclipse_exe,
                 '-nosplash',
                 '-application',
@@ -4721,7 +4893,7 @@
 
 def fsckprojects(args):
     """find directories corresponding to deleted Java projects and delete them"""
-    if not sys.stdout.isatty():
+    if not is_interactive():
         log('fsckprojects command must be run in an interactive shell')
         return
     hg = HgConfig()
@@ -4746,7 +4918,7 @@
                     indicatorsInHg = hg.locate(suite.dir, indicators)
                     # Only proceed if there are indicator files that are not under HG
                     if len(indicators) > len(indicatorsInHg):
-                        if not sys.stdout.isatty() or ask_yes_no(dirpath + ' looks like a removed project -- delete it', 'n'):
+                        if not is_interactive() or ask_yes_no(dirpath + ' looks like a removed project -- delete it', 'n'):
                             shutil.rmtree(dirpath)
                             log('Deleted ' + dirpath)
 
@@ -4865,6 +5037,7 @@
                 # Once https://bugs.openjdk.java.net/browse/JDK-8041628 is fixed,
                 # this should be reverted to:
                 # javadocExe = java().javadoc
+                # we can then also respect _opts.relatex_compliance
                 javadocExe = projectJava.javadoc
 
                 run([javadocExe, memory,
@@ -5085,15 +5258,17 @@
     if len(items) <= 1:
         return items
     else:
+        assert is_interactive()
+        numlen = str(len(str(len(items))))
         if allowMultiple:
-            log('[0] <all>')
+            log(('[{0:>' + numlen + '}] <all>').format(0))
         for i in range(0, len(items)):
             if descriptions is None:
-                log('[{0}] {1}'.format(i + 1, items[i]))
+                log(('[{0:>' + numlen + '}] {1}').format(i + 1, items[i]))
             else:
                 assert len(items) == len(descriptions)
                 wrapper = textwrap.TextWrapper(subsequent_indent='    ')
-                log('\n'.join(wrapper.wrap('[{0}] {1} - {2}'.format(i + 1, items[i], descriptions[i]))))
+                log('\n'.join(wrapper.wrap(('[{0:>' + numlen + '}] {1} - {2}').format(i + 1, items[i], descriptions[i]))))
         while True:
             if allowMultiple:
                 s = raw_input('Enter number(s) of selection (separate multiple choices with spaces): ').split()
@@ -5265,7 +5440,7 @@
 def ask_yes_no(question, default=None):
     """"""
     assert not default or default == 'y' or default == 'n'
-    if not sys.stdout.isatty():
+    if not is_interactive():
         if default:
             return default
         else:
@@ -5395,16 +5570,6 @@
     opts, commandAndArgs = _argParser._parse_cmd_line()
     assert _opts == opts
 
-    global _java_homes
-    defaultJdk = JavaConfig(opts.java_home, opts.java_dbg_port)
-    _java_homes = [defaultJdk]
-    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.javaCompliance > defaultJdk.javaCompliance:
-                abort('Secondary JDK ' + extraJdk.jdk + ' has higher compliance level than default JDK ' + defaultJdk.jdk)
-            _java_homes.append(extraJdk)
-
     for s in suites():
         s._post_init(opts)
 
--- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/OutlineTopComponent.java	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/OutlineTopComponent.java	Mon May 04 19:34:51 2015 +0200
@@ -124,6 +124,8 @@
 
     public void clear() {
         document.clear();
+        root = new FolderNode(document);
+        manager.setRootContext(root);
     }
 
     @Override
--- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/ImportAction.java	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/ImportAction.java	Mon May 04 19:34:51 2015 +0200
@@ -31,31 +31,47 @@
 import com.sun.hotspot.igv.data.serialization.ParseMonitor;
 import com.sun.hotspot.igv.data.serialization.Parser;
 import com.sun.hotspot.igv.settings.Settings;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
+import java.awt.event.ActionEvent;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.channels.FileChannel;
 import java.nio.file.StandardOpenOption;
-import javax.swing.Action;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import javax.swing.JFileChooser;
-import javax.swing.KeyStroke;
 import javax.swing.SwingUtilities;
 import javax.swing.filechooser.FileFilter;
 import org.netbeans.api.progress.ProgressHandle;
 import org.netbeans.api.progress.ProgressHandleFactory;
 import org.openide.util.Exceptions;
+import org.openide.util.RequestProcessor;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.awt.ActionRegistration;
 import org.openide.util.HelpCtx;
 import org.openide.util.NbBundle;
-import org.openide.util.RequestProcessor;
-import org.openide.util.actions.CallableSystemAction;
+import org.openide.util.actions.SystemAction;
 
 /**
  *
  * @author Thomas Wuerthinger
  */
-public final class ImportAction extends CallableSystemAction {
+
+@ActionID(
+        category = "File",
+        id = "com.sun.hotspot.igv.coordinator.actions.ImportAction"
+)
+@ActionRegistration(
+        iconBase = "com/sun/hotspot/igv/coordinator/images/import.png",
+        displayName = "#CTL_ImportAction"
+)
+@ActionReferences({
+    @ActionReference(path = "Menu/File", position = 0),
+    @ActionReference(path = "Shortcuts", name = "C-O")
+})
+public final class ImportAction extends SystemAction {
     private static final int WORKUNITS = 10000;
 
     public static FileFilter getFileFilter() {
@@ -74,14 +90,13 @@
     }
 
     @Override
-    public void performAction() {
-
+    public void actionPerformed(ActionEvent e) {
         JFileChooser fc = new JFileChooser();
         fc.setFileFilter(ImportAction.getFileFilter());
         fc.setCurrentDirectory(new File(Settings.get().get(Settings.DIRECTORY, Settings.DIRECTORY_DEFAULT)));
 
         if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
-            File file = fc.getSelectedFile();
+            final File file = fc.getSelectedFile();
 
             File dir = file;
             if (!dir.isDirectory()) {
@@ -93,6 +108,7 @@
                 final FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.READ);
                 final ProgressHandle handle = ProgressHandleFactory.createHandle("Opening file " + file.getName());
                 handle.start(WORKUNITS);
+                final long startTime = System.currentTimeMillis();
                 final long start = channel.size();
                 ParseMonitor monitor = new ParseMonitor() {
                     @Override
@@ -136,6 +152,8 @@
                             Exceptions.printStackTrace(ex);
                         }
                         handle.finish();
+                        long stop = System.currentTimeMillis();
+                        Logger.getLogger(getClass().getName()).log(Level.INFO, "Loaded in " + file + " in " + ((stop - startTime) / 1000.0) + " seconds");
                     }
                 });
             } catch (FileNotFoundException ex) {
@@ -151,23 +169,13 @@
         return NbBundle.getMessage(ImportAction.class, "CTL_ImportAction");
     }
 
-    public ImportAction() {
-        putValue(Action.SHORT_DESCRIPTION, "Open XML graph document...");
-        putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
-    }
-
     @Override
     protected String iconResource() {
         return "com/sun/hotspot/igv/coordinator/images/import.png";
     }
-
+    
     @Override
     public HelpCtx getHelpCtx() {
         return HelpCtx.DEFAULT_HELP;
     }
-
-    @Override
-    protected boolean asynchronous() {
-        return false;
-    }
 }
--- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/SaveAllAction.java	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/SaveAllAction.java	Mon May 04 19:34:51 2015 +0200
@@ -29,14 +29,30 @@
 import java.awt.event.KeyEvent;
 import javax.swing.Action;
 import javax.swing.KeyStroke;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.awt.ActionRegistration;
 import org.openide.util.HelpCtx;
 import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
 import org.openide.util.actions.CallableSystemAction;
 
 /**
  *
  * @author Thomas Wuerthinger
  */
+@ActionID(
+        category = "File",
+        id = "com.sun.hotspot.igv.coordinator.actions.SaveAllAction"
+)
+@ActionRegistration(
+        displayName = "#CTL_SaveAllAction"
+)
+@ActionReferences({
+    @ActionReference(path = "Menu/File", position = 0),
+    @ActionReference(path = "Shortcuts", name = "C-S")
+})
 public final class SaveAllAction extends CallableSystemAction {
 
     @Override
--- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/layer.xml	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/layer.xml	Mon May 04 19:34:51 2015 +0200
@@ -18,6 +18,8 @@
     
     <folder name="Menu">
         <folder name="File">
+            <file name="Export_hidden"/>
+            <file name="Import_hidden"/>
             <file name="Separator2.instance_hidden"/>
             <file name="Separator3.instance_hidden"/>
             <file name="SeparatorOpen.instance_hidden"/>
@@ -43,11 +45,11 @@
             </file>
             <file name="com-sun-hotspot-igv-coordinator-actions-RemoveAction.shadow">
                 <attr name="originalFile" stringvalue="Actions/Edit/com-sun-hotspot-igv-coordinator-actions-RemoveAction.instance"/>
-                <attr name="position" intvalue="400" />
+                <attr name="position" intvalue="400"/>
             </file>
             <file name="com-sun-hotspot-igv-coordinator-actions-RemoveAllAction.shadow">
                 <attr name="originalFile" stringvalue="Actions/Edit/com-sun-hotspot-igv-coordinator-actions-RemoveAllAction.instance"/>
-                <attr name="position" intvalue="500" />
+                <attr name="position" intvalue="500"/>
             </file>
             
             <!-- Hidden menu entries from other modules -->
--- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/GraphDocument.java	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/GraphDocument.java	Mon May 04 19:34:51 2015 +0200
@@ -51,11 +51,13 @@
     }
 
     public void addGraphDocument(GraphDocument document) {
-        for (FolderElement e : document.elements) {
-            e.setParent(this);
-            this.addElement(e);
+        if (document != this) {
+            for (FolderElement e : document.elements) {
+                e.setParent(this);
+                this.addElement(e);
+            }
+            document.clear();
         }
-        document.clear();
         getChangedEvent().fire();
     }
 
--- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/InputBlock.java	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/InputBlock.java	Mon May 04 19:34:51 2015 +0200
@@ -90,7 +90,9 @@
     public void addNode(int id) {
         InputNode node = graph.getNode(id);
         assert node != null;
-        assert !nodes.contains(node) : "duplicate : " + node;
+        // nodes.contains(node) is too expensive for large graphs so
+        // just make sure the Graph doesn't know it yet.
+        assert graph.getBlock(id) == null : "duplicate : " + node;
         graph.setBlock(node, this);
         nodes.add(node);
     }
--- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/Properties.java	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/Properties.java	Mon May 04 19:34:51 2015 +0200
@@ -24,7 +24,9 @@
 package com.sun.hotspot.igv.data;
 
 import java.io.Serializable;
+import java.lang.ref.WeakReference;
 import java.util.*;
+import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -36,7 +38,7 @@
 public class Properties implements Serializable, Iterable<Property> {
 
     public static final long serialVersionUID = 1L;
-    private String[] map = new String[4];
+    protected String[] map = new String[4];
 
     public Properties() {
     }
@@ -101,6 +103,59 @@
         map = new String[p.map.length];
         System.arraycopy(p.map, 0, map, 0, p.map.length);
     }
+    
+    protected Properties(String[] map) {
+        this.map = map;
+    }
+    
+    static class SharedProperties extends Properties {
+        int hashCode;
+
+        SharedProperties(String[] map) {
+            super(map);
+            this.hashCode = Arrays.hashCode(map);
+        }
+        
+        @Override
+        protected void setPropertyInternal(String name, String value) {
+            throw new UnsupportedOperationException();
+        }
+        
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof SharedProperties)) {
+                return super.equals(other);
+            }
+            SharedProperties props2 = (SharedProperties) other;
+            return Arrays.equals(map, props2.map);
+        }
+        
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+    }
+    
+    private static class PropertyCache {
+        static WeakHashMap<SharedProperties, WeakReference<SharedProperties>> immutableCache = new WeakHashMap<>();
+
+        static synchronized SharedProperties intern(Properties properties) {
+            String[] map = properties.map;
+            SharedProperties key = new SharedProperties(map);
+            WeakReference<SharedProperties> entry = immutableCache.get(key);
+            if (entry != null) {
+                SharedProperties props = entry.get();
+                if (props != null) {
+                    return props;
+                }
+            }
+            immutableCache.put(key, new WeakReference<>(key));
+            return key;
+        }
+    }
 
     public static class Entity implements Provider {
 
@@ -118,6 +173,10 @@
         public Properties getProperties() {
             return properties;
         }
+        
+        public void internProperties() {
+            properties = PropertyCache.intern(properties);
+        }
     }
 
     public interface PropertyMatcher {
@@ -322,8 +381,8 @@
     public void setProperty(String name, String value) {
         setPropertyInternal(name.intern(), value != null ? value.intern() : null);
     }
-    private void setPropertyInternal(String name, String value) {
 
+    protected void setPropertyInternal(String name, String value) {
         for (int i = 0; i < map.length; i += 2) {
             if (map[i] != null && map[i].equals(name)) {
                 String p = map[i + 1];
--- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/serialization/BinaryParser.java	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/serialization/BinaryParser.java	Mon May 04 19:34:51 2015 +0200
@@ -275,28 +275,33 @@
         hashStack = new LinkedList<>();
         this.monitor = monitor;
         try {
-            this.digest = MessageDigest.getInstance("SHA-256");
+            this.digest = MessageDigest.getInstance("SHA-1");
         } catch (NoSuchAlgorithmException e) {
         }
     }
     
     private void fill() throws IOException {
+        // All the data between lastPosition and position has been
+        // used so add it to the digest.
+        int position = buffer.position();
+        buffer.position(lastPosition);
+        byte[] remaining = new byte[position - buffer.position()];
+        buffer.get(remaining);
+        digest.update(remaining);
+        assert position == buffer.position();
+
         buffer.compact();
         if (channel.read(buffer) < 0) {
             throw new EOFException();
         }
         buffer.flip();
+        lastPosition = buffer.position();
     }
     
     private void ensureAvailable(int i) throws IOException {
         while (buffer.remaining() < i) {
             fill();
         }
-        buffer.mark();
-        byte[] result = new byte[i];
-        buffer.get(result);
-        digest.update(result);
-        buffer.reset();
     }
     
     private int readByte() throws IOException {
@@ -650,6 +655,8 @@
         }
         return group;
     }
+
+    int lastPosition = 0;
     
     private InputGraph parseGraph() throws IOException {
         if (monitor != null) {
@@ -657,7 +664,17 @@
         }
         String title = readPoolObject(String.class);
         digest.reset();
+        lastPosition = buffer.position();
         InputGraph graph = parseGraph(title);
+
+        int position = buffer.position();
+        buffer.position(lastPosition);
+        byte[] remaining = new byte[position - buffer.position()];
+        buffer.get(remaining);
+        digest.update(remaining);
+        assert position == buffer.position();
+        lastPosition = buffer.position();
+        
         byte[] d = digest.digest();
         byte[] hash = hashStack.peek();
         if (hash != null && Arrays.equals(hash, d)) {
@@ -674,6 +691,9 @@
         parseNodes(graph);
         parseBlocks(graph);
         graph.ensureNodesInBlocks();
+        for (InputNode node : graph.getNodes()) {
+            node.internProperties();
+        }
         return graph;
     }
     
@@ -822,9 +842,10 @@
         }
     }
     
+    static final Pattern templatePattern = Pattern.compile("\\{(p|i)#([a-zA-Z0-9$_]+)(/(l|m|s))?\\}");
+    
     private String createName(List<Edge> edges, Map<String, Object> properties, String template) {
-        Pattern p = Pattern.compile("\\{(p|i)#([a-zA-Z0-9$_]+)(/(l|m|s))?\\}");
-        Matcher m = p.matcher(template);
+        Matcher m = templatePattern.matcher(template);
         StringBuffer sb = new StringBuffer();
         while (m.find()) {
             String name = m.group(2);
--- a/src/share/tools/IdealGraphVisualizer/nbproject/project.properties	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/tools/IdealGraphVisualizer/nbproject/project.properties	Mon May 04 19:34:51 2015 +0200
@@ -48,5 +48,5 @@
 
 # Disable assertions for RequestProcessor to prevent annoying messages in case
 # of multiple SceneAnimator update tasks in the default RequestProcessor.
-run.args.extra = -J-server -J-da:org.openide.util.RequestProcessor -J-Xms2g -J-Xmx8g -J-Djava.lang.Integer.IntegerCache.high=20000
+run.args.extra = -J-server -J-da:org.openide.util.RequestProcessor -J-Xms2g -J-Xmx8g -J-Djava.lang.Integer.IntegerCache.high=200000
 debug.args.extra = -J-server -J-da:org.openide.util.RequestProcessor
--- a/src/share/vm/code/nmethod.cpp	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/vm/code/nmethod.cpp	Mon May 04 19:34:51 2015 +0200
@@ -3429,6 +3429,7 @@
           }
         }
       }
+      st->print(" {reexecute=%d rethrow=%d return_oop=%d}", sd->should_reexecute(), sd->rethrow_exception(), sd->return_oop());
     }
 
     // Print all scopes
--- a/src/share/vm/graal/graalRuntime.cpp	Mon May 04 19:12:50 2015 +0200
+++ b/src/share/vm/graal/graalRuntime.cpp	Mon May 04 19:34:51 2015 +0200
@@ -600,7 +600,8 @@
 JRT_END
 
 JRT_ENTRY(jboolean, GraalRuntime::thread_is_interrupted(JavaThread* thread, oopDesc* receiver, jboolean clear_interrupted))
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
+  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate.
+  // This locking requires thread_in_vm which is why this method cannot be JRT_LEAF.
   Handle receiverHandle(thread, receiver);
   MutexLockerEx ml(thread->threadObj() == (void*)receiver ? NULL : Threads_lock);
   JavaThread* receiverThread = java_lang_Thread::thread(receiverHandle());