changeset 15527:ff5cacf47b68

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sat, 03 May 2014 21:46:35 +0200
parents 5ecbed00da23 (current diff) d370d87e528f (diff)
children a3f897fb3289
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/BlocksToDoubles.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/NodesToDoubles.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeInliningRelevanceClosure.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java
diffstat 142 files changed, 5626 insertions(+), 4322 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Fri May 02 02:45:26 2014 +0200
+++ b/CHANGELOG.md	Sat May 03 21:46:35 2014 +0200
@@ -12,13 +12,14 @@
 * ...
 
 ### Truffle
-* The method CallTarget#call takes now a variable number of Object arguments.
+* The method `CallTarget#call` takes now a variable number of Object arguments.
 * Support for collecting stack traces and for accessing the current frame in slow paths.
-* Renamed CallNode to DirectCallNode.
-* Renamed TruffleRuntime#createCallNode to TruffleRuntime#createDirectCallNode.
-* Added IndirectCallNode for calls with a changing CallTarget. 
-* Added TruffleRuntime#createIndirectCallNode to create an IndirectCallNode.
-* DirectCallNode#inline was renamed to DirectCallNode#forceInlining().
+* Renamed `CallNode` to `DirectCallNode`.
+* Renamed `TruffleRuntime#createCallNode` to `TruffleRuntime#createDirectCallNode`.
+* Added `IndirectCallNode` for calls with a changing `CallTarget`.
+* Added `TruffleRuntime#createIndirectCallNode` to create an `IndirectCallNode`.
+* `DirectCallNode#inline` was renamed to `DirectCallNode#forceInlining()`.
+* Removed deprecated `Node#adoptChild`.
 * ...
 
 ## Version 0.2
@@ -45,7 +46,7 @@
 * New API to declare the cost of a Node for use in runtime environment specific heuristics. See `NodeCost`, `Node#getCost` and `NodeInfo#cost`.
 * Removed old API for `NodeInfo#Kind` and `NodeInfo#kind`. As a replacement the new `NodeCost` API can be used.
 * Changed `Node#replace` reason parameter type to `CharSequence` (to enable lazy string building)
-* Deprecated `Node#adoptChild` and `Node#adoptChild`, no longer needed in node constructor
+* Deprecated `Node#adoptChild` and `Node#adoptChildren`, no longer needed in node constructor
 * New `Node#insert` method for inserting new nodes into the tree (formerly `adoptChild`)
 * New `Node#adoptChildren` helper method that adopts all (direct and indirect) children of a node
 * New API `Node#atomic` for atomic tree operations
--- a/graal/com.oracle.graal.alloc/src/com/oracle/graal/alloc/ComputeBlockOrder.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.alloc/src/com/oracle/graal/alloc/ComputeBlockOrder.java	Sat May 03 21:46:35 2014 +0200
@@ -26,7 +26,6 @@
 import java.util.*;
 
 import com.oracle.graal.compiler.common.cfg.*;
-import com.oracle.graal.nodes.cfg.*;
 
 /**
  * Computes an ordering of the block that can be used by the linear scan register allocator and the
@@ -67,11 +66,11 @@
      *
      * @return sorted list of blocks
      */
-    public static <T extends AbstractBlock<T>> List<T> computeLinearScanOrder(int blockCount, T startBlock, BlocksToDoubles blockProbabilities) {
+    public static <T extends AbstractBlock<T>> List<T> computeLinearScanOrder(int blockCount, T startBlock) {
         List<T> order = new ArrayList<>();
         BitSet visitedBlocks = new BitSet(blockCount);
-        PriorityQueue<T> worklist = initializeWorklist(startBlock, visitedBlocks, blockProbabilities);
-        computeLinearScanOrder(order, worklist, visitedBlocks, blockProbabilities);
+        PriorityQueue<T> worklist = initializeWorklist(startBlock, visitedBlocks);
+        computeLinearScanOrder(order, worklist, visitedBlocks);
         assert checkOrder(order, blockCount);
         return order;
     }
@@ -81,11 +80,11 @@
      *
      * @return sorted list of blocks
      */
-    public static <T extends AbstractBlock<T>> List<T> computeCodeEmittingOrder(int blockCount, T startBlock, BlocksToDoubles blockProbabilities) {
+    public static <T extends AbstractBlock<T>> List<T> computeCodeEmittingOrder(int blockCount, T startBlock) {
         List<T> order = new ArrayList<>();
         BitSet visitedBlocks = new BitSet(blockCount);
-        PriorityQueue<T> worklist = initializeWorklist(startBlock, visitedBlocks, blockProbabilities);
-        computeCodeEmittingOrder(order, worklist, visitedBlocks, blockProbabilities);
+        PriorityQueue<T> worklist = initializeWorklist(startBlock, visitedBlocks);
+        computeCodeEmittingOrder(order, worklist, visitedBlocks);
         assert checkOrder(order, blockCount);
         return order;
     }
@@ -93,28 +92,28 @@
     /**
      * Iteratively adds paths to the code emission block order.
      */
-    private static <T extends AbstractBlock<T>> void computeCodeEmittingOrder(List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) {
+    private static <T extends AbstractBlock<T>> void computeCodeEmittingOrder(List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
         while (!worklist.isEmpty()) {
             T nextImportantPath = worklist.poll();
-            addPathToCodeEmittingOrder(nextImportantPath, order, worklist, visitedBlocks, blockProbabilities);
+            addPathToCodeEmittingOrder(nextImportantPath, order, worklist, visitedBlocks);
         }
     }
 
     /**
      * Iteratively adds paths to the linear scan block order.
      */
-    private static <T extends AbstractBlock<T>> void computeLinearScanOrder(List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) {
+    private static <T extends AbstractBlock<T>> void computeLinearScanOrder(List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
         while (!worklist.isEmpty()) {
             T nextImportantPath = worklist.poll();
-            addPathToLinearScanOrder(nextImportantPath, order, worklist, visitedBlocks, blockProbabilities);
+            addPathToLinearScanOrder(nextImportantPath, order, worklist, visitedBlocks);
         }
     }
 
     /**
      * Initializes the priority queue used for the work list of blocks and adds the start block.
      */
-    private static <T extends AbstractBlock<T>> PriorityQueue<T> initializeWorklist(T startBlock, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) {
-        PriorityQueue<T> result = new PriorityQueue<>(INITIAL_WORKLIST_CAPACITY, new BlockOrderComparator<T>(blockProbabilities));
+    private static <T extends AbstractBlock<T>> PriorityQueue<T> initializeWorklist(T startBlock, BitSet visitedBlocks) {
+        PriorityQueue<T> result = new PriorityQueue<>(INITIAL_WORKLIST_CAPACITY, new BlockOrderComparator<>());
         result.add(startBlock);
         visitedBlocks.set(startBlock.getId());
         return result;
@@ -123,10 +122,10 @@
     /**
      * Add a linear path to the linear scan order greedily following the most likely successor.
      */
-    private static <T extends AbstractBlock<T>> void addPathToLinearScanOrder(T block, List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) {
+    private static <T extends AbstractBlock<T>> void addPathToLinearScanOrder(T block, List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
         block.setLinearScanNumber(order.size());
         order.add(block);
-        T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks, blockProbabilities);
+        T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks);
         enqueueSuccessors(block, worklist, visitedBlocks);
         if (mostLikelySuccessor != null) {
             if (!mostLikelySuccessor.isLoopHeader() && mostLikelySuccessor.getPredecessorCount() > 1) {
@@ -135,24 +134,24 @@
                 double unscheduledSum = 0.0;
                 for (T pred : mostLikelySuccessor.getPredecessors()) {
                     if (pred.getLinearScanNumber() == -1) {
-                        unscheduledSum += blockProbabilities.get(pred);
+                        unscheduledSum += pred.probability();
                     }
                 }
 
-                if (unscheduledSum > blockProbabilities.get(block) / PENALTY_VERSUS_UNSCHEDULED) {
+                if (unscheduledSum > block.probability() / PENALTY_VERSUS_UNSCHEDULED) {
                     // Add this merge only after at least one additional predecessor gets scheduled.
                     visitedBlocks.clear(mostLikelySuccessor.getId());
                     return;
                 }
             }
-            addPathToLinearScanOrder(mostLikelySuccessor, order, worklist, visitedBlocks, blockProbabilities);
+            addPathToLinearScanOrder(mostLikelySuccessor, order, worklist, visitedBlocks);
         }
     }
 
     /**
      * Add a linear path to the code emission order greedily following the most likely successor.
      */
-    private static <T extends AbstractBlock<T>> void addPathToCodeEmittingOrder(T initialBlock, List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) {
+    private static <T extends AbstractBlock<T>> void addPathToCodeEmittingOrder(T initialBlock, List<T> order, PriorityQueue<T> worklist, BitSet visitedBlocks) {
         T block = initialBlock;
         while (block != null) {
             // Skip loop headers if there is only a single loop end block to
@@ -183,7 +182,7 @@
                 }
             }
 
-            T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks, blockProbabilities);
+            T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks);
             enqueueSuccessors(block, worklist, visitedBlocks);
             block = mostLikelySuccessor;
         }
@@ -200,11 +199,11 @@
     /**
      * Find the highest likely unvisited successor block of a given block.
      */
-    private static <T extends AbstractBlock<T>> T findAndMarkMostLikelySuccessor(T block, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) {
+    private static <T extends AbstractBlock<T>> T findAndMarkMostLikelySuccessor(T block, BitSet visitedBlocks) {
         T result = null;
         for (T successor : block.getSuccessors()) {
-            assert blockProbabilities.get(successor) >= 0.0 : "Probabilities must be positive";
-            if (!visitedBlocks.get(successor.getId()) && successor.getLoopDepth() >= block.getLoopDepth() && (result == null || blockProbabilities.get(successor) >= blockProbabilities.get(result))) {
+            assert successor.probability() >= 0.0 : "Probabilities must be positive";
+            if (!visitedBlocks.get(successor.getId()) && successor.getLoopDepth() >= block.getLoopDepth() && (result == null || successor.probability() >= result.probability())) {
                 result = successor;
             }
         }
@@ -247,12 +246,6 @@
      */
     private static class BlockOrderComparator<T extends AbstractBlock<T>> implements Comparator<T> {
 
-        private final BlocksToDoubles probabilities;
-
-        public BlockOrderComparator(BlocksToDoubles probabilities) {
-            this.probabilities = probabilities;
-        }
-
         @Override
         public int compare(T a, T b) {
             // Loop blocks before any loop exit block.
@@ -262,7 +255,7 @@
             }
 
             // Blocks with high probability before blocks with low probability.
-            if (probabilities.get(a) > probabilities.get(b)) {
+            if (a.probability() > b.probability()) {
                 return -1;
             } else {
                 return 1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/MethodHandleAccessProvider.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014, 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.api.replacements;
+
+import java.lang.invoke.*;
+
+import com.oracle.graal.api.meta.*;
+
+/**
+ * Interface to access the internals of the {@link MethodHandle} implementation of the VM. An
+ * implementation of this interface is usually required to access non-public classes, methods, and
+ * fields of {@link MethodHandle}, i.e., data that is not standardized by the Java specification.
+ */
+public interface MethodHandleAccessProvider {
+
+    /**
+     * Identification for methods defined on the class {@link MethodHandle} that are processed by
+     * the {@link MethodHandleAccessProvider}.
+     */
+    public enum IntrinsicMethod {
+        /** The method {@code MethodHandle.invokeBasic}. */
+        INVOKE_BASIC,
+        /** The method {@code MethodHandle.linkToStatic}. */
+        LINK_TO_STATIC,
+        /** The method {@code MethodHandle.linkToSpecial}. */
+        LINK_TO_SPECIAL,
+        /** The method {@code MethodHandle.linkToVirtual}. */
+        LINK_TO_VIRTUAL,
+        /** The method {@code MethodHandle.linkToInterface}. */
+        LINK_TO_INTERFACE
+    }
+
+    /**
+     * Returns the method handle method intrinsic identifier for the provided method, or
+     * {@code null} if the method is not an intrinsic processed by this interface.
+     */
+    IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method);
+
+    /**
+     * Resolves the invocation target for an invocation of {@link IntrinsicMethod#INVOKE_BASIC
+     * MethodHandle.invokeBasic} with the given constant receiver {@link MethodHandle}. Returns
+     * {@code null} if the invocation target is not available at this time.
+     * <p>
+     * The first invocations of a method handle can use an interpreter to lookup the actual invoked
+     * method; frequently executed method handles can use Java bytecode generation to avoid the
+     * interpreter overhead. If the parameter forceBytecodeGeneration is set to true, the VM should
+     * try to generate bytecodes before this method returns.
+     */
+    ResolvedJavaMethod resolveInvokeBasicTarget(Constant methodHandle, boolean forceBytecodeGeneration);
+
+    /**
+     * Resolves the invocation target for an invocation of a {@code MethodHandle.linkTo*} method
+     * with the given constant member name. The member name is the last parameter of the
+     * {@code linkTo*} method. Returns {@code null} if the invocation target is not available at
+     * this time.
+     */
+    ResolvedJavaMethod resolveLinkToTarget(Constant memberName);
+}
--- a/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java	Sat May 03 21:46:35 2014 +0200
@@ -190,16 +190,9 @@
         emitString("st_spill_" + getArgTypeFromKind(kind) + " " + HSAIL.mapRegister(src) + ", " + mapStackSlot(dest, getArgSizeFromKind(kind)) + ";");
     }
 
-    /**
-     * The mapping to stack slots is always relative to the beginning of the spillseg.
-     * HSAIL.getStackOffset returns the positive version of the originally negative offset. Then we
-     * back up from that by the argSize in bytes. This ensures that slots of different size do not
-     * overlap, even though we have converted from negative to positive offsets.
-     */
     public static String mapStackSlot(Value reg, int argSize) {
-        long offset = HSAIL.getStackOffset(reg);
-        int argSizeBytes = argSize / 8;
-        return "[%spillseg]" + "[" + (offset - argSizeBytes) + "]";
+        long startOffset = HSAIL.getStackOffsetStart(reg, argSize);
+        return "[%spillseg]" + "[" + startOffset + "]";
     }
 
     public void cbr(String target1) {
--- a/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java	Sat May 03 21:46:35 2014 +0200
@@ -45,7 +45,6 @@
 import com.oracle.graal.lir.*;
 import com.oracle.graal.lir.StandardOp.BlockEndOp;
 import com.oracle.graal.lir.gen.*;
-import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.phases.*;
 
 public class BaselineBytecodeParser extends AbstractBytecodeParser<Value, BaselineFrameStateBuilder> implements BytecodeParserTool {
@@ -123,14 +122,9 @@
             // create the control flow graph
             BaselineControlFlowGraph cfg = new BaselineControlFlowGraph(blockMap);
 
-            BlocksToDoubles blockProbabilities = new BlocksToDoubles(blockMap.blocks.size());
-            for (BciBlock b : blockMap.blocks) {
-                blockProbabilities.put(b, 1);
-            }
-
             // create the LIR
-            List<? extends AbstractBlock<?>> linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blockMap.blocks.size(), blockMap.startBlock, blockProbabilities);
-            List<? extends AbstractBlock<?>> codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blockMap.blocks.size(), blockMap.startBlock, blockProbabilities);
+            List<? extends AbstractBlock<?>> linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blockMap.blocks.size(), blockMap.startBlock);
+            List<? extends AbstractBlock<?>> codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blockMap.blocks.size(), blockMap.startBlock);
             LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder);
 
             FrameMap frameMap = backend.newFrameMap(null);
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/cfg/AbstractBlock.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/cfg/AbstractBlock.java	Sat May 03 21:46:35 2014 +0200
@@ -55,4 +55,6 @@
     void setAlign(boolean align);
 
     T getDominator();
+
+    double probability();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/ForceDeoptSubstitutions.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 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.hsail.test.infra;
+
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.api.meta.*;
+
+@ClassSubstitution(GraalKernelTester.class)
+class ForceDeoptSubstitutions {
+
+    /**
+     * Allows us to force a non-exception throwing deopt from java code.
+     */
+    @MethodSubstitution
+    public static int forceDeopt(int x) {
+        DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+        // will never get here but keep the compiler happy
+        return x * x;
+    }
+
+    @MethodSubstitution
+    public static double forceDeopt(double x) {
+        DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+        // will never get here but keep the compiler happy
+        return x * x;
+    }
+}
--- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Sat May 03 21:46:35 2014 +0200
@@ -50,8 +50,18 @@
 
 public abstract class GraalKernelTester extends KernelTester {
 
+    private static boolean substitutionsInstalled;
+
+    private static synchronized void installSubstitutions() {
+        if (!substitutionsInstalled) {
+            getHSAILBackend().getProviders().getReplacements().registerSubstitutions(ForceDeoptSubstitutions.class);
+            substitutionsInstalled = true;
+        }
+    }
+
     public GraalKernelTester() {
         super(getHSAILBackend().isDeviceInitialized());
+        installSubstitutions();
     }
 
     protected static HSAILHotSpotBackend getHSAILBackend() {
@@ -193,4 +203,14 @@
             super.testGeneratedHsailUsingLambdaMethod();
         }
     }
+
+    // used for forcing a deoptimization
+    public static int forceDeopt(int x) {
+        return x * x;
+    }
+
+    public static double forceDeopt(double x) {
+        return x * x;
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DVec3.java	Sat May 03 21:46:35 2014 +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.compiler.hsail.test;
+
+/**
+ * A simple 3 element Vector object used in some junit tests.
+ */
+public class DVec3 {
+
+    public DVec3(double x, double y, double z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    public double x;
+    public double y;
+    public double z;
+
+    public static DVec3 add(DVec3 a, DVec3 b) {
+        return new DVec3(a.x + b.x, a.y + b.y, a.z + b.z);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof DVec3)) {
+            return false;
+        }
+        DVec3 oth = (DVec3) other;
+        return (oth.x == x && oth.y == y && oth.z == z);
+    }
+
+    @Override
+    public String toString() {
+        return ("DVec3[" + x + ", " + y + ", " + z + "]");
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) (x + y + z);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptBase.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,89 @@
+/*
+ * 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.compiler.hsail.test;
+
+import java.util.*;
+
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Base class for testing deopt when objects are in stack slots.
+ */
+public abstract class ObjSpillDeoptBase extends GraalKernelTester {
+
+    abstract int getSize();
+
+    int loopcount = 5;
+    int objcount = 20;
+    @Result double[] out = new double[getSize()];
+    @Result double[] aux = new double[getSize()];
+    DVec3[] in = new DVec3[objcount];
+
+    public void doCompute(int gid, boolean causeDeopt) {
+        int idx = gid * 2 + 7;
+        DVec3 v0 = in[(idx++) % objcount];
+        DVec3 v1 = in[(idx++) % objcount];
+        DVec3 v2 = in[(idx++) % objcount];
+        DVec3 v3 = in[(idx++) % objcount];
+        DVec3 v4 = in[(idx++) % objcount];
+        DVec3 v5 = in[(idx++) % objcount];
+        DVec3 v6 = in[(idx++) % objcount];
+        DVec3 v7 = in[(idx++) % objcount];
+        DVec3 v8 = in[(idx++) % objcount];
+        DVec3 v9 = in[(idx++) % objcount];
+        idx += gid;
+        DVec3 v10 = in[(idx++) % objcount];
+        DVec3 v11 = in[(idx++) % objcount];
+        DVec3 v12 = in[(idx++) % objcount];
+        DVec3 v13 = in[(idx++) % objcount];
+        DVec3 v14 = in[(idx++) % objcount];
+        DVec3 v15 = in[(idx++) % objcount];
+        DVec3 v16 = in[(idx++) % objcount];
+        DVec3 v17 = in[(idx++) % objcount];
+        DVec3 v18 = in[(idx++) % objcount];
+        DVec3 v19 = in[(idx++) % objcount];
+        double sum = 0.0;
+        double sum1 = 0.0;
+        double sum2 = 0.0;
+        for (int i = 0; i < loopcount; i++) {
+            sum1 += v0.x + v1.y + v2.z + v3.x + v4.y + v5.z + v6.x + v7.y + v8.z + v9.x + i;
+            sum2 += v10.y + v11.z + v12.x + v13.y + v14.z + v15.x + v16.y + v17.z + v18.x + v19.y - i;
+            sum += sum1 - sum2 + i;
+            aux[gid] += sum1 + 1.2345;
+        }
+        if (causeDeopt) {
+            aux[gid] += forceDeopt(sum1);
+        }
+        out[gid] += sum;
+    }
+
+    @Override
+    public void runTest() {
+        Arrays.fill(out, -1.0);
+        for (int i = 0; i < objcount; i++) {
+            in[i] = new DVec3(i / 10f, (i + 1) / 10f, (i + 2) / 10f);
+        }
+        dispatchMethodKernel(getSize());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany20000Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,41 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, with many items deopting.
+ */
+public class ObjSpillDeoptMany20000Test extends ObjSpillDeoptManyBase {
+
+    @Override
+    int getSize() {
+        return 20000;
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany5000Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,41 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, with many items deopting.
+ */
+public class ObjSpillDeoptMany5000Test extends ObjSpillDeoptManyBase {
+
+    @Override
+    int getSize() {
+        return 5000;
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany99999Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,41 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, with many items deopting.
+ */
+public class ObjSpillDeoptMany99999Test extends ObjSpillDeoptManyBase {
+
+    @Override
+    int getSize() {
+        return 99999;
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptManyBase.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,34 @@
+/*
+ * 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.compiler.hsail.test;
+
+/**
+ * Base class for testing deopt with objects in stack slots, with many items deopting.
+ */
+public abstract class ObjSpillDeoptManyBase extends ObjSpillDeoptBase {
+
+    public void run(int gid) {
+        boolean causeDeopt = (gid < 4096 && gid % 512 == 1);
+        doCompute(gid, causeDeopt);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost20000Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,41 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, with most items deopting.
+ */
+public class ObjSpillDeoptMost20000Test extends ObjSpillDeoptMostBase {
+
+    @Override
+    int getSize() {
+        return 20000;
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost5000Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,41 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, with most items deopting.
+ */
+public class ObjSpillDeoptMost5000Test extends ObjSpillDeoptMostBase {
+
+    @Override
+    int getSize() {
+        return 5000;
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost99999Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,41 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, with most items deopting.
+ */
+public class ObjSpillDeoptMost99999Test extends ObjSpillDeoptMostBase {
+
+    @Override
+    int getSize() {
+        return 99999;
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMostBase.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,34 @@
+/*
+ * 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.compiler.hsail.test;
+
+/**
+ * Base class for testing deopt with objects in stack slots.
+ */
+public abstract class ObjSpillDeoptMostBase extends ObjSpillDeoptBase {
+
+    public void run(int gid) {
+        boolean causeDeopt = (gid % 500 != 1);
+        doCompute(gid, causeDeopt);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptSingle100Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,48 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, one item deopting.
+ */
+public class ObjSpillDeoptSingle100Test extends ObjSpillDeoptBase {
+
+    final int size = 100;
+
+    @Override
+    int getSize() {
+        return size;
+    }
+
+    public void run(int gid) {
+        boolean causeDeopt = (gid == size / 2);
+        doCompute(gid, causeDeopt);
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptSingle20000Test.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,48 @@
+/*
+ * 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.compiler.hsail.test;
+
+import org.junit.*;
+
+/**
+ * Tests deopt with objects in stack slots, one item deopting.
+ */
+public class ObjSpillDeoptSingle20000Test extends ObjSpillDeoptBase {
+
+    final int size = 20000;
+
+    @Override
+    int getSize() {
+        return size;
+    }
+
+    public void run(int gid) {
+        boolean causeDeopt = (gid == size / 2);
+        doCompute(gid, causeDeopt);
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillBoundsCatchOneTest.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,138 @@
+/*
+ * 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.compiler.hsail.test;
+
+import java.util.*;
+
+import org.junit.*;
+
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests the spilling of double variables into memory with deopt
+ */
+public class StaticDoubleSpillBoundsCatchOneTest extends GraalKernelTester {
+
+    static final int size = 100;
+    private double[] in = new double[size * 400];
+    @Result public double[] out = new double[size * 400];
+    @Result public double[] aux = new double[2];
+
+    public static void run(double[] out, double[] in, double[] aux, int gid) {
+        int id = gid;
+        int step = 20;
+        double sum0;
+        double sum1;
+        double sum2;
+        double sum3;
+        double sum4;
+        double sum5;
+        double sum6;
+        double sum7;
+        double sum8;
+        double sum9;
+        double sum10;
+        double sum11;
+        double sum12;
+        double sum13;
+        double sum14;
+        double sum15;
+        double sum16;
+        double sum17;
+        double sum18;
+        double sum19;
+        sum0 = sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = sum7 = sum8 = sum9 = 0;
+        sum10 = sum11 = sum12 = sum13 = sum14 = sum15 = sum16 = sum17 = sum18 = sum19 = 0;
+        try {
+            for (int i = 0; i < size; i += step) {
+                sum0 += in[i + 0];
+                sum1 += in[i + 1];
+                sum2 += in[i + 2];
+                sum3 += in[i + 3];
+                sum4 += in[i + 4];
+                sum5 += in[i + 5];
+                sum6 += in[i + 6];
+                sum7 += in[i + 7];
+                sum8 += in[i + 8];
+                sum9 += in[i + 9];
+                sum10 += in[i + 0];
+                sum11 += in[i + 1];
+                sum12 += in[i + 2];
+                sum13 += in[i + 3];
+                sum14 += in[i + 4];
+                sum15 += in[i + 5];
+                sum16 += in[i + 6];
+                sum17 += in[i + 7];
+                sum18 += in[i + 8];
+                sum19 += in[i + 9];
+
+                if (id == size / 2) {
+                    aux[id] = sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7 + sum8 + sum9 + sum10 + sum11 + sum12 + sum13 + sum14 + sum15 + sum16;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            aux[0] += sum1 + sum2;
+        }
+
+        out[id * step + 0] = sum0;
+        out[id * step + 1] = sum1;
+        out[id * step + 2] = sum2;
+        out[id * step + 3] = sum3;
+        out[id * step + 4] = sum4;
+        out[id * step + 5] = sum5;
+        out[id * step + 6] = sum6;
+        out[id * step + 7] = sum7;
+        out[id * step + 8] = sum8;
+        out[id * step + 9] = sum9;
+        out[id * step + 10] = sum10;
+        out[id * step + 11] = sum11;
+        out[id * step + 12] = sum12;
+        out[id * step + 13] = sum13;
+        out[id * step + 14] = sum14;
+        out[id * step + 15] = sum15;
+        out[id * step + 16] = sum16;
+        out[id * step + 17] = sum17;
+        out[id * step + 18] = sum18;
+        out[id * step + 19] = sum19;
+    }
+
+    @Override
+    public void runTest() {
+        /**
+         * Call it for a range, specifying testmethod args (but not the fields it uses or the gid
+         * argument).
+         * 
+         */
+        Arrays.fill(out, -1f);
+        for (int i = 0; i < size; i++) {
+            in[i] = i + 1;
+        }
+        dispatchMethodKernel(size, out, in, aux);
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillBoundsCatchTest.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,141 @@
+/*
+ * 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.compiler.hsail.test;
+
+import java.util.*;
+
+import org.junit.*;
+
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests the spilling of double variables into memory with deopt
+ */
+public class StaticDoubleSpillBoundsCatchTest extends GraalKernelTester {
+
+    static final int size = 100;
+    private double[] in = new double[size * 400];
+    @Result public double[] out = new double[size * 400];
+    @Result public double[] aux = new double[size];
+
+    public static void run(double[] out, double[] in, double[] aux, int gid) {
+        int id = gid;
+        int step = 20;
+        double sum0;
+        double sum1;
+        double sum2;
+        double sum3;
+        double sum4;
+        double sum5;
+        double sum6;
+        double sum7;
+        double sum8;
+        double sum9;
+        double sum10;
+        double sum11;
+        double sum12;
+        double sum13;
+        double sum14;
+        double sum15;
+        double sum16;
+        double sum17;
+        double sum18;
+        double sum19;
+        sum0 = sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = sum7 = sum8 = sum9 = 0;
+        sum10 = sum11 = sum12 = sum13 = sum14 = sum15 = sum16 = sum17 = sum18 = sum19 = 0;
+        try {
+            for (int i = 0; i < size; i += step) {
+                sum0 += in[i + 0];
+                sum1 += in[i + 1];
+                sum2 += in[i + 2];
+                sum3 += in[i + 3];
+                sum4 += in[i + 4];
+                sum5 += in[i + 5];
+                sum6 += in[i + 6];
+                sum7 += in[i + 7];
+                sum8 += in[i + 8];
+                sum9 += in[i + 9];
+                sum10 += in[i + 0];
+                sum11 += in[i + 1];
+                sum12 += in[i + 2];
+                sum13 += in[i + 3];
+                sum14 += in[i + 4];
+                sum15 += in[i + 5];
+                sum16 += in[i + 6];
+                sum17 += in[i + 7];
+                sum18 += in[i + 8];
+                sum19 += in[i + 9];
+
+                if (id > size / 2) {
+                    aux[id + 10] += sum1 + sum2;
+                    // + sum3 + sum4 + sum5 + sum6 + sum7 + sum8 + sum9 + sum10 + sum11 + sum12 +
+                    // sum13 + sum14 + sum15 + sum16;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            aux[id] += sum1 + sum2;
+        }
+
+        out[id * step + 0] = sum0;
+        out[id * step + 1] = sum1;
+        out[id * step + 2] = sum2;
+        out[id * step + 3] = sum3;
+        out[id * step + 4] = sum4;
+        out[id * step + 5] = sum5;
+        out[id * step + 6] = sum6;
+        out[id * step + 7] = sum7;
+        out[id * step + 8] = sum8;
+        out[id * step + 9] = sum9;
+        out[id * step + 10] = sum10;
+        out[id * step + 11] = sum11;
+        out[id * step + 12] = sum12;
+        out[id * step + 13] = sum13;
+        out[id * step + 14] = sum14;
+        out[id * step + 15] = sum15;
+        out[id * step + 16] = sum16;
+        out[id * step + 17] = sum17;
+        out[id * step + 18] = sum18;
+        out[id * step + 19] = sum19;
+    }
+
+    @Override
+    public void runTest() {
+        /**
+         * Call it for a range, specifying testmethod args (but not the fields it uses or the gid
+         * argument).
+         * 
+         */
+        Arrays.fill(out, -1f);
+        Arrays.fill(aux, 0f);
+        for (int i = 0; i < size; i++) {
+            in[i] = i + 1;
+        }
+        dispatchMethodKernel(size, out, in, aux);
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+
+}
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillTest.java	Sat May 03 21:46:35 2014 +0200
@@ -113,13 +113,14 @@
          * argument).
          * 
          */
-        Arrays.fill(out, 0f);
-        Arrays.fill(in, 0f);
+        Arrays.fill(out, -1f);
+        for (int i = 0; i < size; i++) {
+            in[i] = i + 1;
+        }
         dispatchMethodKernel(size, out, in);
     }
 
     @Test
-    @Ignore("until stack slots are supported in deopt")
     public void test() {
         testGeneratedHsail();
     }
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticIntSpillTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticIntSpillTest.java	Sat May 03 21:46:35 2014 +0200
@@ -88,7 +88,6 @@
     }
 
     @Test
-    @Ignore("until stack slots are supported in deopt")
     public void test() {
         testGeneratedHsail();
     }
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/ArrayListGetTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/ArrayListGetTest.java	Sat May 03 21:46:35 2014 +0200
@@ -25,12 +25,12 @@
 
 import static com.oracle.graal.debug.Debug.*;
 
-import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester;
-import com.oracle.graal.debug.*;
+import java.util.*;
 
-import org.junit.Test;
+import org.junit.*;
 
-import java.util.ArrayList;
+import com.oracle.graal.compiler.hsail.test.infra.*;
+import com.oracle.graal.debug.*;
 
 /**
  * Tests calling ArrayList.get().
@@ -60,6 +60,7 @@
 
     // NYI emitForeignCall charAlignedDisjointArraycopy
     @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class)
+    @Ignore
     public void testUsingLambdaMethod() {
         try (DebugConfigScope s = disableIntercept()) {
             testGeneratedHsailUsingLambdaMethod();
--- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Sat May 03 21:46:35 2014 +0200
@@ -37,20 +37,10 @@
 import com.oracle.graal.lir.StandardOp.JumpOp;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.hsail.*;
-import com.oracle.graal.lir.hsail.HSAILArithmetic.ConvertOp;
-import com.oracle.graal.lir.hsail.HSAILArithmetic.Op1Stack;
+import com.oracle.graal.lir.hsail.HSAILArithmetic.Op1Reg;
 import com.oracle.graal.lir.hsail.HSAILArithmetic.Op2Reg;
-import com.oracle.graal.lir.hsail.HSAILArithmetic.Op2Stack;
-import com.oracle.graal.lir.hsail.HSAILArithmetic.ShiftOp;
-import com.oracle.graal.lir.hsail.HSAILControlFlow.CompareBranchOp;
-import com.oracle.graal.lir.hsail.HSAILControlFlow.CondMoveOp;
-import com.oracle.graal.lir.hsail.HSAILControlFlow.FloatCondMoveOp;
-import com.oracle.graal.lir.hsail.HSAILControlFlow.ReturnOp;
-import com.oracle.graal.lir.hsail.HSAILControlFlow.StrategySwitchOp;
-import com.oracle.graal.lir.hsail.HSAILMove.LeaOp;
-import com.oracle.graal.lir.hsail.HSAILMove.MembarOp;
-import com.oracle.graal.lir.hsail.HSAILMove.MoveFromRegOp;
-import com.oracle.graal.lir.hsail.HSAILMove.MoveToRegOp;
+import com.oracle.graal.lir.hsail.HSAILControlFlow.*;
+import com.oracle.graal.lir.hsail.HSAILMove.*;
 import com.oracle.graal.phases.util.*;
 
 /**
@@ -262,16 +252,16 @@
             case Int:
                 // Note: The Int case also handles the negation of shorts, bytes, and chars because
                 // Java treats these types as ints at the bytecode level.
-                append(new Op1Stack(INEG, result, input));
+                append(new Op1Reg(INEG, result, input));
                 break;
             case Long:
-                append(new Op1Stack(LNEG, result, input));
+                append(new Op1Reg(LNEG, result, input));
                 break;
             case Double:
-                append(new Op1Stack(DNEG, result, input));
+                append(new Op1Reg(DNEG, result, input));
                 break;
             case Float:
-                append(new Op1Stack(FNEG, result, input));
+                append(new Op1Reg(FNEG, result, input));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -293,10 +283,10 @@
             case Int:
                 // Note: The Int case also covers other primitive integral types smaller than an int
                 // (char, byte, short) because Java treats these types as ints.
-                append(new Op1Stack(INOT, result, input));
+                append(new Op1Reg(INOT, result, input));
                 break;
             case Long:
-                append(new Op1Stack(LNOT, result, input));
+                append(new Op1Reg(LNOT, result, input));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -309,16 +299,16 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(IADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(IADD, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(LADD, result, a, loadNonConst(b)));
                 break;
             case Float:
-                append(new Op2Stack(FADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(FADD, result, a, loadNonConst(b)));
                 break;
             case Double:
-                append(new Op2Stack(DADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(DADD, result, a, loadNonConst(b)));
                 break;
             case Object:
                 throw GraalInternalError.shouldNotReachHere();
@@ -334,19 +324,19 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(IADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(IADD, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(LADD, result, a, loadNonConst(b)));
                 break;
             case Float:
-                append(new Op2Stack(FADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(FADD, result, a, loadNonConst(b)));
                 break;
             case Double:
-                append(new Op2Stack(DADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(DADD, result, a, loadNonConst(b)));
                 break;
             case Object:
-                append(new Op2Stack(OADD, result, a, loadNonConst(b)));
+                append(new Op2Reg(OADD, result, a, loadNonConst(b)));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -359,16 +349,16 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(ISUB, result, a, loadNonConst(b)));
+                append(new Op2Reg(ISUB, result, a, loadNonConst(b)));
                 break;
             case Float:
-                append(new Op2Stack(FSUB, result, a, loadNonConst(b)));
+                append(new Op2Reg(FSUB, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LSUB, result, a, loadNonConst(b)));
+                append(new Op2Reg(LSUB, result, a, loadNonConst(b)));
                 break;
             case Double:
-                append(new Op2Stack(DSUB, result, a, loadNonConst(b)));
+                append(new Op2Reg(DSUB, result, a, loadNonConst(b)));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -418,16 +408,16 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(IDIV, result, a, loadNonConst(b)));
+                append(new Op2Reg(IDIV, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LDIV, result, a, loadNonConst(b)));
+                append(new Op2Reg(LDIV, result, a, loadNonConst(b)));
                 break;
             case Float:
-                append(new Op2Stack(FDIV, result, a, loadNonConst(b)));
+                append(new Op2Reg(FDIV, result, a, loadNonConst(b)));
                 break;
             case Double:
-                append(new Op2Stack(DDIV, result, a, loadNonConst(b)));
+                append(new Op2Reg(DDIV, result, a, loadNonConst(b)));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -441,16 +431,16 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(IREM, result, a, loadNonConst(b)));
+                append(new Op2Reg(IREM, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LREM, result, a, loadNonConst(b)));
+                append(new Op2Reg(LREM, result, a, loadNonConst(b)));
                 break;
             case Float:
-                append(new Op2Stack(FREM, result, a, loadNonConst(b)));
+                append(new Op2Reg(FREM, result, a, loadNonConst(b)));
                 break;
             case Double:
-                append(new Op2Stack(DREM, result, a, loadNonConst(b)));
+                append(new Op2Reg(DREM, result, a, loadNonConst(b)));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -473,10 +463,10 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(IAND, result, a, loadNonConst(b)));
+                append(new Op2Reg(IAND, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LAND, result, a, loadNonConst(b)));
+                append(new Op2Reg(LAND, result, a, loadNonConst(b)));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -489,10 +479,10 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(IOR, result, a, loadNonConst(b)));
+                append(new Op2Reg(IOR, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LOR, result, a, loadNonConst(b)));
+                append(new Op2Reg(LOR, result, a, loadNonConst(b)));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -505,10 +495,10 @@
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
             case Int:
-                append(new Op2Stack(IXOR, result, a, loadNonConst(b)));
+                append(new Op2Reg(IXOR, result, a, loadNonConst(b)));
                 break;
             case Long:
-                append(new Op2Stack(LXOR, result, a, loadNonConst(b)));
+                append(new Op2Reg(LXOR, result, a, loadNonConst(b)));
                 break;
             default:
                 throw GraalInternalError.shouldNotReachHere();
@@ -720,7 +710,7 @@
     @Override
     public Value emitMathAbs(Value input) {
         Variable result = newVariable(input.getPlatformKind());
-        append(new Op1Stack(ABS, result, input));
+        append(new Op1Reg(ABS, result, input));
         return result;
     }
 
@@ -732,7 +722,7 @@
      */
     public Value emitMathCeil(Value input) {
         Variable result = newVariable(input.getPlatformKind());
-        append(new Op1Stack(CEIL, result, input));
+        append(new Op1Reg(CEIL, result, input));
         return result;
     }
 
@@ -744,7 +734,7 @@
      */
     public Value emitMathFloor(Value input) {
         Variable result = newVariable(input.getPlatformKind());
-        append(new Op1Stack(FLOOR, result, input));
+        append(new Op1Reg(FLOOR, result, input));
         return result;
     }
 
@@ -756,7 +746,7 @@
      */
     public Value emitMathRint(Value input) {
         Variable result = newVariable(input.getPlatformKind());
-        append(new Op1Stack(RINT, result, input));
+        append(new Op1Reg(RINT, result, input));
         return result;
     }
 
@@ -769,7 +759,7 @@
     @Override
     public Value emitMathSqrt(Value input) {
         Variable result = newVariable(input.getPlatformKind());
-        append(new Op1Stack(SQRT, result, input));
+        append(new Op1Reg(SQRT, result, input));
         return result;
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java	Sat May 03 21:46:35 2014 +0200
@@ -29,6 +29,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java	Sat May 03 21:46:35 2014 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 /**
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FinalizableSubclassTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FinalizableSubclassTest.java	Sat May 03 21:46:35 2014 +0200
@@ -38,6 +38,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 public class FinalizableSubclassTest extends GraalCompilerTest {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java	Sat May 03 21:46:35 2014 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 public class InvokeExceptionTest extends GraalCompilerTest {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java	Sat May 03 21:46:35 2014 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 public class InvokeHintsTest extends GraalCompilerTest {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java	Sat May 03 21:46:35 2014 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 public class LockEliminationTest extends GraalCompilerTest {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Sat May 03 21:46:35 2014 +0200
@@ -24,7 +24,6 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static org.junit.Assert.*;
-
 import java.util.*;
 
 import org.junit.*;
@@ -43,6 +42,7 @@
 import com.oracle.graal.options.OptionValue.OverrideScope;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.schedule.*;
 import com.oracle.graal.phases.schedule.SchedulePhase.MemoryScheduling;
 import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java	Sat May 03 21:46:35 2014 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.compiler.test;
 
 import static com.oracle.graal.graph.iterators.NodePredicates.*;
-
 import java.util.*;
 
 import org.junit.*;
@@ -35,6 +34,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 /**
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java	Sat May 03 21:46:35 2014 +0200
@@ -37,6 +37,7 @@
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
@@ -126,7 +127,7 @@
     /**
      * Runs Escape Analysis on the given snippet and makes sure that no allocations remain in the
      * graph.
-     * 
+     *
      * @param snippet the name of the method whose graph should be processed
      * @param expectedConstantResult if this is non-null, the resulting graph needs to have the
      *            given constant return value
@@ -156,7 +157,7 @@
             new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
             new DeadCodeEliminationPhase().apply(graph);
             new CanonicalizerPhase(true).apply(graph, context);
-            new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(true)).apply(graph, context);
+            new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(true), null).apply(graph, context);
             returnNodes = graph.getNodes(ReturnNode.class).snapshot();
         } catch (Throwable e) {
             throw Debug.handle(e);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EarlyReadEliminationTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EarlyReadEliminationTest.java	Sat May 03 21:46:35 2014 +0200
@@ -27,6 +27,7 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java	Sat May 03 21:46:35 2014 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.compiler.test.ea;
 
 import static org.junit.Assert.*;
-
 import java.util.*;
 
 import org.junit.*;
@@ -34,6 +33,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
@@ -247,6 +247,6 @@
         Assumptions assumptions = new Assumptions(false);
         HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
         new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
-        new PartialEscapePhase(false, true, new CanonicalizerPhase(true)).apply(graph, context);
+        new PartialEscapePhase(false, true, new CanonicalizerPhase(true), null).apply(graph, context);
     }
 }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Sat May 03 21:46:35 2014 +0200
@@ -23,6 +23,7 @@
 package com.oracle.graal.compiler.test.ea;
 
 import java.lang.ref.*;
+import java.util.function.*;
 
 import org.junit.*;
 
@@ -30,7 +31,6 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.graph.*;
@@ -186,11 +186,11 @@
             Assert.assertTrue("partial escape analysis should have removed all NewInstanceNode allocations", graph.getNodes().filter(NewInstanceNode.class).isEmpty());
             Assert.assertTrue("partial escape analysis should have removed all NewArrayNode allocations", graph.getNodes().filter(NewArrayNode.class).isEmpty());
 
-            NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(graph).apply();
+            ToDoubleFunction<FixedNode> nodeProbabilities = new FixedNodeProbabilityCache();
             double probabilitySum = 0;
             int materializeCount = 0;
             for (CommitAllocationNode materialize : graph.getNodes().filter(CommitAllocationNode.class)) {
-                probabilitySum += nodeProbabilities.get(materialize) * materialize.getVirtualObjects().size();
+                probabilitySum += nodeProbabilities.applyAsDouble(materialize) * materialize.getVirtualObjects().size();
                 materializeCount += materialize.getVirtualObjects().size();
             }
             Assert.assertEquals("unexpected number of MaterializeObjectNodes", expectedCount, materializeCount);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PoorMansEATest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PoorMansEATest.java	Sat May 03 21:46:35 2014 +0200
@@ -34,6 +34,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 /**
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java	Sat May 03 21:46:35 2014 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.compiler.test.inlining;
 
 import static org.junit.Assert.*;
-
 import java.lang.reflect.*;
 
 import org.junit.*;
@@ -37,6 +36,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 public class InliningTest extends GraalCompilerTest {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Sat May 03 21:46:35 2014 +0200
@@ -45,11 +45,9 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
-import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.schedule.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
@@ -230,10 +228,8 @@
         List<Block> linearScanOrder = null;
         try (Scope ds = Debug.scope("MidEnd")) {
             try (Scope s = Debug.scope("ComputeLinearScanOrder")) {
-                NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(graph).apply();
-                BlocksToDoubles blockProbabilities = BlocksToDoubles.createFromNodeProbability(nodeProbabilities, schedule.getCFG());
-                codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock, blockProbabilities);
-                linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock, blockProbabilities);
+                codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
+                linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
 
                 lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder);
                 Debug.dump(lir, "After linear scan order");
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/HighTier.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/HighTier.java	Sat May 03 21:46:35 2014 +0200
@@ -24,13 +24,13 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static com.oracle.graal.compiler.phases.HighTier.Options.*;
-
 import com.oracle.graal.loop.phases.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.common.cfs.IterativeFlowSensitiveReductionPhase;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
--- a/graal/com.oracle.graal.gpu/src/com/oracle/graal/gpu/ExternalCompilationResult.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.gpu/src/com/oracle/graal/gpu/ExternalCompilationResult.java	Sat May 03 21:46:35 2014 +0200
@@ -43,6 +43,8 @@
     private long entryPoint;
     private StructuredGraph hostGraph;
 
+    private int[] oopMapArray;
+
     /**
      * Set the address for the point of entry to the external compilation result.
      * 
@@ -75,4 +77,13 @@
     public StructuredGraph getHostGraph() {
         return hostGraph;
     }
+
+    public void setOopMapArray(int[] arr) {
+        oopMapArray = arr;
+    }
+
+    public int[] getOopMapArray() {
+        return oopMapArray;
+    }
+
 }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Sat May 03 21:46:35 2014 +0200
@@ -113,7 +113,9 @@
         Replacements replacements = createReplacements(runtime, assumptions, p, snippetReflection);
         HotSpotDisassemblerProvider disassembler = createDisassembler(runtime);
         HotSpotSuitesProvider suites = createSuites(runtime);
-        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection);
+        HotSpotMethodHandleAccessProvider methodHandleAccess = new HotSpotMethodHandleAccessProvider();
+        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection,
+                        methodHandleAccess);
 
         return createBackend(runtime, providers);
     }
@@ -189,15 +191,15 @@
         } else {
             /*
              * System V Application Binary Interface, AMD64 Architecture Processor Supplement
-             * 
+             *
              * Draft Version 0.96
-             * 
+             *
              * http://www.uclibc.org/docs/psABI-x86_64.pdf
-             * 
+             *
              * 3.2.1
-             * 
+             *
              * ...
-             * 
+             *
              * This subsection discusses usage of each register. Registers %rbp, %rbx and %r12
              * through %r15 "belong" to the calling function and the called function is required to
              * preserve their values. In other words, a called function must preserve these
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Sat May 03 21:46:35 2014 +0200
@@ -66,7 +66,6 @@
 import com.oracle.graal.lir.hsail.HSAILMove.AtomicReadAndAddOp;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
-import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
@@ -145,7 +144,21 @@
      */
     public HotSpotNmethod compileAndInstallKernel(Method method) {
         ResolvedJavaMethod javaMethod = getProviders().getMetaAccess().lookupJavaMethod(method);
-        return installKernel(javaMethod, compileKernel(javaMethod, true));
+        HotSpotNmethod nm = installKernel(javaMethod, compileKernel(javaMethod, true));
+        try (Scope s = Debug.scope("HostCodeGen")) {
+            if (Debug.isLogEnabled()) {
+                DisassemblerProvider dis = getRuntime().getHostBackend().getDisassembler();
+                if (dis != null) {
+                    String disasm = dis.disassemble(nm);
+                    Debug.log("host code generated for %s%n%s", javaMethod, disasm);
+                } else {
+                    Debug.log("host code disassembler is null");
+                }
+            }
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+        return nm;
     }
 
     /**
@@ -274,7 +287,8 @@
             }
         }
 
-        HotSpotNmethod code = new HotSpotNmethod(javaMethod, hsailCode.getName(), false, true);
+        HSAILHotSpotNmethod code = new HSAILHotSpotNmethod(javaMethod, hsailCode.getName(), false, true);
+        code.setOopMapArray(hsailCode.getOopMapArray());
         HotSpotCompiledNmethod compiled = new HotSpotCompiledNmethod(getTarget(), javaMethod, compilationResult);
         CodeInstallResult result = getRuntime().getCompilerToVM().installCode(compiled, code, null);
         if (result != CodeInstallResult.OK) {
@@ -359,11 +373,16 @@
         if (!deviceInitialized) {
             throw new GraalInternalError("Cannot execute GPU kernel if device is not initialized");
         }
-        Object[] oopsSaveArea = new Object[maxDeoptIndex * 16];
-        return executeKernel0(kernel, jobSize, args, oopsSaveArea, donorThreadPool.get().getThreads(), HsailAllocBytesPerWorkitem.getValue());
+        int[] oopMapArray = ((HSAILHotSpotNmethod) kernel).getOopMapArray();
+        int saveAreaCounts = OopMapArrayBuilder.getSaveAreaCounts(oopMapArray);
+        int numDRegs = (saveAreaCounts >> 8) & 0xff;
+        int numStackSlots = (saveAreaCounts >> 16);
+        // pessimistically assume that any of the DRegs or stackslots could be oops
+        Object[] oopsSaveArea = new Object[maxDeoptIndex * (numDRegs + numStackSlots)];
+        return executeKernel0(kernel, jobSize, args, oopsSaveArea, donorThreadPool.get().getThreads(), HsailAllocBytesPerWorkitem.getValue(), oopMapArray);
     }
 
-    private static native boolean executeKernel0(HotSpotInstalledCode kernel, int jobSize, Object[] args, Object[] oopsSave, Thread[] donorThreads, int allocBytesPerWorkitem)
+    private static native boolean executeKernel0(HotSpotInstalledCode kernel, int jobSize, Object[] args, Object[] oopsSave, Thread[] donorThreads, int allocBytesPerWorkitem, int[] oopMapArray)
                     throws InvalidInstalledCodeException;
 
     /**
@@ -419,6 +438,22 @@
         }
     }
 
+    static class HSAILHotSpotNmethod extends HotSpotNmethod {
+        private int[] oopMapArray;
+
+        HSAILHotSpotNmethod(HotSpotResolvedJavaMethod method, String name, boolean isDefault, boolean isExternal) {
+            super(method, name, isDefault, isExternal);
+        }
+
+        void setOopMapArray(int[] array) {
+            oopMapArray = array;
+        }
+
+        int[] getOopMapArray() {
+            return oopMapArray;
+        }
+    }
+
     @Override
     protected Assembler createAssembler(FrameMap frameMap) {
         return new HSAILHotSpotAssembler(getTarget());
@@ -686,10 +721,54 @@
         asm.emitString(spillsegStringFinal, spillsegDeclarationPosition);
         // Emit the epilogue.
 
-        // TODO: keep track of whether we need it
+        int numSRegs = 0;
+        int numDRegs = 0;
+        int numStackSlotBytes = 0;
         if (useHSAILDeoptimization) {
+            // get the union of registers and stack slots needed to be saved at the infopoints
+            // while doing this compute the highest register in each category
+            HSAILHotSpotRegisterConfig hsailRegConfig = (HSAILHotSpotRegisterConfig) regConfig;
+            Set<Register> infoUsedRegs = new TreeSet<>();
+            Set<StackSlot> infoUsedStackSlots = new HashSet<>();
+            List<Infopoint> infoList = crb.compilationResult.getInfopoints();
+            for (Infopoint info : infoList) {
+                BytecodeFrame frame = info.debugInfo.frame();
+                while (frame != null) {
+                    for (int i = 0; i < frame.numLocals + frame.numStack; i++) {
+                        Value val = frame.values[i];
+                        if (isLegal(val)) {
+                            if (isRegister(val)) {
+                                Register reg = asRegister(val);
+                                infoUsedRegs.add(reg);
+                                if (hsailRegConfig.isAllocatableSReg(reg)) {
+                                    numSRegs = Math.max(numSRegs, reg.encoding + 1);
+                                } else if (hsailRegConfig.isAllocatableDReg(reg)) {
+                                    numDRegs = Math.max(numDRegs, reg.encoding + 1);
+                                }
+                            } else if (isStackSlot(val)) {
+                                StackSlot slot = asStackSlot(val);
+                                Kind slotKind = slot.getKind();
+                                int slotSizeBytes = (slotKind.isObject() ? 8 : slotKind.getByteCount());
+                                int slotOffsetMax = HSAIL.getStackOffsetStart(slot, slotSizeBytes * 8) + slotSizeBytes;
+                                numStackSlotBytes = Math.max(numStackSlotBytes, slotOffsetMax);
+                                infoUsedStackSlots.add(slot);
+                            }
+                        }
+                    }
+                    frame = frame.caller();
+                }
+            }
+
+            // round up numSRegs to even number so dregs start on aligned boundary
+            numSRegs += (numSRegs & 1);
+
+            // numStackSlots is the number of 8-byte locations used for stack variables
+            int numStackSlots = (numStackSlotBytes + 7) / 8;
+
             final int offsetToDeoptSaveStates = config.hsailSaveStatesOffset0;
-            final int sizeofKernelDeopt = config.hsailSaveStatesOffset1 - config.hsailSaveStatesOffset0;
+            final int sizeofKernelDeoptHeader = config.hsailKernelDeoptimizationHeaderSize;
+            final int bytesPerSaveArea = 4 * numSRegs + 8 * numDRegs + 8 * numStackSlots;
+            final int sizeofKernelDeopt = sizeofKernelDeoptHeader + bytesPerSaveArea;
             final int offsetToNeverRanArray = config.hsailNeverRanArrayOffset;
             final int offsetToDeoptNextIndex = config.hsailDeoptNextIndexOffset;
             final int offsetToDeoptimizationWorkItem = config.hsailDeoptimizationWorkItem;
@@ -708,7 +787,6 @@
             AllocatableValue codeBufferOffsetReg = HSAIL.codeBufferOffsetReg.asValue(Kind.Int);
             AllocatableValue scratch32 = HSAIL.s34.asValue(Kind.Int);
             AllocatableValue workidreg = HSAIL.s35.asValue(Kind.Int);
-            AllocatableValue dregOopMapReg = HSAIL.dregOopMapReg.asValue(Kind.Int);
 
             HSAILAddress deoptNextIndexAddr = new HSAILAddressValue(Kind.Int, scratch64, offsetToDeoptNextIndex).toAddress();
             HSAILAddress neverRanArrayAddr = new HSAILAddressValue(Kind.Int, scratch64, offsetToNeverRanArray).toAddress();
@@ -769,8 +847,6 @@
             // Now scratch64 is the _deopt_info._first_frame
             HSAILAddress pcStoreAddr = new HSAILAddressValue(Kind.Int, waveMathScratch1, offsetToFramePc).toAddress();
             HSAILAddress regCountsAddr = new HSAILAddressValue(Kind.Int, waveMathScratch1, offsetToNumSaves).toAddress();
-            HSAILAddress dregOopMapAddr = new HSAILAddressValue(Kind.Int, waveMathScratch1, offsetToNumSaves + 2).toAddress();
-
             asm.emitComment("// store deopting workitem");
             asm.emitWorkItemAbsId(scratch32);
             asm.emitStore(Kind.Int, scratch32, workItemAddr);
@@ -778,58 +854,61 @@
             asm.emitStore(Kind.Int, actionAndReasonReg, actionReasonStoreAddr);
             asm.emitComment("// store PC");
             asm.emitStore(Kind.Int, codeBufferOffsetReg, pcStoreAddr);
-            asm.emitComment("// store regCounts");
-            asm.emitStore(Kind.Short, Constant.forInt(32 + (16 << 8) + (0 << 16)), regCountsAddr);
-            asm.emitComment("// store dreg ref map bits");
-            asm.emitStore(Kind.Short, dregOopMapReg, dregOopMapAddr);
+
+            asm.emitComment("// store regCounts (" + numSRegs + " $s registers and " + numDRegs + " $d registers" + numStackSlots + " stack slots)");
+            asm.emitStore(Kind.Int, Constant.forInt(numSRegs + (numDRegs << 8) + (numStackSlots << 16)), regCountsAddr);
 
-            // get the union of registers needed to be saved at the infopoints
-            boolean[] infoUsedRegs = new boolean[HSAIL.threadRegister.number + 1];
-            List<Infopoint> infoList = crb.compilationResult.getInfopoints();
-            for (Infopoint info : infoList) {
-                BytecodeFrame frame = info.debugInfo.frame();
-                for (int i = 0; i < frame.numLocals + frame.numStack; i++) {
-                    Value val = frame.values[i];
-                    if (isLegal(val) && isRegister(val)) {
-                        Register reg = asRegister(val);
-                        infoUsedRegs[reg.number] = true;
-                    }
+            // loop thru the usedValues storing each of the registers that are used.
+            // we always store in a fixed location, even if some registers are skipped
+            asm.emitComment("// store used regs");
+            for (Register reg : infoUsedRegs) {
+                if (hsailRegConfig.isAllocatableSReg(reg)) {
+                    // 32 bit registers
+                    Kind kind = Kind.Int;
+                    int ofst = offsetToSaveArea + reg.encoding * 4;
+                    HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress();
+                    AllocatableValue regValue = reg.asValue(kind);
+                    asm.emitStore(kind, regValue, addr);
+                } else if (hsailRegConfig.isAllocatableDReg(reg)) {
+                    // 64 bit registers
+                    Kind kind = Kind.Long;
+                    // d reg ofst starts past the 32 sregs
+                    int ofst = offsetToSaveArea + (numSRegs * 4) + reg.encoding * 8;
+                    HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress();
+                    AllocatableValue regValue = reg.asValue(kind);
+                    asm.emitStore(kind, regValue, addr);
+                } else {
+                    throw GraalInternalError.unimplemented();
                 }
             }
 
-            // loop storing each of the 32 s registers that are used by infopoints
-            // we always store in a fixed location, even if some registers are not stored
-            asm.emitComment("// store used s regs");
-            int ofst = offsetToSaveArea;
-            for (Register sreg : HSAIL.sRegisters) {
-                if (infoUsedRegs[sreg.number]) {
-                    Kind kind = Kind.Int;
+            // loop thru the usedStackSlots creating instructions to save in the save area
+            if (numStackSlotBytes > 0) {
+                asm.emitComment("// store stack slots (uses " + numStackSlotBytes + " bytes)");
+                for (StackSlot slot : infoUsedStackSlots) {
+                    asm.emitComment("// store " + slot);
+                    Kind kind = slot.getKind();
+                    int sizeInBits = (kind.isObject() || kind.getByteCount() == 8 ? 64 : 32);
+                    int ofst = offsetToSaveArea + (numSRegs * 4) + (numDRegs * 8) + HSAIL.getStackOffsetStart(slot, sizeInBits);
                     HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress();
-                    AllocatableValue sregValue = sreg.asValue(kind);
-                    asm.emitStore(kind, sregValue, addr);
+                    if (sizeInBits == 64) {
+                        asm.emitSpillLoad(kind, scratch64, slot);
+                        asm.emitStore(kind, scratch64, addr);
+                    } else {
+                        asm.emitSpillLoad(kind, scratch32, slot);
+                        asm.emitStore(kind, scratch32, addr);
+                    }
                 }
-                ofst += 4;
             }
 
-            // loop storing each of the 16 d registers that are used by infopoints
-            asm.emitComment("// store used d regs");
-            for (Register dreg : HSAIL.dRegisters) {
-                if (infoUsedRegs[dreg.number]) {
-                    Kind kind = Kind.Long;
-                    HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress();
-                    AllocatableValue dregValue = dreg.asValue(kind);
-                    asm.emitStore(kind, dregValue, addr);
-                }
-                ofst += 8;
-            }
-
-            // for now, ignore saving the spill variables but that would come here
-
             asm.emitString0(labelExit + ":\n");
 
             // and emit the return
             crb.frameContext.leave(crb);
             asm.exit();
+            // build the oopMap Array
+            int[] oopMapArray = new OopMapArrayBuilder().build(infoList, numSRegs, numDRegs, numStackSlots, hsailRegConfig);
+            ((ExternalCompilationResult) crb.compilationResult).setOopMapArray(oopMapArray);
         } else {
             // Deoptimization is explicitly off, so emit simple return
             asm.emitString0(asm.getDeoptLabelName() + ":\n");
@@ -841,10 +920,115 @@
 
         ExternalCompilationResult compilationResult = (ExternalCompilationResult) crb.compilationResult;
         HSAILHotSpotLIRGenerationResult lirGenRes = ((HSAILCompilationResultBuilder) crb).lirGenRes;
-        compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config));
+        compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config, numSRegs, numDRegs));
     }
 
-    private static StructuredGraph prepareHostGraph(ResolvedJavaMethod method, List<DeoptimizingOp> deopts, HotSpotProviders providers, HotSpotVMConfig config) {
+    private static class OopMapArrayBuilder {
+        // oopMapArray struct
+        // int bytesPerSaveArea; (not strictly part of oopsmap but convenient to put here)
+        // int intsPerInfopoint;
+        static final int SAVEAREACOUNTS_OFST = 0;
+        static final int INTSPERINFOPOINT_OFST = 1;
+        static final int HEADERSIZE = 2;
+        // for each infopoint:
+        // int deoptId
+        // one or more ints of bits for the oopmap
+
+        private int[] array;
+        private int intsPerInfopoint;
+
+        int[] build(List<Infopoint> infoList, int numSRegs, int numDRegs, int numStackSlots, HSAILHotSpotRegisterConfig hsailRegConfig) {
+            // we are told that infoList is always sorted
+            // each infoPoint can have a different oopMap
+
+            // since numStackSlots is the number of 8-byte stack slots used, it is an upper limit on
+            // the number of oop stack slots
+            int bitsPerInfopoint = numDRegs + numStackSlots;
+            int intsForBits = (bitsPerInfopoint + 31) / 32;
+            int numInfopoints = infoList.size();
+            intsPerInfopoint = intsForBits + 1;  // +1 for the pcoffset
+            int arraySize = HEADERSIZE + (numInfopoints * intsPerInfopoint);
+            array = new int[arraySize];
+            array[INTSPERINFOPOINT_OFST] = intsPerInfopoint;
+            // compute saveAreaCounts
+            int saveAreaCounts = (numSRegs & 0xff) + (numDRegs << 8) + (numStackSlots << 16);
+            array[SAVEAREACOUNTS_OFST] = saveAreaCounts;
+
+            // loop thru the infoList
+            int infoIndex = 0;
+            for (Infopoint info : infoList) {
+                setOopMapPcOffset(infoIndex, info.pcOffset);
+                BytecodeFrame frame = info.debugInfo.frame();
+                while (frame != null) {
+                    for (int i = 0; i < frame.numLocals + frame.numStack; i++) {
+                        Value val = frame.values[i];
+                        if (isLegal(val)) {
+                            if (isRegister(val)) {
+                                Register reg = asRegister(val);
+                                if (val.getKind().isObject()) {
+                                    assert (hsailRegConfig.isAllocatableDReg(reg));
+                                    int bitIndex = reg.encoding();
+                                    setOopMapBit(infoIndex, bitIndex);
+                                }
+                            } else if (isStackSlot(val)) {
+                                StackSlot slot = asStackSlot(val);
+                                if (val.getKind().isObject()) {
+                                    assert (HSAIL.getStackOffsetStart(slot, 64) % 8 == 0);
+                                    int bitIndex = numDRegs + HSAIL.getStackOffsetStart(slot, 64) / 8;
+                                    setOopMapBit(infoIndex, bitIndex);
+                                }
+                            }
+                        }
+                    }
+                    frame = frame.caller();
+                }
+                infoIndex++;
+            }
+            try (Scope s = Debug.scope("CodeGen")) {
+                if (Debug.isLogEnabled()) {
+                    Debug.log("numSRegs=%d, numDRegs=%d, numStackSlots=%d", numSRegs, numDRegs, numStackSlots);
+                    // show infopoint oopmap details
+                    for (infoIndex = 0; infoIndex < infoList.size(); infoIndex++) {
+                        String infoString = "Infopoint " + infoIndex + ", pcOffset=" + getOopMapPcOffset(infoIndex) + ",   oopmap=";
+                        for (int i = 0; i < intsForBits; i++) {
+                            infoString += (i != 0 ? ", " : "") + Integer.toHexString(getOopMapBitsAsInt(infoIndex, i));
+                        }
+                        Debug.log(infoString);
+                    }
+                }
+            } catch (Throwable e) {
+                throw Debug.handle(e);
+            }
+
+            return array;
+        }
+
+        private void setOopMapPcOffset(int infoIndex, int pcOffset) {
+            int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint;
+            array[arrIndex] = pcOffset;
+        }
+
+        private int getOopMapPcOffset(int infoIndex) {
+            int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint;
+            return array[arrIndex];
+        }
+
+        private void setOopMapBit(int infoIndex, int bitIndex) {
+            int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint + 1 + bitIndex / 32;
+            array[arrIndex] |= (1 << (bitIndex % 32));
+        }
+
+        private int getOopMapBitsAsInt(int infoIndex, int intIndex) {
+            int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint + 1 + intIndex;
+            return array[arrIndex];
+        }
+
+        public static int getSaveAreaCounts(int[] array) {
+            return array[SAVEAREACOUNTS_OFST];
+        }
+    }
+
+    private static StructuredGraph prepareHostGraph(ResolvedJavaMethod method, List<DeoptimizingOp> deopts, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs) {
         if (deopts.isEmpty()) {
             return null;
         }
@@ -868,7 +1052,7 @@
             keyProbabilities[i] = 1.0 / deopts.size();
             keys[i] = deopt.getCodeBufferPos();
             assert keys[i] >= 0;
-            branches[i] = createHostDeoptBranch(deopt, hsailFrame, reasonAndAction, speculation, providers, config);
+            branches[i] = createHostDeoptBranch(deopt, hsailFrame, reasonAndAction, speculation, providers, config, numSRegs, numDRegs);
 
             i++;
         }
@@ -892,34 +1076,35 @@
         return BeginNode.begin(vmError);
     }
 
-    private static BeginNode createHostDeoptBranch(DeoptimizingOp deopt, ParameterNode hsailFrame, ValueNode reasonAndAction, ValueNode speculation, HotSpotProviders providers, HotSpotVMConfig config) {
+    private static BeginNode createHostDeoptBranch(DeoptimizingOp deopt, ParameterNode hsailFrame, ValueNode reasonAndAction, ValueNode speculation, HotSpotProviders providers,
+                    HotSpotVMConfig config, int numSRegs, int numDRegs) {
         BeginNode branch = hsailFrame.graph().add(new BeginNode());
         DynamicDeoptimizeNode deoptimization = hsailFrame.graph().add(new DynamicDeoptimizeNode(reasonAndAction, speculation));
-        deoptimization.setStateBefore(createFrameState(deopt.getFrameState().topFrame, hsailFrame, providers, config));
+        deoptimization.setStateBefore(createFrameState(deopt.getFrameState().topFrame, hsailFrame, providers, config, numSRegs, numDRegs));
         branch.setNext(deoptimization);
         return branch;
     }
 
-    private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config) {
+    private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs) {
         StructuredGraph hostGraph = hsailFrame.graph();
         ValueNode[] locals = new ValueNode[lowLevelFrame.numLocals];
         for (int i = 0; i < lowLevelFrame.numLocals; i++) {
-            locals[i] = getNodeForValueFromFrame(lowLevelFrame.getLocalValue(i), hsailFrame, hostGraph, providers, config);
+            locals[i] = getNodeForValueFromFrame(lowLevelFrame.getLocalValue(i), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs);
         }
         List<ValueNode> stack = new ArrayList<>(lowLevelFrame.numStack);
         for (int i = 0; i < lowLevelFrame.numStack; i++) {
-            stack.add(getNodeForValueFromFrame(lowLevelFrame.getStackValue(i), hsailFrame, hostGraph, providers, config));
+            stack.add(getNodeForValueFromFrame(lowLevelFrame.getStackValue(i), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs));
         }
         ValueNode[] locks = new ValueNode[lowLevelFrame.numLocks];
         MonitorIdNode[] monitorIds = new MonitorIdNode[lowLevelFrame.numLocks];
         for (int i = 0; i < lowLevelFrame.numLocks; i++) {
             HotSpotMonitorValue lockValue = (HotSpotMonitorValue) lowLevelFrame.getLockValue(i);
-            locks[i] = getNodeForValueFromFrame(lockValue, hsailFrame, hostGraph, providers, config);
+            locks[i] = getNodeForValueFromFrame(lockValue, hsailFrame, hostGraph, providers, config, numSRegs, numDRegs);
             monitorIds[i] = getMonitorIdForHotSpotMonitorValueFromFrame(lockValue, hsailFrame, hostGraph);
         }
         FrameState frameState = hostGraph.add(new FrameState(lowLevelFrame.getMethod(), lowLevelFrame.getBCI(), locals, stack, locks, monitorIds, lowLevelFrame.rethrowException, false));
         if (lowLevelFrame.caller() != null) {
-            frameState.setOuterFrameState(createFrameState(lowLevelFrame.caller(), hsailFrame, providers, config));
+            frameState.setOuterFrameState(createFrameState(lowLevelFrame.caller(), hsailFrame, providers, config, numSRegs, numDRegs));
         }
         return frameState;
     }
@@ -932,21 +1117,23 @@
         throw GraalInternalError.unimplemented();
     }
 
-    private static ValueNode getNodeForValueFromFrame(Value localValue, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config) {
+    private static ValueNode getNodeForValueFromFrame(Value localValue, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs,
+                    int numDRegs) {
         ValueNode valueNode;
         if (localValue instanceof Constant) {
             valueNode = ConstantNode.forConstant((Constant) localValue, providers.getMetaAccess(), hostGraph);
         } else if (localValue instanceof VirtualObject) {
             throw GraalInternalError.unimplemented();
         } else if (localValue instanceof StackSlot) {
-            throw GraalInternalError.unimplemented();
+            StackSlot slot = (StackSlot) localValue;
+            valueNode = getNodeForStackSlotFromFrame(slot, localValue.getKind(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs);
         } else if (localValue instanceof HotSpotMonitorValue) {
             HotSpotMonitorValue hotSpotMonitorValue = (HotSpotMonitorValue) localValue;
-            return getNodeForValueFromFrame(hotSpotMonitorValue.getOwner(), hsailFrame, hostGraph, providers, config);
+            return getNodeForValueFromFrame(hotSpotMonitorValue.getOwner(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs);
         } else if (localValue instanceof RegisterValue) {
             RegisterValue registerValue = (RegisterValue) localValue;
             int regNumber = registerValue.getRegister().number;
-            valueNode = getNodeForRegisterFromFrame(regNumber, localValue.getKind(), hsailFrame, hostGraph, providers, config);
+            valueNode = getNodeForRegisterFromFrame(regNumber, localValue.getKind(), hsailFrame, hostGraph, providers, config, numSRegs);
         } else if (Value.ILLEGAL.equals(localValue)) {
             valueNode = null;
         } else {
@@ -955,20 +1142,18 @@
         return valueNode;
     }
 
-    private static ValueNode getNodeForRegisterFromFrame(int regNumber, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config) {
+    private static ValueNode getNodeForRegisterFromFrame(int regNumber, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config,
+                    int numSRegs) {
         ValueNode valueNode;
         LocationNode location;
+        int longSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Long);
+        int intSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Int);
         if (regNumber >= HSAIL.s0.number && regNumber <= HSAIL.s31.number) {
-            int intSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Int);
             long offset = config.hsailFrameSaveAreaOffset + intSize * (regNumber - HSAIL.s0.number);
             location = ConstantLocationNode.create(FINAL_LOCATION, valueKind, offset, hostGraph);
         } else if (regNumber >= HSAIL.d0.number && regNumber <= HSAIL.d15.number) {
-            int longSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Long);
-            long offset = config.hsailFrameSaveAreaOffset + longSize * (regNumber - HSAIL.d0.number);
-            LocationNode numSRegsLocation = ConstantLocationNode.create(FINAL_LOCATION, Kind.Byte, config.hsailFrameNumSRegOffset, hostGraph);
-            ValueNode numSRegs = hostGraph.unique(new FloatingReadNode(hsailFrame, numSRegsLocation, null, StampFactory.forInteger(8)));
-            numSRegs = SignExtendNode.convert(numSRegs, StampFactory.forKind(Kind.Byte));
-            location = IndexedLocationNode.create(FINAL_LOCATION, valueKind, offset, numSRegs, hostGraph, 4);
+            long offset = config.hsailFrameSaveAreaOffset + intSize * numSRegs + longSize * (regNumber - HSAIL.d0.number);
+            location = ConstantLocationNode.create(FINAL_LOCATION, valueKind, offset, hostGraph);
         } else {
             throw GraalInternalError.shouldNotReachHere("unknown hsail register: " + regNumber);
         }
@@ -976,4 +1161,18 @@
         return valueNode;
     }
 
+    private static ValueNode getNodeForStackSlotFromFrame(StackSlot slot, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config,
+                    int numSRegs, int numDRegs) {
+        int slotSizeInBits = (valueKind == Kind.Object ? 64 : valueKind.getByteCount() * 8);
+        if ((slotSizeInBits == 32) || (slotSizeInBits == 64)) {
+            int longSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Long);
+            int intSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Int);
+            long offset = config.hsailFrameSaveAreaOffset + (intSize * numSRegs) + (longSize * numDRegs) + HSAIL.getStackOffsetStart(slot, slotSizeInBits);
+            LocationNode location = ConstantLocationNode.create(FINAL_LOCATION, valueKind, offset, hostGraph);
+            ValueNode valueNode = hostGraph.unique(new FloatingReadNode(hsailFrame, location, null, StampFactory.forKind(valueKind)));
+            return valueNode;
+        } else {
+            throw GraalInternalError.shouldNotReachHere("unsupported stack slot kind: " + valueKind);
+        }
+    }
 }
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java	Sat May 03 21:46:35 2014 +0200
@@ -57,7 +57,8 @@
         Replacements replacements = new HSAILHotSpotReplacementsImpl(p, host.getSnippetReflection(), assumptions, codeCache.getTarget(), host.getReplacements());
         HotSpotDisassemblerProvider disassembler = host.getDisassembler();
         SuitesProvider suites = new HotSpotSuitesProvider(runtime);
-        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection());
+        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection(),
+                        host.getMethodHandleAccess());
 
         // pass registers info down to ReplacementsUtil (maybe a better way to do this?)
         HSAILHotSpotReplacementsUtil.initialize(providers.getRegisters());
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotRegisterConfig.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotRegisterConfig.java	Sat May 03 21:46:35 2014 +0200
@@ -194,6 +194,14 @@
         throw new UnsupportedOperationException();
     }
 
+    public boolean isAllocatableSReg(Register reg) {
+        return (reg.number >= HSAIL.s0.number && reg.number <= HSAIL.s31.number);
+    }
+
+    public boolean isAllocatableDReg(Register reg) {
+        return (reg.number >= HSAIL.d0.number && reg.number <= HSAIL.d15.number);
+    }
+
     public HSAILHotSpotRegisterConfig() {
 
     }
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotSafepointOp.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotSafepointOp.java	Sat May 03 21:46:35 2014 +0200
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.hotspot.hsail;
 
-import static com.oracle.graal.api.code.ValueUtil.*;
-
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.asm.hsail.*;
@@ -42,7 +40,6 @@
     private Constant actionAndReason;
     @State protected LIRFrameState frameState;
     protected int codeBufferPos = -1;
-    protected int dregOopMap = 0;
     final int offsetToNoticeSafepoints;
 
     public HSAILHotSpotSafepointOp(LIRFrameState state, HotSpotVMConfig config, NodeLIRBuilderTool tool) {
@@ -77,23 +74,10 @@
         masm.emitCompare(Kind.Int, scratch32, Constant.forInt(0), "eq", false, false);
         masm.cbr(afterSafepointLabel);
 
-        BytecodeFrame frame = frameState.debugInfo().frame();
-        for (int i = 0; i < frame.numLocals + frame.numStack; i++) {
-            Value val = frame.values[i];
-            if (isLegal(val) && isRegister(val)) {
-                Register reg = asRegister(val);
-                if (val.getKind() == Kind.Object) {
-                    dregOopMap |= 1 << (reg.encoding());
-                }
-            }
-        }
-
         AllocatableValue actionAndReasonReg = HSAIL.actionAndReasonReg.asValue(Kind.Int);
         AllocatableValue codeBufferOffsetReg = HSAIL.codeBufferOffsetReg.asValue(Kind.Int);
-        AllocatableValue dregOopMapReg = HSAIL.dregOopMapReg.asValue(Kind.Int);
         masm.emitMov(Kind.Int, actionAndReasonReg, actionAndReason);
         masm.emitMov(Kind.Int, codeBufferOffsetReg, Constant.forInt(codeBufferPos));
-        masm.emitMov(Kind.Int, dregOopMapReg, Constant.forInt(dregOopMap));
         masm.emitJumpToLabelName(masm.getDeoptLabelName());
 
         masm.emitString0(afterSafepointLabel + ":\n");
--- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java	Sat May 03 21:46:35 2014 +0200
@@ -47,7 +47,8 @@
         HotSpotDisassemblerProvider disassembler = host.getDisassembler();
         SuitesProvider suites = new DefaultSuitesProvider();
         HotSpotRegistersProvider registers = new HotSpotRegisters(PTX.tid, Register.None, Register.None);
-        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection());
+        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection(),
+                        host.getMethodHandleAccess());
         return new PTXHotSpotBackend(runtime, providers);
     }
 
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java	Sat May 03 21:46:35 2014 +0200
@@ -64,7 +64,9 @@
         HotSpotReplacementsImpl replacements = new HotSpotReplacementsImpl(p, snippetReflection, runtime.getConfig(), assumptions, target);
         HotSpotDisassemblerProvider disassembler = new HotSpotDisassemblerProvider(runtime);
         HotSpotSuitesProvider suites = new HotSpotSuitesProvider(runtime);
-        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection);
+        HotSpotMethodHandleAccessProvider methodHandleAccess = new HotSpotMethodHandleAccessProvider();
+        HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection,
+                        methodHandleAccess);
 
         return new SPARCHotSpotBackend(runtime, providers);
     }
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java	Sat May 03 21:46:35 2014 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.hotspot.test;
 
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
-
 import java.lang.ref.*;
 import java.lang.reflect.*;
 
@@ -44,6 +43,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.runtime.*;
 
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierVerificationTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierVerificationTest.java	Sat May 03 21:46:35 2014 +0200
@@ -40,6 +40,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.graph.ReentrantNodeIterator.NodeIteratorClosure;
 import com.oracle.graal.phases.tiers.*;
@@ -693,7 +694,7 @@
             DebugConfig debugConfig = DebugScope.getConfig();
             DebugConfig fixedConfig = Debug.fixedConfig(false, false, false, false, debugConfig.dumpHandlers(), debugConfig.output());
             try (DebugConfigScope s = Debug.setConfig(fixedConfig)) {
-                ReentrantNodeIterator.apply(closure, graph.start(), false, null);
+                ReentrantNodeIterator.apply(closure, graph.start(), false);
                 new WriteBarrierVerificationPhase().apply(graph);
             } catch (AssertionError error) {
                 /*
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Sat May 03 21:46:35 2014 +0200
@@ -29,7 +29,7 @@
 import static com.oracle.graal.compiler.common.UnsafeAccess.*;
 import static com.oracle.graal.hotspot.bridge.VMToCompilerImpl.*;
 import static com.oracle.graal.nodes.StructuredGraph.*;
-import static com.oracle.graal.phases.common.InliningUtil.*;
+import static com.oracle.graal.phases.common.inlining.InliningUtil.*;
 
 import java.io.*;
 import java.lang.management.*;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java	Sat May 03 21:46:35 2014 +0200
@@ -368,6 +368,8 @@
             return (T) this;
         } else if (clazz == SnippetReflectionProvider.class) {
             return (T) getHostProviders().getSnippetReflection();
+        } else if (clazz == MethodHandleAccessProvider.class) {
+            return (T) getHostProviders().getMethodHandleAccess();
         }
         return null;
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java	Sat May 03 21:46:35 2014 +0200
@@ -37,7 +37,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.hotspot.logging.*;
 import com.oracle.graal.options.*;
-import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 
 /**
  * Called from {@code graalCompiler.cpp} to parse any Graal specific options. Such options are
@@ -113,7 +113,7 @@
 
     /**
      * Parses a given option value specification.
-     * 
+     *
      * @param option the specification of an option and its value
      * @param setter the object to notify of the parsed option and value. If null, the
      *            {@link OptionValue#setValue(Object)} method of the specified option is called
@@ -213,7 +213,7 @@
      * <p>
      * This method verifies that the named field exists and is of an expected type. However, it does
      * not verify that the timer or metric created has the same name of the field.
-     * 
+     *
      * @param c the class in which the field is declared
      * @param name the name of the field
      */
@@ -239,7 +239,7 @@
     /**
      * Called from VM code once all Graal command line options have been processed by
      * {@link #setOption(String)}.
-     * 
+     *
      * @param timeCompilations true if the CITime or CITimeEach HotSpot VM options are set
      */
     public static void finalizeOptions(boolean timeCompilations) {
@@ -255,7 +255,7 @@
 
     /**
      * Wraps some given text to one or more lines of a given maximum width.
-     * 
+     *
      * @param text text to wrap
      * @param width maximum width of an output line, exception for words in {@code text} longer than
      *            this value
@@ -314,7 +314,7 @@
 
     /**
      * Compute string similarity based on Dice's coefficient.
-     * 
+     *
      * Ported from str_similar() in globals.cpp.
      */
     static float stringSimiliarity(String str1, String str2) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Sat May 03 21:46:35 2014 +0200
@@ -86,16 +86,14 @@
         HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method;
         int intrinsicId = hsMethod.intrinsicId();
         if (intrinsicId != 0) {
-            if (intrinsicId == config.vmIntrinsicInvokeBasic) {
-                return MethodHandleInvokeBasicNode.class;
-            } else if (intrinsicId == config.vmIntrinsicLinkToInterface) {
-                return MethodHandleLinkToInterfaceNode.class;
-            } else if (intrinsicId == config.vmIntrinsicLinkToSpecial) {
-                return MethodHandleLinkToSpecialNode.class;
-            } else if (intrinsicId == config.vmIntrinsicLinkToStatic) {
-                return MethodHandleLinkToStaticNode.class;
-            } else if (intrinsicId == config.vmIntrinsicLinkToVirtual) {
-                return MethodHandleLinkToVirtualNode.class;
+            /*
+             * The methods of MethodHandle that need substitution are signature-polymorphic, i.e.,
+             * the VM replicates them for every signature that they are actually used for.
+             * Therefore, we cannot use the usual annotation-driven mechanism to define the
+             * substitution.
+             */
+            if (MethodHandleNode.lookupMethodHandleIntrinsic(method) != null) {
+                return MethodHandleNode.class;
             }
         }
         return super.getMacroSubstitution(method);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Sat May 03 21:46:35 2014 +0200
@@ -1022,8 +1022,6 @@
      * exceptions from Hsail back to C++ runtime.
      */
     @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_notice_safepoints", type = "jint*", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailNoticeSafepointsOffset;
-    @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_save_states[0]", type = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailSaveStatesOffset0;
-    @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_save_states[1]", type = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailSaveStatesOffset1;
     @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_occurred", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailDeoptOccurredOffset;
     @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_never_ran_array", type = "jboolean *", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailNeverRanArrayOffset;
     @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_next_index", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailDeoptNextIndexOffset;
@@ -1035,7 +1033,10 @@
 
     @HotSpotVMField(name = "HSAILFrame::_pc_offset", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFramePcOffset;
     @HotSpotVMField(name = "HSAILFrame::_num_s_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumSRegOffset;
-    @HotSpotVMField(name = "HSAILFrame::_save_area[0]", type = "jlong", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameSaveAreaOffset;
+    @HotSpotVMField(name = "HSAILFrame::_num_d_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumDRegOffset;
+    @HotSpotVMConstant(name = "sizeof(HSAILFrame)") @Stable public int hsailFrameSaveAreaOffset;
+    @HotSpotVMConstant(name = "sizeof(Hsail::HSAILKernelDeoptimization)") @Stable public int hsailKernelDeoptimizationHeaderSize;
+    @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_save_states[0]", type = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailSaveStatesOffset0;
 
     /**
      * Mark word right shift to get identity hash code.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodHandleAccessProvider.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2014, 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.hotspot.meta;
+
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.hotspot.*;
+
+public class HotSpotMethodHandleAccessProvider implements MethodHandleAccessProvider {
+
+    /**
+     * Lazy initialization to break class initialization cycle. Field and method lookup is only
+     * possible after the {@link HotSpotGraalRuntime} is fully initialized.
+     */
+    static class LazyInitialization {
+        static final ResolvedJavaField methodHandleFormField;
+        static final ResolvedJavaField lambdaFormVmentryField;
+        static final ResolvedJavaMethod lambdaFormCompileToBytecodeMethod;
+        static final ResolvedJavaField memberNameVmtargetField;
+
+        /**
+         * Search for an instance field with the given name in a class.
+         *
+         * @param className name of the class to search in
+         * @param fieldName name of the field to be searched
+         * @return resolved java field
+         * @throws ClassNotFoundException
+         */
+        private static ResolvedJavaField findFieldInClass(String className, String fieldName) throws ClassNotFoundException {
+            Class<?> clazz = Class.forName(className);
+            ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz);
+            ResolvedJavaField[] fields = type.getInstanceFields(false);
+            for (ResolvedJavaField field : fields) {
+                if (field.getName().equals(fieldName)) {
+                    return field;
+                }
+            }
+            return null;
+        }
+
+        private static ResolvedJavaMethod findMethodInClass(String className, String methodName) throws ClassNotFoundException {
+            Class<?> clazz = Class.forName(className);
+            ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz);
+            ResolvedJavaMethod result = null;
+            for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
+                if (method.getName().equals(methodName)) {
+                    assert result == null : "more than one method found: " + className + "." + methodName;
+                    result = method;
+                }
+            }
+            assert result != null : "method not found: " + className + "." + methodName;
+            return result;
+        }
+
+        static {
+            try {
+                methodHandleFormField = findFieldInClass("java.lang.invoke.MethodHandle", "form");
+                lambdaFormVmentryField = findFieldInClass("java.lang.invoke.LambdaForm", "vmentry");
+                lambdaFormCompileToBytecodeMethod = findMethodInClass("java.lang.invoke.LambdaForm", "compileToBytecode");
+                memberNameVmtargetField = findFieldInClass("java.lang.invoke.MemberName", "vmtarget");
+            } catch (Throwable ex) {
+                throw GraalInternalError.shouldNotReachHere();
+            }
+        }
+    }
+
+    @Override
+    public IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method) {
+        int intrinsicId = ((HotSpotResolvedJavaMethod) method).intrinsicId();
+        if (intrinsicId != 0) {
+            HotSpotVMConfig config = runtime().getConfig();
+            if (intrinsicId == config.vmIntrinsicInvokeBasic) {
+                return IntrinsicMethod.INVOKE_BASIC;
+            } else if (intrinsicId == config.vmIntrinsicLinkToInterface) {
+                return IntrinsicMethod.LINK_TO_INTERFACE;
+            } else if (intrinsicId == config.vmIntrinsicLinkToSpecial) {
+                return IntrinsicMethod.LINK_TO_SPECIAL;
+            } else if (intrinsicId == config.vmIntrinsicLinkToStatic) {
+                return IntrinsicMethod.LINK_TO_STATIC;
+            } else if (intrinsicId == config.vmIntrinsicLinkToVirtual) {
+                return IntrinsicMethod.LINK_TO_VIRTUAL;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public ResolvedJavaMethod resolveInvokeBasicTarget(Constant methodHandle, boolean forceBytecodeGeneration) {
+        if (methodHandle.isNull()) {
+            return null;
+        }
+
+        /* Load non-public field: LambdaForm MethodHandle.form */
+        Constant lambdaForm = LazyInitialization.methodHandleFormField.readValue(methodHandle);
+        if (lambdaForm.isNull()) {
+            return null;
+        }
+
+        Constant memberName;
+        if (forceBytecodeGeneration) {
+            /* Invoke non-public method: MemberName LambdaForm.compileToBytecode() */
+            memberName = LazyInitialization.lambdaFormCompileToBytecodeMethod.invoke(lambdaForm, new Constant[0]);
+        } else {
+            /* Load non-public field: MemberName LambdaForm.vmentry */
+            memberName = LazyInitialization.lambdaFormVmentryField.readValue(lambdaForm);
+        }
+        return getTargetMethod(memberName);
+    }
+
+    @Override
+    public ResolvedJavaMethod resolveLinkToTarget(Constant memberName) {
+        return getTargetMethod(memberName);
+    }
+
+    /**
+     * Returns the {@link ResolvedJavaMethod} for the vmtarget of a java.lang.invoke.MemberName.
+     */
+    private static ResolvedJavaMethod getTargetMethod(Constant memberName) {
+        if (memberName.isNull()) {
+            return null;
+        }
+
+        /* Load injected field: JVM_Method* MemberName.vmtarget */
+        Constant vmtarget = LazyInitialization.memberNameVmtargetField.readValue(memberName);
+        /* Create a method from the vmtarget method pointer. */
+        return HotSpotResolvedJavaMethod.fromMetaspace(vmtarget.asLong());
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotNmethod.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotNmethod.java	Sat May 03 21:46:35 2014 +0200
@@ -36,7 +36,7 @@
  * instance to be anything but weak. This is due to the fact that HotSpot does not treat nmethods as
  * strong GC roots.
  */
-public final class HotSpotNmethod extends HotSpotInstalledCode {
+public class HotSpotNmethod extends HotSpotInstalledCode {
 
     /**
      * This (indirect) Method* reference is safe since class redefinition preserves all methods
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java	Sat May 03 21:46:35 2014 +0200
@@ -38,15 +38,17 @@
     private final SuitesProvider suites;
     private final HotSpotRegistersProvider registers;
     private final SnippetReflectionProvider snippetReflection;
+    private final HotSpotMethodHandleAccessProvider methodHandleAccess;
 
     public HotSpotProviders(HotSpotMetaAccessProvider metaAccess, HotSpotCodeCacheProvider codeCache, ConstantReflectionProvider constantReflection, HotSpotForeignCallsProvider foreignCalls,
                     LoweringProvider lowerer, Replacements replacements, HotSpotDisassemblerProvider disassembler, SuitesProvider suites, HotSpotRegistersProvider registers,
-                    SnippetReflectionProvider snippetReflection) {
+                    SnippetReflectionProvider snippetReflection, HotSpotMethodHandleAccessProvider methodHandleAccess) {
         super(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements);
         this.disassembler = disassembler;
         this.suites = suites;
         this.registers = registers;
         this.snippetReflection = snippetReflection;
+        this.methodHandleAccess = methodHandleAccess;
     }
 
     @Override
@@ -79,4 +81,8 @@
     public SnippetReflectionProvider getSnippetReflection() {
         return snippetReflection;
     }
+
+    public HotSpotMethodHandleAccessProvider getMethodHandleAccess() {
+        return methodHandleAccess;
+    }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/AheadOfTimeVerificationPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/AheadOfTimeVerificationPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -46,7 +46,7 @@
     protected boolean verify(StructuredGraph graph, PhaseContext context) {
         for (ConstantNode node : getConstantNodes(graph)) {
             if (node.recordsUsages() || !node.gatherUsages(graph).isEmpty()) {
-                if (isObject(node) && !isNullReference(node) && !isInternedString(node) && !isDirectMethodHandle(node)) {
+                if (isObject(node) && !isNullReference(node) && !isInternedString(node) && !isDirectMethodHandle(node) && !isBoundMethodHandle(node)) {
                     throw new VerificationError("illegal object constant: " + node);
                 }
             }
@@ -69,6 +69,13 @@
         return "Ljava/lang/invoke/DirectMethodHandle;".equals(StampTool.typeOrNull(node).getName());
     }
 
+    private static boolean isBoundMethodHandle(ConstantNode node) {
+        if (!isObject(node)) {
+            return false;
+        }
+        return StampTool.typeOrNull(node).getName().startsWith("Ljava/lang/invoke/BoundMethodHandle");
+    }
+
     @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "reference equality is what we want")
     private static boolean isInternedString(ConstantNode node) {
         if (!isObject(node)) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.replacements;
-
-import java.util.*;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.replacements.nodes.*;
-
-/**
- * Common base class for method handle invoke nodes.
- */
-public abstract class AbstractMethodHandleNode extends MacroNode implements Canonicalizable {
-
-    private static final ResolvedJavaField methodHandleFormField;
-    private static final ResolvedJavaField lambdaFormVmentryField;
-    private static final ResolvedJavaField memberNameClazzField;
-    private static final ResolvedJavaField memberNameVmtargetField;
-
-    // Replacement method data
-    private ResolvedJavaMethod replacementTargetMethod;
-    private JavaType replacementReturnType;
-    @Input private final NodeInputList<ValueNode> replacementArguments;
-
-    /**
-     * Search for an instance field with the given name in a class.
-     *
-     * @param className name of the class to search in
-     * @param fieldName name of the field to be searched
-     * @return resolved java field
-     * @throws ClassNotFoundException
-     */
-    private static ResolvedJavaField findFieldInClass(String className, String fieldName) throws ClassNotFoundException {
-        Class<?> clazz = Class.forName(className);
-        ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz);
-        ResolvedJavaField[] fields = type.getInstanceFields(false);
-        for (ResolvedJavaField field : fields) {
-            if (field.getName().equals(fieldName)) {
-                return field;
-            }
-        }
-        return null;
-    }
-
-    static {
-        try {
-            methodHandleFormField = findFieldInClass("java.lang.invoke.MethodHandle", "form");
-            lambdaFormVmentryField = findFieldInClass("java.lang.invoke.LambdaForm", "vmentry");
-            memberNameClazzField = findFieldInClass("java.lang.invoke.MemberName", "clazz");
-            memberNameVmtargetField = findFieldInClass("java.lang.invoke.MemberName", "vmtarget");
-        } catch (ClassNotFoundException | SecurityException ex) {
-            throw GraalInternalError.shouldNotReachHere();
-        }
-    }
-
-    public AbstractMethodHandleNode(Invoke invoke) {
-        super(invoke);
-
-        // See if we need to save some replacement method data.
-        CallTargetNode callTarget = invoke.callTarget();
-        if (callTarget instanceof SelfReplacingMethodCallTargetNode) {
-            SelfReplacingMethodCallTargetNode selfReplacingMethodCallTargetNode = (SelfReplacingMethodCallTargetNode) callTarget;
-            replacementTargetMethod = selfReplacingMethodCallTargetNode.replacementTargetMethod();
-            replacementReturnType = selfReplacingMethodCallTargetNode.replacementReturnType();
-            replacementArguments = selfReplacingMethodCallTargetNode.replacementArguments();
-        } else {
-            // NodeInputList can't be null.
-            replacementArguments = new NodeInputList<>(this);
-        }
-    }
-
-    /**
-     * Get the receiver of a MethodHandle.invokeBasic call.
-     *
-     * @return the receiver argument node
-     */
-    private ValueNode getReceiver() {
-        return arguments.first();
-    }
-
-    /**
-     * Get the MemberName argument of a MethodHandle.linkTo* call.
-     *
-     * @return the MemberName argument node (which is the last argument)
-     */
-    private ValueNode getMemberName() {
-        return arguments.last();
-    }
-
-    /**
-     * Used from {@link MethodHandleInvokeBasicNode} to get the target {@link InvokeNode} if the
-     * method handle receiver is constant.
-     *
-     * @return invoke node for the {@link java.lang.invoke.MethodHandle} target
-     */
-    protected InvokeNode getInvokeBasicTarget() {
-        ValueNode methodHandleNode = getReceiver();
-        if (methodHandleNode.isConstant() && !methodHandleNode.isNullConstant()) {
-            // Get the data we need from MethodHandle.LambdaForm.MemberName
-            Constant methodHandle = methodHandleNode.asConstant();
-            Constant lambdaForm = methodHandleFormField.readValue(methodHandle);
-            Constant memberName = lambdaFormVmentryField.readValue(lambdaForm);
-            return getTargetInvokeNode(memberName);
-        }
-        return null;
-    }
-
-    /**
-     * Used from {@link MethodHandleLinkToStaticNode}, {@link MethodHandleLinkToSpecialNode},
-     * {@link MethodHandleLinkToVirtualNode}, and {@link MethodHandleLinkToInterfaceNode} to get the
-     * target {@link InvokeNode} if the member name argument is constant.
-     *
-     * @return invoke node for the member name target
-     */
-    protected InvokeNode getLinkToTarget() {
-        ValueNode memberNameNode = getMemberName();
-        if (memberNameNode.isConstant() && !memberNameNode.isNullConstant()) {
-            Constant memberName = memberNameNode.asConstant();
-            return getTargetInvokeNode(memberName);
-        }
-        return null;
-    }
-
-    /**
-     * Helper function to get the {@link InvokeNode} for the vmtarget of a
-     * java.lang.invoke.MemberName.
-     *
-     * @param memberName constant member name node
-     * @return invoke node for the member name target
-     */
-    private InvokeNode getTargetInvokeNode(Constant memberName) {
-        // Get the data we need from MemberName
-        Constant clazz = memberNameClazzField.readValue(memberName);
-        Constant vmtarget = memberNameVmtargetField.readValue(memberName);
-
-        // Create a method from the vmtarget pointer
-        Class<?> c = (Class<?>) HotSpotObjectConstant.asObject(clazz);
-        HotSpotResolvedObjectType holderClass = (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromClass(c);
-        HotSpotResolvedJavaMethod targetMethod = HotSpotResolvedJavaMethod.fromMetaspace(vmtarget.asLong());
-
-        // In lambda forms we erase signature types to avoid resolving issues
-        // involving class loaders. When we optimize a method handle invoke
-        // to a direct call we must cast the receiver and arguments to its
-        // actual types.
-        HotSpotSignature signature = targetMethod.getSignature();
-        final boolean isStatic = targetMethod.isStatic();
-        final int receiverSkip = isStatic ? 0 : 1;
-
-        // Cast receiver to its type.
-        if (!isStatic) {
-            JavaType receiverType = holderClass;
-            maybeCastArgument(0, receiverType);
-        }
-
-        // Cast reference arguments to its type.
-        for (int index = 0; index < signature.getParameterCount(false); index++) {
-            JavaType parameterType = signature.getParameterType(index, holderClass);
-            maybeCastArgument(receiverSkip + index, parameterType);
-        }
-
-        // Try to get the most accurate receiver type
-        if (this instanceof MethodHandleLinkToVirtualNode || this instanceof MethodHandleLinkToInterfaceNode) {
-            ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver().stamp());
-            if (receiverType != null) {
-                ResolvedJavaMethod concreteMethod = receiverType.findUniqueConcreteMethod(targetMethod);
-                if (concreteMethod != null) {
-                    return createTargetInvokeNode(concreteMethod);
-                }
-            }
-        }
-
-        if (targetMethod.canBeStaticallyBound()) {
-            return createTargetInvokeNode(targetMethod);
-        }
-
-        ResolvedJavaMethod concreteMethod = targetMethod.uniqueConcreteMethod();
-        if (concreteMethod != null) {
-            return createTargetInvokeNode(concreteMethod);
-        }
-
-        return null;
-    }
-
-    /**
-     * Inserts a node to cast the argument at index to the given type if the given type is more
-     * concrete than the argument type.
-     *
-     * @param index of the argument to be cast
-     * @param type the type the argument should be cast to
-     */
-    private void maybeCastArgument(int index, JavaType type) {
-        if (type instanceof ResolvedJavaType) {
-            ResolvedJavaType targetType = (ResolvedJavaType) type;
-            if (!targetType.isPrimitive()) {
-                ValueNode argument = arguments.get(index);
-                ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp());
-                if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) {
-                    PiNode piNode = graph().unique(new PiNode(argument, StampFactory.declared(targetType)));
-                    arguments.set(index, piNode);
-                }
-            }
-        }
-    }
-
-    /**
-     * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed
-     * to the InvokeNode is in fact a {@link SelfReplacingMethodCallTargetNode}.
-     *
-     * @param targetMethod the method the be called
-     * @return invoke node for the member name target
-     */
-    private InvokeNode createTargetInvokeNode(ResolvedJavaMethod targetMethod) {
-        InvokeKind invokeKind = targetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special;
-        JavaType returnType = targetMethod.getSignature().getReturnType(null);
-
-        // MethodHandleLinkTo* nodes have a trailing MemberName argument which
-        // needs to be popped.
-        ValueNode[] originalArguments = arguments.toArray(new ValueNode[arguments.size()]);
-        ValueNode[] targetArguments;
-        if (this instanceof MethodHandleInvokeBasicNode) {
-            targetArguments = originalArguments;
-        } else {
-            assert this instanceof MethodHandleLinkToStaticNode || this instanceof MethodHandleLinkToSpecialNode || this instanceof MethodHandleLinkToVirtualNode ||
-                            this instanceof MethodHandleLinkToInterfaceNode : this;
-            targetArguments = Arrays.copyOfRange(originalArguments, 0, arguments.size() - 1);
-        }
-
-        // If there is already replacement information, use that instead.
-        MethodCallTargetNode callTarget;
-        if (replacementTargetMethod == null) {
-            callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, getTargetMethod(), originalArguments, getReturnType());
-        } else {
-            ValueNode[] args = replacementArguments.toArray(new ValueNode[replacementArguments.size()]);
-            callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, replacementTargetMethod, args, replacementReturnType);
-        }
-        graph().add(callTarget);
-
-        // The call target can have a different return type than the invoker,
-        // e.g. the target returns an Object but the invoker void. In this case
-        // we need to use the stamp of the invoker. Note: always using the
-        // invoker's stamp would be wrong because it's a less concrete type
-        // (usually java.lang.Object).
-        InvokeNode invoke;
-        if (stamp() == StampFactory.forVoid()) {
-            invoke = new InvokeNode(callTarget, getBci(), stamp());
-        } else {
-            invoke = new InvokeNode(callTarget, getBci());
-        }
-        graph().add(invoke);
-        invoke.setStateAfter(stateAfter());
-        return invoke;
-    }
-
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.replacements;
-
-import java.lang.invoke.*;
-
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.nodes.*;
-
-/**
- * Macro node for {@link MethodHandle}{@code .invokeBasic(Object...)}.
- */
-public class MethodHandleInvokeBasicNode extends AbstractMethodHandleNode {
-
-    public MethodHandleInvokeBasicNode(Invoke invoke) {
-        super(invoke);
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        InvokeNode invoke = getInvokeBasicTarget();
-        if (invoke != null) {
-            return invoke;
-        }
-        return this;
-    }
-
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.replacements;
-
-import java.lang.invoke.*;
-
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.nodes.*;
-
-/**
- * Macro node for {@link MethodHandle}{@code .linkToInterface(Object...)}.
- */
-public class MethodHandleLinkToInterfaceNode extends AbstractMethodHandleNode {
-
-    public MethodHandleLinkToInterfaceNode(Invoke invoke) {
-        super(invoke);
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        InvokeNode invoke = getLinkToTarget();
-        if (invoke != null) {
-            return invoke;
-        }
-        return this;
-    }
-
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.replacements;
-
-import java.lang.invoke.*;
-
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.nodes.*;
-
-/**
- * Macro node for {@link MethodHandle}{@code .linkToSpecial(Object...)}.
- */
-public class MethodHandleLinkToSpecialNode extends AbstractMethodHandleNode {
-
-    public MethodHandleLinkToSpecialNode(Invoke invoke) {
-        super(invoke);
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        InvokeNode invoke = getLinkToTarget();
-        if (invoke != null) {
-            return invoke;
-        }
-        return this;
-    }
-
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.replacements;
-
-import java.lang.invoke.*;
-
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.nodes.*;
-
-/**
- * Macro node for {@link MethodHandle}{@code .linkToStatic(Object...)}.
- */
-public class MethodHandleLinkToStaticNode extends AbstractMethodHandleNode {
-
-    public MethodHandleLinkToStaticNode(Invoke invoke) {
-        super(invoke);
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        InvokeNode invoke = getLinkToTarget();
-        if (invoke != null) {
-            return invoke;
-        }
-        return this;
-    }
-
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.replacements;
-
-import java.lang.invoke.*;
-
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.nodes.*;
-
-/**
- * Macro node for {@link MethodHandle}{@code .linkToVirtual(Object...)}.
- */
-public class MethodHandleLinkToVirtualNode extends AbstractMethodHandleNode {
-
-    public MethodHandleLinkToVirtualNode(Invoke invoke) {
-        super(invoke);
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        InvokeNode invoke = getLinkToTarget();
-        if (invoke != null) {
-            return invoke;
-        }
-        return this;
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hotspot.replacements;
+
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+
+import java.lang.invoke.*;
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.api.replacements.MethodHandleAccessProvider.IntrinsicMethod;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.replacements.nodes.*;
+
+/**
+ * Node for invocation methods defined on the class {@link MethodHandle}.
+ */
+public class MethodHandleNode extends MacroNode implements Canonicalizable {
+
+    /** The method that this node is representing. */
+    private final IntrinsicMethod intrinsicMethod;
+
+    // Replacement method data
+    private ResolvedJavaMethod replacementTargetMethod;
+    private JavaType replacementReturnType;
+    @Input private final NodeInputList<ValueNode> replacementArguments;
+
+    public MethodHandleNode(Invoke invoke) {
+        super(invoke);
+
+        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+        intrinsicMethod = lookupMethodHandleIntrinsic(callTarget.targetMethod());
+        assert intrinsicMethod != null;
+
+        // See if we need to save some replacement method data.
+        if (callTarget instanceof SelfReplacingMethodCallTargetNode) {
+            SelfReplacingMethodCallTargetNode selfReplacingMethodCallTargetNode = (SelfReplacingMethodCallTargetNode) callTarget;
+            replacementTargetMethod = selfReplacingMethodCallTargetNode.replacementTargetMethod();
+            replacementReturnType = selfReplacingMethodCallTargetNode.replacementReturnType();
+            replacementArguments = selfReplacingMethodCallTargetNode.replacementArguments();
+        } else {
+            // NodeInputList can't be null.
+            replacementArguments = new NodeInputList<>(this);
+        }
+    }
+
+    /**
+     * Returns the method handle method intrinsic identifier for the provided method, or
+     * {@code null} if the method is not a method that can be handled by this class.
+     */
+    public static IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method) {
+        return methodHandleAccess().lookupMethodHandleIntrinsic(method);
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        InvokeNode invoke;
+        switch (intrinsicMethod) {
+            case INVOKE_BASIC:
+                invoke = getInvokeBasicTarget();
+                break;
+            case LINK_TO_STATIC:
+            case LINK_TO_SPECIAL:
+            case LINK_TO_VIRTUAL:
+            case LINK_TO_INTERFACE:
+                invoke = getLinkToTarget();
+                break;
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+        if (invoke != null) {
+            return invoke;
+        }
+        return this;
+    }
+
+    /**
+     * Get the receiver of a MethodHandle.invokeBasic call.
+     *
+     * @return the receiver argument node
+     */
+    private ValueNode getReceiver() {
+        return arguments.first();
+    }
+
+    /**
+     * Get the MemberName argument of a MethodHandle.linkTo* call.
+     *
+     * @return the MemberName argument node (which is the last argument)
+     */
+    private ValueNode getMemberName() {
+        return arguments.last();
+    }
+
+    /**
+     * Returns the {@link MethodHandleAccessProvider} that provides introspection of internal
+     * {@link MethodHandle} data.
+     */
+    private static MethodHandleAccessProvider methodHandleAccess() {
+        return runtime().getHostProviders().getMethodHandleAccess();
+    }
+
+    /**
+     * Used for the MethodHandle.invokeBasic method (the {@link IntrinsicMethod#INVOKE_BASIC }
+     * method) to get the target {@link InvokeNode} if the method handle receiver is constant.
+     *
+     * @return invoke node for the {@link java.lang.invoke.MethodHandle} target
+     */
+    protected InvokeNode getInvokeBasicTarget() {
+        ValueNode methodHandleNode = getReceiver();
+        if (methodHandleNode.isConstant()) {
+            return getTargetInvokeNode(methodHandleAccess().resolveInvokeBasicTarget(methodHandleNode.asConstant(), false));
+        }
+        return null;
+    }
+
+    /**
+     * Used for the MethodHandle.linkTo* methods (the {@link IntrinsicMethod#LINK_TO_STATIC},
+     * {@link IntrinsicMethod#LINK_TO_SPECIAL}, {@link IntrinsicMethod#LINK_TO_VIRTUAL}, and
+     * {@link IntrinsicMethod#LINK_TO_INTERFACE} methods) to get the target {@link InvokeNode} if
+     * the member name argument is constant.
+     *
+     * @return invoke node for the member name target
+     */
+    protected InvokeNode getLinkToTarget() {
+        ValueNode memberNameNode = getMemberName();
+        if (memberNameNode.isConstant()) {
+            return getTargetInvokeNode(methodHandleAccess().resolveLinkToTarget(memberNameNode.asConstant()));
+        }
+        return null;
+    }
+
+    /**
+     * Helper function to get the {@link InvokeNode} for the targetMethod of a
+     * java.lang.invoke.MemberName.
+     *
+     * @param targetMethod the target, already loaded from the member name node
+     * @return invoke node for the member name target
+     */
+    private InvokeNode getTargetInvokeNode(ResolvedJavaMethod targetMethod) {
+        if (targetMethod == null) {
+            return null;
+        }
+
+        // In lambda forms we erase signature types to avoid resolving issues
+        // involving class loaders. When we optimize a method handle invoke
+        // to a direct call we must cast the receiver and arguments to its
+        // actual types.
+        Signature signature = targetMethod.getSignature();
+        final boolean isStatic = targetMethod.isStatic();
+        final int receiverSkip = isStatic ? 0 : 1;
+
+        // Cast receiver to its type.
+        if (!isStatic) {
+            JavaType receiverType = targetMethod.getDeclaringClass();
+            maybeCastArgument(0, receiverType);
+        }
+
+        // Cast reference arguments to its type.
+        for (int index = 0; index < signature.getParameterCount(false); index++) {
+            JavaType parameterType = signature.getParameterType(index, targetMethod.getDeclaringClass());
+            maybeCastArgument(receiverSkip + index, parameterType);
+        }
+
+        // Try to get the most accurate receiver type
+        if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) {
+            ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver().stamp());
+            if (receiverType != null) {
+                ResolvedJavaMethod concreteMethod = receiverType.findUniqueConcreteMethod(targetMethod);
+                if (concreteMethod != null) {
+                    return createTargetInvokeNode(concreteMethod);
+                }
+            }
+        }
+
+        if (targetMethod.canBeStaticallyBound()) {
+            return createTargetInvokeNode(targetMethod);
+        }
+
+        ResolvedJavaMethod concreteMethod = targetMethod.getDeclaringClass().findUniqueConcreteMethod(targetMethod);
+        if (concreteMethod != null) {
+            return createTargetInvokeNode(concreteMethod);
+        }
+
+        return null;
+    }
+
+    /**
+     * Inserts a node to cast the argument at index to the given type if the given type is more
+     * concrete than the argument type.
+     *
+     * @param index of the argument to be cast
+     * @param type the type the argument should be cast to
+     */
+    private void maybeCastArgument(int index, JavaType type) {
+        if (type instanceof ResolvedJavaType) {
+            ResolvedJavaType targetType = (ResolvedJavaType) type;
+            if (!targetType.isPrimitive()) {
+                ValueNode argument = arguments.get(index);
+                ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp());
+                if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) {
+                    PiNode piNode = graph().unique(new PiNode(argument, StampFactory.declared(targetType)));
+                    arguments.set(index, piNode);
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed
+     * to the InvokeNode is in fact a {@link SelfReplacingMethodCallTargetNode}.
+     *
+     * @param targetMethod the method the be called
+     * @return invoke node for the member name target
+     */
+    private InvokeNode createTargetInvokeNode(ResolvedJavaMethod targetMethod) {
+        InvokeKind invokeKind = targetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special;
+        JavaType returnType = targetMethod.getSignature().getReturnType(null);
+
+        // MethodHandleLinkTo* nodes have a trailing MemberName argument which
+        // needs to be popped.
+        ValueNode[] originalArguments = arguments.toArray(new ValueNode[arguments.size()]);
+        ValueNode[] targetArguments;
+        switch (intrinsicMethod) {
+            case INVOKE_BASIC:
+                targetArguments = originalArguments;
+                break;
+            case LINK_TO_STATIC:
+            case LINK_TO_SPECIAL:
+            case LINK_TO_VIRTUAL:
+            case LINK_TO_INTERFACE:
+                targetArguments = Arrays.copyOfRange(originalArguments, 0, arguments.size() - 1);
+                break;
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+
+        // If there is already replacement information, use that instead.
+        MethodCallTargetNode callTarget;
+        if (replacementTargetMethod == null) {
+            callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, getTargetMethod(), originalArguments, getReturnType());
+        } else {
+            ValueNode[] args = replacementArguments.toArray(new ValueNode[replacementArguments.size()]);
+            callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, replacementTargetMethod, args, replacementReturnType);
+        }
+        graph().add(callTarget);
+
+        // The call target can have a different return type than the invoker,
+        // e.g. the target returns an Object but the invoker void. In this case
+        // we need to use the stamp of the invoker. Note: always using the
+        // invoker's stamp would be wrong because it's a less concrete type
+        // (usually java.lang.Object).
+        InvokeNode invoke;
+        if (stamp() == StampFactory.forVoid()) {
+            invoke = new InvokeNode(callTarget, getBci(), stamp());
+        } else {
+            invoke = new InvokeNode(callTarget, getBci());
+        }
+        graph().add(invoke);
+        invoke.setStateAfter(stateAfter());
+        return invoke;
+    }
+
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Sat May 03 21:46:35 2014 +0200
@@ -47,7 +47,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.options.*;
-import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.Snippet.ConstantParameter;
 import com.oracle.graal.replacements.Snippet.Fold;
--- a/graal/com.oracle.graal.hsail/src/com/oracle/graal/hsail/HSAIL.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.hsail/src/com/oracle/graal/hsail/HSAIL.java	Sat May 03 21:46:35 2014 +0200
@@ -136,7 +136,6 @@
     public static final Register threadRegister = d20;
     public static final Register actionAndReasonReg = s32;
     public static final Register codeBufferOffsetReg = s33;
-    public static final Register dregOopMapReg = s39;
 
     // @formatter:off
     public static final Register[] cRegisters = {
@@ -178,10 +177,16 @@
         return -(((StackSlot) reg).getRawOffset());
     }
 
-    public static String mapStackSlot(Value reg) {
-        StackSlot s = (StackSlot) reg;
-        long offset = -s.getRawOffset();
-        return "[%spillseg]" + "[" + offset + "]";
+    /**
+     * The mapping to stack slots is always relative to the beginning of the spillseg.
+     * {@link #getStackOffset(Value)} returns the positive version of the originally negative
+     * offset. Then we back up from that by {@code argSize} in bytes. This ensures that slots of
+     * different size do not overlap, even though we have converted from negative to positive
+     * offsets.
+     */
+    public static int getStackOffsetStart(Value reg, int argSize) {
+        int argSizeInBytes = argSize / 8;
+        return getStackOffset(reg) - argSizeInBytes;
     }
 
     public static String mapRegister(Value arg) {
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Sat May 03 21:46:35 2014 +0200
@@ -177,6 +177,10 @@
         public BciBlock getPredecessor(int index) {
             return predecessors.get(index);
         }
+
+        public double probability() {
+            return 1D;
+        }
     }
 
     public static class ExceptionDispatchBlock extends BciBlock {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/ComputeLoopFrequenciesClosure.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011, 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.java;
+
+import java.util.*;
+import java.util.stream.*;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.phases.graph.*;
+
+public class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.NodeIteratorClosure<Double> {
+
+    private static final double EPSILON = Double.MIN_NORMAL;
+    private static final ComputeLoopFrequenciesClosure INSTANCE = new ComputeLoopFrequenciesClosure();
+
+    private ComputeLoopFrequenciesClosure() {
+        // nothing to do
+    }
+
+    @Override
+    protected Double processNode(FixedNode node, Double currentState) {
+        // normal nodes never change the probability of a path
+        return currentState;
+    }
+
+    @Override
+    protected Double merge(MergeNode merge, List<Double> states) {
+        // a merge has the sum of all predecessor probabilities
+        return states.stream().collect(Collectors.summingDouble(d -> d));
+    }
+
+    @Override
+    protected Double afterSplit(BeginNode node, Double oldState) {
+        // a control split splits up the probability
+        ControlSplitNode split = (ControlSplitNode) node.predecessor();
+        return oldState * split.probability(node);
+    }
+
+    @Override
+    protected Map<LoopExitNode, Double> processLoop(LoopBeginNode loop, Double initialState) {
+        Map<LoopExitNode, Double> exitStates = ReentrantNodeIterator.processLoop(this, loop, 1D).exitStates;
+
+        double exitProbability = exitStates.values().stream().mapToDouble(d -> d).sum();
+        assert exitProbability <= 1D && exitProbability >= 0D;
+        if (exitProbability < EPSILON) {
+            exitProbability = EPSILON;
+        }
+        double loopFrequency = 1D / exitProbability;
+        loop.setLoopFrequency(loopFrequency);
+
+        double adjustmentFactor = initialState * loopFrequency;
+        exitStates.replaceAll((exitNode, probability) -> multiplySaturate(probability, adjustmentFactor));
+
+        return exitStates;
+    }
+
+    /**
+     * Multiplies a and b and saturates the result to 1/{@link Double#MIN_NORMAL}.
+     *
+     * @return a times b saturated to 1/{@link Double#MIN_NORMAL}
+     */
+    public static double multiplySaturate(double a, double b) {
+        double r = a * b;
+        if (r > 1 / Double.MIN_NORMAL) {
+            return 1 / Double.MIN_NORMAL;
+        }
+        return r;
+    }
+
+    /**
+     * Computes the frequencies of all loops in the given graph. This is done by performing a
+     * reverse postorder iteration and computing the probability of all fixed nodes. The combined
+     * probability of all exits of a loop can be used to compute the loop's expected frequency.
+     */
+    public static void compute(StructuredGraph graph) {
+        if (graph.hasLoops()) {
+            ReentrantNodeIterator.apply(INSTANCE, graph.start(), 1D);
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -26,6 +26,7 @@
 import static com.oracle.graal.api.meta.DeoptimizationReason.*;
 import static com.oracle.graal.bytecode.Bytecodes.*;
 import static com.oracle.graal.compiler.common.GraalOptions.*;
+
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -158,6 +159,8 @@
                 filter.remove();
             }
             parser = null;
+
+            ComputeLoopFrequenciesClosure.compute(graph);
         }
 
         @Override
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java	Sat May 03 21:46:35 2014 +0200
@@ -108,7 +108,7 @@
         private final String from;
         private final String to;
         @Def({REG}) protected AllocatableValue result;
-        @Use({REG, STACK}) protected AllocatableValue x;
+        @Use({REG}) protected AllocatableValue x;
 
         public ConvertOp(AllocatableValue result, AllocatableValue x, String to, String from) {
             this.from = from;
@@ -123,52 +123,10 @@
         }
     }
 
-    public static class Op1Stack extends HSAILLIRInstruction {
-        @Opcode private final HSAILArithmetic opcode;
-        @Def({REG, HINT}) protected Value result;
-        @Use({REG, STACK, CONST}) protected Value x;
-
-        public Op1Stack(HSAILArithmetic opcode, Value result, Value x) {
-            this.opcode = opcode;
-            this.result = result;
-            this.x = x;
-        }
-
-        @Override
-        public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) {
-            emit(crb, masm, opcode, result, x, null);
-        }
-    }
-
-    public static class Op2Stack extends HSAILLIRInstruction {
+    public static class Op1Reg extends HSAILLIRInstruction {
         @Opcode private final HSAILArithmetic opcode;
         @Def({REG, HINT}) protected Value result;
         @Use({REG, CONST}) protected Value x;
-        @Alive({REG, CONST}) protected Value y;
-
-        public Op2Stack(HSAILArithmetic opcode, Value result, Value x, Value y) {
-            this.opcode = opcode;
-            this.result = result;
-            this.x = x;
-            this.y = y;
-        }
-
-        @Override
-        public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) {
-            emit(crb, masm, opcode, result, x, y, null);
-        }
-
-        @Override
-        public void verify() {
-            super.verify();
-            verifyKind(opcode, result, x, y);
-        }
-    }
-
-    public static class Op1Reg extends HSAILLIRInstruction {
-        @Opcode private final HSAILArithmetic opcode;
-        @Def({REG, HINT}) protected Value result;
-        @Use({REG}) protected Value x;
 
         public Op1Reg(HSAILArithmetic opcode, Value result, Value x) {
             this.opcode = opcode;
@@ -185,7 +143,7 @@
     public static class Op2Reg extends HSAILLIRInstruction {
         @Opcode private final HSAILArithmetic opcode;
         @Def({REG, HINT}) protected Value result;
-        @Use({REG, STACK, CONST}) protected Value x;
+        @Use({REG, CONST}) protected Value x;
         @Alive({REG, CONST}) protected Value y;
 
         public Op2Reg(HSAILArithmetic opcode, Value result, Value x, Value y) {
@@ -207,35 +165,10 @@
         }
     }
 
-    public static class Op2RegCommutative extends HSAILLIRInstruction {
-        @Opcode private final HSAILArithmetic opcode;
-        @Def({REG, HINT}) protected Value result;
-        @Use({REG, STACK, CONST}) protected Value x;
-        @Use({REG, CONST}) protected Value y;
-
-        public Op2RegCommutative(HSAILArithmetic opcode, Value result, Value x, Value y) {
-            this.opcode = opcode;
-            this.result = result;
-            this.x = x;
-            this.y = y;
-        }
-
-        @Override
-        public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) {
-            throw GraalInternalError.shouldNotReachHere();
-        }
-
-        @Override
-        protected void verify() {
-            super.verify();
-            verifyKind(opcode, result, x, y);
-        }
-    }
-
     public static class ShiftOp extends HSAILLIRInstruction {
         @Opcode private final HSAILArithmetic opcode;
         @Def({REG, HINT}) protected Value result;
-        @Use({REG, STACK, CONST}) protected Value x;
+        @Use({REG, CONST}) protected Value x;
         @Alive({REG, CONST}) protected Value y;
 
         public ShiftOp(HSAILArithmetic opcode, Value result, Value x, Value y) {
@@ -295,7 +228,7 @@
 
     /**
      * Emits the HSAIL code for an arithmetic operation taking one input parameter.
-     * 
+     *
      * @param crb the CompilationResultBuilder
      * @param masm the HSAIL assembler
      * @param opcode the opcode of the arithmetic operation
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILCompare.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILCompare.java	Sat May 03 21:46:35 2014 +0200
@@ -50,8 +50,8 @@
     public static class CompareOp extends HSAILLIRInstruction {
 
         @Opcode private final HSAILCompare opcode;
-        @Use({REG, STACK, CONST}) protected Value x;
-        @Use({REG, STACK, CONST}) protected Value y;
+        @Use({REG, CONST}) protected Value x;
+        @Use({REG, CONST}) protected Value y;
         @Def({REG}) protected Value z;
         private final Condition condition;
         public boolean unordered = false;
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Sat May 03 21:46:35 2014 +0200
@@ -146,7 +146,6 @@
         protected MetaAccessProvider metaAccessProvider;
         protected String emitName;
         protected int codeBufferPos = -1;
-        protected int dregOopMap = 0;
 
         public DeoptimizeOp(Value actionAndReason, LIRFrameState frameState, String emitName, MetaAccessProvider metaAccessProvider) {
             super(Value.ILLEGAL);   // return with no ret value
@@ -181,20 +180,10 @@
 
             masm.emitComment("/* HSAIL Deoptimization pos=" + codeBufferPos + ", bci=" + frameState.debugInfo().getBytecodePosition().getBCI() + ", frameState=" + frameState + " */");
 
-            // get the bitmap of $d regs that contain references
-            ReferenceMap referenceMap = frameState.debugInfo().getReferenceMap();
-            for (int dreg = HSAIL.d0.number; dreg <= HSAIL.d15.number; dreg++) {
-                if (referenceMap.getRegister(dreg) == Kind.Object) {
-                    dregOopMap |= 1 << (dreg - HSAIL.d0.number);
-                }
-            }
-
             AllocatableValue actionAndReasonReg = HSAIL.actionAndReasonReg.asValue(Kind.Int);
             AllocatableValue codeBufferOffsetReg = HSAIL.codeBufferOffsetReg.asValue(Kind.Int);
-            AllocatableValue dregOopMapReg = HSAIL.dregOopMapReg.asValue(Kind.Int);
             masm.emitMov(Kind.Int, actionAndReasonReg, actionAndReason);
             masm.emitMov(Kind.Int, codeBufferOffsetReg, Constant.forInt(codeBufferPos));
-            masm.emitMov(Kind.Int, dregOopMapReg, Constant.forInt(dregOopMap));
             masm.emitJumpToLabelName(masm.getDeoptLabelName());
 
             // now record the debuginfo
@@ -305,10 +294,10 @@
 
         @Opcode protected final HSAILCompare opcode;
         @Def({REG, HINT}) protected Value result;
-        @Use({REG, STACK, CONST}) protected Value trueValue;
-        @Use({REG, STACK, CONST}) protected Value falseValue;
-        @Use({REG, STACK, CONST}) protected Value left;
-        @Use({REG, STACK, CONST}) protected Value right;
+        @Use({REG, CONST}) protected Value trueValue;
+        @Use({REG, CONST}) protected Value falseValue;
+        @Use({REG, CONST}) protected Value left;
+        @Use({REG, CONST}) protected Value right;
         protected final Condition condition;
 
         public CondMoveOp(HSAILCompare opcode, Variable left, Variable right, Variable result, Condition condition, Value trueValue, Value falseValue) {
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java	Sat May 03 21:46:35 2014 +0200
@@ -63,29 +63,6 @@
     }
 
     @Opcode("MOVE")
-    public static class SpillMoveOp extends AbstractMoveOp {
-
-        @Def({REG, STACK}) protected AllocatableValue result;
-        @Use({REG, STACK, CONST}) protected Value input;
-
-        public SpillMoveOp(Kind moveKind, AllocatableValue result, Value input) {
-            super(moveKind);
-            this.result = result;
-            this.input = input;
-        }
-
-        @Override
-        public Value getInput() {
-            return input;
-        }
-
-        @Override
-        public AllocatableValue getResult() {
-            return result;
-        }
-    }
-
-    @Opcode("MOVE")
     public static class MoveToRegOp extends AbstractMoveOp {
 
         @Def({REG, HINT}) protected AllocatableValue result;
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopPolicies.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopPolicies.java	Sat May 03 21:46:35 2014 +0200
@@ -24,11 +24,12 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 
+import java.util.function.*;
+
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
-import com.oracle.graal.nodes.util.*;
 
 public abstract class LoopPolicies {
 
@@ -37,12 +38,12 @@
     }
 
     // TODO (gd) change when inversion is available
-    public static boolean shouldPeel(LoopEx loop, NodesToDoubles probabilities) {
+    public static boolean shouldPeel(LoopEx loop, ToDoubleFunction<FixedNode> probabilities) {
         if (loop.detectCounted()) {
             return false;
         }
         LoopBeginNode loopBegin = loop.loopBegin();
-        double entryProbability = probabilities.get(loopBegin.forwardEnd());
+        double entryProbability = probabilities.applyAsDouble(loopBegin.forwardEnd());
         return entryProbability > MinimumPeelProbability.getValue() && loop.size() + loopBegin.graph().getNodeCount() < MaximumDesiredSize.getValue();
     }
 
@@ -70,10 +71,8 @@
         double maxProbability = 0;
         for (Node successor : controlSplit.successors()) {
             BeginNode branch = (BeginNode) successor;
-            inBranchTotal += loop.nodesInLoopFrom(branch, postDom).cardinality(); // this may count
-                                                                                  // twice because
-                                                                                  // of fall-through
-                                                                                  // in switches
+            // this may count twice because of fall-through in switches
+            inBranchTotal += loop.nodesInLoopFrom(branch, postDom).cardinality();
             double probability = controlSplit.probability(branch);
             if (probability > maxProbability) {
                 maxProbability = probability;
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -24,10 +24,11 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 
+import java.util.function.*;
+
 import com.oracle.graal.debug.*;
 import com.oracle.graal.loop.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.graph.*;
 
@@ -37,7 +38,7 @@
     protected void run(StructuredGraph graph) {
         if (graph.hasLoops()) {
             if (LoopPeeling.getValue()) {
-                NodesToDoubles probabilities = new ComputeProbabilityClosure(graph).apply();
+                ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache();
                 LoopsData data = new LoopsData(graph);
                 for (LoopEx loop : data.outterFirst()) {
                     if (LoopPolicies.shouldPeel(loop, probabilities)) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/BeginNode.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/BeginNode.java	Sat May 03 21:46:35 2014 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.util.*;
 
 @NodeInfo(allowedUsageTypes = {InputType.Guard, InputType.Anchor})
 public class BeginNode extends FixedWithNextNode implements LIRLowerable, Simplifiable, GuardingNode, AnchoringNode, IterableNodeType {
@@ -69,12 +70,8 @@
     }
 
     public static BeginNode prevBegin(FixedNode from) {
-        Node prevBegin = from;
-        while (prevBegin != null) {
-            if (prevBegin instanceof BeginNode) {
-                return (BeginNode) prevBegin;
-            }
-            prevBegin = prevBegin.predecessor();
+        for (BeginNode begin : GraphUtil.predecessorIterable(from).filter(BeginNode.class)) {
+            return begin;
         }
         return null;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java	Sat May 03 21:46:35 2014 +0200
@@ -36,7 +36,7 @@
  * The {@code InvokeNode} represents all kinds of method calls.
  */
 @NodeInfo(nameTemplate = "Invoke#{p#targetMethod/s}", allowedUsageTypes = {InputType.Memory})
-public final class InvokeNode extends AbstractMemoryCheckpoint implements Invoke, LIRLowerable, MemoryCheckpoint.Single {
+public final class InvokeNode extends AbstractMemoryCheckpoint implements Invoke, LIRLowerable, MemoryCheckpoint.Single, IterableNodeType {
 
     @Input(InputType.Extension) private CallTargetNode callTarget;
     @Input(InputType.State) private FrameState stateDuring;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java	Sat May 03 21:46:35 2014 +0200
@@ -107,7 +107,7 @@
         ends.clear();
     }
 
-    public NodeIterable<AbstractEndNode> forwardEnds() {
+    public NodeInputList<AbstractEndNode> forwardEnds() {
         return ends;
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Sat May 03 21:46:35 2014 +0200
@@ -231,7 +231,7 @@
     }
 
     public boolean hasLoops() {
-        return getNodes(LoopBeginNode.class).isNotEmpty();
+        return hasNode(LoopBeginNode.class);
     }
 
     public void removeFloating(FloatingNode node) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java	Sat May 03 21:46:35 2014 +0200
@@ -23,7 +23,7 @@
 package com.oracle.graal.nodes.calc;
 
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.meta.ProfilingInfo.*;
+import com.oracle.graal.api.meta.ProfilingInfo.TriState;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.graph.*;
@@ -92,8 +92,8 @@
 
     @Override
     public TriState evaluate(ConstantReflectionProvider constantReflection, ValueNode forX, ValueNode forY) {
-        if (x().isConstant() && y().isConstant()) {
-            return TriState.get(condition().foldCondition(x().asConstant(), y().asConstant(), constantReflection, unorderedIsTrue()));
+        if (forX.isConstant() && forY.isConstant()) {
+            return TriState.get(condition().foldCondition(forX.asConstant(), forY.asConstant(), constantReflection, unorderedIsTrue()));
         }
         return TriState.UNKNOWN;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/Block.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/Block.java	Sat May 03 21:46:35 2014 +0200
@@ -33,6 +33,8 @@
     protected final BeginNode beginNode;
 
     protected FixedNode endNode;
+
+    protected double probability;
     protected Loop<Block> loop;
 
     protected List<Block> dominated;
@@ -175,4 +177,11 @@
         return getDominator().isDominatedBy(block);
     }
 
+    public double probability() {
+        return probability;
+    }
+
+    public void setProbability(double probability) {
+        this.probability = probability;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/BlocksToDoubles.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.nodes.cfg;
-
-import java.util.*;
-
-import com.oracle.graal.compiler.common.cfg.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.util.*;
-
-public class BlocksToDoubles {
-
-    private final IdentityHashMap<AbstractBlock<?>, Double> nodeProbabilities;
-
-    public BlocksToDoubles(int numberOfNodes) {
-        this.nodeProbabilities = new IdentityHashMap<>(numberOfNodes);
-    }
-
-    public void put(AbstractBlock<?> n, double value) {
-        assert value >= 0.0 : value;
-        nodeProbabilities.put(n, value);
-    }
-
-    public boolean contains(AbstractBlock<?> n) {
-        return nodeProbabilities.containsKey(n);
-    }
-
-    public double get(AbstractBlock<?> n) {
-        Double value = nodeProbabilities.get(n);
-        assert value != null;
-        return value;
-    }
-
-    public static BlocksToDoubles createFromNodeProbability(NodesToDoubles nodeProbabilities, ControlFlowGraph cfg) {
-        BlocksToDoubles blockProbabilities = new BlocksToDoubles(cfg.getBlocks().length);
-        for (Block block : cfg.getBlocks()) {
-            blockProbabilities.put(block, nodeProbabilities.get(block.getBeginNode()));
-        }
-        assert verify(nodeProbabilities, cfg, blockProbabilities) : "Probabilities differ for nodes in the same block.";
-        return blockProbabilities;
-    }
-
-    private static boolean verify(NodesToDoubles nodeProbabilities, ControlFlowGraph cfg, BlocksToDoubles blockProbabilities) {
-        for (Block b : cfg.getBlocks()) {
-            double p = blockProbabilities.get(b);
-            for (FixedNode n : b.getNodes()) {
-                if (nodeProbabilities.get(n) != p) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java	Sat May 03 21:46:35 2014 +0200
@@ -200,14 +200,21 @@
     private void connectBlocks() {
         for (Block block : reversePostOrder) {
             List<Block> predecessors = new ArrayList<>(4);
+            double probability = 0;
             for (Node predNode : block.getBeginNode().cfgPredecessors()) {
                 Block predBlock = nodeToBlock.get(predNode);
                 if (predBlock.getId() >= 0) {
                     predecessors.add(predBlock);
+                    probability += predBlock.probability;
                 }
             }
+            if (predecessors.size() == 1 && predecessors.get(0).getEndNode() instanceof ControlSplitNode) {
+                probability *= ((ControlSplitNode) predecessors.get(0).getEndNode()).probability(block.getBeginNode());
+            }
             if (block.getBeginNode() instanceof LoopBeginNode) {
-                for (LoopEndNode predNode : ((LoopBeginNode) block.getBeginNode()).orderedLoopEnds()) {
+                LoopBeginNode loopBegin = (LoopBeginNode) block.getBeginNode();
+                probability *= loopBegin.loopFrequency();
+                for (LoopEndNode predNode : loopBegin.orderedLoopEnds()) {
                     Block predBlock = nodeToBlock.get(predNode);
                     if (predBlock.getId() >= 0) {
                         predecessors.add(predBlock);
@@ -215,6 +222,7 @@
                 }
             }
             block.setPredecessors(predecessors);
+            block.setProbability(probability);
 
             List<Block> successors = new ArrayList<>(4);
             for (Node suxNode : block.getEndNode().cfgSuccessors()) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Sat May 03 21:46:35 2014 +0200
@@ -73,7 +73,8 @@
         if (merge != null) {
             merge.removeEnd(end);
             StructuredGraph graph = end.graph();
-            if (merge instanceof LoopBeginNode && merge.forwardEndCount() == 0) { // dead loop
+            if (merge instanceof LoopBeginNode && merge.forwardEndCount() == 0) {
+                // dead loop
                 for (PhiNode phi : merge.phis().snapshot()) {
                     propagateKill(phi);
                 }
@@ -93,13 +94,13 @@
             } else if (merge instanceof LoopBeginNode && ((LoopBeginNode) merge).loopEnds().isEmpty()) {
                 // not a loop anymore
                 if (tool != null) {
-                    merge.phis().forEach(phi -> phi.usages().forEach(usage -> tool.addToWorkList(usage)));
+                    merge.phis().forEach(phi -> phi.usages().forEach(tool::addToWorkList));
                 }
                 graph.reduceDegenerateLoopBegin((LoopBeginNode) merge);
             } else if (merge.phiPredecessorCount() == 1) {
                 // not a merge anymore
                 if (tool != null) {
-                    merge.phis().forEach(phi -> phi.usages().forEach(usage -> tool.addToWorkList(usage)));
+                    merge.phis().forEach(phi -> phi.usages().forEach(tool::addToWorkList));
                 }
                 graph.reduceTrivialMerge(merge);
             }
@@ -408,4 +409,32 @@
             return true;
         }
     }
+
+    /**
+     * Returns an iterator that will return the given node followed by all its predecessors, up
+     * until the point where {@link Node#predecessor()} returns null;
+     *
+     * @param start the node at which to start iterating
+     */
+    public static NodeIterable<FixedNode> predecessorIterable(final FixedNode start) {
+        return new NodeIterable<FixedNode>() {
+            public Iterator<FixedNode> iterator() {
+                return new Iterator<FixedNode>() {
+                    public FixedNode current = start;
+
+                    public boolean hasNext() {
+                        return current != null;
+                    }
+
+                    public FixedNode next() {
+                        try {
+                            return current;
+                        } finally {
+                            current = (FixedNode) current.predecessor();
+                        }
+                    }
+                };
+            }
+        };
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/NodesToDoubles.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.nodes.util;
-
-import java.util.*;
-
-import com.oracle.graal.nodes.*;
-
-public class NodesToDoubles {
-
-    private final IdentityHashMap<FixedNode, Double> nodeProbabilities;
-
-    public NodesToDoubles(int numberOfNodes) {
-        this.nodeProbabilities = new IdentityHashMap<>(numberOfNodes);
-    }
-
-    public void put(FixedNode n, double value) {
-        assert value >= 0.0 : value;
-        nodeProbabilities.put(n, value);
-    }
-
-    public boolean contains(FixedNode n) {
-        return nodeProbabilities.containsKey(n);
-    }
-
-    public double get(FixedNode n) {
-        Double value = nodeProbabilities.get(n);
-        assert value != null;
-        return value;
-    }
-
-    public int getCount() {
-        return nodeProbabilities.size();
-    }
-}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -35,27 +35,20 @@
 /**
  * This phase will find branches which always end with a {@link DeoptimizeNode} and replace their
  * {@link ControlSplitNode ControlSplitNodes} with {@link FixedGuardNode FixedGuardNodes}.
- * 
+ *
  * This is useful because {@link FixedGuardNode FixedGuardNodes} will be lowered to
  * {@link GuardNode GuardNodes} which can later be optimized more aggressively than control-flow
  * constructs.
- * 
+ *
  * This is currently only done for branches that start from a {@link IfNode}. If it encounters a
  * branch starting at an other kind of {@link ControlSplitNode}, it will only bring the
  * {@link DeoptimizeNode} as close to the {@link ControlSplitNode} as possible.
- * 
+ *
  */
 public class ConvertDeoptimizeToGuardPhase extends Phase {
 
     private static BeginNode findBeginNode(FixedNode startNode) {
-        Node n = startNode;
-        while (true) {
-            if (n instanceof BeginNode) {
-                return (BeginNode) n;
-            } else {
-                n = n.predecessor();
-            }
-        }
+        return GraphUtil.predecessorIterable(startNode).filter(BeginNode.class).first();
     }
 
     @Override
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -113,8 +113,8 @@
     @Override
     protected void run(StructuredGraph graph) {
         Map<LoopBeginNode, Set<LocationIdentity>> modifiedInLoops = new IdentityHashMap<>();
-        ReentrantNodeIterator.apply(new CollectMemoryCheckpointsClosure(modifiedInLoops), graph.start(), new HashSet<LocationIdentity>(), null);
-        ReentrantNodeIterator.apply(new FloatingReadClosure(modifiedInLoops, execmode), graph.start(), new MemoryMapImpl(graph.start()), null);
+        ReentrantNodeIterator.apply(new CollectMemoryCheckpointsClosure(modifiedInLoops), graph.start(), new HashSet<LocationIdentity>());
+        ReentrantNodeIterator.apply(new FloatingReadClosure(modifiedInLoops, execmode), graph.start(), new MemoryMapImpl(graph.start()));
         if (execmode == ExecutionMode.CREATE_FLOATING_READS) {
             assert !graph.isAfterFloatingReadPhase();
             graph.setAfterFloatingReadPhase(true);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -107,7 +107,7 @@
     protected void run(StructuredGraph graph) {
         assert graph.getGuardsStage().ordinal() >= GuardsStage.FIXED_DEOPTS.ordinal() && checkFixedDeopts(graph);
         if (graph.getGuardsStage().ordinal() < GuardsStage.AFTER_FSA.ordinal()) {
-            ReentrantNodeIterator.apply(new FrameStateAssignmentClosure(), graph.start(), null, null);
+            ReentrantNodeIterator.apply(new FrameStateAssignmentClosure(), graph.start(), null);
             graph.setGuardsStage(GuardsStage.AFTER_FSA);
         }
     }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,858 +0,0 @@
-/*
- * Copyright (c) 2011, 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.phases.common;
-
-import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.phases.common.InliningPhase.Options.*;
-
-import java.util.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.debug.Debug.Scope;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.Graph.Mark;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.util.*;
-import com.oracle.graal.options.*;
-import com.oracle.graal.phases.common.InliningUtil.InlineInfo;
-import com.oracle.graal.phases.common.InliningUtil.Inlineable;
-import com.oracle.graal.phases.common.InliningUtil.InlineableGraph;
-import com.oracle.graal.phases.common.InliningUtil.InlineableMacroNode;
-import com.oracle.graal.phases.common.InliningUtil.InliningPolicy;
-import com.oracle.graal.phases.graph.*;
-import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.phases.util.*;
-
-public class InliningPhase extends AbstractInliningPhase {
-
-    static class Options {
-
-        // @formatter:off
-        @Option(help = "Unconditionally inline intrinsics")
-        public static final OptionValue<Boolean> AlwaysInlineIntrinsics = new OptionValue<>(false);
-        // @formatter:on
-    }
-
-    private final InliningPolicy inliningPolicy;
-    private final CanonicalizerPhase canonicalizer;
-
-    private int inliningCount;
-    private int maxMethodPerInlining = Integer.MAX_VALUE;
-
-    // Metrics
-    private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed");
-    private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered");
-    private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize");
-    private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns");
-
-    public InliningPhase(CanonicalizerPhase canonicalizer) {
-        this(new GreedyInliningPolicy(null), canonicalizer);
-    }
-
-    public InliningPhase(Map<Invoke, Double> hints, CanonicalizerPhase canonicalizer) {
-        this(new GreedyInliningPolicy(hints), canonicalizer);
-    }
-
-    public InliningPhase(InliningPolicy policy, CanonicalizerPhase canonicalizer) {
-        this.inliningPolicy = policy;
-        this.canonicalizer = canonicalizer;
-    }
-
-    public void setMaxMethodsPerInlining(int max) {
-        maxMethodPerInlining = max;
-    }
-
-    public int getInliningCount() {
-        return inliningCount;
-    }
-
-    @Override
-    protected void run(final StructuredGraph graph, final HighTierContext context) {
-        final InliningData data = new InliningData(graph, context.getAssumptions());
-
-        while (data.hasUnprocessedGraphs()) {
-            final MethodInvocation currentInvocation = data.currentInvocation();
-            GraphInfo graphInfo = data.currentGraph();
-            if (!currentInvocation.isRoot() &&
-                            !inliningPolicy.isWorthInlining(context.getReplacements(), currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(),
-                                            currentInvocation.relevance(), false)) {
-                int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs();
-                assert remainingGraphs > 0;
-                data.popGraphs(remainingGraphs);
-                data.popInvocation();
-            } else if (graphInfo.hasRemainingInvokes() && inliningPolicy.continueInlining(graphInfo.graph())) {
-                processNextInvoke(data, graphInfo, context);
-            } else {
-                data.popGraph();
-                if (!currentInvocation.isRoot()) {
-                    assert currentInvocation.callee().invoke().asNode().isAlive();
-                    currentInvocation.incrementProcessedGraphs();
-                    if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) {
-                        data.popInvocation();
-                        final MethodInvocation parentInvoke = data.currentInvocation();
-                        try (Scope s = Debug.scope("Inlining", data.inliningContext())) {
-                            tryToInline(data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1, context);
-                        } catch (Throwable e) {
-                            throw Debug.handle(e);
-                        }
-                    }
-                }
-            }
-        }
-
-        assert data.inliningDepth() == 0;
-        assert data.graphCount() == 0;
-    }
-
-    /**
-     * Process the next invoke and enqueue all its graphs for processing.
-     */
-    private void processNextInvoke(InliningData data, GraphInfo graphInfo, HighTierContext context) {
-        Invoke invoke = graphInfo.popInvoke();
-        MethodInvocation callerInvocation = data.currentInvocation();
-        Assumptions parentAssumptions = callerInvocation.assumptions();
-        InlineInfo info = InliningUtil.getInlineInfo(data, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations());
-
-        if (info != null) {
-            double invokeProbability = graphInfo.invokeProbability(invoke);
-            double invokeRelevance = graphInfo.invokeRelevance(invoke);
-            MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance);
-
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                Inlineable elem = getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()));
-                info.setInlinableElement(i, elem);
-                if (elem instanceof InlineableGraph) {
-                    data.pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
-                } else {
-                    assert elem instanceof InlineableMacroNode;
-                    data.pushDummyGraph();
-                }
-            }
-        }
-    }
-
-    private void tryToInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth, HighTierContext context) {
-        InlineInfo callee = calleeInfo.callee();
-        Assumptions callerAssumptions = parentInvocation.assumptions();
-
-        if (inliningPolicy.isWorthInlining(context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) {
-            doInline(callerGraphInfo, calleeInfo, callerAssumptions, context);
-        } else if (context.getOptimisticOptimizations().devirtualizeInvokes()) {
-            callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions);
-        }
-        metricInliningConsidered.increment();
-    }
-
-    private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) {
-        StructuredGraph callerGraph = callerGraphInfo.graph();
-        Mark markBeforeInlining = callerGraph.getMark();
-        InlineInfo callee = calleeInfo.callee();
-        try {
-            try (Scope scope = Debug.scope("doInline", callerGraph)) {
-                List<Node> invokeUsages = callee.invoke().asNode().usages().snapshot();
-                callee.inline(new Providers(context), callerAssumptions);
-                callerAssumptions.record(calleeInfo.assumptions());
-                metricInliningRuns.increment();
-                Debug.dump(callerGraph, "after %s", callee);
-
-                if (OptCanonicalizer.getValue()) {
-                    Mark markBeforeCanonicalization = callerGraph.getMark();
-                    canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining);
-
-                    // process invokes that are possibly created during canonicalization
-                    for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) {
-                        if (newNode instanceof Invoke) {
-                            callerGraphInfo.pushInvoke((Invoke) newNode);
-                        }
-                    }
-                }
-
-                callerGraphInfo.computeProbabilities();
-
-                inliningCount++;
-                metricInliningPerformed.increment();
-            }
-        } catch (BailoutException bailout) {
-            throw bailout;
-        } catch (AssertionError | RuntimeException e) {
-            throw new GraalInternalError(e).addContext(callee.toString());
-        } catch (GraalInternalError e) {
-            throw e.addContext(callee.toString());
-        }
-    }
-
-    private Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context) {
-        Class<? extends FixedWithNextNode> macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method);
-        if (macroNodeClass != null) {
-            return new InlineableMacroNode(macroNodeClass);
-        } else {
-            return new InlineableGraph(buildGraph(method, invoke, context));
-        }
-    }
-
-    private StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context) {
-        final StructuredGraph newGraph;
-        final boolean parseBytecodes;
-
-        // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains
-        // any invokes
-        StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method);
-        if (intrinsicGraph != null) {
-            newGraph = intrinsicGraph.copy();
-            parseBytecodes = false;
-        } else {
-            StructuredGraph cachedGraph = getCachedGraph(method, context);
-            if (cachedGraph != null) {
-                newGraph = cachedGraph.copy();
-                parseBytecodes = false;
-            } else {
-                newGraph = new StructuredGraph(method);
-                parseBytecodes = true;
-            }
-        }
-
-        try (Scope s = Debug.scope("InlineGraph", newGraph)) {
-            if (parseBytecodes) {
-                parseBytecodes(newGraph, context);
-            }
-
-            boolean callerHasMoreInformationAboutArguments = false;
-            NodeInputList<ValueNode> args = invoke.callTarget().arguments();
-            for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) {
-                ValueNode arg = args.get(param.index());
-                if (arg.isConstant()) {
-                    Constant constant = arg.asConstant();
-                    newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph));
-                    callerHasMoreInformationAboutArguments = true;
-                } else {
-                    Stamp joinedStamp = param.stamp().join(arg.stamp());
-                    if (joinedStamp != null && !joinedStamp.equals(param.stamp())) {
-                        param.setStamp(joinedStamp);
-                        callerHasMoreInformationAboutArguments = true;
-                    }
-                }
-            }
-
-            if (!callerHasMoreInformationAboutArguments) {
-                // TODO (chaeubl): if args are not more concrete, inlining should be avoided
-                // in most cases or we could at least use the previous graph size + invoke
-                // probability to check the inlining
-            }
-
-            if (OptCanonicalizer.getValue()) {
-                canonicalizer.apply(newGraph, context);
-            }
-
-            return newGraph;
-        } catch (Throwable e) {
-            throw Debug.handle(e);
-        }
-    }
-
-    private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) {
-        if (context.getGraphCache() != null) {
-            StructuredGraph cachedGraph = context.getGraphCache().get(method);
-            if (cachedGraph != null) {
-                return cachedGraph;
-            }
-        }
-        return null;
-    }
-
-    private StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context) {
-        boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature();
-
-        if (context.getGraphBuilderSuite() != null) {
-            context.getGraphBuilderSuite().apply(newGraph, context);
-        }
-        assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING";
-
-        new DeadCodeEliminationPhase().apply(newGraph);
-
-        if (OptCanonicalizer.getValue()) {
-            canonicalizer.apply(newGraph, context);
-        }
-
-        if (hasMatureProfilingInfo && context.getGraphCache() != null) {
-            context.getGraphCache().put(newGraph.method(), newGraph.copy());
-        }
-        return newGraph;
-    }
-
-    private abstract static class AbstractInliningPolicy implements InliningPolicy {
-
-        protected final Map<Invoke, Double> hints;
-
-        public AbstractInliningPolicy(Map<Invoke, Double> hints) {
-            this.hints = hints;
-        }
-
-        protected double computeMaximumSize(double relevance, int configuredMaximum) {
-            double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance);
-            return configuredMaximum * inlineRatio;
-        }
-
-        protected double getInliningBonus(InlineInfo info) {
-            if (hints != null && hints.containsKey(info.invoke())) {
-                return hints.get(info.invoke());
-            }
-            return 1;
-        }
-
-        protected boolean isIntrinsic(Replacements replacements, InlineInfo info) {
-            if (AlwaysInlineIntrinsics.getValue()) {
-                return onlyIntrinsics(replacements, info);
-            } else {
-                return onlyForcedIntrinsics(replacements, info);
-            }
-        }
-
-        private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) {
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) {
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) {
-                    return false;
-                }
-                if (!replacements.isForcedSubstitution(info.methodAt(i))) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        protected static int previousLowLevelGraphSize(InlineInfo info) {
-            int size = 0;
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                ResolvedJavaMethod m = info.methodAt(i);
-                ProfilingInfo profile = m.getProfilingInfo();
-                int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class);
-                if (compiledGraphSize > 0) {
-                    size += compiledGraphSize;
-                }
-            }
-            return size;
-        }
-
-        protected static int determineNodeCount(InlineInfo info) {
-            int nodes = 0;
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                Inlineable elem = info.inlineableElementAt(i);
-                if (elem != null) {
-                    nodes += elem.getNodeCount();
-                }
-            }
-            return nodes;
-        }
-
-        protected static double determineInvokeProbability(InlineInfo info) {
-            double invokeProbability = 0;
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                Inlineable callee = info.inlineableElementAt(i);
-                Iterable<Invoke> invokes = callee.getInvokes();
-                if (invokes.iterator().hasNext()) {
-                    NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(((InlineableGraph) callee).getGraph()).apply();
-                    for (Invoke invoke : invokes) {
-                        invokeProbability += nodeProbabilities.get(invoke.asNode());
-                    }
-                }
-            }
-            return invokeProbability;
-        }
-    }
-
-    public static class GreedyInliningPolicy extends AbstractInliningPolicy {
-
-        public GreedyInliningPolicy(Map<Invoke, Double> hints) {
-            super(hints);
-        }
-
-        public boolean continueInlining(StructuredGraph currentGraph) {
-            if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) {
-                InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize");
-                metricInliningStoppedByMaxDesiredSize.increment();
-                return false;
-            }
-            return true;
-        }
-
-        @Override
-        public boolean isWorthInlining(Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed) {
-            if (InlineEverything.getValue()) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything");
-            }
-
-            if (isIntrinsic(replacements, info)) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic");
-            }
-
-            if (info.shouldInline()) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining");
-            }
-
-            double inliningBonus = getInliningBonus(info);
-            int nodes = determineNodeCount(info);
-            int lowLevelGraphSize = previousLowLevelGraphSize(info);
-
-            if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) {
-                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)",
-                                lowLevelGraphSize, relevance, probability, inliningBonus, nodes);
-            }
-
-            if (nodes < TrivialInliningSize.getValue() * inliningBonus) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes);
-            }
-
-            /*
-             * TODO (chaeubl): invoked methods that are on important paths but not yet compiled ->
-             * will be compiled anyways and it is likely that we are the only caller... might be
-             * useful to inline those methods but increases bootstrap time (maybe those methods are
-             * also getting queued in the compilation queue concurrently)
-             */
-            double invokes = determineInvokeProbability(info);
-            if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) {
-                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes,
-                                relevance, probability, inliningBonus, nodes);
-            }
-
-            double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus));
-            if (nodes <= maximumNodes) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability,
-                                inliningBonus, nodes, maximumNodes);
-            }
-
-            return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes,
-                            maximumNodes);
-        }
-    }
-
-    public static final class InlineEverythingPolicy implements InliningPolicy {
-
-        public boolean continueInlining(StructuredGraph graph) {
-            if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) {
-                throw new BailoutException("Inline all calls failed. The resulting graph is too large.");
-            }
-            return true;
-        }
-
-        public boolean isWorthInlining(Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed) {
-            return true;
-        }
-    }
-
-    private static class InliningIterator {
-
-        private final FixedNode start;
-        private final Deque<FixedNode> nodeQueue;
-        private final NodeBitMap queuedNodes;
-
-        public InliningIterator(FixedNode start, NodeBitMap visitedFixedNodes) {
-            this.start = start;
-            this.nodeQueue = new ArrayDeque<>();
-            this.queuedNodes = visitedFixedNodes;
-            assert start.isAlive();
-        }
-
-        public LinkedList<Invoke> apply() {
-            LinkedList<Invoke> invokes = new LinkedList<>();
-            FixedNode current;
-            forcedQueue(start);
-
-            while ((current = nextQueuedNode()) != null) {
-                assert current.isAlive();
-
-                if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) {
-                    if (current != start) {
-                        invokes.addLast((Invoke) current);
-                    }
-                    queueSuccessors(current);
-                } else if (current instanceof LoopBeginNode) {
-                    queueSuccessors(current);
-                } else if (current instanceof LoopEndNode) {
-                    // nothing todo
-                } else if (current instanceof MergeNode) {
-                    queueSuccessors(current);
-                } else if (current instanceof FixedWithNextNode) {
-                    queueSuccessors(current);
-                } else if (current instanceof EndNode) {
-                    queueMerge((EndNode) current);
-                } else if (current instanceof ControlSinkNode) {
-                    // nothing todo
-                } else if (current instanceof ControlSplitNode) {
-                    queueSuccessors(current);
-                } else {
-                    assert false : current;
-                }
-            }
-
-            return invokes;
-        }
-
-        private void queueSuccessors(FixedNode x) {
-            for (Node node : x.successors()) {
-                queue(node);
-            }
-        }
-
-        private void queue(Node node) {
-            if (node != null && !queuedNodes.isMarked(node)) {
-                forcedQueue(node);
-            }
-        }
-
-        private void forcedQueue(Node node) {
-            queuedNodes.mark(node);
-            nodeQueue.addFirst((FixedNode) node);
-        }
-
-        private FixedNode nextQueuedNode() {
-            if (nodeQueue.isEmpty()) {
-                return null;
-            }
-
-            FixedNode result = nodeQueue.removeFirst();
-            assert queuedNodes.isMarked(result);
-            return result;
-        }
-
-        private void queueMerge(AbstractEndNode end) {
-            MergeNode merge = end.merge();
-            if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) {
-                queuedNodes.mark(merge);
-                nodeQueue.add(merge);
-            }
-        }
-
-        private boolean visitedAllEnds(MergeNode merge) {
-            for (int i = 0; i < merge.forwardEndCount(); i++) {
-                if (!queuedNodes.isMarked(merge.forwardEndAt(i))) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    /**
-     * Holds the data for building the callee graphs recursively: graphs and invocations (each
-     * invocation can have multiple graphs).
-     */
-    static class InliningData {
-
-        private static final GraphInfo DummyGraphInfo = new GraphInfo(null, new LinkedList<Invoke>(), 1.0, 1.0);
-
-        /**
-         * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee.
-         */
-        private final ArrayDeque<GraphInfo> graphQueue;
-        private final ArrayDeque<MethodInvocation> invocationQueue;
-
-        private int maxGraphs;
-
-        public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions) {
-            this.graphQueue = new ArrayDeque<>();
-            this.invocationQueue = new ArrayDeque<>();
-            this.maxGraphs = 1;
-
-            invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0));
-            pushGraph(rootGraph, 1.0, 1.0);
-        }
-
-        public int graphCount() {
-            return graphQueue.size();
-        }
-
-        public void pushGraph(StructuredGraph graph, double probability, double relevance) {
-            assert !contains(graph);
-            NodeBitMap visitedFixedNodes = graph.createNodeBitMap();
-            LinkedList<Invoke> invokes = new InliningIterator(graph.start(), visitedFixedNodes).apply();
-            assert invokes.size() == count(graph.getInvokes());
-            graphQueue.push(new GraphInfo(graph, invokes, probability, relevance));
-            assert graphQueue.size() <= maxGraphs;
-        }
-
-        public void pushDummyGraph() {
-            graphQueue.push(DummyGraphInfo);
-        }
-
-        public boolean hasUnprocessedGraphs() {
-            return !graphQueue.isEmpty();
-        }
-
-        public GraphInfo currentGraph() {
-            return graphQueue.peek();
-        }
-
-        public void popGraph() {
-            graphQueue.pop();
-            assert graphQueue.size() <= maxGraphs;
-        }
-
-        public void popGraphs(int count) {
-            assert count >= 0;
-            for (int i = 0; i < count; i++) {
-                graphQueue.pop();
-            }
-        }
-
-        private static final Object[] NO_CONTEXT = {};
-
-        /**
-         * Gets the call hierarchy of this inlining from outer most call to inner most callee.
-         */
-        public Object[] inliningContext() {
-            if (!Debug.isDumpEnabled()) {
-                return NO_CONTEXT;
-            }
-            Object[] result = new Object[graphQueue.size()];
-            int i = 0;
-            for (GraphInfo g : graphQueue) {
-                result[i++] = g.graph.method();
-            }
-            return result;
-        }
-
-        public MethodInvocation currentInvocation() {
-            return invocationQueue.peekFirst();
-        }
-
-        public MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) {
-            MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance);
-            invocationQueue.addFirst(methodInvocation);
-            maxGraphs += info.numberOfMethods();
-            assert graphQueue.size() <= maxGraphs;
-            return methodInvocation;
-        }
-
-        public void popInvocation() {
-            maxGraphs -= invocationQueue.peekFirst().callee.numberOfMethods();
-            assert graphQueue.size() <= maxGraphs;
-            invocationQueue.removeFirst();
-        }
-
-        public int countRecursiveInlining(ResolvedJavaMethod method) {
-            int count = 0;
-            for (GraphInfo graphInfo : graphQueue) {
-                if (method.equals(graphInfo.method())) {
-                    count++;
-                }
-            }
-            return count;
-        }
-
-        public int inliningDepth() {
-            assert invocationQueue.size() > 0;
-            return invocationQueue.size() - 1;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder result = new StringBuilder("Invocations: ");
-
-            for (MethodInvocation invocation : invocationQueue) {
-                if (invocation.callee() != null) {
-                    result.append(invocation.callee().numberOfMethods());
-                    result.append("x ");
-                    result.append(invocation.callee().invoke());
-                    result.append("; ");
-                }
-            }
-
-            result.append("\nGraphs: ");
-            for (GraphInfo graph : graphQueue) {
-                result.append(graph.graph());
-                result.append("; ");
-            }
-
-            return result.toString();
-        }
-
-        private boolean contains(StructuredGraph graph) {
-            for (GraphInfo info : graphQueue) {
-                if (info.graph() == graph) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private static int count(Iterable<Invoke> invokes) {
-            int count = 0;
-            Iterator<Invoke> iterator = invokes.iterator();
-            while (iterator.hasNext()) {
-                iterator.next();
-                count++;
-            }
-            return count;
-        }
-    }
-
-    private static class MethodInvocation {
-
-        private final InlineInfo callee;
-        private final Assumptions assumptions;
-        private final double probability;
-        private final double relevance;
-
-        private int processedGraphs;
-
-        public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) {
-            this.callee = info;
-            this.assumptions = assumptions;
-            this.probability = probability;
-            this.relevance = relevance;
-        }
-
-        public void incrementProcessedGraphs() {
-            processedGraphs++;
-            assert processedGraphs <= callee.numberOfMethods();
-        }
-
-        public int processedGraphs() {
-            assert processedGraphs <= callee.numberOfMethods();
-            return processedGraphs;
-        }
-
-        public int totalGraphs() {
-            return callee.numberOfMethods();
-        }
-
-        public InlineInfo callee() {
-            return callee;
-        }
-
-        public Assumptions assumptions() {
-            return assumptions;
-        }
-
-        public double probability() {
-            return probability;
-        }
-
-        public double relevance() {
-            return relevance;
-        }
-
-        public boolean isRoot() {
-            return callee == null;
-        }
-
-        @Override
-        public String toString() {
-            if (isRoot()) {
-                return "<root>";
-            }
-            CallTargetNode callTarget = callee.invoke().callTarget();
-            if (callTarget instanceof MethodCallTargetNode) {
-                ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod();
-                return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod);
-            } else {
-                return "Invoke#" + callTarget.targetName();
-            }
-        }
-    }
-
-    /**
-     * Information about a graph that will potentially be inlined. This includes tracking the
-     * invocations in graph that will subject to inlining themselves.
-     */
-    private static class GraphInfo {
-
-        private final StructuredGraph graph;
-        private final LinkedList<Invoke> remainingInvokes;
-        private final double probability;
-        private final double relevance;
-
-        private NodesToDoubles nodeProbabilities;
-        private NodesToDoubles nodeRelevance;
-
-        public GraphInfo(StructuredGraph graph, LinkedList<Invoke> invokes, double probability, double relevance) {
-            this.graph = graph;
-            this.remainingInvokes = invokes;
-            this.probability = probability;
-            this.relevance = relevance;
-
-            if (graph != null) {
-                computeProbabilities();
-            }
-        }
-
-        /**
-         * Gets the method associated with the {@linkplain #graph() graph} represented by this
-         * object.
-         */
-        public ResolvedJavaMethod method() {
-            return graph.method();
-        }
-
-        public boolean hasRemainingInvokes() {
-            return !remainingInvokes.isEmpty();
-        }
-
-        /**
-         * The graph about which this object contains inlining information.
-         */
-        public StructuredGraph graph() {
-            return graph;
-        }
-
-        public Invoke popInvoke() {
-            return remainingInvokes.removeFirst();
-        }
-
-        public void pushInvoke(Invoke invoke) {
-            remainingInvokes.push(invoke);
-        }
-
-        public void computeProbabilities() {
-            nodeProbabilities = new ComputeProbabilityClosure(graph).apply();
-            nodeRelevance = new ComputeInliningRelevanceClosure(graph, nodeProbabilities).apply();
-        }
-
-        public double invokeProbability(Invoke invoke) {
-            return probability * nodeProbabilities.get(invoke.asNode());
-        }
-
-        public double invokeRelevance(Invoke invoke) {
-            return Math.min(CapInheritedRelevance.getValue(), relevance) * nodeRelevance.get(invoke.asNode());
-        }
-
-        @Override
-        public String toString() {
-            return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "<null method>") + remainingInvokes;
-        }
-    }
-}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1618 +0,0 @@
-/*
- * Copyright (c) 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.phases.common;
-
-import static com.oracle.graal.api.meta.DeoptimizationAction.*;
-import static com.oracle.graal.api.meta.DeoptimizationReason.*;
-import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.compiler.common.type.StampFactory.*;
-import java.util.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.Assumptions.Assumption;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType;
-import com.oracle.graal.api.meta.ResolvedJavaType.Representation;
-import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.compiler.common.calc.*;
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.debug.Debug.Scope;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.Graph.DuplicationReplacement;
-import com.oracle.graal.graph.Node.Verbosity;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.util.*;
-import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.common.InliningPhase.InliningData;
-import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.phases.util.*;
-
-public class InliningUtil {
-
-    private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication");
-    private static final String inliningDecisionsScopeString = "InliningDecisions";
-    /**
-     * Meters the size (in bytecodes) of all methods processed during compilation (i.e., top level
-     * and all inlined methods), irrespective of how many bytecodes in each method are actually
-     * parsed (which may be none for methods whose IR is retrieved from a cache).
-     */
-    public static final DebugMetric InlinedBytecodes = Debug.metric("InlinedBytecodes");
-
-    public interface InliningPolicy {
-
-        boolean continueInlining(StructuredGraph graph);
-
-        boolean isWorthInlining(Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed);
-    }
-
-    public interface Inlineable {
-
-        int getNodeCount();
-
-        Iterable<Invoke> getInvokes();
-    }
-
-    public static class InlineableGraph implements Inlineable {
-
-        private final StructuredGraph graph;
-
-        public InlineableGraph(StructuredGraph graph) {
-            this.graph = graph;
-        }
-
-        @Override
-        public int getNodeCount() {
-            return graph.getNodeCount();
-        }
-
-        @Override
-        public Iterable<Invoke> getInvokes() {
-            return graph.getInvokes();
-        }
-
-        public StructuredGraph getGraph() {
-            return graph;
-        }
-    }
-
-    public static class InlineableMacroNode implements Inlineable {
-
-        private final Class<? extends FixedWithNextNode> macroNodeClass;
-
-        public InlineableMacroNode(Class<? extends FixedWithNextNode> macroNodeClass) {
-            this.macroNodeClass = macroNodeClass;
-        }
-
-        @Override
-        public int getNodeCount() {
-            return 1;
-        }
-
-        @Override
-        public Iterable<Invoke> getInvokes() {
-            return Collections.emptyList();
-        }
-
-        public Class<? extends FixedWithNextNode> getMacroNodeClass() {
-            return macroNodeClass;
-        }
-    }
-
-    /**
-     * Print a HotSpot-style inlining message to the console.
-     */
-    private static void printInlining(final InlineInfo info, final int inliningDepth, final boolean success, final String msg, final Object... args) {
-        printInlining(info.methodAt(0), info.invoke(), inliningDepth, success, msg, args);
-    }
-
-    /**
-     * Print a HotSpot-style inlining message to the console.
-     */
-    private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final int inliningDepth, final boolean success, final String msg, final Object... args) {
-        if (HotSpotPrintInlining.getValue()) {
-            // 1234567
-            TTY.print("        ");     // print timestamp
-            // 1234
-            TTY.print("     ");        // print compilation number
-            // % s ! b n
-            TTY.print("%c%c%c%c%c ", ' ', method.isSynchronized() ? 's' : ' ', ' ', ' ', method.isNative() ? 'n' : ' ');
-            TTY.print("     ");        // more indent
-            TTY.print("    ");         // initial inlining indent
-            for (int i = 0; i < inliningDepth; i++) {
-                TTY.print("  ");
-            }
-            TTY.println(String.format("@ %d  %s   %s%s", invoke.bci(), methodName(method, null), success ? "" : "not inlining ", String.format(msg, args)));
-        }
-    }
-
-    public static boolean logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) {
-        return logInliningDecision(info, inliningDepth, allowLogging, true, msg, args);
-    }
-
-    public static boolean logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) {
-        return logInliningDecision(info, inliningDepth, true, false, msg, args);
-    }
-
-    public static boolean logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) {
-        if (allowLogging) {
-            printInlining(info, inliningDepth, success, msg, args);
-            if (shouldLogInliningDecision()) {
-                logInliningDecision(methodName(info), success, msg, args);
-            }
-        }
-        return success;
-    }
-
-    public static void logInliningDecision(final String msg, final Object... args) {
-        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
-            // Can't use log here since we are varargs
-            if (Debug.isLogEnabled()) {
-                Debug.logv(msg, args);
-            }
-        }
-    }
-
-    private static boolean logNotInlinedMethod(Invoke invoke, String msg) {
-        if (shouldLogInliningDecision()) {
-            String methodString = invoke.toString() + (invoke.callTarget() == null ? " callTarget=null" : invoke.callTarget().targetName());
-            logInliningDecision(methodString, false, msg, new Object[0]);
-        }
-        return false;
-    }
-
-    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) {
-        return logNotInlinedMethodAndReturnNull(invoke, inliningDepth, method, msg, new Object[0]);
-    }
-
-    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) {
-        printInlining(method, invoke, inliningDepth, false, msg, args);
-        if (shouldLogInliningDecision()) {
-            String methodString = methodName(method, invoke);
-            logInliningDecision(methodString, false, msg, args);
-        }
-        return null;
-    }
-
-    private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) {
-        printInlining(method, invoke, inliningDepth, false, msg, new Object[0]);
-        if (shouldLogInliningDecision()) {
-            String methodString = methodName(method, invoke);
-            logInliningDecision(methodString, false, msg, new Object[0]);
-        }
-        return false;
-    }
-
-    private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) {
-        String inliningMsg = "inlining " + methodString + ": " + msg;
-        if (!success) {
-            inliningMsg = "not " + inliningMsg;
-        }
-        logInliningDecision(inliningMsg, args);
-    }
-
-    public static boolean shouldLogInliningDecision() {
-        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
-            return Debug.isLogEnabled();
-        }
-    }
-
-    private static String methodName(ResolvedJavaMethod method, Invoke invoke) {
-        if (invoke != null && invoke.stateAfter() != null) {
-            return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)";
-        } else {
-            return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)";
-        }
-    }
-
-    private static String methodName(InlineInfo info) {
-        if (info == null) {
-            return "null";
-        } else if (info.invoke() != null && info.invoke().stateAfter() != null) {
-            return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString();
-        } else {
-            return info.toString();
-        }
-    }
-
-    private static String methodName(FrameState frameState, int bci) {
-        StringBuilder sb = new StringBuilder();
-        if (frameState.outerFrameState() != null) {
-            sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci));
-            sb.append("->");
-        }
-        sb.append(MetaUtil.format("%h.%n", frameState.method()));
-        sb.append("@").append(bci);
-        return sb.toString();
-    }
-
-    /**
-     * Represents an opportunity for inlining at a given invoke, with the given weight and level.
-     * The weight is the amortized weight of the additional code - so smaller is better. The level
-     * is the number of nested inlinings that lead to this invoke.
-     */
-    public interface InlineInfo {
-
-        /**
-         * The graph containing the {@link #invoke() invocation} that may be inlined.
-         */
-        StructuredGraph graph();
-
-        /**
-         * The invocation that may be inlined.
-         */
-        Invoke invoke();
-
-        /**
-         * Returns the number of methods that may be inlined by the {@link #invoke() invocation}.
-         * This may be more than one in the case of a invocation profile showing a number of "hot"
-         * concrete methods dispatched to by the invocation.
-         */
-        int numberOfMethods();
-
-        ResolvedJavaMethod methodAt(int index);
-
-        Inlineable inlineableElementAt(int index);
-
-        double probabilityAt(int index);
-
-        double relevanceAt(int index);
-
-        void setInlinableElement(int index, Inlineable inlineableElement);
-
-        /**
-         * Performs the inlining described by this object and returns the node that represents the
-         * return value of the inlined method (or null for void methods and methods that have no
-         * non-exceptional exit).
-         */
-        void inline(Providers providers, Assumptions assumptions);
-
-        /**
-         * Try to make the call static bindable to avoid interface and virtual method calls.
-         */
-        void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions);
-
-        boolean shouldInline();
-    }
-
-    public abstract static class AbstractInlineInfo implements InlineInfo {
-
-        protected final Invoke invoke;
-
-        public AbstractInlineInfo(Invoke invoke) {
-            this.invoke = invoke;
-        }
-
-        @Override
-        public StructuredGraph graph() {
-            return invoke.asNode().graph();
-        }
-
-        @Override
-        public Invoke invoke() {
-            return invoke;
-        }
-
-        protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, Inlineable inlineable, Assumptions assumptions, boolean receiverNullCheck) {
-            if (inlineable instanceof InlineableGraph) {
-                StructuredGraph calleeGraph = ((InlineableGraph) inlineable).getGraph();
-                InliningUtil.inline(invoke, calleeGraph, receiverNullCheck);
-            } else {
-                assert inlineable instanceof InlineableMacroNode;
-
-                Class<? extends FixedWithNextNode> macroNodeClass = ((InlineableMacroNode) inlineable).getMacroNodeClass();
-                inlineMacroNode(invoke, concrete, macroNodeClass);
-            }
-
-            InlinedBytecodes.add(concrete.getCodeSize());
-            assumptions.recordMethodContents(concrete);
-        }
-    }
-
-    public static void replaceInvokeCallTarget(Invoke invoke, StructuredGraph graph, InvokeKind invokeKind, ResolvedJavaMethod targetMethod) {
-        MethodCallTargetNode oldCallTarget = (MethodCallTargetNode) invoke.callTarget();
-        MethodCallTargetNode newCallTarget = graph.add(new MethodCallTargetNode(invokeKind, targetMethod, oldCallTarget.arguments().toArray(new ValueNode[0]), oldCallTarget.returnType()));
-        invoke.asNode().replaceFirstInput(oldCallTarget, newCallTarget);
-    }
-
-    /**
-     * Represents an inlining opportunity where the compiler can statically determine a monomorphic
-     * target method and therefore is able to determine the called method exactly.
-     */
-    public static class ExactInlineInfo extends AbstractInlineInfo {
-
-        protected final ResolvedJavaMethod concrete;
-        private Inlineable inlineableElement;
-        private boolean suppressNullCheck;
-
-        public ExactInlineInfo(Invoke invoke, ResolvedJavaMethod concrete) {
-            super(invoke);
-            this.concrete = concrete;
-            assert concrete != null;
-        }
-
-        public void suppressNullCheck() {
-            suppressNullCheck = true;
-        }
-
-        @Override
-        public void inline(Providers providers, Assumptions assumptions) {
-            inline(invoke, concrete, inlineableElement, assumptions, !suppressNullCheck);
-        }
-
-        @Override
-        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
-            // nothing todo, can already be bound statically
-        }
-
-        @Override
-        public int numberOfMethods() {
-            return 1;
-        }
-
-        @Override
-        public ResolvedJavaMethod methodAt(int index) {
-            assert index == 0;
-            return concrete;
-        }
-
-        @Override
-        public double probabilityAt(int index) {
-            assert index == 0;
-            return 1.0;
-        }
-
-        @Override
-        public double relevanceAt(int index) {
-            assert index == 0;
-            return 1.0;
-        }
-
-        @Override
-        public String toString() {
-            return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete);
-        }
-
-        @Override
-        public Inlineable inlineableElementAt(int index) {
-            assert index == 0;
-            return inlineableElement;
-        }
-
-        @Override
-        public void setInlinableElement(int index, Inlineable inlineableElement) {
-            assert index == 0;
-            this.inlineableElement = inlineableElement;
-        }
-
-        public boolean shouldInline() {
-            return concrete.shouldBeInlined();
-        }
-    }
-
-    /**
-     * Represents an inlining opportunity for which profiling information suggests a monomorphic
-     * receiver, but for which the receiver type cannot be proven. A type check guard will be
-     * generated if this inlining is performed.
-     */
-    private static class TypeGuardInlineInfo extends AbstractInlineInfo {
-
-        private final ResolvedJavaMethod concrete;
-        private final ResolvedJavaType type;
-        private Inlineable inlineableElement;
-
-        public TypeGuardInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, ResolvedJavaType type) {
-            super(invoke);
-            this.concrete = concrete;
-            this.type = type;
-            assert type.isArray() || !type.isAbstract() : type;
-        }
-
-        @Override
-        public int numberOfMethods() {
-            return 1;
-        }
-
-        @Override
-        public ResolvedJavaMethod methodAt(int index) {
-            assert index == 0;
-            return concrete;
-        }
-
-        @Override
-        public Inlineable inlineableElementAt(int index) {
-            assert index == 0;
-            return inlineableElement;
-        }
-
-        @Override
-        public double probabilityAt(int index) {
-            assert index == 0;
-            return 1.0;
-        }
-
-        @Override
-        public double relevanceAt(int index) {
-            assert index == 0;
-            return 1.0;
-        }
-
-        @Override
-        public void setInlinableElement(int index, Inlineable inlineableElement) {
-            assert index == 0;
-            this.inlineableElement = inlineableElement;
-        }
-
-        @Override
-        public void inline(Providers providers, Assumptions assumptions) {
-            createGuard(graph(), providers.getMetaAccess());
-            inline(invoke, concrete, inlineableElement, assumptions, false);
-        }
-
-        @Override
-        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
-            createGuard(graph(), metaAccess);
-            replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete);
-        }
-
-        private void createGuard(StructuredGraph graph, MetaAccessProvider metaAccess) {
-            ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
-            ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), metaAccess, graph);
-            LoadHubNode receiverHub = graph.unique(new LoadHubNode(nonNullReceiver, typeHub.getKind()));
-
-            CompareNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub);
-            FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile));
-            assert invoke.predecessor() != null;
-
-            ValueNode anchoredReceiver = createAnchoredReceiver(graph, guard, type, nonNullReceiver, true);
-            invoke.callTarget().replaceFirstInput(nonNullReceiver, anchoredReceiver);
-
-            graph.addBeforeFixed(invoke.asNode(), guard);
-        }
-
-        @Override
-        public String toString() {
-            return "type-checked with type " + type.getName() + " and method " + MetaUtil.format("%H.%n(%p):%r", concrete);
-        }
-
-        public boolean shouldInline() {
-            return concrete.shouldBeInlined();
-        }
-    }
-
-    /**
-     * Polymorphic inlining of m methods with n type checks (n &ge; m) in case that the profiling
-     * information suggests a reasonable amount of different receiver types and different methods.
-     * If an unknown type is encountered a deoptimization is triggered.
-     */
-    private static class MultiTypeGuardInlineInfo extends AbstractInlineInfo {
-
-        private final List<ResolvedJavaMethod> concretes;
-        private final double[] methodProbabilities;
-        private final double maximumMethodProbability;
-        private final ArrayList<Integer> typesToConcretes;
-        private final ArrayList<ProfiledType> ptypes;
-        private final ArrayList<Double> concretesProbabilities;
-        private final double notRecordedTypeProbability;
-        private final Inlineable[] inlineableElements;
-
-        public MultiTypeGuardInlineInfo(Invoke invoke, ArrayList<ResolvedJavaMethod> concretes, ArrayList<Double> concretesProbabilities, ArrayList<ProfiledType> ptypes,
-                        ArrayList<Integer> typesToConcretes, double notRecordedTypeProbability) {
-            super(invoke);
-            assert concretes.size() > 0 : "must have at least one method";
-            assert ptypes.size() == typesToConcretes.size() : "array lengths must match";
-
-            this.concretesProbabilities = concretesProbabilities;
-            this.concretes = concretes;
-            this.ptypes = ptypes;
-            this.typesToConcretes = typesToConcretes;
-            this.notRecordedTypeProbability = notRecordedTypeProbability;
-            this.inlineableElements = new Inlineable[concretes.size()];
-            this.methodProbabilities = computeMethodProbabilities();
-            this.maximumMethodProbability = maximumMethodProbability();
-            assert maximumMethodProbability > 0;
-        }
-
-        private double[] computeMethodProbabilities() {
-            double[] result = new double[concretes.size()];
-            for (int i = 0; i < typesToConcretes.size(); i++) {
-                int concrete = typesToConcretes.get(i);
-                double probability = ptypes.get(i).getProbability();
-                result[concrete] += probability;
-            }
-            return result;
-        }
-
-        private double maximumMethodProbability() {
-            double max = 0;
-            for (int i = 0; i < methodProbabilities.length; i++) {
-                max = Math.max(max, methodProbabilities[i]);
-            }
-            return max;
-        }
-
-        @Override
-        public int numberOfMethods() {
-            return concretes.size();
-        }
-
-        @Override
-        public ResolvedJavaMethod methodAt(int index) {
-            assert index >= 0 && index < concretes.size();
-            return concretes.get(index);
-        }
-
-        @Override
-        public Inlineable inlineableElementAt(int index) {
-            assert index >= 0 && index < concretes.size();
-            return inlineableElements[index];
-        }
-
-        @Override
-        public double probabilityAt(int index) {
-            return methodProbabilities[index];
-        }
-
-        @Override
-        public double relevanceAt(int index) {
-            return probabilityAt(index) / maximumMethodProbability;
-        }
-
-        @Override
-        public void setInlinableElement(int index, Inlineable inlineableElement) {
-            assert index >= 0 && index < concretes.size();
-            inlineableElements[index] = inlineableElement;
-        }
-
-        @Override
-        public void inline(Providers providers, Assumptions assumptions) {
-            if (hasSingleMethod()) {
-                inlineSingleMethod(graph(), providers.getMetaAccess(), assumptions);
-            } else {
-                inlineMultipleMethods(graph(), providers, assumptions);
-            }
-        }
-
-        public boolean shouldInline() {
-            for (ResolvedJavaMethod method : concretes) {
-                if (method.shouldBeInlined()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private boolean hasSingleMethod() {
-            return concretes.size() == 1 && !shouldFallbackToInvoke();
-        }
-
-        private boolean shouldFallbackToInvoke() {
-            return notRecordedTypeProbability > 0;
-        }
-
-        private void inlineMultipleMethods(StructuredGraph graph, Providers providers, Assumptions assumptions) {
-            int numberOfMethods = concretes.size();
-            FixedNode continuation = invoke.next();
-
-            ValueNode originalReceiver = ((MethodCallTargetNode) invoke.callTarget()).receiver();
-            // setup merge and phi nodes for results and exceptions
-            MergeNode returnMerge = graph.add(new MergeNode());
-            returnMerge.setStateAfter(invoke.stateAfter());
-
-            PhiNode returnValuePhi = null;
-            if (invoke.asNode().getKind() != Kind.Void) {
-                returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge));
-            }
-
-            MergeNode exceptionMerge = null;
-            PhiNode exceptionObjectPhi = null;
-            if (invoke instanceof InvokeWithExceptionNode) {
-                InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke;
-                ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge();
-
-                exceptionMerge = graph.add(new MergeNode());
-
-                FixedNode exceptionSux = exceptionEdge.next();
-                graph.addBeforeFixed(exceptionSux, exceptionMerge);
-                exceptionObjectPhi = graph.addWithoutUnique(new ValuePhiNode(StampFactory.forKind(Kind.Object), exceptionMerge));
-                exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Object, exceptionObjectPhi));
-            }
-
-            // create one separate block for each invoked method
-            BeginNode[] successors = new BeginNode[numberOfMethods + 1];
-            for (int i = 0; i < numberOfMethods; i++) {
-                successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, true);
-            }
-
-            // create the successor for an unknown type
-            FixedNode unknownTypeSux;
-            if (shouldFallbackToInvoke()) {
-                unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, false);
-            } else {
-                unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated));
-            }
-            successors[successors.length - 1] = BeginNode.begin(unknownTypeSux);
-
-            // replace the invoke exception edge
-            if (invoke instanceof InvokeWithExceptionNode) {
-                InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke;
-                ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExceptionNode.exceptionEdge();
-                exceptionEdge.replaceAtUsages(exceptionObjectPhi);
-                exceptionEdge.setNext(null);
-                GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge());
-            }
-
-            assert invoke.asNode().isAlive();
-
-            // replace the invoke with a switch on the type of the actual receiver
-            boolean methodDispatch = createDispatchOnTypeBeforeInvoke(graph, successors, false, providers.getMetaAccess());
-
-            assert invoke.next() == continuation;
-            invoke.setNext(null);
-            returnMerge.setNext(continuation);
-            invoke.asNode().replaceAtUsages(returnValuePhi);
-            invoke.asNode().replaceAndDelete(null);
-
-            ArrayList<GuardedValueNode> replacementNodes = new ArrayList<>();
-
-            // do the actual inlining for every invoke
-            for (int i = 0; i < numberOfMethods; i++) {
-                BeginNode node = successors[i];
-                Invoke invokeForInlining = (Invoke) node.next();
-
-                ResolvedJavaType commonType;
-                if (methodDispatch) {
-                    commonType = concretes.get(i).getDeclaringClass();
-                } else {
-                    commonType = getLeastCommonType(i);
-                }
-
-                ValueNode receiver = ((MethodCallTargetNode) invokeForInlining.callTarget()).receiver();
-                boolean exact = (getTypeCount(i) == 1 && !methodDispatch);
-                GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact);
-                invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver);
-
-                inline(invokeForInlining, methodAt(i), inlineableElementAt(i), assumptions, false);
-
-                replacementNodes.add(anchoredReceiver);
-            }
-            if (shouldFallbackToInvoke()) {
-                replacementNodes.add(null);
-            }
-
-            if (OptTailDuplication.getValue()) {
-                /*
-                 * We might want to perform tail duplication at the merge after a type switch, if
-                 * there are invokes that would benefit from the improvement in type information.
-                 */
-                FixedNode current = returnMerge;
-                int opportunities = 0;
-                do {
-                    if (current instanceof InvokeNode && ((InvokeNode) current).callTarget() instanceof MethodCallTargetNode &&
-                                    ((MethodCallTargetNode) ((InvokeNode) current).callTarget()).receiver() == originalReceiver) {
-                        opportunities++;
-                    } else if (current.inputs().contains(originalReceiver)) {
-                        opportunities++;
-                    }
-                    current = ((FixedWithNextNode) current).next();
-                } while (current instanceof FixedWithNextNode);
-
-                if (opportunities > 0) {
-                    metricInliningTailDuplication.increment();
-                    Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities);
-                    PhaseContext phaseContext = new PhaseContext(providers, assumptions);
-                    CanonicalizerPhase canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue());
-                    TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacementNodes, phaseContext, canonicalizer);
-                }
-            }
-        }
-
-        private int getTypeCount(int concreteMethodIndex) {
-            int count = 0;
-            for (int i = 0; i < typesToConcretes.size(); i++) {
-                if (typesToConcretes.get(i) == concreteMethodIndex) {
-                    count++;
-                }
-            }
-            return count;
-        }
-
-        private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) {
-            ResolvedJavaType commonType = null;
-            for (int i = 0; i < typesToConcretes.size(); i++) {
-                if (typesToConcretes.get(i) == concreteMethodIndex) {
-                    if (commonType == null) {
-                        commonType = ptypes.get(i).getType();
-                    } else {
-                        commonType = commonType.findLeastCommonAncestor(ptypes.get(i).getType());
-                    }
-                }
-            }
-            assert commonType != null;
-            return commonType;
-        }
-
-        private ResolvedJavaType getLeastCommonType() {
-            ResolvedJavaType result = getLeastCommonType(0);
-            for (int i = 1; i < concretes.size(); i++) {
-                result = result.findLeastCommonAncestor(getLeastCommonType(i));
-            }
-            return result;
-        }
-
-        private void inlineSingleMethod(StructuredGraph graph, MetaAccessProvider metaAccess, Assumptions assumptions) {
-            assert concretes.size() == 1 && inlineableElements.length == 1 && ptypes.size() > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0;
-
-            BeginNode calleeEntryNode = graph.add(new BeginNode());
-
-            BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph);
-            BeginNode[] successors = new BeginNode[]{calleeEntryNode, unknownTypeSux};
-            createDispatchOnTypeBeforeInvoke(graph, successors, false, metaAccess);
-
-            calleeEntryNode.setNext(invoke.asNode());
-
-            inline(invoke, methodAt(0), inlineableElementAt(0), assumptions, false);
-        }
-
-        private boolean createDispatchOnTypeBeforeInvoke(StructuredGraph graph, BeginNode[] successors, boolean invokeIsOnlySuccessor, MetaAccessProvider metaAccess) {
-            assert ptypes.size() >= 1;
-            ValueNode nonNullReceiver = nonNullReceiver(invoke);
-            Kind hubKind = ((MethodCallTargetNode) invoke.callTarget()).targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind();
-            LoadHubNode hub = graph.unique(new LoadHubNode(nonNullReceiver, hubKind));
-
-            if (!invokeIsOnlySuccessor && chooseMethodDispatch()) {
-                assert successors.length == concretes.size() + 1;
-                assert concretes.size() > 0;
-                Debug.log("Method check cascade with %d methods", concretes.size());
-
-                ValueNode[] constantMethods = new ValueNode[concretes.size()];
-                double[] probability = new double[concretes.size()];
-                for (int i = 0; i < concretes.size(); ++i) {
-                    ResolvedJavaMethod firstMethod = concretes.get(i);
-                    Constant firstMethodConstant = firstMethod.getEncoding();
-
-                    ValueNode firstMethodConstantNode = ConstantNode.forConstant(firstMethodConstant, metaAccess, graph);
-                    constantMethods[i] = firstMethodConstantNode;
-                    double concretesProbability = concretesProbabilities.get(i);
-                    assert concretesProbability >= 0.0;
-                    probability[i] = concretesProbability;
-                    if (i > 0) {
-                        double prevProbability = probability[i - 1];
-                        if (prevProbability == 1.0) {
-                            probability[i] = 1.0;
-                        } else {
-                            probability[i] = Math.min(1.0, Math.max(0.0, probability[i] / (1.0 - prevProbability)));
-                        }
-                    }
-                }
-
-                FixedNode lastSucc = successors[concretes.size()];
-                for (int i = concretes.size() - 1; i >= 0; --i) {
-                    LoadMethodNode method = graph.add(new LoadMethodNode(concretes.get(i), hub, constantMethods[i].getKind()));
-                    CompareNode methodCheck = CompareNode.createCompareNode(graph, Condition.EQ, method, constantMethods[i]);
-                    IfNode ifNode = graph.add(new IfNode(methodCheck, successors[i], lastSucc, probability[i]));
-                    method.setNext(ifNode);
-                    lastSucc = method;
-                }
-
-                FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor();
-                pred.setNext(lastSucc);
-                return true;
-            } else {
-                Debug.log("Type switch with %d types", concretes.size());
-            }
-
-            ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.size()];
-            double[] keyProbabilities = new double[ptypes.size() + 1];
-            int[] keySuccessors = new int[ptypes.size() + 1];
-            for (int i = 0; i < ptypes.size(); i++) {
-                keys[i] = ptypes.get(i).getType();
-                keyProbabilities[i] = ptypes.get(i).getProbability();
-                keySuccessors[i] = invokeIsOnlySuccessor ? 0 : typesToConcretes.get(i);
-                assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux";
-            }
-            keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability;
-            keySuccessors[keySuccessors.length - 1] = successors.length - 1;
-
-            TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, keys, keyProbabilities, keySuccessors));
-            FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor();
-            pred.setNext(typeSwitch);
-            return false;
-        }
-
-        private boolean chooseMethodDispatch() {
-            for (ResolvedJavaMethod concrete : concretes) {
-                if (!concrete.isInVirtualMethodTable()) {
-                    return false;
-                }
-            }
-
-            if (concretes.size() == 1 && this.notRecordedTypeProbability > 0) {
-                // Always chose method dispatch if there is a single concrete method and the call
-                // site is megamorphic.
-                return true;
-            }
-
-            if (concretes.size() == ptypes.size()) {
-                // Always prefer types over methods if the number of types is smaller than the
-                // number of methods.
-                return false;
-            }
-
-            return chooseMethodDispatchCostBased();
-        }
-
-        private boolean chooseMethodDispatchCostBased() {
-            double remainder = 1.0 - this.notRecordedTypeProbability;
-            double costEstimateMethodDispatch = remainder;
-            for (int i = 0; i < concretes.size(); ++i) {
-                if (i != 0) {
-                    costEstimateMethodDispatch += remainder;
-                }
-                remainder -= concretesProbabilities.get(i);
-            }
-
-            double costEstimateTypeDispatch = 0.0;
-            remainder = 1.0;
-            for (int i = 0; i < ptypes.size(); ++i) {
-                if (i != 0) {
-                    costEstimateTypeDispatch += remainder;
-                }
-                remainder -= ptypes.get(i).getProbability();
-            }
-            costEstimateTypeDispatch += notRecordedTypeProbability;
-            return costEstimateMethodDispatch < costEstimateTypeDispatch;
-        }
-
-        private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, MergeNode exceptionMerge, PhiNode exceptionObjectPhi,
-                        boolean useForInlining) {
-            Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining);
-            BeginNode calleeEntryNode = graph.add(new BeginNode());
-            calleeEntryNode.setNext(duplicatedInvoke.asNode());
-
-            AbstractEndNode endNode = graph.add(new EndNode());
-            duplicatedInvoke.setNext(endNode);
-            returnMerge.addForwardEnd(endNode);
-
-            if (returnValuePhi != null) {
-                returnValuePhi.addInput(duplicatedInvoke.asNode());
-            }
-            return calleeEntryNode;
-        }
-
-        private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining) {
-            Invoke result = (Invoke) invoke.asNode().copyWithInputs();
-            Node callTarget = result.callTarget().copyWithInputs();
-            result.asNode().replaceFirstInput(result.callTarget(), callTarget);
-            result.setUseForInlining(useForInlining);
-
-            Kind kind = invoke.asNode().getKind();
-            if (kind != Kind.Void) {
-                FrameState stateAfter = invoke.stateAfter();
-                stateAfter = stateAfter.duplicate(stateAfter.bci);
-                stateAfter.replaceFirstInput(invoke.asNode(), result.asNode());
-                result.setStateAfter(stateAfter);
-            }
-
-            if (invoke instanceof InvokeWithExceptionNode) {
-                assert exceptionMerge != null && exceptionObjectPhi != null;
-
-                InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke;
-                ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge();
-                FrameState stateAfterException = exceptionEdge.stateAfter();
-
-                ExceptionObjectNode newExceptionEdge = (ExceptionObjectNode) exceptionEdge.copyWithInputs();
-                // set new state (pop old exception object, push new one)
-                newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionEdge));
-
-                AbstractEndNode endNode = graph.add(new EndNode());
-                newExceptionEdge.setNext(endNode);
-                exceptionMerge.addForwardEnd(endNode);
-                exceptionObjectPhi.addInput(newExceptionEdge);
-
-                ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge);
-            }
-            return result;
-        }
-
-        @Override
-        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
-            if (hasSingleMethod()) {
-                devirtualizeWithTypeSwitch(graph(), InvokeKind.Special, concretes.get(0), metaAccess);
-            } else {
-                tryToDevirtualizeMultipleMethods(graph(), metaAccess);
-            }
-        }
-
-        private void tryToDevirtualizeMultipleMethods(StructuredGraph graph, MetaAccessProvider metaAccess) {
-            MethodCallTargetNode methodCallTarget = (MethodCallTargetNode) invoke.callTarget();
-            if (methodCallTarget.invokeKind() == InvokeKind.Interface) {
-                ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod();
-                ResolvedJavaType leastCommonType = getLeastCommonType();
-                // check if we have a common base type that implements the interface -> in that case
-                // we have a vtable entry for the interface method and can use a less expensive
-                // virtual call
-                if (!leastCommonType.isInterface() && targetMethod.getDeclaringClass().isAssignableFrom(leastCommonType)) {
-                    ResolvedJavaMethod baseClassTargetMethod = leastCommonType.resolveMethod(targetMethod);
-                    if (baseClassTargetMethod != null) {
-                        devirtualizeWithTypeSwitch(graph, InvokeKind.Virtual, leastCommonType.resolveMethod(targetMethod), metaAccess);
-                    }
-                }
-            }
-        }
-
-        private void devirtualizeWithTypeSwitch(StructuredGraph graph, InvokeKind kind, ResolvedJavaMethod target, MetaAccessProvider metaAccess) {
-            BeginNode invocationEntry = graph.add(new BeginNode());
-            BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph);
-            BeginNode[] successors = new BeginNode[]{invocationEntry, unknownTypeSux};
-            createDispatchOnTypeBeforeInvoke(graph, successors, true, metaAccess);
-
-            invocationEntry.setNext(invoke.asNode());
-            ValueNode receiver = ((MethodCallTargetNode) invoke.callTarget()).receiver();
-            GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, invocationEntry, target.getDeclaringClass(), receiver, false);
-            invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver);
-            replaceInvokeCallTarget(invoke, graph, kind, target);
-        }
-
-        private static BeginNode createUnknownTypeSuccessor(StructuredGraph graph) {
-            return BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated)));
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic");
-            builder.append(", ");
-            builder.append(concretes.size());
-            builder.append(" methods [ ");
-            for (int i = 0; i < concretes.size(); i++) {
-                builder.append(MetaUtil.format("  %H.%n(%p):%r", concretes.get(i)));
-            }
-            builder.append(" ], ");
-            builder.append(ptypes.size());
-            builder.append(" type checks [ ");
-            for (int i = 0; i < ptypes.size(); i++) {
-                builder.append("  ");
-                builder.append(ptypes.get(i).getType().getName());
-                builder.append(ptypes.get(i).getProbability());
-            }
-            builder.append(" ]");
-            return builder.toString();
-        }
-    }
-
-    /**
-     * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic
-     * target method, but for which an assumption has to be registered because of non-final classes.
-     */
-    private static class AssumptionInlineInfo extends ExactInlineInfo {
-
-        private final Assumption takenAssumption;
-
-        public AssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumption takenAssumption) {
-            super(invoke, concrete);
-            this.takenAssumption = takenAssumption;
-        }
-
-        @Override
-        public void inline(Providers providers, Assumptions assumptions) {
-            assumptions.record(takenAssumption);
-            super.inline(providers, assumptions);
-        }
-
-        @Override
-        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
-            assumptions.record(takenAssumption);
-            replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete);
-        }
-
-        @Override
-        public String toString() {
-            return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete);
-        }
-    }
-
-    /**
-     * Determines if inlining is possible at the given invoke node.
-     *
-     * @param invoke the invoke that should be inlined
-     * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke
-     */
-    public static InlineInfo getInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, Assumptions assumptions, OptimisticOptimizations optimisticOpts) {
-        if (!checkInvokeConditions(invoke)) {
-            return null;
-        }
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
-        ResolvedJavaMethod targetMethod = callTarget.targetMethod();
-
-        if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) {
-            return getExactInlineInfo(data, invoke, replacements, optimisticOpts, targetMethod);
-        }
-
-        assert callTarget.invokeKind() == InvokeKind.Virtual || callTarget.invokeKind() == InvokeKind.Interface;
-
-        ResolvedJavaType holder = targetMethod.getDeclaringClass();
-        if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) {
-            return null;
-        }
-        ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp();
-        if (receiverStamp.alwaysNull()) {
-            // Don't inline if receiver is known to be null
-            return null;
-        }
-        if (receiverStamp.type() != null) {
-            // the invoke target might be more specific than the holder (happens after inlining:
-            // parameters lose their declared type...)
-            ResolvedJavaType receiverType = receiverStamp.type();
-            if (receiverType != null && holder.isAssignableFrom(receiverType)) {
-                holder = receiverType;
-                if (receiverStamp.isExactType()) {
-                    assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod;
-                    ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod);
-                    if (resolvedMethod != null) {
-                        return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod);
-                    }
-                }
-            }
-        }
-
-        if (holder.isArray()) {
-            // arrays can be treated as Objects
-            ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod);
-            if (resolvedMethod != null) {
-                return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod);
-            }
-        }
-
-        if (assumptions.useOptimisticAssumptions()) {
-            ResolvedJavaType uniqueSubtype = holder.findUniqueConcreteSubtype();
-            if (uniqueSubtype != null) {
-                ResolvedJavaMethod resolvedMethod = uniqueSubtype.resolveMethod(targetMethod);
-                if (resolvedMethod != null) {
-                    return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod, new Assumptions.ConcreteSubtype(holder, uniqueSubtype));
-                }
-            }
-
-            ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod);
-            if (concrete != null) {
-                return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, concrete, new Assumptions.ConcreteMethod(targetMethod, holder, concrete));
-            }
-        }
-
-        // type check based inlining
-        return getTypeCheckedInlineInfo(data, invoke, maxNumberOfMethods, replacements, targetMethod, optimisticOpts);
-    }
-
-    private static InlineInfo getAssumptionInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod concrete,
-                    Assumption takenAssumption) {
-        assert !concrete.isAbstract();
-        if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) {
-            return null;
-        }
-        return new AssumptionInlineInfo(invoke, concrete, takenAssumption);
-    }
-
-    private static InlineInfo getExactInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod targetMethod) {
-        assert !targetMethod.isAbstract();
-        if (!checkTargetConditions(data, replacements, invoke, targetMethod, optimisticOpts)) {
-            return null;
-        }
-        return new ExactInlineInfo(invoke, targetMethod);
-    }
-
-    private static InlineInfo getTypeCheckedInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, ResolvedJavaMethod targetMethod,
-                    OptimisticOptimizations optimisticOpts) {
-        JavaTypeProfile typeProfile;
-        ValueNode receiver = invoke.callTarget().arguments().get(0);
-        if (receiver instanceof TypeProfileProxyNode) {
-            TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver;
-            typeProfile = typeProfileProxyNode.getProfile();
-        } else {
-            return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no type profile exists");
-        }
-
-        ProfiledType[] ptypes = typeProfile.getTypes();
-        if (ptypes == null || ptypes.length <= 0) {
-            return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types in profile");
-        }
-
-        double notRecordedTypeProbability = typeProfile.getNotRecordedProbability();
-        if (ptypes.length == 1 && notRecordedTypeProbability == 0) {
-            if (!optimisticOpts.inlineMonomorphicCalls()) {
-                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining monomorphic calls is disabled");
-            }
-
-            ResolvedJavaType type = ptypes[0].getType();
-            assert type.isArray() || !type.isAbstract();
-            ResolvedJavaMethod concrete = type.resolveMethod(targetMethod);
-            if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) {
-                return null;
-            }
-            return new TypeGuardInlineInfo(invoke, concrete, type);
-        } else {
-            invoke.setPolymorphic(true);
-
-            if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) {
-                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length);
-            }
-            if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) {
-                // due to filtering impossible types, notRecordedTypeProbability can be > 0 although
-                // the number of types is lower than what can be recorded in a type profile
-                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length,
-                                notRecordedTypeProbability * 100);
-            }
-
-            // Find unique methods and their probabilities.
-            ArrayList<ResolvedJavaMethod> concreteMethods = new ArrayList<>();
-            ArrayList<Double> concreteMethodsProbabilities = new ArrayList<>();
-            for (int i = 0; i < ptypes.length; i++) {
-                ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod);
-                if (concrete == null) {
-                    return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "could not resolve method");
-                }
-                int index = concreteMethods.indexOf(concrete);
-                double curProbability = ptypes[i].getProbability();
-                if (index < 0) {
-                    index = concreteMethods.size();
-                    concreteMethods.add(concrete);
-                    concreteMethodsProbabilities.add(curProbability);
-                } else {
-                    concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability);
-                }
-            }
-
-            if (concreteMethods.size() > maxNumberOfMethods) {
-                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxNumberOfMethods);
-            }
-
-            // Clear methods that fall below the threshold.
-            if (notRecordedTypeProbability > 0) {
-                ArrayList<ResolvedJavaMethod> newConcreteMethods = new ArrayList<>();
-                ArrayList<Double> newConcreteMethodsProbabilities = new ArrayList<>();
-                for (int i = 0; i < concreteMethods.size(); ++i) {
-                    if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue()) {
-                        newConcreteMethods.add(concreteMethods.get(i));
-                        newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i));
-                    }
-                }
-
-                if (newConcreteMethods.size() == 0) {
-                    // No method left that is worth inlining.
-                    return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)",
-                                    concreteMethods.size());
-                }
-
-                concreteMethods = newConcreteMethods;
-                concreteMethodsProbabilities = newConcreteMethodsProbabilities;
-            }
-
-            // Clean out types whose methods are no longer available.
-            ArrayList<ProfiledType> usedTypes = new ArrayList<>();
-            ArrayList<Integer> typesToConcretes = new ArrayList<>();
-            for (ProfiledType type : ptypes) {
-                ResolvedJavaMethod concrete = type.getType().resolveMethod(targetMethod);
-                int index = concreteMethods.indexOf(concrete);
-                if (index == -1) {
-                    notRecordedTypeProbability += type.getProbability();
-                } else {
-                    assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete;
-                    usedTypes.add(type);
-                    typesToConcretes.add(index);
-                }
-            }
-
-            if (usedTypes.size() == 0) {
-                // No type left that is worth checking for.
-                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length);
-            }
-
-            for (ResolvedJavaMethod concrete : concreteMethods) {
-                if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) {
-                    return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined");
-                }
-            }
-            return new MultiTypeGuardInlineInfo(invoke, concreteMethods, concreteMethodsProbabilities, usedTypes, typesToConcretes, notRecordedTypeProbability);
-        }
-    }
-
-    private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) {
-        return createAnchoredReceiver(graph, anchor, receiver, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType));
-    }
-
-    private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ValueNode receiver, Stamp stamp) {
-        // to avoid that floating reads on receiver fields float above the type check
-        return graph.unique(new GuardedValueNode(receiver, anchor, stamp));
-    }
-
-    // TODO (chaeubl): cleanup this method
-    private static boolean checkInvokeConditions(Invoke invoke) {
-        if (invoke.predecessor() == null || !invoke.asNode().isAlive()) {
-            return logNotInlinedMethod(invoke, "the invoke is dead code");
-        } else if (!(invoke.callTarget() instanceof MethodCallTargetNode)) {
-            return logNotInlinedMethod(invoke, "the invoke has already been lowered, or has been created as a low-level node");
-        } else if (((MethodCallTargetNode) invoke.callTarget()).targetMethod() == null) {
-            return logNotInlinedMethod(invoke, "target method is null");
-        } else if (invoke.stateAfter() == null) {
-            // TODO (chaeubl): why should an invoke not have a state after?
-            return logNotInlinedMethod(invoke, "the invoke has no after state");
-        } else if (!invoke.useForInlining()) {
-            return logNotInlinedMethod(invoke, "the invoke is marked to be not used for inlining");
-        } else if (((MethodCallTargetNode) invoke.callTarget()).receiver() != null && ((MethodCallTargetNode) invoke.callTarget()).receiver().isConstant() &&
-                        ((MethodCallTargetNode) invoke.callTarget()).receiver().asConstant().isNull()) {
-            return logNotInlinedMethod(invoke, "receiver is null");
-        } else {
-            return true;
-        }
-    }
-
-    private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) {
-        if (method == null) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved");
-        } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(replacements, method))) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is a non-intrinsic native method");
-        } else if (method.isAbstract()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method");
-        } else if (!method.getDeclaringClass().isInitialized()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized");
-        } else if (!method.canBeInlined()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable");
-        } else if (data.countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it exceeds the maximum recursive inlining depth");
-        } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(optimisticOpts)) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller");
-        } else {
-            return true;
-        }
-    }
-
-    static MonitorExitNode findPrecedingMonitorExit(UnwindNode unwind) {
-        Node pred = unwind.predecessor();
-        while (pred != null) {
-            if (pred instanceof MonitorExitNode) {
-                return (MonitorExitNode) pred;
-            }
-            pred = pred.predecessor();
-        }
-        return null;
-    }
-
-    /**
-     * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph.
-     *
-     * @param invoke the invoke that will be replaced
-     * @param inlineGraph the graph that the invoke will be replaced with
-     * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings,
-     *            false if no such check is required
-     */
-    public static Map<Node, Node> inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) {
-        final NodeInputList<ValueNode> parameters = invoke.callTarget().arguments();
-        FixedNode invokeNode = invoke.asNode();
-        StructuredGraph graph = invokeNode.graph();
-        assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal();
-        assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase";
-
-        FrameState stateAfter = invoke.stateAfter();
-        assert stateAfter == null || stateAfter.isAlive();
-        if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) {
-            nonNullReceiver(invoke);
-        }
-
-        ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count());
-        ArrayList<ReturnNode> returnNodes = new ArrayList<>(4);
-        UnwindNode unwindNode = null;
-        final StartNode entryPointNode = inlineGraph.start();
-        FixedNode firstCFGNode = entryPointNode.next();
-        if (firstCFGNode == null) {
-            throw new IllegalStateException("Inlined graph is in invalid state");
-        }
-        for (Node node : inlineGraph.getNodes()) {
-            if (node == entryPointNode || node == entryPointNode.stateAfter() || node instanceof ParameterNode) {
-                // Do nothing.
-            } else {
-                nodes.add(node);
-                if (node instanceof ReturnNode) {
-                    returnNodes.add((ReturnNode) node);
-                } else if (node instanceof UnwindNode) {
-                    assert unwindNode == null;
-                    unwindNode = (UnwindNode) node;
-                }
-            }
-        }
-
-        final BeginNode prevBegin = BeginNode.prevBegin(invokeNode);
-        DuplicationReplacement localReplacement = new DuplicationReplacement() {
-
-            public Node replacement(Node node) {
-                if (node instanceof ParameterNode) {
-                    return parameters.get(((ParameterNode) node).index());
-                } else if (node == entryPointNode) {
-                    return prevBegin;
-                }
-                return node;
-            }
-        };
-
-        assert invokeNode.successors().first() != null : invoke;
-        assert invokeNode.predecessor() != null;
-
-        Map<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
-        FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
-        invokeNode.replaceAtPredecessor(firstCFGNodeDuplicate);
-
-        FrameState stateAtExceptionEdge = null;
-        if (invoke instanceof InvokeWithExceptionNode) {
-            InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
-            if (unwindNode != null) {
-                assert unwindNode.predecessor() != null;
-                assert invokeWithException.exceptionEdge().successors().count() == 1;
-                ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
-                stateAtExceptionEdge = obj.stateAfter();
-                UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode);
-                obj.replaceAtUsages(unwindDuplicate.exception());
-                unwindDuplicate.clearInputs();
-                Node n = obj.next();
-                obj.setNext(null);
-                unwindDuplicate.replaceAndDelete(n);
-            } else {
-                invokeWithException.killExceptionEdge();
-            }
-
-            // get rid of memory kill
-            BeginNode begin = invokeWithException.next();
-            if (begin instanceof KillingBeginNode) {
-                BeginNode newBegin = new BeginNode();
-                graph.addAfterFixed(begin, graph.add(newBegin));
-                begin.replaceAtUsages(newBegin);
-                graph.removeFixed(begin);
-            }
-        } else {
-            if (unwindNode != null) {
-                UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode);
-                DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
-                unwindDuplicate.replaceAndDelete(deoptimizeNode);
-            }
-        }
-
-        if (stateAfter != null) {
-            processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge);
-            int callerLockDepth = stateAfter.nestedLockDepth();
-            if (callerLockDepth != 0) {
-                for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.class)) {
-                    MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original);
-                    monitor.setLockDepth(monitor.getLockDepth() + callerLockDepth);
-                }
-            }
-        } else {
-            assert checkContainsOnlyInvalidOrAfterFrameState(duplicates);
-        }
-        if (!returnNodes.isEmpty()) {
-            FixedNode n = invoke.next();
-            invoke.setNext(null);
-            if (returnNodes.size() == 1) {
-                ReturnNode returnNode = (ReturnNode) duplicates.get(returnNodes.get(0));
-                Node returnValue = returnNode.result();
-                invokeNode.replaceAtUsages(returnValue);
-                returnNode.clearInputs();
-                returnNode.replaceAndDelete(n);
-            } else {
-                ArrayList<ReturnNode> returnDuplicates = new ArrayList<>(returnNodes.size());
-                for (ReturnNode returnNode : returnNodes) {
-                    returnDuplicates.add((ReturnNode) duplicates.get(returnNode));
-                }
-                MergeNode merge = graph.add(new MergeNode());
-                merge.setStateAfter(stateAfter);
-                ValueNode returnValue = mergeReturns(merge, returnDuplicates);
-                invokeNode.replaceAtUsages(returnValue);
-                merge.setNext(n);
-            }
-        }
-
-        invokeNode.replaceAtUsages(null);
-        GraphUtil.killCFG(invokeNode);
-
-        return duplicates;
-    }
-
-    protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, Map<Node, Node> duplicates, FrameState stateAtExceptionEdge) {
-        FrameState stateAtReturn = invoke.stateAfter();
-        FrameState outerFrameState = null;
-        Kind invokeReturnKind = invoke.asNode().getKind();
-        for (FrameState original : inlineGraph.getNodes(FrameState.class)) {
-            FrameState frameState = (FrameState) duplicates.get(original);
-            if (frameState != null && frameState.isAlive()) {
-                if (frameState.bci == BytecodeFrame.AFTER_BCI) {
-                    /*
-                     * pop return kind from invoke's stateAfter and replace with this frameState's
-                     * return value (top of stack)
-                     */
-                    FrameState stateAfterReturn = stateAtReturn;
-                    if (invokeReturnKind != Kind.Void && frameState.stackSize() > 0 && stateAfterReturn.stackAt(0) != frameState.stackAt(0)) {
-                        stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, frameState.stackAt(0));
-                    }
-                    frameState.replaceAndDelete(stateAfterReturn);
-                } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) {
-                    /*
-                     * pop exception object from invoke's stateAfter and replace with this
-                     * frameState's exception object (top of stack)
-                     */
-                    FrameState stateAfterException = stateAtExceptionEdge;
-                    if (frameState.stackSize() > 0 && stateAtExceptionEdge.stackAt(0) != frameState.stackAt(0)) {
-                        stateAfterException = stateAtExceptionEdge.duplicateModified(Kind.Object, frameState.stackAt(0));
-                    }
-                    frameState.replaceAndDelete(stateAfterException);
-                } else if (frameState.bci == BytecodeFrame.UNWIND_BCI || frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) {
-                    handleMissingAfterExceptionFrameState(frameState);
-                } else {
-                    // only handle the outermost frame states
-                    if (frameState.outerFrameState() == null) {
-                        assert frameState.bci != BytecodeFrame.BEFORE_BCI : frameState;
-                        assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI || frameState.method().equals(inlineGraph.method());
-                        assert frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI && frameState.bci != BytecodeFrame.BEFORE_BCI && frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI &&
-                                        frameState.bci != BytecodeFrame.UNWIND_BCI : frameState.bci;
-                        if (outerFrameState == null) {
-                            outerFrameState = stateAtReturn.duplicateModified(invoke.bci(), stateAtReturn.rethrowException(), invokeReturnKind);
-                            outerFrameState.setDuringCall(true);
-                        }
-                        frameState.setOuterFrameState(outerFrameState);
-                    }
-                }
-            }
-        }
-    }
-
-    private static boolean isStateAfterException(FrameState frameState) {
-        return frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || (frameState.bci == BytecodeFrame.UNWIND_BCI && !frameState.method().isSynchronized());
-    }
-
-    protected static void handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState) {
-        Graph graph = nonReplaceableFrameState.graph();
-        NodeWorkList workList = graph.createNodeWorkList();
-        workList.add(nonReplaceableFrameState);
-        for (Node node : workList) {
-            FrameState fs = (FrameState) node;
-            for (Node usage : fs.usages().snapshot()) {
-                if (!usage.isAlive()) {
-                    continue;
-                }
-                if (usage instanceof FrameState) {
-                    workList.add(usage);
-                } else {
-                    StateSplit stateSplit = (StateSplit) usage;
-                    FixedNode fixedStateSplit = stateSplit.asNode();
-                    if (fixedStateSplit instanceof MergeNode) {
-                        MergeNode merge = (MergeNode) fixedStateSplit;
-                        while (merge.isAlive()) {
-                            AbstractEndNode end = merge.forwardEnds().first();
-                            DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
-                            end.replaceAtPredecessor(deoptimizeNode);
-                            GraphUtil.killCFG(end);
-                        }
-                    } else {
-                        FixedNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
-                        if (fixedStateSplit instanceof BeginNode) {
-                            deoptimizeNode = BeginNode.begin(deoptimizeNode);
-                        }
-                        fixedStateSplit.replaceAtPredecessor(deoptimizeNode);
-                        GraphUtil.killCFG(fixedStateSplit);
-                    }
-                }
-            }
-        }
-    }
-
-    public static ValueNode mergeReturns(MergeNode merge, List<? extends ReturnNode> returnNodes) {
-        PhiNode returnValuePhi = null;
-
-        for (ReturnNode returnNode : returnNodes) {
-            // create and wire up a new EndNode
-            EndNode endNode = merge.graph().add(new EndNode());
-            merge.addForwardEnd(endNode);
-
-            if (returnNode.result() != null) {
-                if (returnValuePhi == null) {
-                    returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge));
-                }
-                returnValuePhi.addInput(returnNode.result());
-            }
-            returnNode.clearInputs();
-            returnNode.replaceAndDelete(endNode);
-
-        }
-        return returnValuePhi;
-    }
-
-    private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map<Node, Node> duplicates) {
-        for (Node node : duplicates.values()) {
-            if (node instanceof FrameState) {
-                FrameState frameState = (FrameState) node;
-                assert frameState.bci == BytecodeFrame.AFTER_BCI || frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI : node.toString(Verbosity.Debugger);
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Gets the receiver for an invoke, adding a guard if necessary to ensure it is non-null.
-     */
-    public static ValueNode nonNullReceiver(Invoke invoke) {
-        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
-        assert !callTarget.isStatic() : callTarget.targetMethod();
-        StructuredGraph graph = callTarget.graph();
-        ValueNode firstParam = callTarget.arguments().get(0);
-        if (firstParam.getKind() == Kind.Object && !StampTool.isObjectNonNull(firstParam)) {
-            IsNullNode condition = graph.unique(new IsNullNode(firstParam));
-            Stamp stamp = firstParam.stamp().join(objectNonNull());
-            GuardingPiNode nonNullReceiver = graph.add(new GuardingPiNode(firstParam, condition, true, NullCheckException, InvalidateReprofile, stamp));
-            graph.addBeforeFixed(invoke.asNode(), nonNullReceiver);
-            callTarget.replaceFirstInput(firstParam, nonNullReceiver);
-            return nonNullReceiver;
-        }
-        return firstParam;
-    }
-
-    public static boolean canIntrinsify(Replacements replacements, ResolvedJavaMethod target) {
-        return getIntrinsicGraph(replacements, target) != null || getMacroNodeClass(replacements, target) != null;
-    }
-
-    public static StructuredGraph getIntrinsicGraph(Replacements replacements, ResolvedJavaMethod target) {
-        return replacements.getMethodSubstitution(target);
-    }
-
-    public static Class<? extends FixedWithNextNode> getMacroNodeClass(Replacements replacements, ResolvedJavaMethod target) {
-        return replacements.getMacroSubstitution(target);
-    }
-
-    public static FixedWithNextNode inlineMacroNode(Invoke invoke, ResolvedJavaMethod concrete, Class<? extends FixedWithNextNode> macroNodeClass) throws GraalInternalError {
-        StructuredGraph graph = invoke.asNode().graph();
-        if (!concrete.equals(((MethodCallTargetNode) invoke.callTarget()).targetMethod())) {
-            assert ((MethodCallTargetNode) invoke.callTarget()).invokeKind() != InvokeKind.Static;
-            InliningUtil.replaceInvokeCallTarget(invoke, graph, InvokeKind.Special, concrete);
-        }
-
-        FixedWithNextNode macroNode = createMacroNodeInstance(macroNodeClass, invoke);
-
-        CallTargetNode callTarget = invoke.callTarget();
-        if (invoke instanceof InvokeNode) {
-            graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode));
-        } else {
-            InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke;
-            invokeWithException.killExceptionEdge();
-            graph.replaceSplitWithFixed(invokeWithException, graph.add(macroNode), invokeWithException.next());
-        }
-        GraphUtil.killWithUnusedFloatingInputs(callTarget);
-        return macroNode;
-    }
-
-    private static FixedWithNextNode createMacroNodeInstance(Class<? extends FixedWithNextNode> macroNodeClass, Invoke invoke) throws GraalInternalError {
-        try {
-            return macroNodeClass.getConstructor(Invoke.class).newInstance(invoke);
-        } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
-            throw new GraalGraphInternalError(e).addContext(invoke.asNode()).addContext("macroSubstitution", macroNodeClass);
-        }
-    }
-}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ProfileCompiledMethodsPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ProfileCompiledMethodsPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -23,6 +23,7 @@
 package com.oracle.graal.phases.common;
 
 import java.util.*;
+import java.util.function.*;
 
 import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.graph.*;
@@ -33,7 +34,6 @@
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.graph.*;
@@ -63,14 +63,13 @@
 
     @Override
     protected void run(StructuredGraph graph) {
-        ComputeProbabilityClosure closure = new ComputeProbabilityClosure(graph);
-        NodesToDoubles probabilities = closure.apply();
+        ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache();
         SchedulePhase schedule = new SchedulePhase();
         schedule.apply(graph, false);
 
         ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true);
         for (Loop<Block> loop : cfg.getLoops()) {
-            double loopProbability = probabilities.get(loop.header.getBeginNode());
+            double loopProbability = probabilities.applyAsDouble(loop.header.getBeginNode());
             if (loopProbability > (1D / Integer.MAX_VALUE)) {
                 addSectionCounters(loop.header.getBeginNode(), loop.blocks, loop.children, schedule, probabilities);
             }
@@ -93,12 +92,13 @@
         }
     }
 
-    private static void addSectionCounters(FixedWithNextNode start, Collection<Block> sectionBlocks, Collection<Loop<Block>> childLoops, SchedulePhase schedule, NodesToDoubles probabilities) {
+    private static void addSectionCounters(FixedWithNextNode start, Collection<Block> sectionBlocks, Collection<Loop<Block>> childLoops, SchedulePhase schedule,
+                    ToDoubleFunction<FixedNode> probabilities) {
         HashSet<Block> blocks = new HashSet<>(sectionBlocks);
         for (Loop<?> loop : childLoops) {
             blocks.removeAll(loop.blocks);
         }
-        double weight = getSectionWeight(schedule, probabilities, blocks) / probabilities.get(start);
+        double weight = getSectionWeight(schedule, probabilities, blocks) / probabilities.applyAsDouble(start);
         DynamicCounterNode.addCounterBefore(GROUP_NAME, sectionHead(start), (long) weight, true, start.next());
         if (WITH_INVOKE_FREE_SECTIONS && !hasInvoke(blocks)) {
             DynamicCounterNode.addCounterBefore(GROUP_NAME_WITHOUT, sectionHead(start), (long) weight, true, start.next());
@@ -113,10 +113,10 @@
         }
     }
 
-    private static double getSectionWeight(SchedulePhase schedule, NodesToDoubles probabilities, Collection<Block> blocks) {
+    private static double getSectionWeight(SchedulePhase schedule, ToDoubleFunction<FixedNode> probabilities, Collection<Block> blocks) {
         double count = 0;
         for (Block block : blocks) {
-            double blockProbability = probabilities.get(block.getBeginNode());
+            double blockProbability = probabilities.applyAsDouble(block.getBeginNode());
             for (ScheduledNode node : schedule.getBlockToNodesMap().get(block)) {
                 count += blockProbability * getNodeWeight(node);
             }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -25,6 +25,7 @@
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 
 import java.util.*;
+import java.util.function.*;
 
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.type.*;
@@ -153,13 +154,15 @@
 
     @Override
     protected void run(StructuredGraph graph, PhaseContext phaseContext) {
-        NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(graph).apply();
+        if (graph.hasNode(MergeNode.class)) {
+            ToDoubleFunction<FixedNode> nodeProbabilities = new FixedNodeProbabilityCache();
 
-        // A snapshot is taken here, so that new MergeNode instances aren't considered for tail
-        // duplication.
-        for (MergeNode merge : graph.getNodes(MergeNode.class).snapshot()) {
-            if (!(merge instanceof LoopBeginNode) && nodeProbabilities.get(merge) >= TailDuplicationProbability.getValue()) {
-                tailDuplicate(merge, DEFAULT_DECISION, null, phaseContext, canonicalizer);
+            // A snapshot is taken here, so that new MergeNode instances aren't considered for tail
+            // duplication.
+            for (MergeNode merge : graph.getNodes(MergeNode.class).snapshot()) {
+                if (!(merge instanceof LoopBeginNode) && nodeProbabilities.applyAsDouble(merge) >= TailDuplicationProbability.getValue()) {
+                    tailDuplicate(merge, DEFAULT_DECISION, null, phaseContext, canonicalizer);
+                }
             }
         }
     }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java	Sat May 03 21:46:35 2014 +0200
@@ -44,11 +44,16 @@
  * disadvantages.
  * </p>
  *
- *
  * <p>
  * This class makes available little more than a few fields and a few utility methods used
  * throughout the remaining components making up control-flow sensitive reductions.
  * </p>
+ *
+ * <p>
+ * The laundry-list of all flow-sensitive reductions is summarized in
+ * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction}
+ * </p>
+ *
  */
 public abstract class BaseReduction extends PostOrderNodeIterator<State> {
 
@@ -83,6 +88,12 @@
             this.deoptReason = deoptReason;
         }
 
+        /*
+         * TODO Actually, we want to emit instructions to signal "should-not-reach-here". An
+         * imperfect substitute (as done here) is emitting FixedGuard(false).
+         * "should-not-reach-here" would be better for the runtime error it raises, thus pointing to
+         * a bug in FlowSensitiveReduction (the code was reachable, after all).
+         */
         public void doRewrite(LogicNode falseConstant) {
             StructuredGraph graph = fixed.graph();
             // have to insert a FixedNode other than a ControlSinkNode
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java	Sat May 03 21:46:35 2014 +0200
@@ -41,6 +41,11 @@
  * {@link com.oracle.graal.nodes.java.CheckCastNode}.
  * </p>
  *
+ * <p>
+ * The laundry-list of all flow-sensitive reductions is summarized in
+ * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction}
+ * </p>
+ *
  * @see #visitCheckCastNode(com.oracle.graal.nodes.java.CheckCastNode)
  */
 public abstract class CheckCastReduction extends GuardingPiReduction {
@@ -51,17 +56,37 @@
 
     /**
      * <p>
-     * This phase is able to refine the types of reference-values at use sites provided a
-     * {@link com.oracle.graal.nodes.extended.GuardingNode GuardingNode} is available witnessing
-     * that fact.
+     * Upon visiting a {@link com.oracle.graal.nodes.java.CheckCastNode}, based on flow-sensitive
+     * conditions, we need to determine whether:
+     * <ul>
+     * <li>it is redundant (in which case it should be simplified), or</li>
+     * <li>flow-sensitive information can be gained from it. "Gain information from it" requires
+     * lowering the {@link com.oracle.graal.nodes.java.CheckCastNode} such that a
+     * {@link com.oracle.graal.nodes.extended.GuardingNode GuardingNode} becomes available.</li>
+     * </ul>
      * </p>
      *
      * <p>
-     * This method turns non-redundant {@link com.oracle.graal.nodes.java.CheckCastNode}s into
-     * {@link com.oracle.graal.nodes.GuardingPiNode}s. Once such lowering has been performed (during
-     * run N of this phase) follow-up runs attempt to further simplify the resulting node, see
-     * {@link EquationalReasoner#downcastGuardingPiNode(com.oracle.graal.nodes.GuardingPiNode, Witness)}
-     * and {@link #visitGuardingPiNode(com.oracle.graal.nodes.GuardingPiNode)}
+     * This method realizes the above by testing first for situations that require less work:
+     * <ol>
+     * <li>the stamp of the subject deems the check-cast redundant or unsatisfiable (ie,
+     * always-succeeds or always-fails). A previous round of canonicalization takes care of this
+     * situation, however it can also arise due to consecutive runs of
+     * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReductionPhase} without intervening
+     * {@link com.oracle.graal.phases.common.CanonicalizerPhase canonicalization}.</li>
+     * <li>
+     * flow-sensitive information reveals the subject to be null, trivially fulfilling the
+     * check-cast.</li>
+     * <li>
+     * flow-sensitive information reveals the subject to be narrower than it stamp says. If the
+     * narrower ("downcasted") value fulfills the check-cast, the check-cast is removed.</li>
+     * <li>
+     * otherwise the check-cast provides additional flow-sensitive information. For that, a
+     * {@link com.oracle.graal.nodes.FixedGuardNode} is needed, as described in
+     * {@link #lowerCheckCastAnchorFriendlyWay(com.oracle.graal.nodes.java.CheckCastNode, com.oracle.graal.nodes.ValueNode)}
+     * . Please notice this lowering is currently performed unconditionally: it might occur no
+     * flow-sensitive reduction is enabled down the road.</li>
+     * </ol>
      * </p>
      *
      * <p>
@@ -155,19 +180,24 @@
      *
      * <p>
      * Rather than tracking the CheckCastNode via {@link com.oracle.graal.phases.common.cfs.State
-     * State} (doing so woud add a special case because a
+     * State} (doing so would add a special case because a
      * {@link com.oracle.graal.nodes.java.CheckCastNode} isn't a
-     * {@link com.oracle.graal.nodes.extended.GuardingNode}) this method creates an anchor by
-     * lowering the CheckCastNode into a FixedGuardNode. Not the same way as done by
-     * {@link com.oracle.graal.nodes.java.CheckCastNode#lower(com.oracle.graal.nodes.spi.LoweringTool)}
-     * which lowers into a {@link com.oracle.graal.nodes.GuardingPiNode} (which is not a
-     * {@link com.oracle.graal.nodes.extended.GuardingNode}).
+     * {@link com.oracle.graal.nodes.extended.GuardingNode guarding node}) this method creates an
+     * anchor by lowering the CheckCastNode into a FixedGuardNode. Not the same as the
+     * {@link com.oracle.graal.nodes.java.CheckCastNode#lower(com.oracle.graal.nodes.spi.LoweringTool)
+     * lowering of a CheckCastNode} which results in a {@link com.oracle.graal.nodes.GuardingPiNode}
+     * (which is not a {@link com.oracle.graal.nodes.extended.GuardingNode guarding node}).
      * </p>
      *
      * <p>
      * With that, state tracking can proceed as usual.
      * </p>
      *
+     * <p>
+     * TODO This lowering is currently performed unconditionally: it might occur no flow-sensitive
+     * reduction is enabled down the road
+     * </p>
+     *
      * @see #visitCheckCastNode(com.oracle.graal.nodes.java.CheckCastNode)
      *
      */
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java	Sat May 03 21:46:35 2014 +0200
@@ -219,7 +219,7 @@
         if (n == null) {
             return null;
         }
-        assert !(n instanceof GuardNode) : "This phase not yet ready to run during MidTier";
+        assert !(n instanceof GuardNode) : "This phase not intended to run during MidTier";
         if (!(n instanceof ValueNode)) {
             return n;
         }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java	Sat May 03 21:46:35 2014 +0200
@@ -33,6 +33,11 @@
  * This class implements control-flow sensitive reductions for
  * {@link com.oracle.graal.nodes.FixedGuardNode}.
  * </p>
+ *
+ * <p>
+ * The laundry-list of all flow-sensitive reductions is summarized in
+ * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction}
+ * </p>
  * 
  * @see #visitFixedGuardNode(com.oracle.graal.nodes.FixedGuardNode)
  */
@@ -43,15 +48,33 @@
     }
 
     /**
-     * In case the condition is constant,
-     * {@link com.oracle.graal.nodes.FixedGuardNode#simplify(com.oracle.graal.graph.spi.SimplifierTool)
-     * FixedGuardNode#simplify(SimplifierTool)} will eventually remove the
-     * {@link com.oracle.graal.nodes.FixedGuardNode} ("always succeeds") or kill the code that
-     * should be killed ("always fails").
+     * <p>
+     * Upon visiting a {@link com.oracle.graal.nodes.FixedGuardNode}, based on flow-sensitive
+     * conditions, we need to determine whether:
+     * <ul>
+     * <li>it is redundant (in which case it should be simplified), or</li>
+     * <li>flow-sensitive information can be gained from it.</li>
+     * </ul>
+     * </p>
      * 
      * <p>
-     * The only thing we do here is tracking as true fact (from this program point onwards) the
-     * condition of the {@link com.oracle.graal.nodes.FixedGuardNode FixedGuardNode}.
+     * This method realizes the above by inspecting the
+     * {@link com.oracle.graal.nodes.FixedGuardNode}'s condition:
+     * <ol>
+     * <li>a constant condition signals the node won't be reduced here</li>
+     * <li>the outcome of the condition can be predicted:</li>
+     * <ul>
+     * <li>
+     * "always succeeds", after finding an equivalent (or stronger)
+     * {@link com.oracle.graal.nodes.extended.GuardingNode} in scope. The
+     * {@link com.oracle.graal.nodes.FixedGuardNode} is removed after replacing its usages with the
+     * existing guarding node</li>
+     * <li>
+     * "always fails", which warrants making that explicit by making the condition constant, see
+     * {@link #markFixedGuardNodeAlwaysFails(com.oracle.graal.nodes.FixedGuardNode)}</li>
+     * </ul>
+     * <li>otherwise the condition is tracked flow-sensitively</li>
+     * </ol>
      * </p>
      * 
      * <p>
@@ -62,6 +85,8 @@
 
         /*
          * A FixedGuardNode with LogicConstantNode condition is left untouched.
+         * `FixedGuardNode.simplify()` will eventually remove the FixedGuardNode (in case it
+         * "always succeeds") or kill code ("always fails").
          */
 
         if (f.condition() instanceof LogicConstantNode) {
@@ -92,10 +117,10 @@
         final boolean isTrue = !f.isNegated();
 
         /*
-         * FixedGuardNode requires handling similar to that of GuardingPiNode, (ie the condition
-         * can't simply be deverbosified in place). A replacement anchor is needed, ie an anchor
-         * that amounts to the same combination of (negated, condition) for the FixedGuardNode at
-         * hand.
+         * A FixedGuardNode can only be removed provided a replacement anchor is found (so called
+         * "evidence"), ie an anchor that amounts to the same combination of (negated, condition) as
+         * for the FixedGuardNode at hand. Just deverbosifying the condition in place isn't
+         * semantics-preserving.
          */
 
         // TODO what about isDependencyTainted
@@ -202,7 +227,8 @@
      * Porcelain method.
      * 
      * <p>
-     * The `replacement` guard must be such that it implies the `old` guard.
+     * The `replacement` guard must be such that it implies the `old` guard. Moreover, rhe
+     * `replacement` guard must be in scope.
      * </p>
      */
     private void removeFixedGuardNode(FixedGuardNode old, GuardingNode replacement) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java	Sat May 03 21:46:35 2014 +0200
@@ -43,20 +43,37 @@
 
 /**
  * <p>
- * All control-flow-sensitive reductions follow the common pattern of
+ * In a nutshell, {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReductionPhase} makes a
+ * single pass in dominator-based order over the graph:
+ * <ol>
+ * <li>collecting properties of interest at control-splits; as well as for check-casts,
+ * guarding-pis, null-checks, and fixed-guards. Such flow-sensitive information is tracked via a
+ * dedicated {@link com.oracle.graal.phases.common.cfs.State state instance} for each control-flow
+ * path.</li>
+ * <li>performing rewritings that are safe at specific program-points. This comprises:
  * <ul>
- * <li>Recognizing properties of interest (ie, LogicNode-s) at control-flow splits, as well as upon
- * check-casts and fixed-guards.</li>
- * <li>Using the information thus tracked to simplify
- * <ul>
- * <li>side-effects free expressions, via
+ * <li>simplification of side-effects free expressions, via
  * {@link com.oracle.graal.phases.common.cfs.EquationalReasoner#deverbosify(com.oracle.graal.graph.Node)}
  * </li>
- * <li>control-flow, eg. by eliminating redundant fixed-guards and check-casts, ie which are known
- * always to hold.</li>
+ * <li>simplification of control-flow:
+ * <ul>
+ * <li>
+ * by simplifying the input-condition to an {@link com.oracle.graal.nodes.IfNode}</li>
+ * <li>
+ * by eliminating redundant check-casts, guarding-pis, null-checks, and fixed-guards; where
+ * "redundancy" is determined using flow-sensitive information. In these cases, redundancy can be
+ * due to:
+ * <ul>
+ * <li>an equivalent, existing, guarding node is already in scope (thus, use it as replacement and
+ * remove the redundant one)</li>
+ * <li>"always fails" (thus, replace the node in question with <code>FixedGuardNode(false)</code>)</li>
  * </ul>
  * </li>
  * </ul>
+ * </li>
+ * </ul>
+ * </li>
+ * </ol>
  * </p>
  *
  * @see com.oracle.graal.phases.common.cfs.CheckCastReduction
@@ -145,6 +162,9 @@
 
         if (begin instanceof LoopExitNode) {
             state.clear();
+            /*
+             * TODO return or not? (by not returning we agree it's ok to update the state as below)
+             */
         }
 
         if (pred instanceof IfNode) {
@@ -253,7 +273,7 @@
     public boolean deverbosifyInputsInPlace(ValueNode parent) {
         boolean changed = false;
         for (ValueNode i : FlowUtil.distinctValueAndConditionInputs(parent)) {
-            assert !(i instanceof GuardNode) : "ConditionalElim shouldn't run in MidTier";
+            assert !(i instanceof GuardNode) : "This phase not intended to run during MidTier";
             ValueNode j = (ValueNode) reasoner.deverbosify(i);
             if (i != j) {
                 changed = true;
@@ -390,7 +410,7 @@
          * Step 5: After special-case handling, we do our best for those FixedNode-s
          * where the effort to reduce their inputs might pay off.
          *
-         * Why is this useful? For example, by the time the AbstractBeginNode for an If-branch
+         * Why is this useful? For example, by the time the BeginNode for an If-branch
          * is visited (in general a ControlSplitNode), the If-condition will have gone already
          * through simplification (and thus potentially have been reduced to a
          * LogicConstantNode).
@@ -407,8 +427,7 @@
             paysOffToReduce = true;
         }
 
-        // TODO comb the remaining FixedWithNextNode subclasses, pick those with good changes of
-        // paying-off
+        // TODO comb remaining FixedWithNextNode subclasses, pick those with chances of paying-off
 
         // TODO UnsafeLoadNode takes a condition
 
@@ -423,8 +442,7 @@
          */
 
         // TODO some nodes are GuardingNodes (eg, FixedAccessNode) we could use them to track state
-        // TODO others are additionally guarded (eg JavaReadNode), thus *their* guards could be
-        // simplified.
+        // TODO other nodes are guarded (eg JavaReadNode), thus *their* guards could be replaced.
 
     }
 
@@ -498,11 +516,29 @@
     }
 
     /**
-     * One or more arguments at `invoke` may have control-flow sensitive simplifications. In such
-     * case, a new {@link com.oracle.graal.nodes.java.MethodCallTargetNode MethodCallTargetNode} is
-     * prepared just for this callsite, consuming reduced arguments. This proves useful in
-     * connection with inlining, in order to specialize callees on the types of arguments other than
-     * the receiver (examples: multi-methods, the inlining problem, lambdas as arguments).
+     * <p>
+     * For one or more `invoke` arguments, flow-sensitive information may suggest their narrowing or
+     * simplification. In those cases, a new
+     * {@link com.oracle.graal.nodes.java.MethodCallTargetNode MethodCallTargetNode} is prepared
+     * just for this callsite, consuming reduced arguments.
+     * </p>
+     *
+     * <p>
+     * Specializing the {@link com.oracle.graal.nodes.java.MethodCallTargetNode
+     * MethodCallTargetNode} as described above may enable two optimizations:
+     * <ul>
+     * <li>
+     * devirtualization of an
+     * {@link com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind#Interface} or
+     * {@link com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind#Virtual} callsite
+     * (devirtualization made possible after narrowing the type of the receiver)</li>
+     * <li>
+     * (future work) actual-argument-aware inlining, ie, to specialize callees on the types of
+     * arguments other than the receiver (examples: multi-methods, the inlining problem, lambdas as
+     * arguments).</li>
+     *
+     * </ul>
+     * </p>
      *
      * <p>
      * Precondition: inputs haven't been deverbosified yet.
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReductionPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReductionPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -43,6 +43,10 @@
     @Override
     protected final void run(StructuredGraph graph, PhaseContext context) {
         try (Debug.Scope s = Debug.scope("FlowSensitiveReduction")) {
+            if (graph.isOSR()) {
+                Debug.log("Skipping OSR method %s", graph.method() == null ? "" : MetaUtil.format("%H.%n", graph.method()));
+                return;
+            }
             Debug.dump(graph, "FlowSensitiveReduction initial");
             new FlowSensitiveReduction(graph.start(), new State(), context).apply();
             Debug.dump(graph, "FlowSensitiveReduction done");
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowUtil.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowUtil.java	Sat May 03 21:46:35 2014 +0200
@@ -23,10 +23,6 @@
 package com.oracle.graal.phases.common.cfs;
 
 import com.oracle.graal.api.meta.ResolvedJavaType;
-import com.oracle.graal.debug.Debug;
-import com.oracle.graal.debug.DebugConfig;
-import com.oracle.graal.debug.DebugConfigScope;
-import com.oracle.graal.debug.internal.DebugScope;
 import com.oracle.graal.graph.InputType;
 import com.oracle.graal.graph.Node;
 import com.oracle.graal.graph.NodeClass;
@@ -290,28 +286,4 @@
         // `oldInput` if unused wil be removed in finished()
     }
 
-    public static StructuredGraph visualize(StructuredGraph graph, String title) {
-        DebugConfig debugConfig = DebugScope.getConfig();
-        DebugConfig fixedConfig = Debug.fixedConfig(false, true, false, false, debugConfig.dumpHandlers(), debugConfig.output());
-        try (DebugConfigScope s = Debug.setConfig(fixedConfig)) {
-            Debug.dump(graph, title);
-
-            return graph;
-        }
-    }
-
-    public static final String ANSI_RESET = "\u001B[0m";
-    public static final String ANSI_BLACK = "\u001B[30m";
-    public static final String ANSI_RED = "\u001B[31m";
-    public static final String ANSI_GREEN = "\u001B[32m";
-    public static final String ANSI_YELLOW = "\u001B[33m";
-    public static final String ANSI_BLUE = "\u001B[34m";
-    public static final String ANSI_PURPLE = "\u001B[35m";
-    public static final String ANSI_CYAN = "\u001B[36m";
-    public static final String ANSI_WHITE = "\u001B[37m";
-
-    public static void highlightInRed(String msg) {
-        System.out.println(ANSI_RED + msg + ANSI_RESET);
-    }
-
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/GuardingPiReduction.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/GuardingPiReduction.java	Sat May 03 21:46:35 2014 +0200
@@ -37,7 +37,12 @@
  * This class implements control-flow sensitive reductions for
  * {@link com.oracle.graal.nodes.GuardingPiNode}.
  * </p>
- * 
+ *
+ * <p>
+ * The laundry-list of all flow-sensitive reductions is summarized in
+ * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction}
+ * </p>
+ *
  * @see #visitGuardingPiNode(com.oracle.graal.nodes.GuardingPiNode)
  */
 public abstract class GuardingPiReduction extends BaseReduction {
@@ -139,6 +144,12 @@
         FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(envelope.condition(), envelope.getReason(), envelope.getAction(), envelope.isNegated()));
         graph.addBeforeFixed(envelope, fixedGuard);
 
+        /*
+         * 
+         * TODO This lowering is currently performed unconditionally: it might occur no
+         * flow-sensitive reduction is enabled down the road
+         */
+
         if (!FlowUtil.lacksUsages(envelope)) {
             // not calling wrapInPiNode() because we don't want to rememberSubstitution()
             PiNode replacement = graph.unique(new PiNode(envelope.object(), envelope.stamp(), fixedGuard));
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Histogram.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Histogram.java	Sat May 03 21:46:35 2014 +0200
@@ -54,7 +54,7 @@
             int percentOut = (int) (numCases / casesTotal * 100);
             String msg = prefix + String.format("%d iters in %4d cases (%2d %%)", entry.getKey(), numCases, percentOut);
             if (entry.getKey() > 3) {
-                FlowUtil.highlightInRed(msg);
+                highlightInRed(msg);
             } else {
                 System.out.println(msg);
             }
@@ -67,4 +67,11 @@
         return (Histogram) super.clone();
     }
 
+    public static final String ANSI_RESET = "\u001B[0m";
+    public static final String ANSI_RED = "\u001B[31m";
+
+    public static void highlightInRed(String msg) {
+        System.out.println(ANSI_RED + msg + ANSI_RESET);
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.phases.common.inlining;
+
+import java.util.*;
+import java.util.function.*;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+
+import edu.umd.cs.findbugs.annotations.*;
+
+public class ComputeInliningRelevance {
+
+    private static final double EPSILON = 1d / Integer.MAX_VALUE;
+    private static final double UNINITIALIZED = -1D;
+
+    private static final int EXPECTED_MIN_INVOKE_COUNT = 3;
+    private static final int EXPECTED_INVOKE_RATIO = 20;
+    private static final int EXPECTED_LOOP_COUNT = 3;
+
+    private final StructuredGraph graph;
+    private final ToDoubleFunction<FixedNode> nodeProbabilities;
+
+    /**
+     * Node relevances are pre-computed for all invokes if the graph contains loops. If there are no
+     * loops, the computation happens lazily based on {@link #rootScope}.
+     */
+    private IdentityHashMap<FixedNode, Double> nodeRelevances;
+    /**
+     * This scope is non-null if (and only if) there are no loops in the graph. In this case, the
+     * root scope is used to compute invoke relevances on the fly.
+     */
+    private Scope rootScope;
+
+    public ComputeInliningRelevance(StructuredGraph graph, ToDoubleFunction<FixedNode> nodeProbabilities) {
+        this.graph = graph;
+        this.nodeProbabilities = nodeProbabilities;
+    }
+
+    /**
+     * Initializes or updates the relevance computation. If there are no loops within the graph,
+     * most computation happens lazily.
+     */
+    public void compute() {
+        rootScope = null;
+        if (!graph.hasLoops()) {
+            // fast path for the frequent case of no loops
+            rootScope = new Scope(graph.start(), null);
+        } else {
+            if (nodeRelevances == null) {
+                nodeRelevances = new IdentityHashMap<>(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO);
+            }
+            NodeWorkList workList = graph.createNodeWorkList();
+            IdentityHashMap<LoopBeginNode, Scope> loops = new IdentityHashMap<>(EXPECTED_LOOP_COUNT);
+
+            loops.put(null, new Scope(graph.start(), null));
+            for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.class)) {
+                createLoopScope(loopBegin, loops);
+            }
+
+            for (Scope scope : loops.values()) {
+                scope.process(workList);
+            }
+        }
+    }
+
+    public double getRelevance(Invoke invoke) {
+        if (rootScope != null) {
+            return rootScope.computeInvokeRelevance(invoke);
+        }
+        assert nodeRelevances != null : "uninitialized relevance";
+        return nodeRelevances.get(invoke);
+    }
+
+    /**
+     * Determines the parent of the given loop and creates a {@link Scope} object for each one. This
+     * method will call itself recursively if no {@link Scope} for the parent loop exists.
+     */
+    private Scope createLoopScope(LoopBeginNode loopBegin, IdentityHashMap<LoopBeginNode, Scope> loops) {
+        Scope scope = loops.get(loopBegin);
+        if (scope == null) {
+            final Scope parent;
+            // look for the parent scope
+            FixedNode current = loopBegin.forwardEnd();
+            while (true) {
+                if (current.predecessor() == null) {
+                    if (current instanceof LoopBeginNode) {
+                        // if we reach a LoopBeginNode then we're within this loop
+                        parent = createLoopScope((LoopBeginNode) current, loops);
+                        break;
+                    } else if (current instanceof StartNode) {
+                        // we're within the outermost scope
+                        parent = loops.get(null);
+                        break;
+                    } else {
+                        assert current.getClass() == MergeNode.class : current;
+                        // follow any path upwards - it doesn't matter which one
+                        current = ((MergeNode) current).forwardEndAt(0);
+                    }
+                } else if (current instanceof LoopExitNode) {
+                    // if we reach a loop exit then we follow this loop and have the same parent
+                    parent = createLoopScope(((LoopExitNode) current).loopBegin(), loops).parent;
+                    break;
+                } else {
+                    current = (FixedNode) current.predecessor();
+                }
+            }
+            scope = new Scope(loopBegin, parent);
+            loops.put(loopBegin, scope);
+        }
+        return scope;
+    }
+
+    /**
+     * A scope holds information for the contents of one loop or of the root of the method. It does
+     * not include child loops, i.e., the iteration in {@link #process(NodeWorkList)} explicitly
+     * excludes the nodes of child loops.
+     */
+    private class Scope {
+        public final FixedNode start;
+        public final Scope parent; // can be null for the outermost scope
+
+        /**
+         * The minimum probability along the most probable path in this scope. Computed lazily.
+         */
+        private double fastPathMinProbability = UNINITIALIZED;
+        /**
+         * A measure of how important this scope is within its parent scope. Computed lazily.
+         */
+        private double scopeRelevanceWithinParent = UNINITIALIZED;
+
+        public Scope(FixedNode start, Scope parent) {
+            this.start = start;
+            this.parent = parent;
+        }
+
+        @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY")
+        public double getFastPathMinProbability() {
+            if (fastPathMinProbability == UNINITIALIZED) {
+                fastPathMinProbability = Math.max(EPSILON, computeFastPathMinProbability(start));
+            }
+            return fastPathMinProbability;
+        }
+
+        /**
+         * Computes the ratio between the probabilities of the current scope's entry point and the
+         * parent scope's fastPathMinProbability.
+         */
+        @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY")
+        public double getScopeRelevanceWithinParent() {
+            if (scopeRelevanceWithinParent == UNINITIALIZED) {
+                if (start instanceof LoopBeginNode) {
+                    assert parent != null;
+                    double scopeEntryProbability = nodeProbabilities.applyAsDouble(((LoopBeginNode) start).forwardEnd());
+
+                    scopeRelevanceWithinParent = scopeEntryProbability / parent.getFastPathMinProbability();
+                } else {
+                    scopeRelevanceWithinParent = 1D;
+                }
+            }
+            return scopeRelevanceWithinParent;
+        }
+
+        /**
+         * Processes all invokes in this scope by starting at the scope's start node and iterating
+         * all fixed nodes. Child loops are skipped by going from loop entries directly to the loop
+         * exits. Processing stops at loop exits of the current loop.
+         */
+        public void process(NodeWorkList workList) {
+            assert !(start instanceof Invoke);
+            workList.addAll(start.successors());
+
+            for (Node current : workList) {
+                assert current.isAlive();
+
+                if (current instanceof Invoke) {
+                    // process the invoke and queue its successors
+                    nodeRelevances.put((FixedNode) current, computeInvokeRelevance((Invoke) current));
+                    workList.addAll(current.successors());
+                } else if (current instanceof LoopBeginNode) {
+                    // skip child loops by advancing over the loop exits
+                    ((LoopBeginNode) current).loopExits().forEach(exit -> workList.add(exit.next()));
+                } else if (current instanceof LoopEndNode) {
+                    // nothing to do
+                } else if (current instanceof LoopExitNode) {
+                    // nothing to do
+                } else if (current instanceof FixedWithNextNode) {
+                    workList.add(((FixedWithNextNode) current).next());
+                } else if (current instanceof EndNode) {
+                    workList.add(((EndNode) current).merge());
+                } else if (current instanceof ControlSinkNode) {
+                    // nothing to do
+                } else if (current instanceof ControlSplitNode) {
+                    workList.addAll(current.successors());
+                } else {
+                    assert false : current;
+                }
+            }
+        }
+
+        /**
+         * The relevance of an invoke is the ratio between the invoke's probability and the current
+         * scope's fastPathMinProbability, adjusted by scopeRelevanceWithinParent.
+         */
+        public double computeInvokeRelevance(Invoke invoke) {
+            double invokeProbability = nodeProbabilities.applyAsDouble(invoke.asNode());
+            assert !Double.isNaN(invokeProbability);
+
+            double relevance = (invokeProbability / getFastPathMinProbability()) * Math.min(1.0, getScopeRelevanceWithinParent());
+            assert !Double.isNaN(relevance);
+            return relevance;
+        }
+    }
+
+    /**
+     * Computes the minimum probability along the most probable path within the scope. During
+     * iteration, the method returns immediately once a loop exit is discovered.
+     */
+    private double computeFastPathMinProbability(FixedNode scopeStart) {
+        ArrayList<FixedNode> pathBeginNodes = new ArrayList<>();
+        pathBeginNodes.add(scopeStart);
+        double minPathProbability = nodeProbabilities.applyAsDouble(scopeStart);
+        boolean isLoopScope = scopeStart instanceof LoopBeginNode;
+
+        do {
+            Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1);
+            do {
+                if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) {
+                    return minPathProbability;
+                } else if (current instanceof LoopBeginNode && current != scopeStart) {
+                    current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes);
+                    minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability);
+                } else if (current instanceof ControlSplitNode) {
+                    current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes);
+                    minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability);
+                } else {
+                    assert current.successors().count() <= 1;
+                    current = current.successors().first();
+                }
+            } while (current != null);
+        } while (!pathBeginNodes.isEmpty());
+
+        return minPathProbability;
+    }
+
+    private double getMinPathProbability(FixedNode current, double minPathProbability) {
+        return current == null ? minPathProbability : Math.min(minPathProbability, nodeProbabilities.applyAsDouble(current));
+    }
+
+    /**
+     * Returns the most probable successor. If multiple successors share the maximum probability,
+     * one is returned and the others are enqueued in pathBeginNodes.
+     */
+    private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList<FixedNode> pathBeginNodes) {
+        Node maxSux = null;
+        double maxProbability = 0.0;
+        int pathBeginCount = pathBeginNodes.size();
+
+        for (Node sux : controlSplit.successors()) {
+            double probability = controlSplit.probability((BeginNode) sux);
+            if (probability > maxProbability) {
+                maxProbability = probability;
+                maxSux = sux;
+                truncate(pathBeginNodes, pathBeginCount);
+            } else if (probability == maxProbability) {
+                pathBeginNodes.add((FixedNode) sux);
+            }
+        }
+
+        return maxSux;
+    }
+
+    /**
+     * Returns the most probable loop exit. If multiple successors share the maximum probability,
+     * one is returned and the others are enqueued in pathBeginNodes.
+     */
+    private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList<FixedNode> pathBeginNodes) {
+        Node maxSux = null;
+        double maxProbability = 0.0;
+        int pathBeginCount = pathBeginNodes.size();
+
+        for (LoopExitNode sux : loopBegin.loopExits()) {
+            double probability = nodeProbabilities.applyAsDouble(sux);
+            if (probability > maxProbability) {
+                maxProbability = probability;
+                maxSux = sux;
+                truncate(pathBeginNodes, pathBeginCount);
+            } else if (probability == maxProbability) {
+                pathBeginNodes.add(sux);
+            }
+        }
+
+        return maxSux;
+    }
+
+    private static void truncate(ArrayList<FixedNode> pathBeginNodes, int pathBeginCount) {
+        for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) {
+            pathBeginNodes.remove(pathBeginNodes.size() - 1);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2011, 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.phases.common.inlining;
+
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.phases.common.inlining.InliningPhase.Options.*;
+
+import java.util.*;
+import java.util.function.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.Debug.Scope;
+import com.oracle.graal.graph.Graph.Mark;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.options.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.InliningUtil.InlineInfo;
+import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable;
+import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableGraph;
+import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableMacroNode;
+import com.oracle.graal.phases.common.inlining.InliningUtil.InliningPolicy;
+import com.oracle.graal.phases.graph.*;
+import com.oracle.graal.phases.tiers.*;
+import com.oracle.graal.phases.util.*;
+
+public class InliningPhase extends AbstractInliningPhase {
+
+    static class Options {
+
+        // @formatter:off
+        @Option(help = "Unconditionally inline intrinsics")
+        public static final OptionValue<Boolean> AlwaysInlineIntrinsics = new OptionValue<>(false);
+        // @formatter:on
+    }
+
+    private final InliningPolicy inliningPolicy;
+    private final CanonicalizerPhase canonicalizer;
+
+    private int inliningCount;
+    private int maxMethodPerInlining = Integer.MAX_VALUE;
+
+    // Metrics
+    private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed");
+    private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered");
+    private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize");
+    private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns");
+
+    public InliningPhase(CanonicalizerPhase canonicalizer) {
+        this(new GreedyInliningPolicy(null), canonicalizer);
+    }
+
+    public InliningPhase(Map<Invoke, Double> hints, CanonicalizerPhase canonicalizer) {
+        this(new GreedyInliningPolicy(hints), canonicalizer);
+    }
+
+    public InliningPhase(InliningPolicy policy, CanonicalizerPhase canonicalizer) {
+        this.inliningPolicy = policy;
+        this.canonicalizer = canonicalizer;
+    }
+
+    public void setMaxMethodsPerInlining(int max) {
+        maxMethodPerInlining = max;
+    }
+
+    public int getInliningCount() {
+        return inliningCount;
+    }
+
+    @Override
+    protected void run(final StructuredGraph graph, final HighTierContext context) {
+        final InliningData data = new InliningData(graph, context.getAssumptions());
+        ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache();
+
+        while (data.hasUnprocessedGraphs()) {
+            final MethodInvocation currentInvocation = data.currentInvocation();
+            GraphInfo graphInfo = data.currentGraph();
+            if (!currentInvocation.isRoot() &&
+                            !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(),
+                                            currentInvocation.relevance(), false)) {
+                int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs();
+                assert remainingGraphs > 0;
+                data.popGraphs(remainingGraphs);
+                data.popInvocation();
+            } else if (graphInfo.hasRemainingInvokes() && inliningPolicy.continueInlining(graphInfo.graph())) {
+                processNextInvoke(data, graphInfo, context);
+            } else {
+                data.popGraph();
+                if (!currentInvocation.isRoot()) {
+                    assert currentInvocation.callee().invoke().asNode().isAlive();
+                    currentInvocation.incrementProcessedGraphs();
+                    if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) {
+                        data.popInvocation();
+                        final MethodInvocation parentInvoke = data.currentInvocation();
+                        try (Scope s = Debug.scope("Inlining", data.inliningContext())) {
+                            tryToInline(probabilities, data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1, context);
+                        } catch (Throwable e) {
+                            throw Debug.handle(e);
+                        }
+                    }
+                }
+            }
+        }
+
+        assert data.inliningDepth() == 0;
+        assert data.graphCount() == 0;
+    }
+
+    /**
+     * Process the next invoke and enqueue all its graphs for processing.
+     */
+    private void processNextInvoke(InliningData data, GraphInfo graphInfo, HighTierContext context) {
+        Invoke invoke = graphInfo.popInvoke();
+        MethodInvocation callerInvocation = data.currentInvocation();
+        Assumptions parentAssumptions = callerInvocation.assumptions();
+        InlineInfo info = InliningUtil.getInlineInfo(data, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations());
+
+        if (info != null) {
+            double invokeProbability = graphInfo.invokeProbability(invoke);
+            double invokeRelevance = graphInfo.invokeRelevance(invoke);
+            MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance);
+
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                Inlineable elem = getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()));
+                info.setInlinableElement(i, elem);
+                if (elem instanceof InlineableGraph) {
+                    data.pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
+                } else {
+                    assert elem instanceof InlineableMacroNode;
+                    data.pushDummyGraph();
+                }
+            }
+        }
+    }
+
+    private void tryToInline(ToDoubleFunction<FixedNode> probabilities, GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth,
+                    HighTierContext context) {
+        InlineInfo callee = calleeInfo.callee();
+        Assumptions callerAssumptions = parentInvocation.assumptions();
+
+        if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) {
+            doInline(callerGraphInfo, calleeInfo, callerAssumptions, context);
+        } else if (context.getOptimisticOptimizations().devirtualizeInvokes()) {
+            callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions);
+        }
+        metricInliningConsidered.increment();
+    }
+
+    private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) {
+        StructuredGraph callerGraph = callerGraphInfo.graph();
+        Mark markBeforeInlining = callerGraph.getMark();
+        InlineInfo callee = calleeInfo.callee();
+        try {
+            try (Scope scope = Debug.scope("doInline", callerGraph)) {
+                List<Node> invokeUsages = callee.invoke().asNode().usages().snapshot();
+                callee.inline(new Providers(context), callerAssumptions);
+                callerAssumptions.record(calleeInfo.assumptions());
+                metricInliningRuns.increment();
+                Debug.dump(callerGraph, "after %s", callee);
+
+                if (OptCanonicalizer.getValue()) {
+                    Mark markBeforeCanonicalization = callerGraph.getMark();
+                    canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining);
+
+                    // process invokes that are possibly created during canonicalization
+                    for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) {
+                        if (newNode instanceof Invoke) {
+                            callerGraphInfo.pushInvoke((Invoke) newNode);
+                        }
+                    }
+                }
+
+                callerGraphInfo.computeProbabilities();
+
+                inliningCount++;
+                metricInliningPerformed.increment();
+            }
+        } catch (BailoutException bailout) {
+            throw bailout;
+        } catch (AssertionError | RuntimeException e) {
+            throw new GraalInternalError(e).addContext(callee.toString());
+        } catch (GraalInternalError e) {
+            throw e.addContext(callee.toString());
+        }
+    }
+
+    private Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context) {
+        Class<? extends FixedWithNextNode> macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method);
+        if (macroNodeClass != null) {
+            return new InlineableMacroNode(macroNodeClass);
+        } else {
+            return new InlineableGraph(buildGraph(method, invoke, context));
+        }
+    }
+
+    private StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context) {
+        final StructuredGraph newGraph;
+        final boolean parseBytecodes;
+
+        // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains
+        // any invokes
+        StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method);
+        if (intrinsicGraph != null) {
+            newGraph = intrinsicGraph.copy();
+            parseBytecodes = false;
+        } else {
+            StructuredGraph cachedGraph = getCachedGraph(method, context);
+            if (cachedGraph != null) {
+                newGraph = cachedGraph.copy();
+                parseBytecodes = false;
+            } else {
+                newGraph = new StructuredGraph(method);
+                parseBytecodes = true;
+            }
+        }
+
+        try (Scope s = Debug.scope("InlineGraph", newGraph)) {
+            if (parseBytecodes) {
+                parseBytecodes(newGraph, context);
+            }
+
+            boolean callerHasMoreInformationAboutArguments = false;
+            NodeInputList<ValueNode> args = invoke.callTarget().arguments();
+            for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) {
+                ValueNode arg = args.get(param.index());
+                if (arg.isConstant()) {
+                    Constant constant = arg.asConstant();
+                    newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph));
+                    callerHasMoreInformationAboutArguments = true;
+                } else {
+                    Stamp joinedStamp = param.stamp().join(arg.stamp());
+                    if (joinedStamp != null && !joinedStamp.equals(param.stamp())) {
+                        param.setStamp(joinedStamp);
+                        callerHasMoreInformationAboutArguments = true;
+                    }
+                }
+            }
+
+            if (!callerHasMoreInformationAboutArguments) {
+                // TODO (chaeubl): if args are not more concrete, inlining should be avoided
+                // in most cases or we could at least use the previous graph size + invoke
+                // probability to check the inlining
+            }
+
+            if (OptCanonicalizer.getValue()) {
+                canonicalizer.apply(newGraph, context);
+            }
+
+            return newGraph;
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+    }
+
+    private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) {
+        if (context.getGraphCache() != null) {
+            StructuredGraph cachedGraph = context.getGraphCache().get(method);
+            if (cachedGraph != null) {
+                return cachedGraph;
+            }
+        }
+        return null;
+    }
+
+    private StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context) {
+        boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature();
+
+        if (context.getGraphBuilderSuite() != null) {
+            context.getGraphBuilderSuite().apply(newGraph, context);
+        }
+        assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING";
+
+        new DeadCodeEliminationPhase().apply(newGraph);
+
+        if (OptCanonicalizer.getValue()) {
+            canonicalizer.apply(newGraph, context);
+        }
+
+        if (hasMatureProfilingInfo && context.getGraphCache() != null) {
+            context.getGraphCache().put(newGraph.method(), newGraph.copy());
+        }
+        return newGraph;
+    }
+
+    private abstract static class AbstractInliningPolicy implements InliningPolicy {
+
+        protected final Map<Invoke, Double> hints;
+
+        public AbstractInliningPolicy(Map<Invoke, Double> hints) {
+            this.hints = hints;
+        }
+
+        protected double computeMaximumSize(double relevance, int configuredMaximum) {
+            double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance);
+            return configuredMaximum * inlineRatio;
+        }
+
+        protected double getInliningBonus(InlineInfo info) {
+            if (hints != null && hints.containsKey(info.invoke())) {
+                return hints.get(info.invoke());
+            }
+            return 1;
+        }
+
+        protected boolean isIntrinsic(Replacements replacements, InlineInfo info) {
+            if (AlwaysInlineIntrinsics.getValue()) {
+                return onlyIntrinsics(replacements, info);
+            } else {
+                return onlyForcedIntrinsics(replacements, info);
+            }
+        }
+
+        private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) {
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) {
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) {
+                    return false;
+                }
+                if (!replacements.isForcedSubstitution(info.methodAt(i))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        protected static int previousLowLevelGraphSize(InlineInfo info) {
+            int size = 0;
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                ResolvedJavaMethod m = info.methodAt(i);
+                ProfilingInfo profile = m.getProfilingInfo();
+                int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class);
+                if (compiledGraphSize > 0) {
+                    size += compiledGraphSize;
+                }
+            }
+            return size;
+        }
+
+        protected static int determineNodeCount(InlineInfo info) {
+            int nodes = 0;
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                Inlineable elem = info.inlineableElementAt(i);
+                if (elem != null) {
+                    nodes += elem.getNodeCount();
+                }
+            }
+            return nodes;
+        }
+
+        protected static double determineInvokeProbability(ToDoubleFunction<FixedNode> probabilities, InlineInfo info) {
+            double invokeProbability = 0;
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                Inlineable callee = info.inlineableElementAt(i);
+                Iterable<Invoke> invokes = callee.getInvokes();
+                if (invokes.iterator().hasNext()) {
+                    for (Invoke invoke : invokes) {
+                        invokeProbability += probabilities.applyAsDouble(invoke.asNode());
+                    }
+                }
+            }
+            return invokeProbability;
+        }
+    }
+
+    public static class GreedyInliningPolicy extends AbstractInliningPolicy {
+
+        public GreedyInliningPolicy(Map<Invoke, Double> hints) {
+            super(hints);
+        }
+
+        public boolean continueInlining(StructuredGraph currentGraph) {
+            if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) {
+                InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize");
+                metricInliningStoppedByMaxDesiredSize.increment();
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance,
+                        boolean fullyProcessed) {
+            if (InlineEverything.getValue()) {
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything");
+            }
+
+            if (isIntrinsic(replacements, info)) {
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic");
+            }
+
+            if (info.shouldInline()) {
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining");
+            }
+
+            double inliningBonus = getInliningBonus(info);
+            int nodes = determineNodeCount(info);
+            int lowLevelGraphSize = previousLowLevelGraphSize(info);
+
+            if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) {
+                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)",
+                                lowLevelGraphSize, relevance, probability, inliningBonus, nodes);
+            }
+
+            if (nodes < TrivialInliningSize.getValue() * inliningBonus) {
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes);
+            }
+
+            /*
+             * TODO (chaeubl): invoked methods that are on important paths but not yet compiled ->
+             * will be compiled anyways and it is likely that we are the only caller... might be
+             * useful to inline those methods but increases bootstrap time (maybe those methods are
+             * also getting queued in the compilation queue concurrently)
+             */
+            double invokes = determineInvokeProbability(probabilities, info);
+            if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) {
+                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes,
+                                relevance, probability, inliningBonus, nodes);
+            }
+
+            double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus));
+            if (nodes <= maximumNodes) {
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability,
+                                inliningBonus, nodes, maximumNodes);
+            }
+
+            return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes,
+                            maximumNodes);
+        }
+    }
+
+    public static final class InlineEverythingPolicy implements InliningPolicy {
+
+        public boolean continueInlining(StructuredGraph graph) {
+            if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) {
+                throw new BailoutException("Inline all calls failed. The resulting graph is too large.");
+            }
+            return true;
+        }
+
+        public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance,
+                        boolean fullyProcessed) {
+            return true;
+        }
+    }
+
+    private static class InliningIterator {
+
+        private final FixedNode start;
+        private final Deque<FixedNode> nodeQueue;
+        private final NodeBitMap queuedNodes;
+
+        public InliningIterator(FixedNode start, NodeBitMap visitedFixedNodes) {
+            this.start = start;
+            this.nodeQueue = new ArrayDeque<>();
+            this.queuedNodes = visitedFixedNodes;
+            assert start.isAlive();
+        }
+
+        public LinkedList<Invoke> apply() {
+            LinkedList<Invoke> invokes = new LinkedList<>();
+            FixedNode current;
+            forcedQueue(start);
+
+            while ((current = nextQueuedNode()) != null) {
+                assert current.isAlive();
+
+                if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) {
+                    if (current != start) {
+                        invokes.addLast((Invoke) current);
+                    }
+                    queueSuccessors(current);
+                } else if (current instanceof LoopBeginNode) {
+                    queueSuccessors(current);
+                } else if (current instanceof LoopEndNode) {
+                    // nothing todo
+                } else if (current instanceof MergeNode) {
+                    queueSuccessors(current);
+                } else if (current instanceof FixedWithNextNode) {
+                    queueSuccessors(current);
+                } else if (current instanceof EndNode) {
+                    queueMerge((EndNode) current);
+                } else if (current instanceof ControlSinkNode) {
+                    // nothing todo
+                } else if (current instanceof ControlSplitNode) {
+                    queueSuccessors(current);
+                } else {
+                    assert false : current;
+                }
+            }
+
+            return invokes;
+        }
+
+        private void queueSuccessors(FixedNode x) {
+            for (Node node : x.successors()) {
+                queue(node);
+            }
+        }
+
+        private void queue(Node node) {
+            if (node != null && !queuedNodes.isMarked(node)) {
+                forcedQueue(node);
+            }
+        }
+
+        private void forcedQueue(Node node) {
+            queuedNodes.mark(node);
+            nodeQueue.addFirst((FixedNode) node);
+        }
+
+        private FixedNode nextQueuedNode() {
+            if (nodeQueue.isEmpty()) {
+                return null;
+            }
+
+            FixedNode result = nodeQueue.removeFirst();
+            assert queuedNodes.isMarked(result);
+            return result;
+        }
+
+        private void queueMerge(AbstractEndNode end) {
+            MergeNode merge = end.merge();
+            if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) {
+                queuedNodes.mark(merge);
+                nodeQueue.add(merge);
+            }
+        }
+
+        private boolean visitedAllEnds(MergeNode merge) {
+            for (int i = 0; i < merge.forwardEndCount(); i++) {
+                if (!queuedNodes.isMarked(merge.forwardEndAt(i))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Holds the data for building the callee graphs recursively: graphs and invocations (each
+     * invocation can have multiple graphs).
+     */
+    static class InliningData {
+
+        private static final GraphInfo DummyGraphInfo = new GraphInfo(null, new LinkedList<Invoke>(), 1.0, 1.0);
+
+        /**
+         * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee.
+         */
+        private final ArrayDeque<GraphInfo> graphQueue;
+        private final ArrayDeque<MethodInvocation> invocationQueue;
+
+        private int maxGraphs;
+
+        public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions) {
+            this.graphQueue = new ArrayDeque<>();
+            this.invocationQueue = new ArrayDeque<>();
+            this.maxGraphs = 1;
+
+            invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0));
+            pushGraph(rootGraph, 1.0, 1.0);
+        }
+
+        public int graphCount() {
+            return graphQueue.size();
+        }
+
+        public void pushGraph(StructuredGraph graph, double probability, double relevance) {
+            assert !contains(graph);
+            NodeBitMap visitedFixedNodes = graph.createNodeBitMap();
+            LinkedList<Invoke> invokes = new InliningIterator(graph.start(), visitedFixedNodes).apply();
+            assert invokes.size() == count(graph.getInvokes());
+            graphQueue.push(new GraphInfo(graph, invokes, probability, relevance));
+            assert graphQueue.size() <= maxGraphs;
+        }
+
+        public void pushDummyGraph() {
+            graphQueue.push(DummyGraphInfo);
+        }
+
+        public boolean hasUnprocessedGraphs() {
+            return !graphQueue.isEmpty();
+        }
+
+        public GraphInfo currentGraph() {
+            return graphQueue.peek();
+        }
+
+        public void popGraph() {
+            graphQueue.pop();
+            assert graphQueue.size() <= maxGraphs;
+        }
+
+        public void popGraphs(int count) {
+            assert count >= 0;
+            for (int i = 0; i < count; i++) {
+                graphQueue.pop();
+            }
+        }
+
+        private static final Object[] NO_CONTEXT = {};
+
+        /**
+         * Gets the call hierarchy of this inlining from outer most call to inner most callee.
+         */
+        public Object[] inliningContext() {
+            if (!Debug.isDumpEnabled()) {
+                return NO_CONTEXT;
+            }
+            Object[] result = new Object[graphQueue.size()];
+            int i = 0;
+            for (GraphInfo g : graphQueue) {
+                result[i++] = g.graph.method();
+            }
+            return result;
+        }
+
+        public MethodInvocation currentInvocation() {
+            return invocationQueue.peekFirst();
+        }
+
+        public MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) {
+            MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance);
+            invocationQueue.addFirst(methodInvocation);
+            maxGraphs += info.numberOfMethods();
+            assert graphQueue.size() <= maxGraphs;
+            return methodInvocation;
+        }
+
+        public void popInvocation() {
+            maxGraphs -= invocationQueue.peekFirst().callee.numberOfMethods();
+            assert graphQueue.size() <= maxGraphs;
+            invocationQueue.removeFirst();
+        }
+
+        public int countRecursiveInlining(ResolvedJavaMethod method) {
+            int count = 0;
+            for (GraphInfo graphInfo : graphQueue) {
+                if (method.equals(graphInfo.method())) {
+                    count++;
+                }
+            }
+            return count;
+        }
+
+        public int inliningDepth() {
+            assert invocationQueue.size() > 0;
+            return invocationQueue.size() - 1;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder result = new StringBuilder("Invocations: ");
+
+            for (MethodInvocation invocation : invocationQueue) {
+                if (invocation.callee() != null) {
+                    result.append(invocation.callee().numberOfMethods());
+                    result.append("x ");
+                    result.append(invocation.callee().invoke());
+                    result.append("; ");
+                }
+            }
+
+            result.append("\nGraphs: ");
+            for (GraphInfo graph : graphQueue) {
+                result.append(graph.graph());
+                result.append("; ");
+            }
+
+            return result.toString();
+        }
+
+        private boolean contains(StructuredGraph graph) {
+            for (GraphInfo info : graphQueue) {
+                if (info.graph() == graph) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private static int count(Iterable<Invoke> invokes) {
+            int count = 0;
+            Iterator<Invoke> iterator = invokes.iterator();
+            while (iterator.hasNext()) {
+                iterator.next();
+                count++;
+            }
+            return count;
+        }
+    }
+
+    private static class MethodInvocation {
+
+        private final InlineInfo callee;
+        private final Assumptions assumptions;
+        private final double probability;
+        private final double relevance;
+
+        private int processedGraphs;
+
+        public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) {
+            this.callee = info;
+            this.assumptions = assumptions;
+            this.probability = probability;
+            this.relevance = relevance;
+        }
+
+        public void incrementProcessedGraphs() {
+            processedGraphs++;
+            assert processedGraphs <= callee.numberOfMethods();
+        }
+
+        public int processedGraphs() {
+            assert processedGraphs <= callee.numberOfMethods();
+            return processedGraphs;
+        }
+
+        public int totalGraphs() {
+            return callee.numberOfMethods();
+        }
+
+        public InlineInfo callee() {
+            return callee;
+        }
+
+        public Assumptions assumptions() {
+            return assumptions;
+        }
+
+        public double probability() {
+            return probability;
+        }
+
+        public double relevance() {
+            return relevance;
+        }
+
+        public boolean isRoot() {
+            return callee == null;
+        }
+
+        @Override
+        public String toString() {
+            if (isRoot()) {
+                return "<root>";
+            }
+            CallTargetNode callTarget = callee.invoke().callTarget();
+            if (callTarget instanceof MethodCallTargetNode) {
+                ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod();
+                return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod);
+            } else {
+                return "Invoke#" + callTarget.targetName();
+            }
+        }
+    }
+
+    /**
+     * Information about a graph that will potentially be inlined. This includes tracking the
+     * invocations in graph that will subject to inlining themselves.
+     */
+    private static class GraphInfo {
+
+        private final StructuredGraph graph;
+        private final LinkedList<Invoke> remainingInvokes;
+        private final double probability;
+        private final double relevance;
+
+        private final ToDoubleFunction<FixedNode> probabilities;
+        private final ComputeInliningRelevance computeInliningRelevance;
+
+        public GraphInfo(StructuredGraph graph, LinkedList<Invoke> invokes, double probability, double relevance) {
+            this.graph = graph;
+            this.remainingInvokes = invokes;
+            this.probability = probability;
+            this.relevance = relevance;
+
+            if (graph != null && (graph.hasNode(InvokeNode.class) || graph.hasNode(InvokeWithExceptionNode.class))) {
+                probabilities = new FixedNodeProbabilityCache();
+                computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities);
+                computeProbabilities();
+            } else {
+                probabilities = null;
+                computeInliningRelevance = null;
+            }
+        }
+
+        /**
+         * Gets the method associated with the {@linkplain #graph() graph} represented by this
+         * object.
+         */
+        public ResolvedJavaMethod method() {
+            return graph.method();
+        }
+
+        public boolean hasRemainingInvokes() {
+            return !remainingInvokes.isEmpty();
+        }
+
+        /**
+         * The graph about which this object contains inlining information.
+         */
+        public StructuredGraph graph() {
+            return graph;
+        }
+
+        public Invoke popInvoke() {
+            return remainingInvokes.removeFirst();
+        }
+
+        public void pushInvoke(Invoke invoke) {
+            remainingInvokes.push(invoke);
+        }
+
+        public void computeProbabilities() {
+            computeInliningRelevance.compute();
+        }
+
+        public double invokeProbability(Invoke invoke) {
+            return probability * probabilities.applyAsDouble(invoke.asNode());
+        }
+
+        public double invokeRelevance(Invoke invoke) {
+            return Math.min(CapInheritedRelevance.getValue(), relevance) * computeInliningRelevance.getRelevance(invoke);
+        }
+
+        @Override
+        public String toString() {
+            return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "<null method>") + remainingInvokes;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,1610 @@
+/*
+ * Copyright (c) 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.phases.common.inlining;
+
+import static com.oracle.graal.api.meta.DeoptimizationAction.*;
+import static com.oracle.graal.api.meta.DeoptimizationReason.*;
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.compiler.common.type.StampFactory.*;
+
+import java.util.*;
+import java.util.function.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.code.Assumptions.Assumption;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType;
+import com.oracle.graal.api.meta.ResolvedJavaType.Representation;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.calc.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.Debug.Scope;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.DuplicationReplacement;
+import com.oracle.graal.graph.Node.Verbosity;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.InliningPhase.*;
+import com.oracle.graal.phases.tiers.*;
+import com.oracle.graal.phases.util.*;
+
+public class InliningUtil {
+
+    private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication");
+    private static final String inliningDecisionsScopeString = "InliningDecisions";
+    /**
+     * Meters the size (in bytecodes) of all methods processed during compilation (i.e., top level
+     * and all inlined methods), irrespective of how many bytecodes in each method are actually
+     * parsed (which may be none for methods whose IR is retrieved from a cache).
+     */
+    public static final DebugMetric InlinedBytecodes = Debug.metric("InlinedBytecodes");
+
+    public interface InliningPolicy {
+
+        boolean continueInlining(StructuredGraph graph);
+
+        boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed);
+    }
+
+    public interface Inlineable {
+
+        int getNodeCount();
+
+        Iterable<Invoke> getInvokes();
+    }
+
+    public static class InlineableGraph implements Inlineable {
+
+        private final StructuredGraph graph;
+
+        public InlineableGraph(StructuredGraph graph) {
+            this.graph = graph;
+        }
+
+        @Override
+        public int getNodeCount() {
+            return graph.getNodeCount();
+        }
+
+        @Override
+        public Iterable<Invoke> getInvokes() {
+            return graph.getInvokes();
+        }
+
+        public StructuredGraph getGraph() {
+            return graph;
+        }
+    }
+
+    public static class InlineableMacroNode implements Inlineable {
+
+        private final Class<? extends FixedWithNextNode> macroNodeClass;
+
+        public InlineableMacroNode(Class<? extends FixedWithNextNode> macroNodeClass) {
+            this.macroNodeClass = macroNodeClass;
+        }
+
+        @Override
+        public int getNodeCount() {
+            return 1;
+        }
+
+        @Override
+        public Iterable<Invoke> getInvokes() {
+            return Collections.emptyList();
+        }
+
+        public Class<? extends FixedWithNextNode> getMacroNodeClass() {
+            return macroNodeClass;
+        }
+    }
+
+    /**
+     * Print a HotSpot-style inlining message to the console.
+     */
+    private static void printInlining(final InlineInfo info, final int inliningDepth, final boolean success, final String msg, final Object... args) {
+        printInlining(info.methodAt(0), info.invoke(), inliningDepth, success, msg, args);
+    }
+
+    /**
+     * Print a HotSpot-style inlining message to the console.
+     */
+    private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final int inliningDepth, final boolean success, final String msg, final Object... args) {
+        if (HotSpotPrintInlining.getValue()) {
+            // 1234567
+            TTY.print("        ");     // print timestamp
+            // 1234
+            TTY.print("     ");        // print compilation number
+            // % s ! b n
+            TTY.print("%c%c%c%c%c ", ' ', method.isSynchronized() ? 's' : ' ', ' ', ' ', method.isNative() ? 'n' : ' ');
+            TTY.print("     ");        // more indent
+            TTY.print("    ");         // initial inlining indent
+            for (int i = 0; i < inliningDepth; i++) {
+                TTY.print("  ");
+            }
+            TTY.println(String.format("@ %d  %s   %s%s", invoke.bci(), methodName(method, null), success ? "" : "not inlining ", String.format(msg, args)));
+        }
+    }
+
+    public static boolean logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) {
+        return logInliningDecision(info, inliningDepth, allowLogging, true, msg, args);
+    }
+
+    public static boolean logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) {
+        return logInliningDecision(info, inliningDepth, true, false, msg, args);
+    }
+
+    public static boolean logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) {
+        if (allowLogging) {
+            printInlining(info, inliningDepth, success, msg, args);
+            if (shouldLogInliningDecision()) {
+                logInliningDecision(methodName(info), success, msg, args);
+            }
+        }
+        return success;
+    }
+
+    public static void logInliningDecision(final String msg, final Object... args) {
+        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
+            // Can't use log here since we are varargs
+            if (Debug.isLogEnabled()) {
+                Debug.logv(msg, args);
+            }
+        }
+    }
+
+    private static boolean logNotInlinedMethod(Invoke invoke, String msg) {
+        if (shouldLogInliningDecision()) {
+            String methodString = invoke.toString() + (invoke.callTarget() == null ? " callTarget=null" : invoke.callTarget().targetName());
+            logInliningDecision(methodString, false, msg, new Object[0]);
+        }
+        return false;
+    }
+
+    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) {
+        return logNotInlinedMethodAndReturnNull(invoke, inliningDepth, method, msg, new Object[0]);
+    }
+
+    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) {
+        printInlining(method, invoke, inliningDepth, false, msg, args);
+        if (shouldLogInliningDecision()) {
+            String methodString = methodName(method, invoke);
+            logInliningDecision(methodString, false, msg, args);
+        }
+        return null;
+    }
+
+    private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) {
+        printInlining(method, invoke, inliningDepth, false, msg, new Object[0]);
+        if (shouldLogInliningDecision()) {
+            String methodString = methodName(method, invoke);
+            logInliningDecision(methodString, false, msg, new Object[0]);
+        }
+        return false;
+    }
+
+    private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) {
+        String inliningMsg = "inlining " + methodString + ": " + msg;
+        if (!success) {
+            inliningMsg = "not " + inliningMsg;
+        }
+        logInliningDecision(inliningMsg, args);
+    }
+
+    public static boolean shouldLogInliningDecision() {
+        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
+            return Debug.isLogEnabled();
+        }
+    }
+
+    private static String methodName(ResolvedJavaMethod method, Invoke invoke) {
+        if (invoke != null && invoke.stateAfter() != null) {
+            return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)";
+        } else {
+            return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)";
+        }
+    }
+
+    private static String methodName(InlineInfo info) {
+        if (info == null) {
+            return "null";
+        } else if (info.invoke() != null && info.invoke().stateAfter() != null) {
+            return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString();
+        } else {
+            return info.toString();
+        }
+    }
+
+    private static String methodName(FrameState frameState, int bci) {
+        StringBuilder sb = new StringBuilder();
+        if (frameState.outerFrameState() != null) {
+            sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci));
+            sb.append("->");
+        }
+        sb.append(MetaUtil.format("%h.%n", frameState.method()));
+        sb.append("@").append(bci);
+        return sb.toString();
+    }
+
+    /**
+     * Represents an opportunity for inlining at a given invoke, with the given weight and level.
+     * The weight is the amortized weight of the additional code - so smaller is better. The level
+     * is the number of nested inlinings that lead to this invoke.
+     */
+    public interface InlineInfo {
+
+        /**
+         * The graph containing the {@link #invoke() invocation} that may be inlined.
+         */
+        StructuredGraph graph();
+
+        /**
+         * The invocation that may be inlined.
+         */
+        Invoke invoke();
+
+        /**
+         * Returns the number of methods that may be inlined by the {@link #invoke() invocation}.
+         * This may be more than one in the case of a invocation profile showing a number of "hot"
+         * concrete methods dispatched to by the invocation.
+         */
+        int numberOfMethods();
+
+        ResolvedJavaMethod methodAt(int index);
+
+        Inlineable inlineableElementAt(int index);
+
+        double probabilityAt(int index);
+
+        double relevanceAt(int index);
+
+        void setInlinableElement(int index, Inlineable inlineableElement);
+
+        /**
+         * Performs the inlining described by this object and returns the node that represents the
+         * return value of the inlined method (or null for void methods and methods that have no
+         * non-exceptional exit).
+         */
+        void inline(Providers providers, Assumptions assumptions);
+
+        /**
+         * Try to make the call static bindable to avoid interface and virtual method calls.
+         */
+        void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions);
+
+        boolean shouldInline();
+    }
+
+    public abstract static class AbstractInlineInfo implements InlineInfo {
+
+        protected final Invoke invoke;
+
+        public AbstractInlineInfo(Invoke invoke) {
+            this.invoke = invoke;
+        }
+
+        @Override
+        public StructuredGraph graph() {
+            return invoke.asNode().graph();
+        }
+
+        @Override
+        public Invoke invoke() {
+            return invoke;
+        }
+
+        protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, Inlineable inlineable, Assumptions assumptions, boolean receiverNullCheck) {
+            if (inlineable instanceof InlineableGraph) {
+                StructuredGraph calleeGraph = ((InlineableGraph) inlineable).getGraph();
+                InliningUtil.inline(invoke, calleeGraph, receiverNullCheck);
+            } else {
+                assert inlineable instanceof InlineableMacroNode;
+
+                Class<? extends FixedWithNextNode> macroNodeClass = ((InlineableMacroNode) inlineable).getMacroNodeClass();
+                inlineMacroNode(invoke, concrete, macroNodeClass);
+            }
+
+            InlinedBytecodes.add(concrete.getCodeSize());
+            assumptions.recordMethodContents(concrete);
+        }
+    }
+
+    public static void replaceInvokeCallTarget(Invoke invoke, StructuredGraph graph, InvokeKind invokeKind, ResolvedJavaMethod targetMethod) {
+        MethodCallTargetNode oldCallTarget = (MethodCallTargetNode) invoke.callTarget();
+        MethodCallTargetNode newCallTarget = graph.add(new MethodCallTargetNode(invokeKind, targetMethod, oldCallTarget.arguments().toArray(new ValueNode[0]), oldCallTarget.returnType()));
+        invoke.asNode().replaceFirstInput(oldCallTarget, newCallTarget);
+    }
+
+    /**
+     * Represents an inlining opportunity where the compiler can statically determine a monomorphic
+     * target method and therefore is able to determine the called method exactly.
+     */
+    public static class ExactInlineInfo extends AbstractInlineInfo {
+
+        protected final ResolvedJavaMethod concrete;
+        private Inlineable inlineableElement;
+        private boolean suppressNullCheck;
+
+        public ExactInlineInfo(Invoke invoke, ResolvedJavaMethod concrete) {
+            super(invoke);
+            this.concrete = concrete;
+            assert concrete != null;
+        }
+
+        public void suppressNullCheck() {
+            suppressNullCheck = true;
+        }
+
+        @Override
+        public void inline(Providers providers, Assumptions assumptions) {
+            inline(invoke, concrete, inlineableElement, assumptions, !suppressNullCheck);
+        }
+
+        @Override
+        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
+            // nothing todo, can already be bound statically
+        }
+
+        @Override
+        public int numberOfMethods() {
+            return 1;
+        }
+
+        @Override
+        public ResolvedJavaMethod methodAt(int index) {
+            assert index == 0;
+            return concrete;
+        }
+
+        @Override
+        public double probabilityAt(int index) {
+            assert index == 0;
+            return 1.0;
+        }
+
+        @Override
+        public double relevanceAt(int index) {
+            assert index == 0;
+            return 1.0;
+        }
+
+        @Override
+        public String toString() {
+            return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete);
+        }
+
+        @Override
+        public Inlineable inlineableElementAt(int index) {
+            assert index == 0;
+            return inlineableElement;
+        }
+
+        @Override
+        public void setInlinableElement(int index, Inlineable inlineableElement) {
+            assert index == 0;
+            this.inlineableElement = inlineableElement;
+        }
+
+        public boolean shouldInline() {
+            return concrete.shouldBeInlined();
+        }
+    }
+
+    /**
+     * Represents an inlining opportunity for which profiling information suggests a monomorphic
+     * receiver, but for which the receiver type cannot be proven. A type check guard will be
+     * generated if this inlining is performed.
+     */
+    private static class TypeGuardInlineInfo extends AbstractInlineInfo {
+
+        private final ResolvedJavaMethod concrete;
+        private final ResolvedJavaType type;
+        private Inlineable inlineableElement;
+
+        public TypeGuardInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, ResolvedJavaType type) {
+            super(invoke);
+            this.concrete = concrete;
+            this.type = type;
+            assert type.isArray() || !type.isAbstract() : type;
+        }
+
+        @Override
+        public int numberOfMethods() {
+            return 1;
+        }
+
+        @Override
+        public ResolvedJavaMethod methodAt(int index) {
+            assert index == 0;
+            return concrete;
+        }
+
+        @Override
+        public Inlineable inlineableElementAt(int index) {
+            assert index == 0;
+            return inlineableElement;
+        }
+
+        @Override
+        public double probabilityAt(int index) {
+            assert index == 0;
+            return 1.0;
+        }
+
+        @Override
+        public double relevanceAt(int index) {
+            assert index == 0;
+            return 1.0;
+        }
+
+        @Override
+        public void setInlinableElement(int index, Inlineable inlineableElement) {
+            assert index == 0;
+            this.inlineableElement = inlineableElement;
+        }
+
+        @Override
+        public void inline(Providers providers, Assumptions assumptions) {
+            createGuard(graph(), providers.getMetaAccess());
+            inline(invoke, concrete, inlineableElement, assumptions, false);
+        }
+
+        @Override
+        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
+            createGuard(graph(), metaAccess);
+            replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete);
+        }
+
+        private void createGuard(StructuredGraph graph, MetaAccessProvider metaAccess) {
+            ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
+            ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), metaAccess, graph);
+            LoadHubNode receiverHub = graph.unique(new LoadHubNode(nonNullReceiver, typeHub.getKind()));
+
+            CompareNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub);
+            FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile));
+            assert invoke.predecessor() != null;
+
+            ValueNode anchoredReceiver = createAnchoredReceiver(graph, guard, type, nonNullReceiver, true);
+            invoke.callTarget().replaceFirstInput(nonNullReceiver, anchoredReceiver);
+
+            graph.addBeforeFixed(invoke.asNode(), guard);
+        }
+
+        @Override
+        public String toString() {
+            return "type-checked with type " + type.getName() + " and method " + MetaUtil.format("%H.%n(%p):%r", concrete);
+        }
+
+        public boolean shouldInline() {
+            return concrete.shouldBeInlined();
+        }
+    }
+
+    /**
+     * Polymorphic inlining of m methods with n type checks (n &ge; m) in case that the profiling
+     * information suggests a reasonable amount of different receiver types and different methods.
+     * If an unknown type is encountered a deoptimization is triggered.
+     */
+    private static class MultiTypeGuardInlineInfo extends AbstractInlineInfo {
+
+        private final List<ResolvedJavaMethod> concretes;
+        private final double[] methodProbabilities;
+        private final double maximumMethodProbability;
+        private final ArrayList<Integer> typesToConcretes;
+        private final ArrayList<ProfiledType> ptypes;
+        private final ArrayList<Double> concretesProbabilities;
+        private final double notRecordedTypeProbability;
+        private final Inlineable[] inlineableElements;
+
+        public MultiTypeGuardInlineInfo(Invoke invoke, ArrayList<ResolvedJavaMethod> concretes, ArrayList<Double> concretesProbabilities, ArrayList<ProfiledType> ptypes,
+                        ArrayList<Integer> typesToConcretes, double notRecordedTypeProbability) {
+            super(invoke);
+            assert concretes.size() > 0 : "must have at least one method";
+            assert ptypes.size() == typesToConcretes.size() : "array lengths must match";
+
+            this.concretesProbabilities = concretesProbabilities;
+            this.concretes = concretes;
+            this.ptypes = ptypes;
+            this.typesToConcretes = typesToConcretes;
+            this.notRecordedTypeProbability = notRecordedTypeProbability;
+            this.inlineableElements = new Inlineable[concretes.size()];
+            this.methodProbabilities = computeMethodProbabilities();
+            this.maximumMethodProbability = maximumMethodProbability();
+            assert maximumMethodProbability > 0;
+        }
+
+        private double[] computeMethodProbabilities() {
+            double[] result = new double[concretes.size()];
+            for (int i = 0; i < typesToConcretes.size(); i++) {
+                int concrete = typesToConcretes.get(i);
+                double probability = ptypes.get(i).getProbability();
+                result[concrete] += probability;
+            }
+            return result;
+        }
+
+        private double maximumMethodProbability() {
+            double max = 0;
+            for (int i = 0; i < methodProbabilities.length; i++) {
+                max = Math.max(max, methodProbabilities[i]);
+            }
+            return max;
+        }
+
+        @Override
+        public int numberOfMethods() {
+            return concretes.size();
+        }
+
+        @Override
+        public ResolvedJavaMethod methodAt(int index) {
+            assert index >= 0 && index < concretes.size();
+            return concretes.get(index);
+        }
+
+        @Override
+        public Inlineable inlineableElementAt(int index) {
+            assert index >= 0 && index < concretes.size();
+            return inlineableElements[index];
+        }
+
+        @Override
+        public double probabilityAt(int index) {
+            return methodProbabilities[index];
+        }
+
+        @Override
+        public double relevanceAt(int index) {
+            return probabilityAt(index) / maximumMethodProbability;
+        }
+
+        @Override
+        public void setInlinableElement(int index, Inlineable inlineableElement) {
+            assert index >= 0 && index < concretes.size();
+            inlineableElements[index] = inlineableElement;
+        }
+
+        @Override
+        public void inline(Providers providers, Assumptions assumptions) {
+            if (hasSingleMethod()) {
+                inlineSingleMethod(graph(), providers.getMetaAccess(), assumptions);
+            } else {
+                inlineMultipleMethods(graph(), providers, assumptions);
+            }
+        }
+
+        public boolean shouldInline() {
+            for (ResolvedJavaMethod method : concretes) {
+                if (method.shouldBeInlined()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean hasSingleMethod() {
+            return concretes.size() == 1 && !shouldFallbackToInvoke();
+        }
+
+        private boolean shouldFallbackToInvoke() {
+            return notRecordedTypeProbability > 0;
+        }
+
+        private void inlineMultipleMethods(StructuredGraph graph, Providers providers, Assumptions assumptions) {
+            int numberOfMethods = concretes.size();
+            FixedNode continuation = invoke.next();
+
+            ValueNode originalReceiver = ((MethodCallTargetNode) invoke.callTarget()).receiver();
+            // setup merge and phi nodes for results and exceptions
+            MergeNode returnMerge = graph.add(new MergeNode());
+            returnMerge.setStateAfter(invoke.stateAfter());
+
+            PhiNode returnValuePhi = null;
+            if (invoke.asNode().getKind() != Kind.Void) {
+                returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge));
+            }
+
+            MergeNode exceptionMerge = null;
+            PhiNode exceptionObjectPhi = null;
+            if (invoke instanceof InvokeWithExceptionNode) {
+                InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke;
+                ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge();
+
+                exceptionMerge = graph.add(new MergeNode());
+
+                FixedNode exceptionSux = exceptionEdge.next();
+                graph.addBeforeFixed(exceptionSux, exceptionMerge);
+                exceptionObjectPhi = graph.addWithoutUnique(new ValuePhiNode(StampFactory.forKind(Kind.Object), exceptionMerge));
+                exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Object, exceptionObjectPhi));
+            }
+
+            // create one separate block for each invoked method
+            BeginNode[] successors = new BeginNode[numberOfMethods + 1];
+            for (int i = 0; i < numberOfMethods; i++) {
+                successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, true);
+            }
+
+            // create the successor for an unknown type
+            FixedNode unknownTypeSux;
+            if (shouldFallbackToInvoke()) {
+                unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, false);
+            } else {
+                unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated));
+            }
+            successors[successors.length - 1] = BeginNode.begin(unknownTypeSux);
+
+            // replace the invoke exception edge
+            if (invoke instanceof InvokeWithExceptionNode) {
+                InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke;
+                ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExceptionNode.exceptionEdge();
+                exceptionEdge.replaceAtUsages(exceptionObjectPhi);
+                exceptionEdge.setNext(null);
+                GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge());
+            }
+
+            assert invoke.asNode().isAlive();
+
+            // replace the invoke with a switch on the type of the actual receiver
+            boolean methodDispatch = createDispatchOnTypeBeforeInvoke(graph, successors, false, providers.getMetaAccess());
+
+            assert invoke.next() == continuation;
+            invoke.setNext(null);
+            returnMerge.setNext(continuation);
+            invoke.asNode().replaceAtUsages(returnValuePhi);
+            invoke.asNode().replaceAndDelete(null);
+
+            ArrayList<GuardedValueNode> replacementNodes = new ArrayList<>();
+
+            // do the actual inlining for every invoke
+            for (int i = 0; i < numberOfMethods; i++) {
+                BeginNode node = successors[i];
+                Invoke invokeForInlining = (Invoke) node.next();
+
+                ResolvedJavaType commonType;
+                if (methodDispatch) {
+                    commonType = concretes.get(i).getDeclaringClass();
+                } else {
+                    commonType = getLeastCommonType(i);
+                }
+
+                ValueNode receiver = ((MethodCallTargetNode) invokeForInlining.callTarget()).receiver();
+                boolean exact = (getTypeCount(i) == 1 && !methodDispatch);
+                GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact);
+                invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver);
+
+                inline(invokeForInlining, methodAt(i), inlineableElementAt(i), assumptions, false);
+
+                replacementNodes.add(anchoredReceiver);
+            }
+            if (shouldFallbackToInvoke()) {
+                replacementNodes.add(null);
+            }
+
+            if (OptTailDuplication.getValue()) {
+                /*
+                 * We might want to perform tail duplication at the merge after a type switch, if
+                 * there are invokes that would benefit from the improvement in type information.
+                 */
+                FixedNode current = returnMerge;
+                int opportunities = 0;
+                do {
+                    if (current instanceof InvokeNode && ((InvokeNode) current).callTarget() instanceof MethodCallTargetNode &&
+                                    ((MethodCallTargetNode) ((InvokeNode) current).callTarget()).receiver() == originalReceiver) {
+                        opportunities++;
+                    } else if (current.inputs().contains(originalReceiver)) {
+                        opportunities++;
+                    }
+                    current = ((FixedWithNextNode) current).next();
+                } while (current instanceof FixedWithNextNode);
+
+                if (opportunities > 0) {
+                    metricInliningTailDuplication.increment();
+                    Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities);
+                    PhaseContext phaseContext = new PhaseContext(providers, assumptions);
+                    CanonicalizerPhase canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue());
+                    TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacementNodes, phaseContext, canonicalizer);
+                }
+            }
+        }
+
+        private int getTypeCount(int concreteMethodIndex) {
+            int count = 0;
+            for (int i = 0; i < typesToConcretes.size(); i++) {
+                if (typesToConcretes.get(i) == concreteMethodIndex) {
+                    count++;
+                }
+            }
+            return count;
+        }
+
+        private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) {
+            ResolvedJavaType commonType = null;
+            for (int i = 0; i < typesToConcretes.size(); i++) {
+                if (typesToConcretes.get(i) == concreteMethodIndex) {
+                    if (commonType == null) {
+                        commonType = ptypes.get(i).getType();
+                    } else {
+                        commonType = commonType.findLeastCommonAncestor(ptypes.get(i).getType());
+                    }
+                }
+            }
+            assert commonType != null;
+            return commonType;
+        }
+
+        private ResolvedJavaType getLeastCommonType() {
+            ResolvedJavaType result = getLeastCommonType(0);
+            for (int i = 1; i < concretes.size(); i++) {
+                result = result.findLeastCommonAncestor(getLeastCommonType(i));
+            }
+            return result;
+        }
+
+        private void inlineSingleMethod(StructuredGraph graph, MetaAccessProvider metaAccess, Assumptions assumptions) {
+            assert concretes.size() == 1 && inlineableElements.length == 1 && ptypes.size() > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0;
+
+            BeginNode calleeEntryNode = graph.add(new BeginNode());
+
+            BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph);
+            BeginNode[] successors = new BeginNode[]{calleeEntryNode, unknownTypeSux};
+            createDispatchOnTypeBeforeInvoke(graph, successors, false, metaAccess);
+
+            calleeEntryNode.setNext(invoke.asNode());
+
+            inline(invoke, methodAt(0), inlineableElementAt(0), assumptions, false);
+        }
+
+        private boolean createDispatchOnTypeBeforeInvoke(StructuredGraph graph, BeginNode[] successors, boolean invokeIsOnlySuccessor, MetaAccessProvider metaAccess) {
+            assert ptypes.size() >= 1;
+            ValueNode nonNullReceiver = nonNullReceiver(invoke);
+            Kind hubKind = ((MethodCallTargetNode) invoke.callTarget()).targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind();
+            LoadHubNode hub = graph.unique(new LoadHubNode(nonNullReceiver, hubKind));
+
+            if (!invokeIsOnlySuccessor && chooseMethodDispatch()) {
+                assert successors.length == concretes.size() + 1;
+                assert concretes.size() > 0;
+                Debug.log("Method check cascade with %d methods", concretes.size());
+
+                ValueNode[] constantMethods = new ValueNode[concretes.size()];
+                double[] probability = new double[concretes.size()];
+                for (int i = 0; i < concretes.size(); ++i) {
+                    ResolvedJavaMethod firstMethod = concretes.get(i);
+                    Constant firstMethodConstant = firstMethod.getEncoding();
+
+                    ValueNode firstMethodConstantNode = ConstantNode.forConstant(firstMethodConstant, metaAccess, graph);
+                    constantMethods[i] = firstMethodConstantNode;
+                    double concretesProbability = concretesProbabilities.get(i);
+                    assert concretesProbability >= 0.0;
+                    probability[i] = concretesProbability;
+                    if (i > 0) {
+                        double prevProbability = probability[i - 1];
+                        if (prevProbability == 1.0) {
+                            probability[i] = 1.0;
+                        } else {
+                            probability[i] = Math.min(1.0, Math.max(0.0, probability[i] / (1.0 - prevProbability)));
+                        }
+                    }
+                }
+
+                FixedNode lastSucc = successors[concretes.size()];
+                for (int i = concretes.size() - 1; i >= 0; --i) {
+                    LoadMethodNode method = graph.add(new LoadMethodNode(concretes.get(i), hub, constantMethods[i].getKind()));
+                    CompareNode methodCheck = CompareNode.createCompareNode(graph, Condition.EQ, method, constantMethods[i]);
+                    IfNode ifNode = graph.add(new IfNode(methodCheck, successors[i], lastSucc, probability[i]));
+                    method.setNext(ifNode);
+                    lastSucc = method;
+                }
+
+                FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor();
+                pred.setNext(lastSucc);
+                return true;
+            } else {
+                Debug.log("Type switch with %d types", concretes.size());
+            }
+
+            ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.size()];
+            double[] keyProbabilities = new double[ptypes.size() + 1];
+            int[] keySuccessors = new int[ptypes.size() + 1];
+            for (int i = 0; i < ptypes.size(); i++) {
+                keys[i] = ptypes.get(i).getType();
+                keyProbabilities[i] = ptypes.get(i).getProbability();
+                keySuccessors[i] = invokeIsOnlySuccessor ? 0 : typesToConcretes.get(i);
+                assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux";
+            }
+            keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability;
+            keySuccessors[keySuccessors.length - 1] = successors.length - 1;
+
+            TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, keys, keyProbabilities, keySuccessors));
+            FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor();
+            pred.setNext(typeSwitch);
+            return false;
+        }
+
+        private boolean chooseMethodDispatch() {
+            for (ResolvedJavaMethod concrete : concretes) {
+                if (!concrete.isInVirtualMethodTable()) {
+                    return false;
+                }
+            }
+
+            if (concretes.size() == 1 && this.notRecordedTypeProbability > 0) {
+                // Always chose method dispatch if there is a single concrete method and the call
+                // site is megamorphic.
+                return true;
+            }
+
+            if (concretes.size() == ptypes.size()) {
+                // Always prefer types over methods if the number of types is smaller than the
+                // number of methods.
+                return false;
+            }
+
+            return chooseMethodDispatchCostBased();
+        }
+
+        private boolean chooseMethodDispatchCostBased() {
+            double remainder = 1.0 - this.notRecordedTypeProbability;
+            double costEstimateMethodDispatch = remainder;
+            for (int i = 0; i < concretes.size(); ++i) {
+                if (i != 0) {
+                    costEstimateMethodDispatch += remainder;
+                }
+                remainder -= concretesProbabilities.get(i);
+            }
+
+            double costEstimateTypeDispatch = 0.0;
+            remainder = 1.0;
+            for (int i = 0; i < ptypes.size(); ++i) {
+                if (i != 0) {
+                    costEstimateTypeDispatch += remainder;
+                }
+                remainder -= ptypes.get(i).getProbability();
+            }
+            costEstimateTypeDispatch += notRecordedTypeProbability;
+            return costEstimateMethodDispatch < costEstimateTypeDispatch;
+        }
+
+        private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, MergeNode exceptionMerge, PhiNode exceptionObjectPhi,
+                        boolean useForInlining) {
+            Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining);
+            BeginNode calleeEntryNode = graph.add(new BeginNode());
+            calleeEntryNode.setNext(duplicatedInvoke.asNode());
+
+            AbstractEndNode endNode = graph.add(new EndNode());
+            duplicatedInvoke.setNext(endNode);
+            returnMerge.addForwardEnd(endNode);
+
+            if (returnValuePhi != null) {
+                returnValuePhi.addInput(duplicatedInvoke.asNode());
+            }
+            return calleeEntryNode;
+        }
+
+        private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining) {
+            Invoke result = (Invoke) invoke.asNode().copyWithInputs();
+            Node callTarget = result.callTarget().copyWithInputs();
+            result.asNode().replaceFirstInput(result.callTarget(), callTarget);
+            result.setUseForInlining(useForInlining);
+
+            Kind kind = invoke.asNode().getKind();
+            if (kind != Kind.Void) {
+                FrameState stateAfter = invoke.stateAfter();
+                stateAfter = stateAfter.duplicate(stateAfter.bci);
+                stateAfter.replaceFirstInput(invoke.asNode(), result.asNode());
+                result.setStateAfter(stateAfter);
+            }
+
+            if (invoke instanceof InvokeWithExceptionNode) {
+                assert exceptionMerge != null && exceptionObjectPhi != null;
+
+                InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke;
+                ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge();
+                FrameState stateAfterException = exceptionEdge.stateAfter();
+
+                ExceptionObjectNode newExceptionEdge = (ExceptionObjectNode) exceptionEdge.copyWithInputs();
+                // set new state (pop old exception object, push new one)
+                newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionEdge));
+
+                AbstractEndNode endNode = graph.add(new EndNode());
+                newExceptionEdge.setNext(endNode);
+                exceptionMerge.addForwardEnd(endNode);
+                exceptionObjectPhi.addInput(newExceptionEdge);
+
+                ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge);
+            }
+            return result;
+        }
+
+        @Override
+        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
+            if (hasSingleMethod()) {
+                devirtualizeWithTypeSwitch(graph(), InvokeKind.Special, concretes.get(0), metaAccess);
+            } else {
+                tryToDevirtualizeMultipleMethods(graph(), metaAccess);
+            }
+        }
+
+        private void tryToDevirtualizeMultipleMethods(StructuredGraph graph, MetaAccessProvider metaAccess) {
+            MethodCallTargetNode methodCallTarget = (MethodCallTargetNode) invoke.callTarget();
+            if (methodCallTarget.invokeKind() == InvokeKind.Interface) {
+                ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod();
+                ResolvedJavaType leastCommonType = getLeastCommonType();
+                // check if we have a common base type that implements the interface -> in that case
+                // we have a vtable entry for the interface method and can use a less expensive
+                // virtual call
+                if (!leastCommonType.isInterface() && targetMethod.getDeclaringClass().isAssignableFrom(leastCommonType)) {
+                    ResolvedJavaMethod baseClassTargetMethod = leastCommonType.resolveMethod(targetMethod);
+                    if (baseClassTargetMethod != null) {
+                        devirtualizeWithTypeSwitch(graph, InvokeKind.Virtual, leastCommonType.resolveMethod(targetMethod), metaAccess);
+                    }
+                }
+            }
+        }
+
+        private void devirtualizeWithTypeSwitch(StructuredGraph graph, InvokeKind kind, ResolvedJavaMethod target, MetaAccessProvider metaAccess) {
+            BeginNode invocationEntry = graph.add(new BeginNode());
+            BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph);
+            BeginNode[] successors = new BeginNode[]{invocationEntry, unknownTypeSux};
+            createDispatchOnTypeBeforeInvoke(graph, successors, true, metaAccess);
+
+            invocationEntry.setNext(invoke.asNode());
+            ValueNode receiver = ((MethodCallTargetNode) invoke.callTarget()).receiver();
+            GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, invocationEntry, target.getDeclaringClass(), receiver, false);
+            invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver);
+            replaceInvokeCallTarget(invoke, graph, kind, target);
+        }
+
+        private static BeginNode createUnknownTypeSuccessor(StructuredGraph graph) {
+            return BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated)));
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic");
+            builder.append(", ");
+            builder.append(concretes.size());
+            builder.append(" methods [ ");
+            for (int i = 0; i < concretes.size(); i++) {
+                builder.append(MetaUtil.format("  %H.%n(%p):%r", concretes.get(i)));
+            }
+            builder.append(" ], ");
+            builder.append(ptypes.size());
+            builder.append(" type checks [ ");
+            for (int i = 0; i < ptypes.size(); i++) {
+                builder.append("  ");
+                builder.append(ptypes.get(i).getType().getName());
+                builder.append(ptypes.get(i).getProbability());
+            }
+            builder.append(" ]");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic
+     * target method, but for which an assumption has to be registered because of non-final classes.
+     */
+    private static class AssumptionInlineInfo extends ExactInlineInfo {
+
+        private final Assumption takenAssumption;
+
+        public AssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumption takenAssumption) {
+            super(invoke, concrete);
+            this.takenAssumption = takenAssumption;
+        }
+
+        @Override
+        public void inline(Providers providers, Assumptions assumptions) {
+            assumptions.record(takenAssumption);
+            super.inline(providers, assumptions);
+        }
+
+        @Override
+        public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) {
+            assumptions.record(takenAssumption);
+            replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete);
+        }
+
+        @Override
+        public String toString() {
+            return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete);
+        }
+    }
+
+    /**
+     * Determines if inlining is possible at the given invoke node.
+     *
+     * @param invoke the invoke that should be inlined
+     * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke
+     */
+    public static InlineInfo getInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, Assumptions assumptions, OptimisticOptimizations optimisticOpts) {
+        if (!checkInvokeConditions(invoke)) {
+            return null;
+        }
+        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+        ResolvedJavaMethod targetMethod = callTarget.targetMethod();
+
+        if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) {
+            return getExactInlineInfo(data, invoke, replacements, optimisticOpts, targetMethod);
+        }
+
+        assert callTarget.invokeKind() == InvokeKind.Virtual || callTarget.invokeKind() == InvokeKind.Interface;
+
+        ResolvedJavaType holder = targetMethod.getDeclaringClass();
+        if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) {
+            return null;
+        }
+        ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp();
+        if (receiverStamp.alwaysNull()) {
+            // Don't inline if receiver is known to be null
+            return null;
+        }
+        if (receiverStamp.type() != null) {
+            // the invoke target might be more specific than the holder (happens after inlining:
+            // parameters lose their declared type...)
+            ResolvedJavaType receiverType = receiverStamp.type();
+            if (receiverType != null && holder.isAssignableFrom(receiverType)) {
+                holder = receiverType;
+                if (receiverStamp.isExactType()) {
+                    assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod;
+                    ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod);
+                    if (resolvedMethod != null) {
+                        return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod);
+                    }
+                }
+            }
+        }
+
+        if (holder.isArray()) {
+            // arrays can be treated as Objects
+            ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod);
+            if (resolvedMethod != null) {
+                return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod);
+            }
+        }
+
+        if (assumptions.useOptimisticAssumptions()) {
+            ResolvedJavaType uniqueSubtype = holder.findUniqueConcreteSubtype();
+            if (uniqueSubtype != null) {
+                ResolvedJavaMethod resolvedMethod = uniqueSubtype.resolveMethod(targetMethod);
+                if (resolvedMethod != null) {
+                    return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod, new Assumptions.ConcreteSubtype(holder, uniqueSubtype));
+                }
+            }
+
+            ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod);
+            if (concrete != null) {
+                return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, concrete, new Assumptions.ConcreteMethod(targetMethod, holder, concrete));
+            }
+        }
+
+        // type check based inlining
+        return getTypeCheckedInlineInfo(data, invoke, maxNumberOfMethods, replacements, targetMethod, optimisticOpts);
+    }
+
+    private static InlineInfo getAssumptionInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod concrete,
+                    Assumption takenAssumption) {
+        assert !concrete.isAbstract();
+        if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) {
+            return null;
+        }
+        return new AssumptionInlineInfo(invoke, concrete, takenAssumption);
+    }
+
+    private static InlineInfo getExactInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod targetMethod) {
+        assert !targetMethod.isAbstract();
+        if (!checkTargetConditions(data, replacements, invoke, targetMethod, optimisticOpts)) {
+            return null;
+        }
+        return new ExactInlineInfo(invoke, targetMethod);
+    }
+
+    private static InlineInfo getTypeCheckedInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, ResolvedJavaMethod targetMethod,
+                    OptimisticOptimizations optimisticOpts) {
+        JavaTypeProfile typeProfile;
+        ValueNode receiver = invoke.callTarget().arguments().get(0);
+        if (receiver instanceof TypeProfileProxyNode) {
+            TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver;
+            typeProfile = typeProfileProxyNode.getProfile();
+        } else {
+            return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no type profile exists");
+        }
+
+        ProfiledType[] ptypes = typeProfile.getTypes();
+        if (ptypes == null || ptypes.length <= 0) {
+            return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types in profile");
+        }
+
+        double notRecordedTypeProbability = typeProfile.getNotRecordedProbability();
+        if (ptypes.length == 1 && notRecordedTypeProbability == 0) {
+            if (!optimisticOpts.inlineMonomorphicCalls()) {
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining monomorphic calls is disabled");
+            }
+
+            ResolvedJavaType type = ptypes[0].getType();
+            assert type.isArray() || !type.isAbstract();
+            ResolvedJavaMethod concrete = type.resolveMethod(targetMethod);
+            if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) {
+                return null;
+            }
+            return new TypeGuardInlineInfo(invoke, concrete, type);
+        } else {
+            invoke.setPolymorphic(true);
+
+            if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) {
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length);
+            }
+            if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) {
+                // due to filtering impossible types, notRecordedTypeProbability can be > 0 although
+                // the number of types is lower than what can be recorded in a type profile
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length,
+                                notRecordedTypeProbability * 100);
+            }
+
+            // Find unique methods and their probabilities.
+            ArrayList<ResolvedJavaMethod> concreteMethods = new ArrayList<>();
+            ArrayList<Double> concreteMethodsProbabilities = new ArrayList<>();
+            for (int i = 0; i < ptypes.length; i++) {
+                ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod);
+                if (concrete == null) {
+                    return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "could not resolve method");
+                }
+                int index = concreteMethods.indexOf(concrete);
+                double curProbability = ptypes[i].getProbability();
+                if (index < 0) {
+                    index = concreteMethods.size();
+                    concreteMethods.add(concrete);
+                    concreteMethodsProbabilities.add(curProbability);
+                } else {
+                    concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability);
+                }
+            }
+
+            if (concreteMethods.size() > maxNumberOfMethods) {
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxNumberOfMethods);
+            }
+
+            // Clear methods that fall below the threshold.
+            if (notRecordedTypeProbability > 0) {
+                ArrayList<ResolvedJavaMethod> newConcreteMethods = new ArrayList<>();
+                ArrayList<Double> newConcreteMethodsProbabilities = new ArrayList<>();
+                for (int i = 0; i < concreteMethods.size(); ++i) {
+                    if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue()) {
+                        newConcreteMethods.add(concreteMethods.get(i));
+                        newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i));
+                    }
+                }
+
+                if (newConcreteMethods.size() == 0) {
+                    // No method left that is worth inlining.
+                    return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)",
+                                    concreteMethods.size());
+                }
+
+                concreteMethods = newConcreteMethods;
+                concreteMethodsProbabilities = newConcreteMethodsProbabilities;
+            }
+
+            // Clean out types whose methods are no longer available.
+            ArrayList<ProfiledType> usedTypes = new ArrayList<>();
+            ArrayList<Integer> typesToConcretes = new ArrayList<>();
+            for (ProfiledType type : ptypes) {
+                ResolvedJavaMethod concrete = type.getType().resolveMethod(targetMethod);
+                int index = concreteMethods.indexOf(concrete);
+                if (index == -1) {
+                    notRecordedTypeProbability += type.getProbability();
+                } else {
+                    assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete;
+                    usedTypes.add(type);
+                    typesToConcretes.add(index);
+                }
+            }
+
+            if (usedTypes.size() == 0) {
+                // No type left that is worth checking for.
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length);
+            }
+
+            for (ResolvedJavaMethod concrete : concreteMethods) {
+                if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) {
+                    return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined");
+                }
+            }
+            return new MultiTypeGuardInlineInfo(invoke, concreteMethods, concreteMethodsProbabilities, usedTypes, typesToConcretes, notRecordedTypeProbability);
+        }
+    }
+
+    private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) {
+        return createAnchoredReceiver(graph, anchor, receiver, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType));
+    }
+
+    private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ValueNode receiver, Stamp stamp) {
+        // to avoid that floating reads on receiver fields float above the type check
+        return graph.unique(new GuardedValueNode(receiver, anchor, stamp));
+    }
+
+    // TODO (chaeubl): cleanup this method
+    private static boolean checkInvokeConditions(Invoke invoke) {
+        if (invoke.predecessor() == null || !invoke.asNode().isAlive()) {
+            return logNotInlinedMethod(invoke, "the invoke is dead code");
+        } else if (!(invoke.callTarget() instanceof MethodCallTargetNode)) {
+            return logNotInlinedMethod(invoke, "the invoke has already been lowered, or has been created as a low-level node");
+        } else if (((MethodCallTargetNode) invoke.callTarget()).targetMethod() == null) {
+            return logNotInlinedMethod(invoke, "target method is null");
+        } else if (invoke.stateAfter() == null) {
+            // TODO (chaeubl): why should an invoke not have a state after?
+            return logNotInlinedMethod(invoke, "the invoke has no after state");
+        } else if (!invoke.useForInlining()) {
+            return logNotInlinedMethod(invoke, "the invoke is marked to be not used for inlining");
+        } else if (((MethodCallTargetNode) invoke.callTarget()).receiver() != null && ((MethodCallTargetNode) invoke.callTarget()).receiver().isConstant() &&
+                        ((MethodCallTargetNode) invoke.callTarget()).receiver().asConstant().isNull()) {
+            return logNotInlinedMethod(invoke, "receiver is null");
+        } else {
+            return true;
+        }
+    }
+
+    private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) {
+        if (method == null) {
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved");
+        } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(replacements, method))) {
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is a non-intrinsic native method");
+        } else if (method.isAbstract()) {
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method");
+        } else if (!method.getDeclaringClass().isInitialized()) {
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized");
+        } else if (!method.canBeInlined()) {
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable");
+        } else if (data.countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) {
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it exceeds the maximum recursive inlining depth");
+        } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(optimisticOpts)) {
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller");
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph.
+     *
+     * @param invoke the invoke that will be replaced
+     * @param inlineGraph the graph that the invoke will be replaced with
+     * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings,
+     *            false if no such check is required
+     */
+    public static Map<Node, Node> inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) {
+        final NodeInputList<ValueNode> parameters = invoke.callTarget().arguments();
+        FixedNode invokeNode = invoke.asNode();
+        StructuredGraph graph = invokeNode.graph();
+        assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal();
+        assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase";
+
+        FrameState stateAfter = invoke.stateAfter();
+        assert stateAfter == null || stateAfter.isAlive();
+        if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) {
+            nonNullReceiver(invoke);
+        }
+
+        ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count());
+        ArrayList<ReturnNode> returnNodes = new ArrayList<>(4);
+        UnwindNode unwindNode = null;
+        final StartNode entryPointNode = inlineGraph.start();
+        FixedNode firstCFGNode = entryPointNode.next();
+        if (firstCFGNode == null) {
+            throw new IllegalStateException("Inlined graph is in invalid state");
+        }
+        for (Node node : inlineGraph.getNodes()) {
+            if (node == entryPointNode || node == entryPointNode.stateAfter() || node instanceof ParameterNode) {
+                // Do nothing.
+            } else {
+                nodes.add(node);
+                if (node instanceof ReturnNode) {
+                    returnNodes.add((ReturnNode) node);
+                } else if (node instanceof UnwindNode) {
+                    assert unwindNode == null;
+                    unwindNode = (UnwindNode) node;
+                }
+            }
+        }
+
+        final BeginNode prevBegin = BeginNode.prevBegin(invokeNode);
+        DuplicationReplacement localReplacement = new DuplicationReplacement() {
+
+            public Node replacement(Node node) {
+                if (node instanceof ParameterNode) {
+                    return parameters.get(((ParameterNode) node).index());
+                } else if (node == entryPointNode) {
+                    return prevBegin;
+                }
+                return node;
+            }
+        };
+
+        assert invokeNode.successors().first() != null : invoke;
+        assert invokeNode.predecessor() != null;
+
+        Map<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
+        FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
+        invokeNode.replaceAtPredecessor(firstCFGNodeDuplicate);
+
+        FrameState stateAtExceptionEdge = null;
+        if (invoke instanceof InvokeWithExceptionNode) {
+            InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
+            if (unwindNode != null) {
+                assert unwindNode.predecessor() != null;
+                assert invokeWithException.exceptionEdge().successors().count() == 1;
+                ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
+                stateAtExceptionEdge = obj.stateAfter();
+                UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode);
+                obj.replaceAtUsages(unwindDuplicate.exception());
+                unwindDuplicate.clearInputs();
+                Node n = obj.next();
+                obj.setNext(null);
+                unwindDuplicate.replaceAndDelete(n);
+            } else {
+                invokeWithException.killExceptionEdge();
+            }
+
+            // get rid of memory kill
+            BeginNode begin = invokeWithException.next();
+            if (begin instanceof KillingBeginNode) {
+                BeginNode newBegin = new BeginNode();
+                graph.addAfterFixed(begin, graph.add(newBegin));
+                begin.replaceAtUsages(newBegin);
+                graph.removeFixed(begin);
+            }
+        } else {
+            if (unwindNode != null) {
+                UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode);
+                DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
+                unwindDuplicate.replaceAndDelete(deoptimizeNode);
+            }
+        }
+
+        if (stateAfter != null) {
+            processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge);
+            int callerLockDepth = stateAfter.nestedLockDepth();
+            if (callerLockDepth != 0) {
+                for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.class)) {
+                    MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original);
+                    monitor.setLockDepth(monitor.getLockDepth() + callerLockDepth);
+                }
+            }
+        } else {
+            assert checkContainsOnlyInvalidOrAfterFrameState(duplicates);
+        }
+        if (!returnNodes.isEmpty()) {
+            FixedNode n = invoke.next();
+            invoke.setNext(null);
+            if (returnNodes.size() == 1) {
+                ReturnNode returnNode = (ReturnNode) duplicates.get(returnNodes.get(0));
+                Node returnValue = returnNode.result();
+                invokeNode.replaceAtUsages(returnValue);
+                returnNode.clearInputs();
+                returnNode.replaceAndDelete(n);
+            } else {
+                ArrayList<ReturnNode> returnDuplicates = new ArrayList<>(returnNodes.size());
+                for (ReturnNode returnNode : returnNodes) {
+                    returnDuplicates.add((ReturnNode) duplicates.get(returnNode));
+                }
+                MergeNode merge = graph.add(new MergeNode());
+                merge.setStateAfter(stateAfter);
+                ValueNode returnValue = mergeReturns(merge, returnDuplicates);
+                invokeNode.replaceAtUsages(returnValue);
+                merge.setNext(n);
+            }
+        }
+
+        invokeNode.replaceAtUsages(null);
+        GraphUtil.killCFG(invokeNode);
+
+        return duplicates;
+    }
+
+    protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, Map<Node, Node> duplicates, FrameState stateAtExceptionEdge) {
+        FrameState stateAtReturn = invoke.stateAfter();
+        FrameState outerFrameState = null;
+        Kind invokeReturnKind = invoke.asNode().getKind();
+        for (FrameState original : inlineGraph.getNodes(FrameState.class)) {
+            FrameState frameState = (FrameState) duplicates.get(original);
+            if (frameState != null && frameState.isAlive()) {
+                if (frameState.bci == BytecodeFrame.AFTER_BCI) {
+                    /*
+                     * pop return kind from invoke's stateAfter and replace with this frameState's
+                     * return value (top of stack)
+                     */
+                    FrameState stateAfterReturn = stateAtReturn;
+                    if (invokeReturnKind != Kind.Void && frameState.stackSize() > 0 && stateAfterReturn.stackAt(0) != frameState.stackAt(0)) {
+                        stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, frameState.stackAt(0));
+                    }
+                    frameState.replaceAndDelete(stateAfterReturn);
+                } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) {
+                    /*
+                     * pop exception object from invoke's stateAfter and replace with this
+                     * frameState's exception object (top of stack)
+                     */
+                    FrameState stateAfterException = stateAtExceptionEdge;
+                    if (frameState.stackSize() > 0 && stateAtExceptionEdge.stackAt(0) != frameState.stackAt(0)) {
+                        stateAfterException = stateAtExceptionEdge.duplicateModified(Kind.Object, frameState.stackAt(0));
+                    }
+                    frameState.replaceAndDelete(stateAfterException);
+                } else if (frameState.bci == BytecodeFrame.UNWIND_BCI || frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) {
+                    handleMissingAfterExceptionFrameState(frameState);
+                } else {
+                    // only handle the outermost frame states
+                    if (frameState.outerFrameState() == null) {
+                        assert frameState.bci != BytecodeFrame.BEFORE_BCI : frameState;
+                        assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI || frameState.method().equals(inlineGraph.method());
+                        assert frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI && frameState.bci != BytecodeFrame.BEFORE_BCI && frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI &&
+                                        frameState.bci != BytecodeFrame.UNWIND_BCI : frameState.bci;
+                        if (outerFrameState == null) {
+                            outerFrameState = stateAtReturn.duplicateModified(invoke.bci(), stateAtReturn.rethrowException(), invokeReturnKind);
+                            outerFrameState.setDuringCall(true);
+                        }
+                        frameState.setOuterFrameState(outerFrameState);
+                    }
+                }
+            }
+        }
+    }
+
+    private static boolean isStateAfterException(FrameState frameState) {
+        return frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || (frameState.bci == BytecodeFrame.UNWIND_BCI && !frameState.method().isSynchronized());
+    }
+
+    protected static void handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState) {
+        Graph graph = nonReplaceableFrameState.graph();
+        NodeWorkList workList = graph.createNodeWorkList();
+        workList.add(nonReplaceableFrameState);
+        for (Node node : workList) {
+            FrameState fs = (FrameState) node;
+            for (Node usage : fs.usages().snapshot()) {
+                if (!usage.isAlive()) {
+                    continue;
+                }
+                if (usage instanceof FrameState) {
+                    workList.add(usage);
+                } else {
+                    StateSplit stateSplit = (StateSplit) usage;
+                    FixedNode fixedStateSplit = stateSplit.asNode();
+                    if (fixedStateSplit instanceof MergeNode) {
+                        MergeNode merge = (MergeNode) fixedStateSplit;
+                        while (merge.isAlive()) {
+                            AbstractEndNode end = merge.forwardEnds().first();
+                            DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
+                            end.replaceAtPredecessor(deoptimizeNode);
+                            GraphUtil.killCFG(end);
+                        }
+                    } else {
+                        FixedNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
+                        if (fixedStateSplit instanceof BeginNode) {
+                            deoptimizeNode = BeginNode.begin(deoptimizeNode);
+                        }
+                        fixedStateSplit.replaceAtPredecessor(deoptimizeNode);
+                        GraphUtil.killCFG(fixedStateSplit);
+                    }
+                }
+            }
+        }
+    }
+
+    public static ValueNode mergeReturns(MergeNode merge, List<? extends ReturnNode> returnNodes) {
+        PhiNode returnValuePhi = null;
+
+        for (ReturnNode returnNode : returnNodes) {
+            // create and wire up a new EndNode
+            EndNode endNode = merge.graph().add(new EndNode());
+            merge.addForwardEnd(endNode);
+
+            if (returnNode.result() != null) {
+                if (returnValuePhi == null) {
+                    returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge));
+                }
+                returnValuePhi.addInput(returnNode.result());
+            }
+            returnNode.clearInputs();
+            returnNode.replaceAndDelete(endNode);
+
+        }
+        return returnValuePhi;
+    }
+
+    private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map<Node, Node> duplicates) {
+        for (Node node : duplicates.values()) {
+            if (node instanceof FrameState) {
+                FrameState frameState = (FrameState) node;
+                assert frameState.bci == BytecodeFrame.AFTER_BCI || frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI : node.toString(Verbosity.Debugger);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Gets the receiver for an invoke, adding a guard if necessary to ensure it is non-null.
+     */
+    public static ValueNode nonNullReceiver(Invoke invoke) {
+        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+        assert !callTarget.isStatic() : callTarget.targetMethod();
+        StructuredGraph graph = callTarget.graph();
+        ValueNode firstParam = callTarget.arguments().get(0);
+        if (firstParam.getKind() == Kind.Object && !StampTool.isObjectNonNull(firstParam)) {
+            IsNullNode condition = graph.unique(new IsNullNode(firstParam));
+            Stamp stamp = firstParam.stamp().join(objectNonNull());
+            GuardingPiNode nonNullReceiver = graph.add(new GuardingPiNode(firstParam, condition, true, NullCheckException, InvalidateReprofile, stamp));
+            graph.addBeforeFixed(invoke.asNode(), nonNullReceiver);
+            callTarget.replaceFirstInput(firstParam, nonNullReceiver);
+            return nonNullReceiver;
+        }
+        return firstParam;
+    }
+
+    public static boolean canIntrinsify(Replacements replacements, ResolvedJavaMethod target) {
+        return getIntrinsicGraph(replacements, target) != null || getMacroNodeClass(replacements, target) != null;
+    }
+
+    public static StructuredGraph getIntrinsicGraph(Replacements replacements, ResolvedJavaMethod target) {
+        return replacements.getMethodSubstitution(target);
+    }
+
+    public static Class<? extends FixedWithNextNode> getMacroNodeClass(Replacements replacements, ResolvedJavaMethod target) {
+        return replacements.getMacroSubstitution(target);
+    }
+
+    public static FixedWithNextNode inlineMacroNode(Invoke invoke, ResolvedJavaMethod concrete, Class<? extends FixedWithNextNode> macroNodeClass) throws GraalInternalError {
+        StructuredGraph graph = invoke.asNode().graph();
+        if (!concrete.equals(((MethodCallTargetNode) invoke.callTarget()).targetMethod())) {
+            assert ((MethodCallTargetNode) invoke.callTarget()).invokeKind() != InvokeKind.Static;
+            InliningUtil.replaceInvokeCallTarget(invoke, graph, InvokeKind.Special, concrete);
+        }
+
+        FixedWithNextNode macroNode = createMacroNodeInstance(macroNodeClass, invoke);
+
+        CallTargetNode callTarget = invoke.callTarget();
+        if (invoke instanceof InvokeNode) {
+            graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode));
+        } else {
+            InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke;
+            invokeWithException.killExceptionEdge();
+            graph.replaceSplitWithFixed(invokeWithException, graph.add(macroNode), invokeWithException.next());
+        }
+        GraphUtil.killWithUnusedFloatingInputs(callTarget);
+        return macroNode;
+    }
+
+    private static FixedWithNextNode createMacroNodeInstance(Class<? extends FixedWithNextNode> macroNodeClass, Invoke invoke) throws GraalInternalError {
+        try {
+            return macroNodeClass.getConstructor(Invoke.class).newInstance(invoke);
+        } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
+            throw new GraalGraphInternalError(e).addContext(invoke.asNode()).addContext("macroSubstitution", macroNodeClass);
+        }
+    }
+}
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeInliningRelevanceClosure.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,224 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.phases.graph;
-
-import java.util.*;
-
-import com.oracle.graal.compiler.common.cfg.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.cfg.*;
-import com.oracle.graal.nodes.util.*;
-
-public class ComputeInliningRelevanceClosure {
-
-    private static final double EPSILON = 1d / Integer.MAX_VALUE;
-
-    private final StructuredGraph graph;
-    private final NodesToDoubles nodeProbabilities;
-    private final NodesToDoubles nodeRelevances;
-
-    public ComputeInliningRelevanceClosure(StructuredGraph graph, NodesToDoubles nodeProbabilities) {
-        this.graph = graph;
-        this.nodeProbabilities = nodeProbabilities;
-        this.nodeRelevances = new NodesToDoubles(graph.getNodeCount());
-    }
-
-    public NodesToDoubles apply() {
-        new ComputeInliningRelevanceIterator(graph).apply();
-        return nodeRelevances;
-    }
-
-    private class ComputeInliningRelevanceIterator extends ScopedPostOrderNodeIterator {
-
-        private final HashMap<FixedNode, Scope> scopes;
-        private double currentProbability;
-        private double parentRelevance;
-
-        public ComputeInliningRelevanceIterator(StructuredGraph graph) {
-            super(graph);
-            this.scopes = computeScopesAndProbabilities();
-        }
-
-        @Override
-        protected void initializeScope() {
-            Scope scope = scopes.get(currentScopeStart);
-            parentRelevance = getParentScopeRelevance(scope);
-            currentProbability = scope.probability;
-        }
-
-        private double getParentScopeRelevance(Scope scope) {
-            if (scope.start instanceof LoopBeginNode) {
-                assert scope.parent != null;
-                double parentProbability = 0;
-                for (AbstractEndNode end : ((LoopBeginNode) scope.start).forwardEnds()) {
-                    parentProbability += nodeProbabilities.get(end);
-                }
-                return parentProbability / scope.parent.probability;
-            } else {
-                assert scope.parent == null;
-                return 1.0;
-            }
-        }
-
-        @Override
-        protected void invoke(Invoke invoke) {
-            double probability = nodeProbabilities.get(invoke.asNode());
-            assert !Double.isNaN(probability);
-
-            double relevance = (probability / currentProbability) * Math.min(1.0, parentRelevance);
-            nodeRelevances.put(invoke.asNode(), relevance);
-            assert !Double.isNaN(relevance);
-        }
-
-        private HashMap<FixedNode, Scope> computeScopesAndProbabilities() {
-            HashMap<FixedNode, Scope> result = new HashMap<>();
-
-            for (Scope scope : computeScopes()) {
-                double lowestPathProbability = computeLowestPathProbability(scope);
-                scope.probability = Math.max(EPSILON, lowestPathProbability);
-                result.put(scope.start, scope);
-            }
-
-            return result;
-        }
-
-        private Scope[] computeScopes() {
-            ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, false, false);
-
-            List<Loop<Block>> loops = cfg.getLoops();
-            HashMap<Loop<Block>, Scope> processedScopes = new HashMap<>();
-            Scope[] result = new Scope[loops.size() + 1];
-            Scope methodScope = new Scope(graph.start(), null);
-            processedScopes.put(null, methodScope);
-
-            result[0] = methodScope;
-            for (int i = 0; i < loops.size(); i++) {
-                result[i + 1] = createScope(loops.get(i), processedScopes);
-            }
-
-            return result;
-        }
-
-        private Scope createScope(Loop<Block> loop, HashMap<Loop<Block>, Scope> processedLoops) {
-            Scope parent = processedLoops.get(loop.parent);
-            if (parent == null) {
-                parent = createScope(loop.parent, processedLoops);
-            }
-            Scope result = new Scope(loop.header.getBeginNode(), parent);
-            processedLoops.put(loop, result);
-            return result;
-        }
-    }
-
-    private double computeLowestPathProbability(Scope scope) {
-        FixedNode scopeStart = scope.start;
-        ArrayList<FixedNode> pathBeginNodes = new ArrayList<>();
-        pathBeginNodes.add(scopeStart);
-        double minPathProbability = nodeProbabilities.get(scopeStart);
-        boolean isLoopScope = scopeStart instanceof LoopBeginNode;
-
-        do {
-            Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1);
-            do {
-                if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) {
-                    return minPathProbability;
-                } else if (current instanceof LoopBeginNode && current != scopeStart) {
-                    current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes);
-                    minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability);
-                } else if (current instanceof ControlSplitNode) {
-                    current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes);
-                    minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability);
-                } else {
-                    assert current.successors().count() <= 1;
-                    current = current.successors().first();
-                }
-            } while (current != null);
-        } while (!pathBeginNodes.isEmpty());
-
-        return minPathProbability;
-    }
-
-    private double getMinPathProbability(FixedNode current, double minPathProbability) {
-        if (current != null && nodeProbabilities.get(current) < minPathProbability) {
-            return nodeProbabilities.get(current);
-        }
-        return minPathProbability;
-    }
-
-    private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList<FixedNode> pathBeginNodes) {
-        Node maxSux = null;
-        double maxProbability = 0.0;
-        int pathBeginCount = pathBeginNodes.size();
-
-        for (Node sux : controlSplit.successors()) {
-            double probability = controlSplit.probability((BeginNode) sux);
-            if (probability > maxProbability) {
-                maxProbability = probability;
-                maxSux = sux;
-                truncate(pathBeginNodes, pathBeginCount);
-            } else if (probability == maxProbability) {
-                pathBeginNodes.add((FixedNode) sux);
-            }
-        }
-
-        return maxSux;
-    }
-
-    private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList<FixedNode> pathBeginNodes) {
-        Node maxSux = null;
-        double maxProbability = 0.0;
-        int pathBeginCount = pathBeginNodes.size();
-
-        for (LoopExitNode sux : loopBegin.loopExits()) {
-            double probability = nodeProbabilities.get(sux);
-            if (probability > maxProbability) {
-                maxProbability = probability;
-                maxSux = sux;
-                truncate(pathBeginNodes, pathBeginCount);
-            } else if (probability == maxProbability) {
-                pathBeginNodes.add(sux);
-            }
-        }
-
-        return maxSux;
-    }
-
-    private static void truncate(ArrayList<FixedNode> pathBeginNodes, int pathBeginCount) {
-        for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) {
-            pathBeginNodes.remove(pathBeginNodes.size() - 1);
-        }
-    }
-
-    private static class Scope {
-
-        public final FixedNode start;
-        public final Scope parent;
-        public double probability;
-
-        public Scope(FixedNode start, Scope parent) {
-            this.start = start;
-            this.parent = parent;
-        }
-    }
-}
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java	Fri May 02 02:45:26 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-/*
- * Copyright (c) 2011, 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.phases.graph;
-
-import java.util.*;
-
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.util.*;
-import com.oracle.graal.phases.util.*;
-
-/**
- * Computes probabilities for nodes in a graph.
- * <p>
- * The computation of absolute probabilities works in three steps:
- * <ol>
- * <li>{@link PropagateProbability} traverses the graph in post order (merges after their ends, ...)
- * and keeps track of the "probability state". Whenever it encounters a {@link ControlSplitNode} it
- * uses the split's probability information to divide the probability upon the successors. Whenever
- * it encounters an {@link Invoke} it assumes that the exception edge is unlikely and propagates the
- * whole probability to the normal successor. Whenever it encounters a {@link MergeNode} it sums up
- * the probability of all predecessors. It also maintains a set of active loops (whose
- * {@link LoopBeginNode} has been visited) and builds def/use information for step 2.</li>
- * <li></li>
- * <li>{@link PropagateLoopFrequency} propagates the loop frequencies and multiplies each
- * {@link FixedNode}'s probability with its loop frequency.</li>
- * </ol>
- */
-public class ComputeProbabilityClosure {
-
-    private static final double EPSILON = Double.MIN_NORMAL;
-
-    private final StructuredGraph graph;
-    private final NodesToDoubles nodeProbabilities;
-    private final Set<LoopInfo> loopInfos;
-    private final Map<MergeNode, Set<LoopInfo>> mergeLoops;
-
-    public ComputeProbabilityClosure(StructuredGraph graph) {
-        this.graph = graph;
-        this.nodeProbabilities = new NodesToDoubles(graph.getNodeCount());
-        this.loopInfos = new ArraySet<>();
-        this.mergeLoops = new IdentityHashMap<>();
-    }
-
-    public NodesToDoubles apply() {
-        // adjustControlSplitProbabilities();
-        new PropagateProbability(graph.start()).apply();
-        computeLoopFactors();
-        new PropagateLoopFrequency(graph.start()).apply();
-        // assert verifyProbabilities();
-        return nodeProbabilities;
-    }
-
-    private void computeLoopFactors() {
-        for (LoopInfo info : loopInfos) {
-            double frequency = info.loopFrequency(nodeProbabilities);
-            assert frequency != -1;
-        }
-    }
-
-    public static class LoopInfo {
-
-        public final LoopBeginNode loopBegin;
-
-        public final NodeMap<Set<LoopInfo>> requires;
-
-        private double loopFrequency = -1.0;
-        public boolean ended = false;
-
-        public LoopInfo(LoopBeginNode loopBegin) {
-            this.loopBegin = loopBegin;
-            this.requires = loopBegin.graph().createNodeMap();
-        }
-
-        public double loopFrequency(NodesToDoubles nodeProbabilities) {
-            // loopFrequency is initialized with -1.0
-            if (loopFrequency < 0.0 && ended) {
-                double backEdgeProb = 0.0;
-                for (LoopEndNode le : loopBegin.loopEnds()) {
-                    double factor = 1;
-                    Set<LoopInfo> requireds = requires.get(le);
-                    for (LoopInfo required : requireds) {
-                        double t = required.loopFrequency(nodeProbabilities);
-                        if (t == -1) {
-                            return -1;
-                        }
-                        factor = multiplySaturate(factor, t);
-                    }
-                    backEdgeProb += nodeProbabilities.get(le) * factor;
-                }
-                double entryProb = nodeProbabilities.get(loopBegin);
-                double d = entryProb - backEdgeProb;
-                if (d <= EPSILON) {
-                    d = EPSILON;
-                }
-                loopFrequency = entryProb / d;
-                loopBegin.setLoopFrequency(loopFrequency);
-            }
-            return loopFrequency;
-        }
-    }
-
-    /**
-     * Multiplies a and b and saturates the result to 1/{@link Double#MIN_NORMAL}.
-     * 
-     * @param a
-     * @param b
-     * @return a times b saturated to 1/{@link Double#MIN_NORMAL}
-     */
-    public static double multiplySaturate(double a, double b) {
-        double r = a * b;
-        if (r > 1 / Double.MIN_NORMAL) {
-            return 1 / Double.MIN_NORMAL;
-        }
-        return r;
-    }
-
-    private class Probability extends MergeableState<Probability> implements Cloneable {
-
-        public double probability;
-        public Set<LoopInfo> loops;
-        public LoopInfo loopInfo;
-
-        public Probability(double probability, Set<LoopInfo> loops) {
-            assert probability >= 0.0;
-            this.probability = probability;
-            this.loops = new ArraySet<>(4);
-            if (loops != null) {
-                this.loops.addAll(loops);
-            }
-        }
-
-        @Override
-        public Probability clone() {
-            return new Probability(probability, loops);
-        }
-
-        @Override
-        public boolean merge(MergeNode merge, List<Probability> withStates) {
-            if (merge.forwardEndCount() > 1) {
-                Set<LoopInfo> intersection = new ArraySet<>(loops);
-                for (Probability other : withStates) {
-                    intersection.retainAll(other.loops);
-                }
-                for (LoopInfo info : loops) {
-                    if (!intersection.contains(info)) {
-                        double loopFrequency = info.loopFrequency(nodeProbabilities);
-                        if (loopFrequency == -1) {
-                            return false;
-                        }
-                        probability = multiplySaturate(probability, loopFrequency);
-                        assert probability >= 0;
-                    }
-                }
-                for (Probability other : withStates) {
-                    double prob = other.probability;
-                    for (LoopInfo info : other.loops) {
-                        if (!intersection.contains(info)) {
-                            double loopFrequency = info.loopFrequency(nodeProbabilities);
-                            if (loopFrequency == -1) {
-                                return false;
-                            }
-                            prob = multiplySaturate(prob, loopFrequency);
-                            assert prob >= 0;
-                        }
-                    }
-                    probability += prob;
-                    assert probability >= 0;
-                }
-                loops = intersection;
-                mergeLoops.put(merge, new ArraySet<>(intersection));
-                probability = Math.max(0.0, probability);
-            }
-            return true;
-        }
-
-        @Override
-        public void loopBegin(LoopBeginNode loopBegin) {
-            loopInfo = new LoopInfo(loopBegin);
-            loopInfos.add(loopInfo);
-            loops.add(loopInfo);
-        }
-
-        @Override
-        public void loopEnds(LoopBeginNode loopBegin, List<Probability> loopEndStates) {
-            assert loopInfo != null;
-            List<LoopEndNode> loopEnds = loopBegin.orderedLoopEnds();
-            int i = 0;
-            for (Probability proba : loopEndStates) {
-                LoopEndNode loopEnd = loopEnds.get(i++);
-                Set<LoopInfo> requires = loopInfo.requires.get(loopEnd);
-                if (requires == null) {
-                    requires = new HashSet<>();
-                    loopInfo.requires.set(loopEnd, requires);
-                }
-                for (LoopInfo innerLoop : proba.loops) {
-                    if (innerLoop != loopInfo && !this.loops.contains(innerLoop)) {
-                        requires.add(innerLoop);
-                    }
-                }
-            }
-            loopInfo.ended = true;
-        }
-
-        @Override
-        public void afterSplit(BeginNode node) {
-            assert node.predecessor() != null;
-            Node pred = node.predecessor();
-            ControlSplitNode x = (ControlSplitNode) pred;
-            double nodeProbability = x.probability(node);
-            assert nodeProbability >= 0.0 : "Node " + x + " provided negative probability for begin " + node + ": " + nodeProbability;
-            probability *= nodeProbability;
-            assert probability >= 0.0;
-        }
-    }
-
-    private class PropagateProbability extends PostOrderNodeIterator<Probability> {
-
-        public PropagateProbability(FixedNode start) {
-            super(start, new Probability(1d, null));
-        }
-
-        @Override
-        protected void node(FixedNode node) {
-            nodeProbabilities.put(node, state.probability);
-        }
-    }
-
-    private class LoopCount extends MergeableState<LoopCount> implements Cloneable {
-
-        public double count;
-
-        public LoopCount(double count) {
-            this.count = count;
-        }
-
-        @Override
-        public LoopCount clone() {
-            return new LoopCount(count);
-        }
-
-        @Override
-        public boolean merge(MergeNode merge, List<LoopCount> withStates) {
-            assert merge.forwardEndCount() == withStates.size() + 1;
-            if (merge.forwardEndCount() > 1) {
-                Set<LoopInfo> loops = mergeLoops.get(merge);
-                assert loops != null;
-                double countProd = 1;
-                for (LoopInfo loop : loops) {
-                    countProd = multiplySaturate(countProd, loop.loopFrequency(nodeProbabilities));
-                }
-                count = countProd;
-            }
-            return true;
-        }
-
-        @Override
-        public void loopBegin(LoopBeginNode loopBegin) {
-            count = multiplySaturate(count, loopBegin.loopFrequency());
-        }
-    }
-
-    private class PropagateLoopFrequency extends PostOrderNodeIterator<LoopCount> {
-
-        public PropagateLoopFrequency(FixedNode start) {
-            super(start, new LoopCount(1d));
-        }
-
-        @Override
-        protected void node(FixedNode node) {
-            nodeProbabilities.put(node, nodeProbabilities.get(node) * state.count);
-        }
-
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/FixedNodeProbabilityCache.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 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.phases.graph;
+
+import java.util.*;
+import java.util.function.*;
+
+import com.oracle.graal.debug.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+
+/**
+ * Compute probabilities for fixed nodes on the fly and cache at {@link BeginNode}s and
+ * {@link ControlSplitNode}s.
+ */
+public class FixedNodeProbabilityCache implements ToDoubleFunction<FixedNode> {
+
+    private static final DebugMetric metricComputeNodeProbability = Debug.metric("ComputeNodeProbability");
+
+    private final IdentityHashMap<FixedNode, Double> cache = new IdentityHashMap<>();
+
+    public double applyAsDouble(FixedNode node) {
+        metricComputeNodeProbability.increment();
+
+        FixedNode current = node;
+        while (true) {
+            Node predecessor = current.predecessor();
+            if (current instanceof BeginNode) {
+                if (predecessor == null) {
+                    break;
+                } else if (predecessor.successors().count() != 1) {
+                    assert predecessor instanceof ControlSplitNode : "a FixedNode with multiple successors needs to be a ControlSplitNode: " + current + " / " + predecessor;
+                    break;
+                }
+            }
+            current = (FixedNode) predecessor;
+        }
+
+        Double cachedValue = cache.get(current);
+        if (cachedValue != null) {
+            return cachedValue;
+        }
+
+        double probability;
+        if (current.predecessor() == null) {
+            if (current instanceof MergeNode) {
+                probability = ((MergeNode) current).forwardEnds().stream().mapToDouble(end -> applyAsDouble(end)).sum();
+                if (current instanceof LoopBeginNode) {
+                    probability *= ((LoopBeginNode) current).loopFrequency();
+                }
+            } else if (current instanceof StartNode) {
+                probability = 1D;
+            } else {
+                // this should only appear for dead code
+                probability = 1D;
+            }
+        } else {
+            ControlSplitNode split = (ControlSplitNode) current.predecessor();
+            probability = split.probability((BeginNode) current) * applyAsDouble(split);
+        }
+        cache.put(current, probability);
+        return probability;
+    }
+}
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantNodeIterator.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantNodeIterator.java	Sat May 03 21:46:35 2014 +0200
@@ -47,7 +47,7 @@
 
         /**
          * Determine whether iteration should continue in the current state.
-         * 
+         *
          * @param currentState
          */
         protected boolean continueIteration(StateT currentState) {
@@ -60,11 +60,7 @@
     }
 
     public static <StateT> LoopInfo<StateT> processLoop(NodeIteratorClosure<StateT> closure, LoopBeginNode loop, StateT initialState) {
-        HashSet<FixedNode> boundary = new HashSet<>();
-        for (LoopExitNode exit : loop.loopExits()) {
-            boundary.add(exit);
-        }
-        Map<FixedNode, StateT> blockEndStates = apply(closure, loop, initialState, boundary);
+        Map<FixedNode, StateT> blockEndStates = apply(closure, loop, initialState, loop);
 
         LoopInfo<StateT> info = new LoopInfo<>();
         for (LoopEndNode end : loop.loopEnds()) {
@@ -80,7 +76,11 @@
         return info;
     }
 
-    public static <StateT> Map<FixedNode, StateT> apply(NodeIteratorClosure<StateT> closure, FixedNode start, StateT initialState, Set<FixedNode> boundary) {
+    public static <StateT> Map<FixedNode, StateT> apply(NodeIteratorClosure<StateT> closure, FixedNode start, StateT initialState) {
+        return apply(closure, start, initialState, null);
+    }
+
+    private static <StateT> Map<FixedNode, StateT> apply(NodeIteratorClosure<StateT> closure, FixedNode start, StateT initialState, LoopBeginNode boundary) {
         Deque<BeginNode> nodeQueue = new ArrayDeque<>();
         IdentityHashMap<FixedNode, StateT> blockEndStates = new IdentityHashMap<>();
 
@@ -88,7 +88,7 @@
         FixedNode current = start;
         do {
             while (current instanceof FixedWithNextNode) {
-                if (boundary != null && boundary.contains(current)) {
+                if (boundary != null && current instanceof LoopExitNode && ((LoopExitNode) current).loopBegin() == boundary) {
                     blockEndStates.put(current, state);
                     current = null;
                 } else {
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Sat May 03 21:46:35 2014 +0200
@@ -29,6 +29,7 @@
 import java.nio.channels.*;
 import java.util.*;
 import java.util.Map.Entry;
+import java.util.function.*;
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.cfg.*;
@@ -36,7 +37,6 @@
 import com.oracle.graal.graph.NodeClass.Position;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.schedule.*;
 
@@ -394,10 +394,10 @@
     }
 
     private void writeNodes(Graph graph) throws IOException {
-        NodesToDoubles probabilities = null;
+        ToDoubleFunction<FixedNode> probabilities = null;
         if (PrintGraphProbabilities.getValue()) {
             try {
-                probabilities = new ComputeProbabilityClosure((StructuredGraph) graph).apply();
+                probabilities = new FixedNodeProbabilityCache();
             } catch (Throwable t) {
             }
         }
@@ -408,8 +408,8 @@
         for (Node node : graph.getNodes()) {
             NodeClass nodeClass = node.getNodeClass();
             node.getDebugProperties(props);
-            if (probabilities != null && node instanceof FixedNode && probabilities.contains((FixedNode) node)) {
-                props.put("probability", probabilities.get((FixedNode) node));
+            if (probabilities != null && node instanceof FixedNode) {
+                props.put("probability", probabilities.applyAsDouble((FixedNode) node));
             }
             writeInt(getNodeId(node));
             writePoolObject(nodeClass);
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java	Sat May 03 21:46:35 2014 +0200
@@ -31,6 +31,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.nodes.*;
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/MethodSubstitutionTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/MethodSubstitutionTest.java	Sat May 03 21:46:35 2014 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.replacements.test;
 
 import static org.junit.Assert.*;
-
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.compiler.test.*;
@@ -33,6 +32,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 /**
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -36,11 +36,11 @@
 /**
  * This phase ensures that there's a single {@linkplain BytecodeFrame#AFTER_BCI collapsed frame
  * state} per path.
- * 
+ *
  * Removes other frame states from {@linkplain StateSplit#hasSideEffect() non-side-effecting} nodes
  * in the graph, and replaces them with {@linkplain BytecodeFrame#INVALID_FRAMESTATE_BCI invalid
  * frame states}.
- * 
+ *
  * The invalid frame states ensure that no deoptimization to a snippet frame state will happen.
  */
 public class CollapseFrameForSingleSideEffectPhase extends Phase {
@@ -112,7 +112,7 @@
     @Override
     protected void run(StructuredGraph graph) {
         CollapseFrameForSingleSideEffectClosure closure = new CollapseFrameForSingleSideEffectClosure();
-        ReentrantNodeIterator.apply(closure, graph.start(), new IterationState(), null);
+        ReentrantNodeIterator.apply(closure, graph.start(), new IterationState());
         closure.finishProcessing(graph);
     }
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Sat May 03 21:46:35 2014 +0200
@@ -35,6 +35,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing;
 import com.oracle.graal.word.phases.*;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Sat May 03 21:46:35 2014 +0200
@@ -49,6 +49,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.replacements.Snippet.DefaultSnippetInliningPolicy;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Sat May 03 21:46:35 2014 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.replacements;
 
 import static com.oracle.graal.api.meta.LocationIdentity.*;
-
 import static com.oracle.graal.api.meta.MetaUtil.*;
 import static com.oracle.graal.debug.Debug.*;
 import static java.util.FormattableFlags.*;
@@ -54,6 +53,7 @@
 import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.common.FloatingReadPhase.MemoryMapImpl;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.replacements.Snippet.ConstantParameter;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Sat May 03 21:46:35 2014 +0200
@@ -35,6 +35,7 @@
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.replacements.*;
 
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Sat May 03 21:46:35 2014 +0200
@@ -45,14 +45,14 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.printer.*;
 import com.oracle.graal.runtime.*;
 import com.oracle.graal.truffle.*;
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.SlowPath;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.impl.*;
 import com.oracle.truffle.api.nodes.*;
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Sat May 03 21:46:35 2014 +0200
@@ -37,6 +37,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.printer.*;
 import com.oracle.graal.truffle.*;
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Sat May 03 21:46:35 2014 +0200
@@ -48,6 +48,7 @@
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.common.CanonicalizerPhase.CustomCanonicalizer;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.truffle.nodes.asserts.*;
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCacheImpl.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCacheImpl.java	Sat May 03 21:46:35 2014 +0200
@@ -43,6 +43,7 @@
 import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.truffle.phases.*;
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/ReplaceIntrinsicsPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/ReplaceIntrinsicsPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -28,7 +28,7 @@
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
 
 /**
  * Compiler phase for intrinsifying the access to the Truffle virtual frame.
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -24,6 +24,8 @@
 
 import static com.oracle.graal.debug.Debug.*;
 
+import java.util.*;
+
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.graph.*;
@@ -46,7 +48,7 @@
     }
 
     private final int maxIterations;
-    private final CanonicalizerPhase canonicalizer;
+    protected final CanonicalizerPhase canonicalizer;
 
     public EffectsPhase(int maxIterations, CanonicalizerPhase canonicalizer) {
         this.maxIterations = maxIterations;
@@ -85,19 +87,24 @@
 
                 new DeadCodeEliminationPhase().apply(graph);
 
+                Set<Node> changedNodes = listener.getChangedNodes();
                 for (Node node : graph.getNodes()) {
                     if (node instanceof Simplifiable) {
-                        listener.getChangedNodes().add(node);
+                        changedNodes.add(node);
                     }
                 }
-                if (canonicalizer != null) {
-                    canonicalizer.applyIncremental(graph, context, listener.getChangedNodes());
-                }
+                postIteration(graph, context, changedNodes);
             }
             changed = true;
         }
         return changed;
     }
 
+    protected void postIteration(final StructuredGraph graph, final PhaseContextT context, Set<Node> changedNodes) {
+        if (canonicalizer != null) {
+            canonicalizer.applyIncremental(graph, context, changedNodes);
+        }
+    }
+
     protected abstract Closure<?> createEffectsClosure(PhaseContextT context, SchedulePhase schedule);
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/IterativeInliningPhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/IterativeInliningPhase.java	Sat May 03 21:46:35 2014 +0200
@@ -31,7 +31,8 @@
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.common.*;
-import com.oracle.graal.phases.common.cfs.IterativeFlowSensitiveReductionPhase;
+import com.oracle.graal.phases.common.cfs.*;
+import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.tiers.*;
 
 public class IterativeInliningPhase extends AbstractInliningPhase {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapePhase.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapePhase.java	Sat May 03 21:46:35 2014 +0200
@@ -26,14 +26,15 @@
 import static com.oracle.graal.virtual.phases.ea.PartialEscapePhase.Options.*;
 
 import java.util.*;
+import java.util.function.*;
 
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.options.*;
+import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.schedule.*;
@@ -42,7 +43,6 @@
 public class PartialEscapePhase extends EffectsPhase<PhaseContext> {
 
     static class Options {
-
         //@formatter:off
         @Option(help = "")
         public static final OptionValue<Boolean> OptEarlyReadElimination = new OptionValue<>(true);
@@ -50,14 +50,28 @@
     }
 
     private final boolean readElimination;
+    private final BasePhase<PhaseContext> cleanupPhase;
 
     public PartialEscapePhase(boolean iterative, CanonicalizerPhase canonicalizer) {
-        this(iterative, OptEarlyReadElimination.getValue(), canonicalizer);
+        this(iterative, OptEarlyReadElimination.getValue(), canonicalizer, null);
+    }
+
+    public PartialEscapePhase(boolean iterative, CanonicalizerPhase canonicalizer, BasePhase<PhaseContext> cleanupPhase) {
+        this(iterative, OptEarlyReadElimination.getValue(), canonicalizer, cleanupPhase);
     }
 
-    public PartialEscapePhase(boolean iterative, boolean readElimination, CanonicalizerPhase canonicalizer) {
+    public PartialEscapePhase(boolean iterative, boolean readElimination, CanonicalizerPhase canonicalizer, BasePhase<PhaseContext> cleanupPhase) {
         super(iterative ? EscapeAnalysisIterations.getValue() : 1, canonicalizer);
         this.readElimination = readElimination;
+        this.cleanupPhase = cleanupPhase;
+    }
+
+    @Override
+    protected void postIteration(StructuredGraph graph, PhaseContext context, Set<Node> changedNodes) {
+        super.postIteration(graph, context, changedNodes);
+        if (cleanupPhase != null) {
+            cleanupPhase.apply(graph, context);
+        }
     }
 
     @Override
@@ -79,7 +93,7 @@
     }
 
     public static Map<Invoke, Double> getHints(StructuredGraph graph) {
-        NodesToDoubles probabilities = new ComputeProbabilityClosure(graph).apply();
+        ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache();
         Map<Invoke, Double> hints = null;
         for (CommitAllocationNode commit : graph.getNodes().filter(CommitAllocationNode.class)) {
             double sum = 0;
@@ -87,14 +101,14 @@
             for (Node commitUsage : commit.usages()) {
                 for (Node usage : commitUsage.usages()) {
                     if (usage instanceof FixedNode) {
-                        sum += probabilities.get((FixedNode) usage);
+                        sum += probabilities.applyAsDouble((FixedNode) usage);
                     } else {
                         if (usage instanceof MethodCallTargetNode) {
-                            invokeSum += probabilities.get(((MethodCallTargetNode) usage).invoke().asNode());
+                            invokeSum += probabilities.applyAsDouble(((MethodCallTargetNode) usage).invoke().asNode());
                         }
                         for (Node secondLevelUage : usage.usages()) {
                             if (secondLevelUage instanceof FixedNode) {
-                                sum += probabilities.get(((FixedNode) secondLevelUage));
+                                sum += probabilities.applyAsDouble(((FixedNode) secondLevelUage));
                             }
                         }
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java	Sat May 03 21:46:35 2014 +0200
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 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.truffle.api.dsl.test;
+
+import java.lang.reflect.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.LazyClassLoadingTestFactory.TestNodeFactory;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+
+public class LazyClassLoadingTest {
+    @Test
+    public void test() {
+        String testClassName = getClass().getName();
+        String factoryClassName = testClassName + "Factory";
+        String nodeFactoryClassName = factoryClassName + "$TestNodeFactory";
+
+        Assert.assertFalse(isLoaded(factoryClassName + "$TestNode"));
+        Assert.assertFalse(isLoaded(nodeFactoryClassName));
+
+        NodeFactory<TestNode> factory = TestNodeFactory.getInstance();
+
+        Assert.assertTrue(isLoaded(nodeFactoryClassName));
+        Assert.assertFalse(isLoaded(nodeFactoryClassName + "$TestBaseNode"));
+
+        TestHelper.createRoot(factory);
+
+        Assert.assertTrue(isLoaded(nodeFactoryClassName + "$TestBaseNode"));
+        Assert.assertTrue(isLoaded(nodeFactoryClassName + "$TestUninitializedNode"));
+        Assert.assertFalse(isLoaded(nodeFactoryClassName + "$TestGenericNode"));
+    }
+
+    private boolean isLoaded(String className) {
+        ClassLoader classLoader = getClass().getClassLoader();
+        Method m;
+        try {
+            m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
+            m.setAccessible(true);
+            return m.invoke(classLoader, className) != null;
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    @NodeChildren({@NodeChild("left"), @NodeChild("right")})
+    abstract static class TestNode extends ValueNode {
+        @Specialization(order = 1)
+        int add(int left, int right) {
+            return 42;
+        }
+
+        @Specialization(order = 2)
+        int add(boolean left, boolean right) {
+            return 21;
+        }
+
+        @Specialization(order = 4)
+        String add(boolean left, int right) {
+            return "(boolean,int)";
+        }
+    }
+}
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java	Sat May 03 21:46:35 2014 +0200
@@ -36,10 +36,8 @@
  *
  * <p>
  * Child nodes are stored in the class of the parent node in fields that are marked with the
- * {@link Child} annotation. Before such a field is assigned, {@link Node#adoptChild} must be
- * called. This method automatically establishes a link from the child to the parent. The
- * {@link Node#getParent()} method allows access to this field. Every node also provides the ability
- * to iterate over its children using {@link Node#getChildren()}.
+ * {@link Child} annotation. The {@link Node#getParent()} method allows access to this field. Every
+ * node also provides the ability to iterate over its children using {@link Node#getChildren()}.
  * </p>
  *
  * <p>
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java	Sat May 03 21:46:35 2014 +0200
@@ -24,8 +24,6 @@
  */
 package com.oracle.truffle.api.instrument.impl;
 
-import java.io.*;
-
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
@@ -53,16 +51,7 @@
             section = node.getEncapsulatingSourceSection();
             estimated = true;
         }
-
-        String sourceString;
-        if (section == null || section.getSource() == null) {
-            sourceString = "<unknown source>";
-        } else {
-            String sourceName = new File(section.getSource().getName()).getName();
-            int startLine = section.getStartLine();
-            sourceString = String.format("%s:%d%s", sourceName, startLine, estimated ? "~" : "");
-        }
-        return sourceString;
+        return section.getShortDescription() + (estimated ? "~" : "");
     }
 
     public String displayMethodName(Node node) {
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Sat May 03 21:46:35 2014 +0200
@@ -134,31 +134,6 @@
      * @param newChildren the array of new children whose parent should be updated
      * @return the array of new children
      */
-    @SuppressWarnings("static-method")
-    @Deprecated
-    protected final <T extends Node> T[] adoptChildren(final T[] newChildren) {
-        return newChildren;
-    }
-
-    /**
-     * Method that updates the link to the parent in the specified new child node to this node.
-     *
-     * @param newChild the new child whose parent should be updated
-     * @return the new child
-     */
-    @SuppressWarnings("static-method")
-    @Deprecated
-    protected final <T extends Node> T adoptChild(final T newChild) {
-        return newChild;
-    }
-
-    /**
-     * Method that updates the link to the parent in the array of specified new child nodes to this
-     * node.
-     *
-     * @param newChildren the array of new children whose parent should be updated
-     * @return the array of new children
-     */
     protected final <T extends Node> T[] insert(final T[] newChildren) {
         CompilerDirectives.transferToInterpreterAndInvalidate();
         assert newChildren != null;
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Fri May 02 02:45:26 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Sat May 03 21:46:35 2014 +0200
@@ -49,6 +49,8 @@
     private static final String EXECUTE_POLYMORPHIC_NAME = "executePolymorphic0";
 
     private static final String UPDATE_TYPES_NAME = "updateTypes";
+    private static final String COPY_WITH_CONSTRUCTOR_NAME = "copyWithConstructor";
+    private static final String CREATE_SPECIALIZATION_NAME = "createSpecialization";
 
     public NodeCodeGenerator(ProcessorContext context) {
         super(context);
@@ -834,7 +836,7 @@
             if (node.getSpecializations().isEmpty()) {
                 body.nullLiteral();
             } else {
-                body.startNew(nodeSpecializationClassName(node.getSpecializations().get(0)));
+                body.startCall(nodeSpecializationClassName(node.getSpecializations().get(0)), CREATE_SPECIALIZATION_NAME);
                 for (VariableElement var : method.getParameters()) {
                     body.string(var.getSimpleName().toString());
                 }
@@ -865,7 +867,7 @@
             if (found == null) {
                 body.startThrow().startNew(getContext().getType(UnsupportedOperationException.class)).end().end();
             } else {
-                body.startReturn().startNew(nodeSpecializationClassName(found)).startGroup().cast(baseClassName(node)).string(THIS_NODE_LOCAL_VAR_NAME).end().end().end();
+                body.startReturn().startCall(nodeSpecializationClassName(found), CREATE_SPECIALIZATION_NAME).startGroup().string(THIS_NODE_LOCAL_VAR_NAME).end().end().end();
             }
             return method;
         }
@@ -952,7 +954,6 @@
             if (node.getGenericSpecialization() != null && node.getGenericSpecialization().isReachable()) {
                 clazz.add(createGenericExecute(node, rootGroup));
             }
-
         }
 
         protected boolean needsInvokeCopyConstructorMethod() {
@@ -960,7 +961,7 @@
         }
 
         protected CodeExecutableElement createCopy(TypeMirror baseType, SpecializationData specialization) {
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), baseType, "copyWithConstructor");
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), baseType, COPY_WITH_CONSTRUCTOR_NAME);
             if (specialization == null) {
                 method.getModifiers().add(ABSTRACT);
             } else {
@@ -968,7 +969,7 @@
                 builder.startReturn();
                 builder.startNew(getElement().asType());
                 builder.string("this");
-                for (ActualParameter param : getImplicitTypeParamters(specialization)) {
+                for (ActualParameter param : getImplicitTypeParameters(specialization)) {
                     builder.string(implicitTypeName(param));
                 }
                 builder.end().end();
@@ -1738,7 +1739,7 @@
             } else {
                 replaceCall.startCall("replace");
             }
-            replaceCall.startGroup().startNew(className).string(source);
+            replaceCall.startGroup().cast(baseClassName(current.getNode())).startCall(className, CREATE_SPECIALIZATION_NAME).string(source);
             for (ActualParameter param : current.getSignatureParameters()) {
                 NodeChildData child = param.getSpecification().getExecution().getChild();
                 List<TypeData> types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType());
@@ -1762,7 +1763,7 @@
             String uninitializedName = nodeSpecializationClassName(node.getUninitializedSpecialization());
             CodeTreeBuilder builder = parent.create();
 
-            builder.declaration(getElement().asType(), "currentCopy", currentNode + ".copyWithConstructor()");
+            builder.declaration(getElement().asType(), "currentCopy", currentNode + "." + COPY_WITH_CONSTRUCTOR_NAME + "()");
             for (ActualParameter param : getModel().getSignatureParameters()) {
                 NodeExecutionData execution = param.getSpecification().getExecution();
                 builder.startStatement().tree(createAccessChild(execution, "currentCopy")).string(" = ").nullLiteral().end();
@@ -1928,7 +1929,7 @@
             }
         }
 
-        protected final List<ActualParameter> getImplicitTypeParamters(SpecializationData model) {
+        protected final List<ActualParameter> getImplicitTypeParameters(SpecializationData model) {
             List<ActualParameter> parameter = new ArrayList<>();
             for (ActualParameter param : model.getSignatureParameters()) {
                 NodeChildData child = param.getSpecification().getExecution().getChild();
@@ -2546,6 +2547,18 @@
             if (needsInvokeCopyConstructorMethod()) {
                 clazz.add(createCopy(nodeGen.asType(), specialization));
             }
+
+            if (!specialization.isUninitialized() && specialization.getNode().needsRewrites(context)) {
+                clazz.add(createCopyConstructorFactoryMethod(nodeGen.asType(), specialization));
+            } else {
+                for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
+                    if (constructor.getParameters().size() == 1 && ((CodeVariableElement) constructor.getParameters().get(0)).getType().equals(nodeGen.asType())) {
+                        // skip copy constructor - not used
+                        continue;
+                    }
+                    clazz.add(createConstructorFactoryMethod(specialization, constructor));
+                }
+            }
         }
 
         protected void createConstructors(CodeTypeElement clazz) {
@@ -2579,7 +2592,7 @@
                     }
                 }
                 if (superConstructor != null) {
-                    for (ActualParameter param : getImplicitTypeParamters(getModel())) {
+                    for (ActualParameter param : getImplicitTypeParameters(getModel())) {
                         clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), getContext().getType(Class.class), implicitTypeName(param)));
                         superConstructor.getParameters().add(new CodeVariableElement(getContext().getType(Class.class), implicitTypeName(param)));
 
@@ -2911,6 +2924,40 @@
             return builder.getRoot();
         }
 
+        protected CodeExecutableElement createCopyConstructorFactoryMethod(TypeMirror baseType, SpecializationData specialization) {
+            List<ActualParameter> implicitTypeParams = getImplicitTypeParameters(specialization);
+            CodeVariableElement[] parameters = new CodeVariableElement[implicitTypeParams.size() + 1];
+            int i = 0;
+            String baseName = "current";
+            parameters[i++] = new CodeVariableElement(specialization.getNode().getNodeType(), baseName);
+            for (ActualParameter implicitTypeParam : implicitTypeParams) {
+                parameters[i++] = new CodeVariableElement(getContext().getType(Class.class), implicitTypeName(implicitTypeParam));
+            }
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), CREATE_SPECIALIZATION_NAME, parameters);
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startReturn();
+            builder.startNew(getElement().asType());
+            builder.startGroup().cast(baseType, CodeTreeBuilder.singleString(baseName)).end();
+            for (ActualParameter param : implicitTypeParams) {
+                builder.string(implicitTypeName(param));
+            }
+            builder.end().end();
+            return method;
+        }
+
+        protected CodeExecutableElement createConstructorFactoryMethod(SpecializationData specialization, ExecutableElement constructor) {
+            List<? extends VariableElement> parameters = constructor.getParameters();
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), CREATE_SPECIALIZATION_NAME,
+                            parameters.toArray(new CodeVariableElement[parameters.size()]));
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startReturn();
+            builder.startNew(getElement().asType());
+            for (VariableElement param : parameters) {
+                builder.string(((CodeVariableElement) param).getName());
+            }
+            builder.end().end();
+            return method;
+        }
     }
 
     private interface CodeBlock<T> {
--- a/mx/projects	Fri May 02 02:45:26 2014 +0200
+++ b/mx/projects	Sat May 03 21:46:35 2014 +0200
@@ -315,7 +315,7 @@
 # graal.alloc
 project@com.oracle.graal.alloc@subDir=graal
 project@com.oracle.graal.alloc@sourceDirs=src
-project@com.oracle.graal.alloc@dependencies=com.oracle.graal.nodes
+project@com.oracle.graal.alloc@dependencies=com.oracle.graal.compiler.common
 project@com.oracle.graal.alloc@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.alloc@javaCompliance=1.8
 project@com.oracle.graal.alloc@workingSets=Graal
--- a/src/gpu/hsail/vm/gpu_hsail.cpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/gpu/hsail/vm/gpu_hsail.cpp	Sat May 03 21:46:35 2014 +0200
@@ -67,7 +67,7 @@
 JNINativeMethod Hsail::HSAIL_methods[] = {
   {CC"initialize",       CC"()Z",                               FN_PTR(Hsail::initialize)},
   {CC"generateKernel",   CC"([B" STRING ")J",                   FN_PTR(Hsail::generate_kernel)},
-  {CC"executeKernel0",   CC"("HS_INSTALLED_CODE"I["OBJECT"["OBJECT"["JLTHREAD"I)Z",  FN_PTR(Hsail::execute_kernel_void_1d)},
+  {CC"executeKernel0",   CC"("HS_INSTALLED_CODE"I["OBJECT"["OBJECT"["JLTHREAD"I[I)Z",  FN_PTR(Hsail::execute_kernel_void_1d)},
 };
 
 void * Hsail::_device_context = NULL;
@@ -147,7 +147,7 @@
 }
 
 GPU_VMENTRY(jboolean, Hsail::execute_kernel_void_1d, (JNIEnv* env, jclass, jobject kernel_handle, jint dimX, jobject args, jobject oops_save,
-                                                      jobject donor_threads, jint allocBytesPerWorkitem))
+                                                      jobject donor_threads, jint allocBytesPerWorkitem, jobject oop_map_array))
 
   ResourceMark rm;
   jlong nmethodValue = InstalledCode::address(kernel_handle);
@@ -163,7 +163,7 @@
     SharedRuntime::throw_and_post_jvmti_exception(JavaThread::current(), vmSymbols::com_oracle_graal_api_code_InvalidInstalledCodeException(), NULL);
   }
 
-  return execute_kernel_void_1d_internal((address) kernel, dimX, args, mh, nm, oops_save, donor_threads, allocBytesPerWorkitem, CHECK_0);
+return execute_kernel_void_1d_internal((address) kernel, dimX, args, mh, nm, oops_save, donor_threads, allocBytesPerWorkitem, oop_map_array, CHECK_0);
 GPU_END
 
 static void showRanges(jboolean *a, int len) {
@@ -215,9 +215,141 @@
   tty->print_cr("(%p, %p, %p), siz=%ld, free=%ld (%f%%)", start, top, end, tlabSize, tlabFree, freePct);
 }
 
+class OopSaver : public StackObj {
+private:
+  objArrayOop _oopsSaveArray;
+  typeArrayOop _oopMapArray;
+  jobject  _oops_save;
+  jobject _oop_map_array;
+  int _last_pcoffset;
+  int _last_idx;
+  int _saveAreaCounts;
 
-jboolean Hsail::execute_kernel_void_1d_internal(address kernel, int dimX, jobject args, methodHandle& mh, nmethod *nm, jobject oops_save, 
-                                                jobject donor_threads, int allocBytesPerWorkitem, TRAPS) {
+  enum {
+    SAVEAREACOUNTS_OFST=0,
+    SPAN_OFST=1,
+    HEADERSIZE=2
+  }; 
+  int mapPcOffsetToIndex(int pcOffset) {
+    if (pcOffset == _last_pcoffset) {
+      return _last_idx;
+    }
+    int span = _oopMapArray->int_at(SPAN_OFST);
+    for (int idx = HEADERSIZE; idx < _oopMapArray->length(); idx += span) {
+      int ofst = _oopMapArray->int_at(idx);
+      if (ofst == pcOffset) {
+        _last_pcoffset = pcOffset;
+        _last_idx = idx + 1;
+        return _last_idx;
+      }
+    }
+    ShouldNotReachHere();
+    return -1;
+  }
+
+public:
+  OopSaver(jobject oops_save, jobject oop_map_array) {
+    _oops_save = oops_save;
+    _oop_map_array = oop_map_array;
+    _last_pcoffset = -1;
+    _saveAreaCounts = getSaveAreaCounts(oop_map_array);
+    resolveArrays();
+  }
+ 
+  void resolveArrays() {
+    _oopsSaveArray = (objArrayOop) JNIHandles::resolve(_oops_save);
+    _oopMapArray = (typeArrayOop) JNIHandles::resolve(_oop_map_array);
+  }
+
+  void * getOopForBit(HSAILFrame * hsailFrame, int bit) {
+    assert(isOop(hsailFrame, bit), "");
+    void *oop;
+    if (bit < hsailFrame->num_d_regs()) {
+      // d register
+      oop = (void*) hsailFrame->get_d_reg(bit);
+    } else {
+      // stack slot
+      int stackOffset = (bit - hsailFrame->num_d_regs()) * 8;  // 8 bytes per stack slot
+      oop = (void *) hsailFrame->get_stackslot64(stackOffset);
+    }
+    return oop;
+  }
+
+  void putOopForBit(HSAILFrame * hsailFrame, int bit, void *oop) {
+    assert(isOop(hsailFrame, bit), "");
+    if (bit < hsailFrame->num_d_regs()) {
+      // d register
+      hsailFrame->put_d_reg(bit, (jlong) oop);
+    } else {
+      // stack slot
+      int stackOffset = (bit - hsailFrame->num_d_regs()) * 8;  // 8 bytes per stack slot
+      hsailFrame->put_stackslot64(stackOffset, (jlong) oop);
+    }
+  }
+
+  void saveOopsFromFrame(HSAILFrame * hsailFrame, int deoptSlot){
+    // as used, no need to resolve arrays on each call
+    int oopsPerDeopt = hsailFrame->num_d_regs() + hsailFrame->num_stack_slots();
+
+    // handle the dregister and stackSlot based oops
+    for (int bit = 0; bit < oopsPerDeopt; bit++) {
+      if (isOop(hsailFrame, bit)) {
+        void* saved_oop = getOopForBit(hsailFrame, bit);
+        int saveArrayIndex = deoptSlot * oopsPerDeopt + bit;
+        _oopsSaveArray->obj_at_put(saveArrayIndex, (oop) saved_oop);
+      }
+    }
+  }
+
+  void restoreOopsToFrame(HSAILFrame * hsailFrame, int deoptSlot, int workitem){
+    // need to re-resolve on each restore
+    resolveArrays();
+    int oopsPerDeopt = hsailFrame->num_d_regs() + hsailFrame->num_stack_slots();
+
+    // handle the dregister and stackSlot based oops
+    for (int bit = 0; bit < oopsPerDeopt; bit++) {
+      if (isOop(hsailFrame, bit)) {
+        // the dregister or stack slot at this bit is an oop, retrieve it from array and put back in frame
+        int saveArrayIndex = deoptSlot * oopsPerDeopt + bit;
+        void * newValue = (void *) _oopsSaveArray->obj_at(saveArrayIndex);
+        void * oldValue = getOopForBit(hsailFrame, bit);
+        assert((oldValue != 0 ? newValue != 0 : newValue == 0), "bad dregValue retrieved");
+        if (newValue != oldValue) {
+          if (TraceGPUInteraction) {
+            int numDRegs = hsailFrame->num_d_regs();
+            const char *name = (bit < numDRegs ? "$d" : "stk");
+            int num = (bit < numDRegs ? bit : bit - numDRegs);
+            tty->print_cr("oop moved for %s%d, workitem %d, slot %d, old=%p, new=%p",
+                          name, num, workitem, deoptSlot, oldValue, newValue);
+          }
+          putOopForBit(hsailFrame, bit, newValue);
+        }
+      }
+    }
+  }
+
+  bool isOop(HSAILFrame * hsailFrame, int bit){
+    // re-resolve on each access
+    resolveArrays();
+    if (bit > hsailFrame->num_d_regs() + hsailFrame->num_stack_slots()) {
+      return false;
+    }
+    int pcOffset = hsailFrame->pc_offset();
+    int bits_int_idx = mapPcOffsetToIndex(pcOffset) + (bit / 32);
+    int bitpos = bit % 32;
+    int bits = _oopMapArray->int_at(bits_int_idx);
+    return ((bits & (1 << bitpos)) != 0);
+  }
+
+  static int getSaveAreaCounts(jobject oopMapArrayObject) {
+    typeArrayOop oopMapArray = (typeArrayOop) JNIHandles::resolve(oopMapArrayObject);
+    return oopMapArray->int_at(SAVEAREACOUNTS_OFST);
+  }
+
+};
+
+jboolean Hsail::execute_kernel_void_1d_internal(address kernel, int dimX, jobject args, methodHandle& mh, nmethod *nm, jobject oops_save,
+                                                jobject donor_threads, int allocBytesPerWorkitem, jobject oop_map_array, TRAPS) {
   ResourceMark rm(THREAD);
   objArrayOop argsArray = (objArrayOop) JNIHandles::resolve(args);
 
@@ -260,9 +392,16 @@
   // Reset the kernel arguments
   _okra_clearargs(kernel);
 
+  // get how many bytes per deopt save area are required
+  int saveAreaCounts = OopSaver::getSaveAreaCounts(oop_map_array);
+  int numSRegs = saveAreaCounts & 0xff;
+  int numDRegs = (saveAreaCounts >> 8) & 0xff;
+  int numStackSlots = (saveAreaCounts >> 16);
+  int bytesPerSaveArea = numSRegs * 4 + (numDRegs + numStackSlots) * 8;
+
   HSAILDeoptimizationInfo* e;
   if (UseHSAILDeoptimization) {
-    e = new (ResourceObj::C_HEAP, mtInternal) HSAILDeoptimizationInfo();
+    e = new (MAX_DEOPT_SLOTS, bytesPerSaveArea) HSAILDeoptimizationInfo(MAX_DEOPT_SLOTS, bytesPerSaveArea);
     e->set_never_ran_array(NEW_C_HEAP_ARRAY(jboolean, dimX, mtInternal));
     memset(e->never_ran_array(), 0, dimX * sizeof(jboolean));
     e->set_donor_threads(donorThreads);
@@ -318,7 +457,6 @@
   if (UseHSAILDeoptimization) {
     kernelStats.incDeopts();
     // check if any workitem requested a deopt
-    // currently we only support at most one such workitem
     int deoptcode = e->deopt_occurred();
     if (deoptcode != 1) {
       if (deoptcode == 0) {
@@ -337,40 +475,30 @@
         TraceTime t3("handle deoptimizing workitems", TraceGPUInteraction);
         if (TraceGPUInteraction) {
           tty->print_cr("deopt happened.");
-          HSAILKernelDeoptimization * pdeopt = &e->_deopt_save_states[0];
+          HSAILKernelDeoptimization * pdeopt = e->get_deopt_save_state(0);
           tty->print_cr("first deopter was workitem %d", pdeopt->workitem());
         }
 
         // Before handling any deopting workitems, save the pointers from
         // the hsail frames in oops_save so they get adjusted by any
         // GC. Need to do this before leaving thread_in_vm mode.
+        OopSaver oopSaver(oops_save, oop_map_array);
         // resolve handle only needed once here (not exiting vm mode)
-        objArrayOop oopsSaveArray = (objArrayOop) JNIHandles::resolve(oops_save);
+        oopSaver.resolveArrays();
 
         // since slots are allocated from the beginning, we know how far to look
-        assert(e->num_deopts() < MAX_DEOPT_SAVE_STATES_SIZE, "deopt save state overflow");
+        assert(e->num_deopts() < e->num_slots(), "deopt save state overflow");
         for (int k = 0; k < e->num_deopts(); k++) {
-          HSAILKernelDeoptimization * pdeopt = &e->_deopt_save_states[k];
-          jint workitem = pdeopt->workitem();
-          if (workitem != -1) {
-            // this is a workitem that deopted
-            HSAILFrame *hsailFrame = pdeopt->first_frame();
-            int dregOopMap = hsailFrame->dreg_oops_map();
-            for (int bit = 0; bit < 16; bit++) {
-              if ((dregOopMap & (1 << bit)) != 0) {
-                // the dregister at this bit is an oop, save it in the array
-                int index = k * 16 + bit;
-                void* saved_oop = (void*) hsailFrame->get_d_reg(bit);
-                oopsSaveArray->obj_at_put(index, (oop) saved_oop);
-              }
-            }
-          }
+          HSAILKernelDeoptimization * pdeopt = e->get_deopt_save_state(k);
+          assert (pdeopt->workitem() >= 0, "bad workitem in deopt");
+          // this is a workitem that deopted
+          oopSaver.saveOopsFromFrame(pdeopt->first_frame(), k);
         }
 
         // Handle any deopting workitems.
         int count_deoptimized = 0;
         for (int k = 0; k < e->num_deopts(); k++) {
-          HSAILKernelDeoptimization * pdeopt = &e->_deopt_save_states[k];
+          HSAILKernelDeoptimization * pdeopt = e->get_deopt_save_state(k);
 
           jint workitem = pdeopt->workitem();
           if (workitem != -1) {
@@ -378,25 +506,8 @@
             HSAILFrame *hsailFrame = pdeopt->first_frame();
 
             // update the hsailFrame from the oopsSaveArray
-            // re-resolve the handle
-            oopsSaveArray = (objArrayOop) JNIHandles::resolve(oops_save);
-
-            int dregOopMap = hsailFrame->dreg_oops_map();
-            for (int bit = 0; bit < 16; bit++) {
-              if ((dregOopMap & (1 << bit)) != 0) {
-                // the dregister at this bit is an oop, retrieve it from array and put back in frame
-                int index = k * 16 + bit;
-                void * dregValue = (void *) oopsSaveArray->obj_at(index);
-                void * oldDregValue = (void *) hsailFrame->get_d_reg(bit);
-                assert((oldDregValue != 0 ? dregValue != 0 : dregValue == 0), "bad dregValue retrieved");
-                if (TraceGPUInteraction) {
-                  if (dregValue != oldDregValue) {
-                    tty->print_cr("oop moved for $d%d, workitem %d, slot %d, old=%p, new=%p", bit, workitem, k, oldDregValue, dregValue);
-                  }
-                }
-                hsailFrame->put_d_reg(bit, (jlong) dregValue);
-              }
-            }
+            // will re-resolve the handles each time
+            oopSaver.restoreOopsToFrame(hsailFrame, k, workitem);
 
             JavaValue result(T_VOID);
             JavaCallArguments javaArgs;
@@ -410,12 +521,19 @@
             javaArgs.push_int(myActionReason);
             javaArgs.push_oop((oop) NULL);
             if (TraceGPUInteraction) {
-              int dregOopMap = hsailFrame->dreg_oops_map();
-              tty->print_cr("[HSAIL] Deoptimizing to host for workitem=%d (slot=%d) with deoptId=%d, frame=" INTPTR_FORMAT ", actionAndReason=%d, dregOopMap=%04x", workitem, k, deoptId, hsailFrame, myActionReason, dregOopMap);
-              // show the registers containing references
-              for (int bit = 0; bit < 16; bit++) {
-                if ((dregOopMap & (1 << bit)) != 0) {
-                  tty->print_cr("  oop $d%d = %p", bit, hsailFrame->get_d_reg(bit));
+              tty->print_cr("[HSAIL] Deoptimizing to host for workitem=%d (slot=%d) with deoptId=%d, frame=" INTPTR_FORMAT ", actionAndReason=%d", workitem, k, deoptId, hsailFrame, myActionReason);
+              // show the $d registers or stack slots containing references
+              int maxOopBits = hsailFrame->num_d_regs() + hsailFrame->num_stack_slots();
+              for (int bit = 0; bit < maxOopBits; bit++) {
+                if (oopSaver.isOop(hsailFrame, bit)) {
+                  if (bit < hsailFrame->num_d_regs()) {
+                    // show $d reg oop
+                    tty->print_cr("  oop $d%d = %p", bit, oopSaver.getOopForBit(hsailFrame, bit));
+                  } else {
+                    // show stack slot oop
+                    int stackOffset = (bit - hsailFrame->num_d_regs()) * 8;  // 8 bytes per stack slot
+                    tty->print_cr("  oop stk:%d = %p", stackOffset, oopSaver.getOopForBit(hsailFrame, bit));
+                  }
                 }
               }
             }
@@ -461,7 +579,7 @@
           }
         }
         TraceGPUInteraction = savedTraceGPUInteraction;
-        if (TraceGPUInteraction) {
+        if (TraceGPUInteraction && (count_never_ran > 0)) {
           tty->print_cr("%d workitems never ran, have been run via JavaCall", count_never_ran);
           showRanges(never_ran_array, dimX);
         }
--- a/src/gpu/hsail/vm/gpu_hsail.hpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/gpu/hsail/vm/gpu_hsail.hpp	Sat May 03 21:46:35 2014 +0200
@@ -51,7 +51,8 @@
   };
 
 // 8 compute units * 40 waves per cu * wavesize 64
-#define MAX_DEOPT_SAVE_STATES_SIZE    (8 * 40 * 64)
+// TODO: query the device to get this number
+#define MAX_DEOPT_SLOTS    (8 * 40 * 64)
 
   class HSAILDeoptimizationInfo : public ResourceObj {
     friend class VMStructs;
@@ -61,14 +62,23 @@
     jint _deopt_next_index;
     JavaThread** _donor_threads;
     jboolean * _never_ran_array;
+    jint _num_slots;
+    jint _bytesPerSaveArea;
+    jint _deopt_span;
 
    public:
-    HSAILKernelDeoptimization _deopt_save_states[MAX_DEOPT_SAVE_STATES_SIZE];
+    HSAILKernelDeoptimization _deopt_save_states[1];  // number and size of these can vary per kernel
 
-    inline HSAILDeoptimizationInfo() {
+    inline HSAILDeoptimizationInfo(int numSlots, int bytesPerSaveArea) {
       _notice_safepoints = &Hsail::_notice_safepoints;
       _deopt_occurred = 0;
       _deopt_next_index = 0;
+      _num_slots = numSlots;
+      _bytesPerSaveArea = bytesPerSaveArea;
+      _deopt_span = sizeof(HSAILKernelDeoptimization) + bytesPerSaveArea;
+      if (TraceGPUInteraction) {
+        tty->print_cr("HSAILDeoptimizationInfo allocated, %d slots of size %d, total size = 0x%lx bytes", _num_slots, _deopt_span, (_num_slots * _deopt_span + sizeof(HSAILDeoptimizationInfo)));
+      }
     }
 
     inline jint deopt_occurred() {
@@ -78,6 +88,23 @@
     inline jboolean *never_ran_array() { return _never_ran_array; }
     inline void  set_never_ran_array(jboolean *p) { _never_ran_array = p; }
     inline void  set_donor_threads(JavaThread **threads) { _donor_threads = threads; }
+    inline jint num_slots() {return _num_slots;}
+
+    inline HSAILKernelDeoptimization * get_deopt_save_state(int slot) {
+      // use _deopt_span to index into _deopt_states
+      char *p = (char *) _deopt_save_states;
+      p += _deopt_span * slot;
+      return (HSAILKernelDeoptimization *) p;
+    }
+
+    void * operator new (size_t hdrSize, int numSlots, int bytesPerSaveArea) {
+      size_t totalSizeBytes = hdrSize + numSlots * (sizeof(HSAILKernelDeoptimization) + bytesPerSaveArea);
+      return NEW_C_HEAP_ARRAY(char, totalSizeBytes, mtInternal);
+    }
+
+    void operator delete (void *ptr) {
+      FREE_C_HEAP_ARRAY(char, ptr, mtInternal);
+    }
   };
 
 
@@ -93,7 +120,7 @@
 
   // static native boolean executeKernel0(HotSpotInstalledCode kernel, int jobSize, Object[] args);
   JNIEXPORT static jboolean execute_kernel_void_1d(JNIEnv *env, jclass, jobject hotspotInstalledCode, jint dimX, jobject args, jobject oopsSave,
-                                                   jobject donorThreads, int allocBytesPerWorkitem);
+                                                   jobject donorThreads, int allocBytesPerWorkitem, jobject oop_map_array);
 
   // static native void getThreadPointers(Object[] donorThreads, long[] threadPointersOut);
   JNIEXPORT static void get_thread_pointers(JNIEnv *env, jclass, jobject donor_threads_handle, jobject thread_ptrs_handle);
@@ -101,8 +128,8 @@
   static void getNewTlabForDonorThread(ThreadLocalAllocBuffer* tlab, size_t tlabMinHsail);
 
   static jboolean execute_kernel_void_1d_internal(address kernel, int dimX, jobject args, methodHandle& mh, nmethod *nm, jobject oopsSave,
-                                                  jobject donor_threads, int allocBytesPerWorkitem, TRAPS);
-  
+                                                  jobject donorThreads, int allocBytesPerWorkitem, jobject oop_map_array, TRAPS);
+
   static void register_heap();
 
   static GraalEnv::CodeInstallResult install_code(Handle& compiled_code, CodeBlob*& cb, Handle installed_code, Handle triggered_deoptimizations);
--- a/src/gpu/hsail/vm/gpu_hsail_Frame.hpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/gpu/hsail/vm/gpu_hsail_Frame.hpp	Sat May 03 21:46:35 2014 +0200
@@ -5,39 +5,44 @@
 #include "code/debugInfo.hpp"
 #include "code/location.hpp"
 
-// maximum registers that could be saved for now
-#define MAX_SREGS 32
-#define MAX_DREGS 16
-
 class HSAILFrame {
   friend class VMStructs;
 private:
   jint  _pc_offset;  // The HSAIL "pc_offset" where the exception happens
   jbyte _num_s_regs;
   jbyte _num_d_regs;
-  jshort _dreg_oops_map;  // bits = 1 if that dreg is an oop
-  jlong  _save_area[MAX_SREGS/2 + MAX_DREGS];  
+  jshort _num_stack_slots; 
+  jbyte  _save_area[0];     // save area size can vary per kernel compilation  
 
 public:
   // Accessors
   jint pc_offset() { return _pc_offset; }
   jint num_s_regs() {return _num_s_regs; }
   jint num_d_regs() {return _num_d_regs; }
-  jint dreg_oops_map() {return _dreg_oops_map; }
+  jint num_stack_slots() {return _num_stack_slots; }
   jlong get_d_reg(int idx) {
-    char *p = (char *) _save_area;
     int ofst = num_s_regs() * 4 + idx * 8;
-    return(*(jlong *) (p + ofst));
+    return(*(jlong *) (_save_area + ofst));
   }
   jint get_s_reg(int idx) {
-    char *p = (char *) _save_area;
     int ofst = idx * 4;
-    return(*(jint *) (p + ofst));
+    return(*(jint *) (_save_area + ofst));
   }
   void put_d_reg(int idx, jlong val) {
-    char *p = (char *) _save_area;
     int ofst = num_s_regs() * 4 + idx * 8;
-    (*(jlong *) (p + ofst)) = val;
+    (*(jlong *) (_save_area + ofst)) = val;
+  }
+  jint get_stackslot32(int stackOffset) {
+    int ofst = num_s_regs() * 4 + num_d_regs() * 8 + stackOffset;
+    return(*(jint *) (_save_area + ofst));
+  }
+  jlong get_stackslot64(int stackOffset) {
+    int ofst = num_s_regs() * 4 + num_d_regs() * 8 + stackOffset;
+    return(*(jlong *) (_save_area + ofst));
+  }
+  void put_stackslot64(int stackOffset, jlong val) {
+    int ofst = num_s_regs() * 4 + num_d_regs() * 8 + stackOffset;
+    (*(jlong *) (_save_area + ofst)) = val;
   }
 };
   
--- a/src/gpu/hsail/vm/vmStructs_hsail.hpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/gpu/hsail/vm/vmStructs_hsail.hpp	Sat May 03 21:46:35 2014 +0200
@@ -32,10 +32,11 @@
 // constants required by the Serviceability Agent. This file is
 // referenced by vmStructs.cpp.
 
-#define VM_STRUCTS_GPU_HSAIL(nonstatic_field)                                                                                         \
+#define VM_STRUCTS_GPU_HSAIL(nonstatic_field)                           \
   nonstatic_field(HSAILFrame, _pc_offset,                                                  jint)                                      \
   nonstatic_field(HSAILFrame, _num_s_regs,                                                 jbyte)                                     \
-  nonstatic_field(HSAILFrame, _save_area[0],                                               jlong)                                     \
+  nonstatic_field(HSAILFrame, _num_d_regs,                                                 jbyte)                                     \
+  nonstatic_field(HSAILFrame, _num_stack_slots,                                            jshort)                                    \
                                                                                                                                       \
   nonstatic_field(Hsail::HSAILKernelDeoptimization, _workitemid,                                jint)                                 \
   nonstatic_field(Hsail::HSAILKernelDeoptimization, _actionAndReason,                           jint)                                 \
@@ -47,7 +48,6 @@
   nonstatic_field(Hsail::HSAILDeoptimizationInfo, _donor_threads,                          JavaThread**)                              \
   nonstatic_field(Hsail::HSAILDeoptimizationInfo, _never_ran_array,                        jboolean *)                                \
   nonstatic_field(Hsail::HSAILDeoptimizationInfo, _deopt_save_states[0],                   Hsail::HSAILKernelDeoptimization)          \
-  nonstatic_field(Hsail::HSAILDeoptimizationInfo, _deopt_save_states[1],                   Hsail::HSAILKernelDeoptimization)
 
 #define VM_TYPES_GPU_HSAIL(declare_type, declare_toplevel_type)                 \
   declare_toplevel_type(HSAILFrame)                                  \
@@ -55,4 +55,8 @@
   declare_toplevel_type(Hsail::HSAILKernelDeoptimization)            \
   declare_toplevel_type(Hsail::HSAILDeoptimizationInfo)
 
+#define VM_INT_CONSTANTS_GPU_HSAIL(declare_constant)                                                                                  \
+  declare_constant(sizeof(HSAILFrame))                                                                                                \
+  declare_constant(sizeof(Hsail::HSAILKernelDeoptimization))                                                                          \
+
 #endif // GPU_HSAIL_VM_VMSTRUCTS_HSAIL_HPP
--- a/src/share/vm/compiler/compileBroker.cpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/share/vm/compiler/compileBroker.cpp	Sat May 03 21:46:35 2014 +0200
@@ -516,7 +516,8 @@
   if (_osr_bci != CompileBroker::standard_entry_bci) {
     log->print(" osr_bci='%d'", _osr_bci);
   }
-  if (_comp_level != CompLevel_highest_tier) {
+  // Always print the level in tiered.
+  if (_comp_level != CompLevel_highest_tier || TieredCompilation) {
     log->print(" level='%d'", _comp_level);
   }
   if (_is_blocking) {
--- a/src/share/vm/graal/graalCompiler.cpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/share/vm/graal/graalCompiler.cpp	Sat May 03 21:46:35 2014 +0200
@@ -58,12 +58,18 @@
   NOT_LP64(error("check TLAB allocation code for address space conflicts"));
 
   BufferBlob* buffer_blob = initialize_buffer_blob();
-  if (buffer_blob == NULL) {
-    // If we are called from JNI_CreateJavaVM we cannot use set_state yet because it takes a lock.
-    // set_state(failed);
-  } else {
-    // set_state(initialized);
+#ifdef COMPILERGRAAL
+  if (!UseGraalCompilationQueue) {
+    // This path is used for initialization both by the native queue and the graal queue
+    // but set_state acquired a lock which might not be safe during JVM_CreateJavaVM, so
+    // only update the state flag for the native queue.
+    if (buffer_blob == NULL) {
+      set_state(failed);
+    } else {
+      set_state(initialized);
+    }
   }
+#endif
 
   JNIEnv *env = ((JavaThread *) Thread::current())->jni_environment();
   jclass klass = env->FindClass("com/oracle/graal/hotspot/bridge/CompilerToVMImpl");
--- a/src/share/vm/graal/graalGlobals.hpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/share/vm/graal/graalGlobals.hpp	Sat May 03 21:46:35 2014 +0200
@@ -52,7 +52,7 @@
   COMPILERGRAAL_PRESENT(product(bool, BootstrapGraal, true,                 \
           "Bootstrap Graal before running Java main method"))               \
                                                                             \
-  COMPILERGRAAL_PRESENT(product(bool, UseGraalCompilationQueue, false,      \
+  COMPILERGRAAL_PRESENT(product(bool, UseGraalCompilationQueue, true,       \
           "Use non-native compilation queue for Graal"))                    \
                                                                             \
   product(bool, ForceGraalInitialization, false,                            \
--- a/src/share/vm/runtime/vmStructs.cpp	Fri May 02 02:45:26 2014 +0200
+++ b/src/share/vm/runtime/vmStructs.cpp	Sat May 03 21:46:35 2014 +0200
@@ -106,6 +106,7 @@
 #include "utilities/macros.hpp"
 #ifdef GRAAL
 # include "graal/vmStructs_graal.hpp"
+# include "hsail/vm/vmStructs_hsail.hpp"
 #endif
 #ifdef TARGET_ARCH_x86
 # include "vmStructs_x86.hpp"
@@ -153,8 +154,6 @@
 # include "vmStructs_bsd_zero.hpp"
 #endif
 
-#include "hsail/vm/vmStructs_hsail.hpp"
-
 #if INCLUDE_ALL_GCS
 #include "gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp"
 #include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp"
@@ -3042,7 +3041,9 @@
                  GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY,
                  GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY)
 
+#ifdef GRAAL
   VM_STRUCTS_GPU_HSAIL(GENERATE_NONSTATIC_VM_STRUCT_ENTRY)
+#endif
           
   VM_STRUCTS_OS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY,
                     GENERATE_STATIC_VM_STRUCT_ENTRY,
@@ -3094,8 +3095,10 @@
                GENERATE_C2_VM_TYPE_ENTRY,
                GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY)
 
+#ifdef GRAAL
   VM_TYPES_GPU_HSAIL(GENERATE_VM_TYPE_ENTRY,
                GENERATE_TOPLEVEL_VM_TYPE_ENTRY)
+#endif
 
   VM_TYPES_OS_CPU(GENERATE_VM_TYPE_ENTRY,
                   GENERATE_TOPLEVEL_VM_TYPE_ENTRY,
@@ -3120,6 +3123,8 @@
 #ifdef GRAAL
   VM_INT_CONSTANTS_GRAAL(GENERATE_VM_INT_CONSTANT_ENTRY,
                          GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY)
+
+  VM_INT_CONSTANTS_GPU_HSAIL(GENERATE_VM_INT_CONSTANT_ENTRY)
 #endif
 
 #if INCLUDE_ALL_GCS
@@ -3201,7 +3206,9 @@
                  CHECK_NO_OP,
                  CHECK_NO_OP);
 
-  VM_STRUCTS_GPU_HSAIL(CHECK_NONSTATIC_VM_STRUCT_ENTRY);
+#ifdef GRAAL
+  VM_STRUCTS_GPU_HSAIL(CHECK_NONSTATIC_VM_STRUCT_ENTRY)
+#endif
 
   VM_STRUCTS_OS_CPU(CHECK_NONSTATIC_VM_STRUCT_ENTRY,
                     CHECK_STATIC_VM_STRUCT_ENTRY,
@@ -3243,8 +3250,10 @@
                CHECK_C2_VM_TYPE_ENTRY,
                CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY);
 
+#ifdef GRAAL
   VM_TYPES_GPU_HSAIL(CHECK_VM_TYPE_ENTRY,
                CHECK_SINGLE_ARG_VM_TYPE_NO_OP);
+#endif
 
   VM_TYPES_OS_CPU(CHECK_VM_TYPE_ENTRY,
                   CHECK_SINGLE_ARG_VM_TYPE_NO_OP,