changeset 15780:bdf260d8e163

Merge with 9ae1d2f3bda60f9d91243c883c5aa7812e2ab256
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Mon, 19 May 2014 17:21:30 -0700
parents 8c34e2cc4add (current diff) 9ae1d2f3bda6 (diff)
children 8b9e7f235d85 6a6cb7f2db90
files graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/DepthSearchUtil.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningIterator.java
diffstat 135 files changed, 5875 insertions(+), 2936 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Mon May 19 17:14:36 2014 -0700
+++ b/CHANGELOG.md	Mon May 19 17:21:30 2014 -0700
@@ -3,6 +3,8 @@
 ## `tip`
 ### Graal
 * Made initialization of Graal runtime lazy in hosted mode.
+* Added supported for new 'jrelibrary' dependency type in mx/projects.
+* Java projects with compliance level higher than the JDKs specified by JAVA_HOME and EXTRA_JAVA_HOMES are ignored once mx/projects has been processed.
 
 ### Truffle
 * `truffle.jar`: strip out build-time only dependency into a seperated JAR file (`truffle-dsl-processor.jar`)
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java	Mon May 19 17:21:30 2014 -0700
@@ -96,7 +96,8 @@
         public ConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) {
             this.context = context;
             this.subtype = subtype;
-            assert !subtype.isInterface() : subtype.toString() + " : " + context.toString();
+            assert !subtype.isAbstract() : subtype.toString() + " : " + context.toString();
+            assert !subtype.isArray() || getElementalType(subtype).isFinal() : subtype.toString() + " : " + context.toString();
         }
 
         @Override
@@ -264,7 +265,7 @@
 
     /**
      * Returns whether any assumptions have been registered.
-     * 
+     *
      * @return {@code true} if at least one assumption has been registered, {@code false} otherwise.
      */
     public boolean isEmpty() {
@@ -303,7 +304,7 @@
 
     /**
      * Records an assumption that the specified type has no finalizable subclasses.
-     * 
+     *
      * @param receiverType the type that is assumed to have no finalizable subclasses
      */
     public void recordNoFinalizableSubclassAssumption(ResolvedJavaType receiverType) {
@@ -314,7 +315,7 @@
     /**
      * Records that {@code subtype} is the only concrete subtype in the class hierarchy below
      * {@code context}.
-     * 
+     *
      * @param context the root of the subtree of the class hierarchy that this assumptions is about
      * @param subtype the one concrete subtype
      */
@@ -326,7 +327,7 @@
     /**
      * Records that {@code impl} is the only possible concrete target for a virtual call to
      * {@code method} with a receiver of type {@code context}.
-     * 
+     *
      * @param method a method that is the target of a virtual call
      * @param context the receiver type of a call to {@code method}
      * @param impl the concrete method that is the only possible target for the virtual call
@@ -338,7 +339,7 @@
 
     /**
      * Records that {@code method} was used during the compilation.
-     * 
+     *
      * @param method a method whose contents were used
      */
     public void recordMethodContents(ResolvedJavaMethod method) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/ResolvedJavaTypeResolveMethodTest.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,173 @@
+/*
+ * 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.api.meta.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.phases.util.*;
+import com.oracle.graal.runtime.*;
+
+public class ResolvedJavaTypeResolveMethodTest {
+    public final MetaAccessProvider metaAccess;
+
+    public ResolvedJavaTypeResolveMethodTest() {
+        Providers providers = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getProviders();
+        metaAccess = providers.getMetaAccess();
+    }
+
+    protected static abstract class A {
+        @SuppressWarnings("unused")
+        private void priv() {
+        }
+
+        public void v1() {
+        }
+
+        public void v2() {
+        }
+
+        public abstract void abs();
+    }
+
+    protected static class B extends A implements I {
+        public void i() {
+        }
+
+        @Override
+        public void v2() {
+        }
+
+        @Override
+        public void abs() {
+
+        }
+    }
+
+    protected static class C extends B {
+        public void d() {
+        }
+    }
+
+    protected static abstract class D extends A {
+
+    }
+
+    protected static class E extends D {
+        @Override
+        public void abs() {
+        }
+    }
+
+    protected interface I {
+        void i();
+
+        default void d() {
+        }
+    }
+
+    @Test
+    public void testDefaultMethod() {
+        ResolvedJavaType i = getType(I.class);
+        ResolvedJavaType b = getType(B.class);
+        ResolvedJavaType c = getType(C.class);
+        ResolvedJavaMethod di = getMethod(i, "d");
+        ResolvedJavaMethod dc = getMethod(c, "d");
+
+        assertEquals(di, i.resolveMethod(di, c));
+        assertEquals(di, b.resolveMethod(di, c));
+        assertEquals(dc, c.resolveMethod(di, c));
+    }
+
+    @Test
+    public void testPrivateMethod() {
+        ResolvedJavaType a = getType(A.class);
+        ResolvedJavaType b = getType(B.class);
+        ResolvedJavaType c = getType(C.class);
+        ResolvedJavaMethod priv = getMethod(a, "priv");
+
+        assertNull(a.resolveMethod(priv, c));
+        assertNull(b.resolveMethod(priv, c));
+    }
+
+    @Test
+    public void testAbstractMethod() {
+        ResolvedJavaType a = getType(A.class);
+        ResolvedJavaType b = getType(B.class);
+        ResolvedJavaType c = getType(C.class);
+        ResolvedJavaType d = getType(D.class);
+        ResolvedJavaType e = getType(E.class);
+        ResolvedJavaMethod absa = getMethod(a, "abs");
+        ResolvedJavaMethod absb = getMethod(b, "abs");
+        ResolvedJavaMethod abse = getMethod(e, "abs");
+
+        assertNull(a.resolveMethod(absa, c));
+        assertNull(d.resolveMethod(absa, c));
+
+        assertEquals(absb, b.resolveMethod(absa, c));
+        assertEquals(absb, b.resolveMethod(absb, c));
+        assertEquals(absb, c.resolveMethod(absa, c));
+        assertEquals(absb, c.resolveMethod(absb, c));
+        assertEquals(abse, e.resolveMethod(absa, c));
+        assertNull(e.resolveMethod(absb, c));
+        assertEquals(abse, e.resolveMethod(abse, c));
+    }
+
+    @Test
+    public void testVirtualMethod() {
+        ResolvedJavaType a = getType(A.class);
+        ResolvedJavaType b = getType(B.class);
+        ResolvedJavaType c = getType(C.class);
+        ResolvedJavaMethod v1a = getMethod(a, "v1");
+        ResolvedJavaMethod v2a = getMethod(a, "v2");
+        ResolvedJavaMethod v2b = getMethod(b, "v2");
+
+        assertEquals(v1a, a.resolveMethod(v1a, c));
+        assertEquals(v1a, b.resolveMethod(v1a, c));
+        assertEquals(v1a, c.resolveMethod(v1a, c));
+        assertEquals(v2a, a.resolveMethod(v2a, c));
+        assertEquals(v2b, b.resolveMethod(v2a, c));
+        assertEquals(v2b, b.resolveMethod(v2b, c));
+        assertEquals(v2b, c.resolveMethod(v2a, c));
+        assertEquals(v2b, c.resolveMethod(v2b, c));
+
+    }
+
+    private static ResolvedJavaMethod getMethod(ResolvedJavaType type, String methodName) {
+        for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
+            if (method.getName().equals(methodName)) {
+                return method;
+            }
+        }
+        throw new IllegalArgumentException();
+    }
+
+    protected ResolvedJavaType getType(Class<?> clazz) {
+        ResolvedJavaType type = metaAccess.lookupJavaType(clazz);
+        type.initialize();
+        return type;
+    }
+}
--- a/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java	Mon May 19 17:21:30 2014 -0700
@@ -265,10 +265,13 @@
     static class Concrete3 extends Concrete2 {
     }
 
+    static final class Final1 extends Abstract1 {
+    }
+
     abstract static class Abstract4 extends Concrete3 {
     }
 
-    void checkConcreteSubtype(ResolvedJavaType type, Class<?> expected) {
+    void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) {
         ResolvedJavaType subtype = type.findUniqueConcreteSubtype();
         if (subtype == null) {
             // findUniqueConcreteSubtype() is conservative
@@ -276,7 +279,7 @@
             if (expected == null) {
                 assertNull(subtype);
             } else {
-                assertTrue(subtype.equals(metaAccess.lookupJavaType(expected)));
+                assertTrue(subtype.equals(expected));
             }
         }
 
@@ -294,31 +297,44 @@
     @Test
     public void findUniqueConcreteSubtypeTest() {
         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
-        checkConcreteSubtype(base, Base.class);
+        checkConcreteSubtype(base, base);
 
         ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class);
         ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class);
 
         checkConcreteSubtype(base, null);
-        checkConcreteSubtype(a1, Concrete1.class);
-        checkConcreteSubtype(c1, Concrete1.class);
+        checkConcreteSubtype(a1, c1);
+        checkConcreteSubtype(c1, c1);
 
         ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class);
         ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class);
 
         checkConcreteSubtype(base, null);
         checkConcreteSubtype(a1, null);
-        checkConcreteSubtype(c1, Concrete1.class);
-        checkConcreteSubtype(i1, Concrete2.class);
-        checkConcreteSubtype(c2, Concrete2.class);
+        checkConcreteSubtype(c1, c1);
+        checkConcreteSubtype(i1, c2);
+        checkConcreteSubtype(c2, c2);
 
         ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class);
         checkConcreteSubtype(c2, null);
-        checkConcreteSubtype(c3, Concrete3.class);
+        checkConcreteSubtype(c3, c3);
 
         ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class);
         checkConcreteSubtype(c3, null);
         checkConcreteSubtype(a4, null);
+
+        ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class);
+        checkConcreteSubtype(a1a, null);
+        ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class);
+        checkConcreteSubtype(c1a, null);
+        ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class);
+        checkConcreteSubtype(f1a, f1a);
+
+        ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class);
+        checkConcreteSubtype(obja, null);
+
+        ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class);
+        checkConcreteSubtype(inta, inta);
     }
 
     @Test
@@ -431,20 +447,21 @@
         return declarations;
     }
 
-    private static void checkResolveMethod(ResolvedJavaType type, ResolvedJavaMethod decl, ResolvedJavaMethod expected) {
-        ResolvedJavaMethod impl = type.resolveMethod(decl);
+    private static void checkResolveMethod(ResolvedJavaType type, ResolvedJavaType context, ResolvedJavaMethod decl, ResolvedJavaMethod expected) {
+        ResolvedJavaMethod impl = type.resolveMethod(decl, context);
         assertEquals(expected, impl);
     }
 
     @Test
     public void resolveMethodTest() {
+        ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
         for (Class<?> c : classes) {
             if (c.isInterface() || c.isPrimitive()) {
                 ResolvedJavaType type = metaAccess.lookupJavaType(c);
                 for (Method m : c.getDeclaredMethods()) {
                     if (JAVA_VERSION <= 1.7D || (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers()))) {
                         ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
-                        ResolvedJavaMethod impl = type.resolveMethod(resolved);
+                        ResolvedJavaMethod impl = type.resolveMethod(resolved, context);
                         ResolvedJavaMethod expected = resolved.isDefault() ? resolved : null;
                         assertEquals(m.toString(), expected, impl);
                     } else {
@@ -458,12 +475,14 @@
                     Set<Method> decls = findDeclarations(impl, c);
                     for (Method decl : decls) {
                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
-                        ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
-                        checkResolveMethod(type, m, i);
+                        if (m.isPublic()) {
+                            ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
+                            checkResolveMethod(type, context, m, i);
+                        }
                     }
                 }
                 for (Method m : c.getDeclaredMethods()) {
-                    ResolvedJavaMethod impl = type.resolveMethod(metaAccess.lookupJavaMethod(m));
+                    ResolvedJavaMethod impl = type.resolveMethod(metaAccess.lookupJavaMethod(m), context);
                     ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl;
                     assertEquals(type + " " + m.toString(), expected, impl);
                 }
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java	Mon May 19 17:21:30 2014 -0700
@@ -218,9 +218,10 @@
     Constant getEncoding();
 
     /**
-     * Checks if this method is present in the virtual table.
+     * Checks if this method is present in the virtual table for subtypes of the specified
+     * {@linkplain ResolvedJavaType type}.
      *
-     * @return true is this method is present in the virtual table
+     * @return true is this method is present in the virtual table for subtypes of this type.
      */
-    boolean isInVirtualMethodTable();
+    boolean isInVirtualMethodTable(ResolvedJavaType resolved);
 }
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java	Mon May 19 17:21:30 2014 -0700
@@ -214,7 +214,7 @@
      * @return the concrete method that would be selected at runtime, or {@code null} if there is no
      *         concrete implementation of {@code method} in this type or any of its superclasses
      */
-    ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method);
+    ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType);
 
     /**
      * Given a {@link ResolvedJavaMethod} A, returns a concrete {@link ResolvedJavaMethod} B that is
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Mon May 19 17:21:30 2014 -0700
@@ -784,15 +784,39 @@
     }
 
     public final void idivl(Register src) {
-        int encode = prefixAndEncode(src.encoding);
+        int encode = prefixAndEncode(7, src.encoding);
         emitByte(0xF7);
-        emitByte(0xF8 | encode);
+        emitByte(0xC0 | encode);
     }
 
     public final void divl(Register src) {
-        int encode = prefixAndEncode(src.encoding);
+        int encode = prefixAndEncode(6, src.encoding);
+        emitByte(0xF7);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void mull(Register src) {
+        int encode = prefixAndEncode(4, src.encoding);
+        emitByte(0xF7);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void mull(AMD64Address src) {
+        prefix(src);
         emitByte(0xF7);
-        emitByte(0xF0 | encode);
+        emitOperandHelper(4, src);
+    }
+
+    public final void imull(Register src) {
+        int encode = prefixAndEncode(5, src.encoding);
+        emitByte(0xF7);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void imull(AMD64Address src) {
+        prefix(src);
+        emitByte(0xF7);
+        emitOperandHelper(5, src);
     }
 
     public final void imull(Register dst, Register src) {
@@ -2346,15 +2370,39 @@
     }
 
     public final void divq(Register src) {
-        int encode = prefixqAndEncode(src.encoding);
+        int encode = prefixqAndEncode(6, src.encoding);
         emitByte(0xF7);
-        emitByte(0xF0 | encode);
+        emitByte(0xC0 | encode);
     }
 
     public final void idivq(Register src) {
-        int encode = prefixqAndEncode(src.encoding);
+        int encode = prefixqAndEncode(7, src.encoding);
+        emitByte(0xF7);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void mulq(Register src) {
+        int encode = prefixqAndEncode(4, src.encoding);
+        emitByte(0xF7);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void mulq(AMD64Address src) {
+        prefixq(src);
         emitByte(0xF7);
-        emitByte(0xF8 | encode);
+        emitOperandHelper(4, src);
+    }
+
+    public final void imulq(Register src) {
+        int encode = prefixqAndEncode(5, src.encoding);
+        emitByte(0xF7);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void imulq(AMD64Address src) {
+        prefixq(src);
+        emitByte(0xF7);
+        emitOperandHelper(5, src);
     }
 
     public final void imulq(Register dst, Register src) {
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Mon May 19 17:21:30 2014 -0700
@@ -601,6 +601,37 @@
         }
     }
 
+    private Value emitMulHigh(AMD64Arithmetic opcode, Value a, Value b) {
+        MulHighOp mulHigh = new MulHighOp(opcode, asAllocatable(b));
+        emitMove(mulHigh.x, a);
+        append(mulHigh);
+        return emitMove(mulHigh.highResult);
+    }
+
+    @Override
+    public Value emitMulHigh(Value a, Value b) {
+        switch (a.getKind().getStackKind()) {
+            case Int:
+                return emitMulHigh(IMUL, a, b);
+            case Long:
+                return emitMulHigh(LMUL, a, b);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    @Override
+    public Value emitUMulHigh(Value a, Value b) {
+        switch (a.getKind().getStackKind()) {
+            case Int:
+                return emitMulHigh(IUMUL, a, b);
+            case Long:
+                return emitMulHigh(LUMUL, a, b);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
     public Value emitBinaryMemory(AMD64Arithmetic op, Kind kind, AllocatableValue a, AMD64AddressValue location, LIRFrameState state) {
         Variable result = newVariable(a.getKind());
         append(new BinaryMemory(op, kind, result, a, location, state));
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ObjectStamp.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ObjectStamp.java	Mon May 19 17:21:30 2014 -0700
@@ -107,6 +107,11 @@
             return StampFactory.illegal(Kind.Illegal);
         }
         ObjectStamp other = (ObjectStamp) otherStamp;
+        if (!isLegal()) {
+            return other;
+        } else if (!other.isLegal()) {
+            return this;
+        }
         ResolvedJavaType meetType;
         boolean meetExactType;
         boolean meetNonNull;
--- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Mon May 19 17:21:30 2014 -0700
@@ -131,11 +131,18 @@
     }
 
     /**
+     * Determines if the JVM supports the required typeProfileWidth.
+     */
+    public boolean typeProfileWidthAtLeast(int val) {
+        return (getHSAILBackend().getRuntime().getConfig().typeProfileWidth >= val);
+    }
+
+    /**
      * Determines if the runtime supports {@link VirtualObject}s in {@link DebugInfo} associated
      * with HSAIL code.
      */
     public boolean canHandleDeoptVirtualObjects() {
-        return false;
+        return true;
     }
 
     /**
--- /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/lambda/VecmathNBodyDeoptTest.java	Mon May 19 17:21:30 2014 -0700
@@ -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.lambda;
+
+import java.util.*;
+import org.junit.*;
+import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester;
+import javax.vecmath.*;
+
+/**
+ * Tests NBody algorithm using the javax.vecmath package (all objects non-escaping).
+ */
+public class VecmathNBodyDeoptTest extends GraalKernelTester {
+    static final int bodies = 1024;
+    static final float delT = .005f;
+    static final float espSqr = 1.0f;
+    static final float mass = 5f;
+    static final int width = 768;
+    static final int height = 768;
+
+    static class Body extends Vector3f {
+
+        /**
+         *
+         */
+        private static final long serialVersionUID = 1L;
+
+        public Body(float _x, float _y, float _z, float _m) {
+            super(_x, _y, _z);
+            m = _m;
+            v = new Vector3f(0, 0, 0);
+        }
+
+        float m;
+        Vector3f v;
+
+        public float getM() {
+            return m;
+        }
+
+        public Vector3f computeAcc(Body[] in_bodies, float espSqr1, float delT1) {
+            Vector3f acc = new Vector3f();
+
+            for (Body b : in_bodies) {
+                Vector3f d = new Vector3f();
+                d.sub(b, this);
+                float invDist = 1.0f / (float) Math.sqrt(d.lengthSquared() + espSqr1);
+                float s = b.getM() * invDist * invDist * invDist;
+                acc.scaleAdd(s, d, acc);
+            }
+
+            // now return acc scaled by delT
+            acc.scale(delT1);
+            return acc;
+        }
+    }
+
+    @Result Body[] in_bodies = new Body[bodies];
+    @Result Body[] out_bodies = new Body[bodies];
+
+    static Body[] seed_bodies = new Body[bodies];
+
+    static {
+        java.util.Random randgen = new Random(0);
+        final float maxDist = width / 4;
+        for (int body = 0; body < bodies; body++) {
+            final float theta = (float) (randgen.nextFloat() * Math.PI * 2);
+            final float phi = (float) (randgen.nextFloat() * Math.PI * 2);
+            final float radius = randgen.nextFloat() * maxDist;
+            float x = (float) (radius * Math.cos(theta) * Math.sin(phi)) + width / 2;
+            float y = (float) (radius * Math.sin(theta) * Math.sin(phi)) + height / 2;
+            float z = (float) (radius * Math.cos(phi));
+            seed_bodies[body] = new Body(x, y, z, mass);
+        }
+    }
+
+    @Override
+    public void runTest() {
+        System.arraycopy(seed_bodies, 0, in_bodies, 0, seed_bodies.length);
+        for (int b = 0; b < bodies; b++) {
+            out_bodies[b] = new Body(0, 0, 0, mass);
+        }
+        // no local copies of arrays so we make it an instance lambda
+
+        dispatchLambdaKernel(bodies, (gid) -> {
+            Body inb = in_bodies[gid];
+            Body outb = out_bodies[gid];
+            Vector3f acc = inb.computeAcc(in_bodies, espSqr, delT);
+
+            Vector3f tmpPos = new Vector3f();
+            tmpPos.scaleAdd(delT, inb.v, inb);
+            if (gid == bodies / 2) {
+                tmpPos.x += forceDeopt(gid);
+            }
+            tmpPos.scaleAdd(0.5f * delT, acc, tmpPos);
+            outb.set(tmpPos);
+
+            outb.v.add(inb.v, acc);
+        });
+    }
+
+    @Override
+    protected boolean supportsRequiredCapabilities() {
+        return (canHandleDeoptVirtualObjects());
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+
+    @Test
+    public void testUsingLambdaMethod() {
+        testGeneratedHsailUsingLambdaMethod();
+    }
+
+}
--- /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/lambda/VirtualCall3Test.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,67 @@
+/*
+ * 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.lambda;
+
+import org.junit.Test;
+
+/**
+ * Tests a true virtual method call with 3 targets.
+ */
+public class VirtualCall3Test extends VirtualCallBase {
+
+    void setupArrays() {
+        for (int i = 0; i < NUM; i++) {
+            outArray[i] = -i;
+            inShapeArray[i] = createShape(i % 3, i + 1);
+        }
+    }
+
+    // although runTest is the same in each class derived from VirtualCallBase
+    // we duplicate the logic in each derived test so as to have different lambda call sites
+    @Override
+    public void runTest() {
+        setupArrays();
+
+        dispatchLambdaKernel(NUM, (gid) -> {
+            Shape shape = inShapeArray[gid];
+            outArray[gid] = shape.getArea();
+        });
+    }
+
+    @Override
+    protected boolean supportsRequiredCapabilities() {
+        return typeProfileWidthAtLeast(3);
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+
+    @Test
+    public void testUsingLambdaMethod() {
+        testGeneratedHsailUsingLambdaMethod();
+    }
+
+}
--- /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/lambda/VirtualCall4Test.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,67 @@
+/*
+ * 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.lambda;
+
+import org.junit.Test;
+
+/**
+ * Tests a true virtual method call with 4 targets.
+ */
+public class VirtualCall4Test extends VirtualCallBase {
+
+    void setupArrays() {
+        for (int i = 0; i < NUM; i++) {
+            outArray[i] = -i;
+            inShapeArray[i] = createShape(i % 4, i + 1);
+        }
+    }
+
+    // although runTest is the same in each class derived from VirtualCallBase
+    // we duplicate the logic in each derived test so as to have different lambda call sites
+    @Override
+    public void runTest() {
+        setupArrays();
+
+        dispatchLambdaKernel(NUM, (gid) -> {
+            Shape shape = inShapeArray[gid];
+            outArray[gid] = shape.getArea();
+        });
+    }
+
+    @Override
+    protected boolean supportsRequiredCapabilities() {
+        return typeProfileWidthAtLeast(4);
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+
+    @Test
+    public void testUsingLambdaMethod() {
+        testGeneratedHsailUsingLambdaMethod();
+    }
+
+}
--- /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/lambda/VirtualCallBase.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,117 @@
+/*
+ * 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.lambda;
+
+import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester;
+
+/**
+ * Base class for testing virtual method calls.
+ */
+abstract public class VirtualCallBase extends GraalKernelTester {
+
+    static final int NUM = 20000;
+
+    @Result public float[] outArray = new float[NUM];
+    public Shape[] inShapeArray = new Shape[NUM];
+
+    static abstract class Shape {
+
+        abstract public float getArea();
+    }
+
+    static class Circle extends Shape {
+
+        private float radius;
+
+        Circle(float r) {
+            radius = r;
+        }
+
+        @Override
+        public float getArea() {
+            return (float) (Math.PI * radius * radius);
+        }
+    }
+
+    static class Square extends Shape {
+
+        private float len;
+
+        Square(float _len) {
+            len = _len;
+        }
+
+        @Override
+        public float getArea() {
+            return len * len;
+        }
+    }
+
+    static class Triangle extends Shape {
+
+        private float base;
+        private float height;
+
+        Triangle(float base, float height) {
+            this.base = base;
+            this.height = height;
+        }
+
+        @Override
+        public float getArea() {
+            return (base * height / 2.0f);
+        }
+    }
+
+    static class Rectangle extends Shape {
+
+        private float base;
+        private float height;
+
+        Rectangle(float base, float height) {
+            this.base = base;
+            this.height = height;
+        }
+
+        @Override
+        public float getArea() {
+            return (base * height);
+        }
+    }
+
+    Shape createShape(int kind, int size) {
+        switch (kind) {
+            case 0:
+                return new Circle(size);
+            case 1:
+                return new Square(size);
+            case 2:
+                return new Triangle(size, size + 1);
+            case 3:
+                return new Rectangle(size, size + 1);
+            default:
+                return null;
+        }
+    }
+}
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallTest.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallTest.java	Mon May 19 17:21:30 2014 -0700
@@ -23,66 +23,23 @@
 
 package com.oracle.graal.compiler.hsail.test.lambda;
 
-import static com.oracle.graal.debug.Debug.*;
-
-import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester;
-import com.oracle.graal.debug.*;
-
 import org.junit.Test;
 
 /**
- * Tests a true virtual method call.
+ * Tests a true virtual method call with 2 targets.
  */
-public class VirtualCallTest extends GraalKernelTester {
-
-    static final int NUM = 20;
-
-    static abstract class Shape {
-
-        abstract public float getArea();
-    }
+public class VirtualCallTest extends VirtualCallBase {
 
-    static class Circle extends Shape {
-
-        private float radius;
-
-        Circle(float r) {
-            radius = r;
-        }
-
-        @Override
-        public float getArea() {
-            return (float) (Math.PI * radius * radius);
+    void setupArrays() {
+        for (int i = 0; i < NUM; i++) {
+            outArray[i] = -i;
+            int kind = i % 3 == 0 ? 0 : 1;
+            inShapeArray[i] = createShape(kind, i + 1);
         }
     }
 
-    static class Square extends Shape {
-
-        private float len;
-
-        Square(float _len) {
-            len = _len;
-        }
-
-        @Override
-        public float getArea() {
-            return len * len;
-        }
-    }
-
-    @Result public float[] outArray = new float[NUM];
-    public Shape[] inShapeArray = new Shape[NUM];
-
-    void setupArrays() {
-        for (int i = 0; i < NUM; i++) {
-            if (i % 2 == 0)
-                inShapeArray[i] = new Circle(i + 1);
-            else
-                inShapeArray[i] = new Square(i + 1);
-            outArray[i] = -i;
-        }
-    }
-
+    // although runTest is the same in each class derived from VirtualCallBase
+    // we duplicate the logic in each derived test so as to have different lambda call sites
     @Override
     public void runTest() {
         setupArrays();
@@ -93,19 +50,19 @@
         });
     }
 
-    // graal says not inlining getArea():float (0 bytes): no type profile exists
-    @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class)
-    public void test() {
-        try (DebugConfigScope s = disableIntercept()) {
-            testGeneratedHsail();
-        }
+    @Override
+    protected boolean supportsRequiredCapabilities() {
+        return typeProfileWidthAtLeast(2);
     }
 
-    @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class)
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+
+    @Test
     public void testUsingLambdaMethod() {
-        try (DebugConfigScope s = disableIntercept()) {
-            testGeneratedHsailUsingLambdaMethod();
-        }
+        testGeneratedHsailUsingLambdaMethod();
     }
 
 }
--- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Mon May 19 17:21:30 2014 -0700
@@ -404,6 +404,16 @@
     }
 
     @Override
+    public Value emitMulHigh(Value a, Value b) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @Override
+    public Value emitUMulHigh(Value a, Value b) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @Override
     public Value emitDiv(Value a, Value b, LIRFrameState state) {
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
@@ -828,16 +838,6 @@
      * series of cascading compare and branch instructions. This is currently the recommended way of
      * generating performant HSAIL code for switch constructs.
      *
-     * In Java bytecode the keys for switch statements are always ints.
-     *
-     * The x86 backend also adds support for handling keys of type long or Object but these two
-     * special cases are for handling the TypeSwitchNode, which is a node that the JVM produces for
-     * handling operations related to method dispatch. We haven't yet added support for the
-     * TypeSwitchNode, so for the time being we have added a check to ensure that the keys are of
-     * type int. This also allows us to flag any test cases/execution paths that may trigger the
-     * creation of a TypeSwitchNode which we don't support yet.
-     *
-     *
      * @param strategy the strategy used for this switch.
      * @param keyTargets array of branch targets for each of the cases.
      * @param defaultTarget the branch target for the default case.
@@ -845,12 +845,16 @@
      */
     @Override
     public void emitStrategySwitch(SwitchStrategy strategy, Variable key, LabelRef[] keyTargets, LabelRef defaultTarget) {
-        if ((key.getKind() == Kind.Int) || (key.getKind() == Kind.Long)) {
-            // Append the LIR instruction for generating compare and branch instructions.
-            append(new StrategySwitchOp(strategy, keyTargets, defaultTarget, key));
-        } else {
-            // Throw an exception if the keys aren't ints.
-            throw GraalInternalError.unimplemented("Switch statements are only supported for keys of type int or long, not " + key.getKind());
+        switch (key.getKind()) {
+            case Int:
+            case Long:
+            case Object:
+                // Append the LIR instruction for generating compare and branch instructions.
+                append(new StrategySwitchOp(strategy, keyTargets, defaultTarget, key));
+                break;
+            default:
+                // Throw an exception if the key kind is anything else.
+                throw GraalInternalError.unimplemented("Switch statements not supported for keys of type " + key.getKind());
         }
     }
 
--- a/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Mon May 19 17:21:30 2014 -0700
@@ -485,6 +485,16 @@
     }
 
     @Override
+    public Value emitMulHigh(Value a, Value b) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @Override
+    public Value emitUMulHigh(Value a, Value b) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @Override
     public Value emitDiv(Value a, Value b, LIRFrameState state) {
         Variable result = newVariable(a.getKind());
         switch (a.getKind()) {
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Mon May 19 17:21:30 2014 -0700
@@ -580,6 +580,16 @@
     }
 
     @Override
+    public Value emitMulHigh(Value a, Value b) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @Override
+    public Value emitUMulHigh(Value a, Value b) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @Override
     public Value emitDiv(Value a, Value b, LIRFrameState state) {
         Variable result = newVariable(a.getKind());
         switch (a.getKind().getStackKind()) {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java	Mon May 19 17:21:30 2014 -0700
@@ -30,6 +30,8 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 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.tiers.*;
 
@@ -137,6 +139,59 @@
         return 1;
     }
 
+    @Test
+    public void test6() {
+        testCombinedIf("test6Snippet", 3);
+        test("test6Snippet", new int[]{0});
+    }
+
+    public static int test6Snippet(int[] a) {
+        int i = a[0];
+        if (i >= 0 && i < a.length) {
+            return a[i];
+        }
+        return 1;
+    }
+
+    @Test
+    public void test7() {
+        testCombinedIf("test7Snippet", 1);
+        test("test7Snippet", -1);
+    }
+
+    public static int test7Snippet(int v) {
+        if (v >= 0 && v < 1024) {
+            return v + 1;
+        }
+        return v - 1;
+    }
+
+    @Test
+    public void test8() {
+        testCombinedIf("test8Snippet", 1);
+        test("test8Snippet", -1);
+    }
+
+    public static int test8Snippet(int v) {
+        if (v >= 0 && v <= 1024) {
+            return v + 1;
+        }
+        return v - 1;
+    }
+
+    private void testCombinedIf(String snippet, int count) {
+        StructuredGraph graph = parse(snippet);
+        PhaseContext context = new PhaseContext(getProviders(), new Assumptions(false));
+        new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new FloatingReadPhase().apply(graph);
+        MidTierContext midContext = new MidTierContext(getProviders(), new Assumptions(false), getCodeCache().getTarget(), OptimisticOptimizations.ALL, graph.method().getProfilingInfo(), null);
+        new GuardLoweringPhase().apply(graph, midContext);
+        new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
+        new ValueAnchorCleanupPhase().apply(graph);
+        new CanonicalizerPhase(true).apply(graph, context);
+        assertDeepEquals(count, graph.getNodes().filter(IfNode.class).count());
+    }
+
     private void test(String snippet) {
         StructuredGraph graph = parse(snippet);
         ParameterNode param = graph.getNodes(ParameterNode.class).iterator().next();
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java	Mon May 19 17:21:30 2014 -0700
@@ -32,5 +32,10 @@
      * @returns null if the match can't be generated or a {@link ComplexMatchResult} that can be
      *          evaluated during LIR generation to produce the final LIR value.
      */
-    ComplexMatchResult match(NodeLIRBuilder gen);
+    ComplexMatchResult match(NodeLIRBuilder gen, Object... args);
+
+    /**
+     * @return a descriptive name meaningful to the user.
+     */
+    String getName();
 }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java	Mon May 19 17:21:30 2014 -0700
@@ -22,7 +22,6 @@
  */
 package com.oracle.graal.compiler.match;
 
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.Node.Verbosity;
 import com.oracle.graal.graph.*;
@@ -197,36 +196,6 @@
         return result;
     }
 
-    /**
-     * Convert a list of field names into {@link com.oracle.graal.graph.NodeClass.Position} objects
-     * that can be used to read them during a match. The names should already have been confirmed to
-     * exist in the type.
-     *
-     * @param theClass
-     * @param names
-     * @return an array of Position objects corresponding to the named fields.
-     */
-    public static NodeClass.Position[] findPositions(Class<? extends ValueNode> theClass, String[] names) {
-        NodeClass.Position[] result = new NodeClass.Position[names.length];
-        NodeClass nodeClass = NodeClass.get(theClass);
-        for (int i = 0; i < names.length; i++) {
-            for (NodeClass.Position position : nodeClass.getFirstLevelInputPositions()) {
-                String name = nodeClass.getName(position);
-                if (name.endsWith("#NDF")) {
-                    name = name.substring(0, name.length() - 4);
-                }
-                if (name.equals(names[i])) {
-                    result[i] = position;
-                    break;
-                }
-            }
-            if (result[i] == null) {
-                throw new GraalInternalError("unknown field \"%s\" in class %s", names[i], theClass);
-            }
-        }
-        return result;
-    }
-
     private Result matchUsage(ValueNode node, MatchContext context, boolean atRoot) {
         Result result = matchType(node);
         if (result != Result.OK) {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java	Mon May 19 17:21:30 2014 -0700
@@ -331,7 +331,8 @@
     List<String> requiredPackages = new ArrayList<>();
 
     /**
-     * The java.lang.reflect.Method for invoking a method based MatchRule.
+     * The mapping between elements with MatchRules and the wrapper class used invoke the code
+     * generation after the match.
      */
     private Map<ExecutableElement, MethodInvokerItem> invokers = new LinkedHashMap<>();
 
@@ -455,7 +456,7 @@
         }
 
         String generatePositionDeclaration() {
-            return String.format("private static final NodeClass.Position[] %s_positions = MatchPattern.findPositions(%s.class, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass,
+            return String.format("NodeClass.Position[] %s_positions = MatchRuleRegistry.findPositions(lookup, %s.class, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass,
                             String.join("\", \"", nodeType.inputs));
         }
     }
@@ -486,9 +487,7 @@
             out.println("package " + pkg + ";");
             out.println("");
             out.println("import java.util.*;");
-            out.println("import java.lang.reflect.*;");
             out.println("import " + MatchStatementSet.class.getPackage().getName() + ".*;");
-            out.println("import " + GraalInternalError.class.getName() + ";");
             out.println("import " + NodeLIRBuilder.class.getName() + ";");
             out.println("import " + NodeClass.class.getName() + ";");
             for (String p : requiredPackages) {
@@ -498,64 +497,67 @@
             out.println("public class " + matchStatementClassName + " implements " + MatchStatementSet.class.getSimpleName() + " {");
 
             out.println();
-            out.println("    private static Method lookupMethod(Class<?> theClass, String name, Class<?>... args) {");
-            out.println("        try {");
-            out.println("            return theClass.getDeclaredMethod(name, args);");
-            out.println("        } catch (Exception e) {");
-            out.println("            throw new GraalInternalError(e);");
-            out.println("        }");
-            out.println("    }");
-            out.println();
 
-            // Generate declarations for the reflective invocation of the code generation methods.
+            // Generate declarations for the wrapper class to invoke the code generation methods.
             for (MethodInvokerItem invoker : invokers.values()) {
                 StringBuilder args = new StringBuilder();
                 StringBuilder types = new StringBuilder();
                 int count = invoker.fields.size();
+                int index = 0;
                 for (VariableElement arg : invoker.fields) {
                     args.append('"');
                     args.append(arg.getSimpleName());
                     args.append('"');
-                    types.append(fullClassName(typeUtils.asElement(arg.asType())));
-                    types.append(".class");
+                    types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++));
                     if (count-- > 1) {
                         args.append(", ");
                         types.append(", ");
                     }
                 }
                 out.printf("    private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args);
-                out.printf("    private static final Method %s = lookupMethod(%s.class, \"%s\", %s);\n", invoker.reflectiveMethodName(), invoker.nodeLIRBuilderClass, invoker.methodName, types);
+                out.printf("    private static final class %s implements MatchGenerator {\n", invoker.wrapperClass());
+                out.printf("        static MatchGenerator instance = new %s();\n", invoker.wrapperClass());
+                out.printf("        public ComplexMatchResult match(NodeLIRBuilder builder, Object...args) {\n");
+                out.printf("            return ((%s) builder).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types);
+                out.printf("        }\n");
+                out.printf("        public String getName() {\n");
+                out.printf("             return \"%s\";\n", invoker.methodName);
+                out.printf("        }\n");
+                out.printf("    }\n");
                 out.println();
 
             }
 
-            for (String positionDeclaration : info.positionDeclarations) {
-                out.println("    " + positionDeclaration);
-            }
-            out.println();
-
             String desc = MatchStatement.class.getSimpleName();
-            out.println("    // CheckStyle: stop line length check");
-            out.println("    private static final List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList(");
-
-            int i = 0;
-            for (MatchRuleItem matchRule : info.matchRules) {
-                String comma = i == info.matchRules.size() - 1 ? "" : ",";
-                out.printf("        %s%s\n", matchRule.ruleBuilder(), comma);
-                i++;
-            }
-            out.println("    ));");
-            out.println("    // CheckStyle: resume line length check");
-            out.println();
 
             out.println("    public Class<? extends NodeLIRBuilder> forClass() {");
             out.println("        return " + topDeclaringClass + ".class;");
             out.println("    }");
             out.println();
             out.println("    @Override");
-            out.println("    public List<" + desc + "> statements() {");
+            out.println("    public List<" + desc + "> statements(MatchRuleRegistry.NodeClassLookup lookup) {");
+
+            for (String positionDeclaration : info.positionDeclarations) {
+                out.println("        " + positionDeclaration);
+            }
+            out.println();
+
+            out.println("        // CheckStyle: stop line length check");
+            out.println("        List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList(");
+
+            int i = 0;
+            for (MatchRuleItem matchRule : info.matchRules) {
+                String comma = i == info.matchRules.size() - 1 ? "" : ",";
+                out.printf("            %s%s\n", matchRule.ruleBuilder(), comma);
+                i++;
+            }
+            out.println("        ));");
+            out.println("        // CheckStyle: resume line length check");
             out.println("        return statements;");
             out.println("    }");
+
+            out.println();
+
             out.println("}");
         }
 
@@ -606,13 +608,12 @@
          * @return a string which will construct the MatchStatement instance to match this pattern.
          */
         public String ruleBuilder() {
-            return String.format("new MatchStatement(\"%s\", %s, %s, %s)", invoker.name, matchPattern, invoker.reflectiveMethodName(), invoker.argumentsListName());
+            return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.name, matchPattern, invoker.wrapperClass(), invoker.argumentsListName());
         }
     }
 
     /**
-     * Used to generate the declarations needed for reflective invocation of the code generation
-     * method.
+     * Used to generate the wrapper class to invoke the code generation method.
      */
     static class MethodInvokerItem {
         final String name;
@@ -627,8 +628,8 @@
             this.fields = fields;
         }
 
-        String reflectiveMethodName() {
-            return methodName + "_invoke";
+        String wrapperClass() {
+            return "MatchGenerator_" + methodName;
         }
 
         String argumentsListName() {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java	Mon May 19 17:21:30 2014 -0700
@@ -27,13 +27,61 @@
 import java.util.*;
 import java.util.Map.Entry;
 
+import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 
 public class MatchRuleRegistry {
 
+    /**
+     * Helper interface for mapping between Class and NodeClass. In static compilation environments,
+     * the current NodeClass might not be the same NodeClass used in the target so this provides a
+     * level of indirection.
+     */
+    public static interface NodeClassLookup {
+        NodeClass get(Class<?> theClass);
+
+    }
+
+    static class DefaultNodeClassLookup implements NodeClassLookup {
+        public NodeClass get(Class<?> theClass) {
+            return NodeClass.get(theClass);
+        }
+    }
+
+    /**
+     * Convert a list of field names into {@link com.oracle.graal.graph.NodeClass.Position} objects
+     * that can be used to read them during a match. The names should already have been confirmed to
+     * exist in the type.
+     *
+     * @param theClass
+     * @param names
+     * @return an array of Position objects corresponding to the named fields.
+     */
+    public static NodeClass.Position[] findPositions(NodeClassLookup lookup, Class<? extends ValueNode> theClass, String[] names) {
+        NodeClass.Position[] result = new NodeClass.Position[names.length];
+        NodeClass nodeClass = lookup.get(theClass);
+        for (int i = 0; i < names.length; i++) {
+            for (NodeClass.Position position : nodeClass.getFirstLevelInputPositions()) {
+                String name = nodeClass.getName(position);
+                if (name.endsWith("#NDF")) {
+                    name = name.substring(0, name.length() - 4);
+                }
+                if (name.equals(names[i])) {
+                    result[i] = position;
+                    break;
+                }
+            }
+            if (result[i] == null) {
+                throw new GraalInternalError("unknown field \"%s\" in class %s", names[i], theClass);
+            }
+        }
+        return result;
+    }
+
     private static final HashMap<Class<? extends NodeLIRBuilder>, Map<Class<? extends ValueNode>, List<MatchStatement>>> registry = new HashMap<>();
 
     /**
@@ -46,10 +94,11 @@
         Map<Class<? extends ValueNode>, List<MatchStatement>> result = registry.get(theClass);
 
         if (result == null) {
+            NodeClassLookup lookup = new DefaultNodeClassLookup();
             HashMap<Class<? extends NodeLIRBuilder>, List<MatchStatement>> localRules = new HashMap<>();
             ServiceLoader<MatchStatementSet> sl = ServiceLoader.loadInstalled(MatchStatementSet.class);
             for (MatchStatementSet rules : sl) {
-                localRules.put(rules.forClass(), rules.statements());
+                localRules.put(rules.forClass(), rules.statements(lookup));
             }
 
             // Walk the class hierarchy collecting lists and merge them together. The subclass
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java	Mon May 19 17:21:30 2014 -0700
@@ -24,11 +24,9 @@
 
 import static com.oracle.graal.compiler.GraalDebugConfig.*;
 
-import java.lang.reflect.*;
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.match.MatchPattern.MatchResultCode;
 import com.oracle.graal.compiler.match.MatchPattern.Result;
@@ -59,14 +57,14 @@
     /**
      * The method in the {@link NodeLIRBuilder} subclass that will actually do the code emission.
      */
-    private Method generatorMethod;
+    private MatchGenerator generatorMethod;
 
     /**
      * The name of arguments in the order they are expected to be passed to the generator method.
      */
     private String[] arguments;
 
-    public MatchStatement(String name, MatchPattern pattern, Method generator, String[] arguments) {
+    public MatchStatement(String name, MatchPattern pattern, MatchGenerator generator, String[] arguments) {
         this.name = name;
         this.pattern = pattern;
         this.generatorMethod = generator;
@@ -93,23 +91,19 @@
         MatchContext context = new MatchContext(builder, this, index, node, nodes);
         result = pattern.matchUsage(node, context);
         if (result == Result.OK) {
-            try {
-                // Invoke the generator method and set the result if it's non null.
-                ComplexMatchResult value = (ComplexMatchResult) generatorMethod.invoke(builder, buildArgList(context));
-                if (value != null) {
-                    context.setResult(value);
-                    MatchStatementSuccess.increment();
-                    Debug.metric("MatchStatement[%s]", getName()).increment();
-                    return true;
-                }
-                // The pattern matched but some other code generation constraint disallowed code
-                // generation for the pattern.
-                if (LogVerbose.getValue()) {
-                    Debug.log("while matching %s|%s %s %s returned null", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), generatorMethod.getName());
-                    Debug.log("with nodes %s", formatMatch(node));
-                }
-            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                throw new GraalInternalError(e);
+            // Invoke the generator method and set the result if it's non null.
+            ComplexMatchResult value = generatorMethod.match(builder, buildArgList(context));
+            if (value != null) {
+                context.setResult(value);
+                MatchStatementSuccess.increment();
+                Debug.metric("MatchStatement[%s]", getName()).increment();
+                return true;
+            }
+            // The pattern matched but some other code generation constraint disallowed code
+            // generation for the pattern.
+            if (LogVerbose.getValue()) {
+                Debug.log("while matching %s|%s %s %s returned null", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), generatorMethod.getName());
+                Debug.log("with nodes %s", formatMatch(node));
             }
         } else {
             if (LogVerbose.getValue() && result.code != MatchResultCode.WRONG_CLASS) {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java	Mon May 19 17:21:30 2014 -0700
@@ -36,5 +36,5 @@
     /**
      * @return the {@link MatchStatement}s available for this {@link NodeLIRBuilder} subclass.
      */
-    public List<MatchStatement> statements();
+    public List<MatchStatement> statements(MatchRuleRegistry.NodeClassLookup lookup);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/AnsiColor.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,88 @@
+/*
+ * 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.debug;
+
+/**
+ * Ansi terminal color escape codes.
+ */
+public final class AnsiColor {
+    /** Foreground black */
+    public static final String BLACK = "\u001b[30m";
+    /** Foreground red */
+    public static final String RED = "\u001b[31m";
+    /** Foreground green */
+    public static final String GREEN = "\u001b[32m";
+    /** Foreground yellow */
+    public static final String YELLOW = "\u001b[33m";
+    /** Foreground blue */
+    public static final String BLUE = "\u001b[34m";
+    /** Foreground magenta */
+    public static final String MAGENTA = "\u001b[35m";
+    /** Foreground cyan */
+    public static final String CYAN = "\u001b[36m";
+    /** Foreground white */
+    public static final String WHITE = "\u001b[37m";
+
+    /** Foreground bold black */
+    public static final String BOLD_BLACK = "\u001b[30;1m";
+    /** Foreground bold red */
+    public static final String BOLD_RED = "\u001b[31;1m";
+    /** Foreground bold green */
+    public static final String BOLD_GREEN = "\u001b[32;1m";
+    /** Foreground bold yellow */
+    public static final String BOLD_YELLOW = "\u001b[33;1m";
+    /** Foreground bold blue */
+    public static final String BOLD_BLUE = "\u001b[34;1m";
+    /** Foreground bold magenta */
+    public static final String BOLD_MAGENTA = "\u001b[35;1m";
+    /** Foreground bold cyan */
+    public static final String BOLD_CYAN = "\u001b[36;1m";
+    /** Foreground bold white */
+    public static final String BOLD_WHITE = "\u001b[37;1m";
+
+    /** Background black */
+    public static final String BG_BLACK = "\u001b[40m";
+    /** Background red */
+    public static final String BG_RED = "\u001b[41m";
+    /** Background green */
+    public static final String BG_GREEN = "\u001b[42m";
+    /** Background yellow */
+    public static final String BG_YELLOW = "\u001b[43m";
+    /** Background blue */
+    public static final String BG_BLUE = "\u001b[44m";
+    /** Background magenta */
+    public static final String BG_MAGENTA = "\u001b[45m";
+    /** Background cyan */
+    public static final String BG_CYAN = "\u001b[46m";
+    /** Background white */
+    public static final String BG_WHITE = "\u001b[47m";
+
+    /** Reset */
+    public static final String RESET = "\u001b[0m";
+    /** Underline */
+    public static final String UNDERLINED = "\u001b[4m";
+
+    /** Prevent instantiation */
+    private AnsiColor() {
+    }
+}
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Mon May 19 17:21:30 2014 -0700
@@ -741,7 +741,7 @@
 
     private static DebugMetric createMetric(String format, Object arg1, Object arg2) {
         String name = formatDebugName(format, arg1, arg2);
-        boolean conditional = enabledMetrics != null && enabledMetrics.contains(name);
+        boolean conditional = enabledMetrics == null || !enabledMetrics.contains(name);
         return new MetricImpl(name, conditional);
     }
 
@@ -981,7 +981,7 @@
 
     private static DebugTimer createTimer(String format, Object arg1, Object arg2) {
         String name = formatDebugName(format, arg1, arg2);
-        boolean conditional = enabledTimers != null && enabledTimers.contains(name);
+        boolean conditional = enabledTimers == null || !enabledTimers.contains(name);
         return new TimerImpl(name, conditional);
     }
 
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java	Mon May 19 17:21:30 2014 -0700
@@ -197,4 +197,9 @@
     public boolean contains(Node node) {
         return isMarked(node);
     }
+
+    @Override
+    public String toString() {
+        return snapshot().toString();
+    }
 }
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Mon May 19 17:21:30 2014 -0700
@@ -32,6 +32,9 @@
 
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.Map.Entry;
+import java.util.function.*;
+import java.util.stream.*;
 
 import com.amd.okra.*;
 import com.oracle.graal.api.code.*;
@@ -69,9 +72,11 @@
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.tiers.*;
+import com.oracle.graal.virtual.nodes.*;
 
 /**
  * HSAIL specific backend.
@@ -266,7 +271,7 @@
         StructuredGraph hostGraph = hsailCode.getHostGraph();
         if (hostGraph != null) {
             // TODO get rid of the unverified entry point in the host code
-            try (Scope ds = Debug.scope("GeneratingHostGraph")) {
+            try (Scope ds = Debug.scope("GeneratingHostGraph", new DebugDumpScope("HostGraph"))) {
                 HotSpotBackend hostBackend = getRuntime().getHostBackend();
                 JavaType[] parameterTypes = new JavaType[hostGraph.getNodes(ParameterNode.class).count()];
                 Debug.log("Param count: %d", parameterTypes.length);
@@ -726,6 +731,8 @@
         asm.emitString(spillsegStringFinal, spillsegDeclarationPosition);
         // Emit the epilogue.
 
+        HSAILHotSpotLIRGenerationResult lirGenRes = ((HSAILCompilationResultBuilder) crb).lirGenRes;
+
         int numSRegs = 0;
         int numDRegs = 0;
         int numStackSlotBytes = 0;
@@ -736,31 +743,39 @@
             Set<Register> infoUsedRegs = new TreeSet<>();
             Set<StackSlot> infoUsedStackSlots = new HashSet<>();
             List<Infopoint> infoList = crb.compilationResult.getInfopoints();
+            Queue<Value[]> workList = new LinkedList<>();
             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);
+                    workList.add(frame.values);
+                    frame = frame.caller();
+                }
+            }
+            while (!workList.isEmpty()) {
+                Value[] values = workList.poll();
+                for (Value val : values) {
+                    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);
+                        } else if (isVirtualObject(val)) {
+                            workList.add(((VirtualObject) val).getValues());
+                        } else {
+                            assert isConstant(val) : "Unsupported value: " + val;
                         }
                     }
-                    frame = frame.caller();
                 }
             }
 
@@ -923,8 +938,9 @@
         asm.emitString0("}; \n");
 
         ExternalCompilationResult compilationResult = (ExternalCompilationResult) crb.compilationResult;
-        HSAILHotSpotLIRGenerationResult lirGenRes = ((HSAILCompilationResultBuilder) crb).lirGenRes;
-        compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config, numSRegs, numDRegs));
+        if (useHSAILDeoptimization) {
+            compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config, numSRegs, numDRegs));
+        }
     }
 
     private static class OopMapArrayBuilder {
@@ -1090,26 +1106,53 @@
     }
 
     private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs) {
+        return createFrameState(lowLevelFrame, hsailFrame, providers, config, numSRegs, numDRegs, new HashMap<VirtualObject, VirtualObjectNode>());
+    }
+
+    private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs,
+                    Map<VirtualObject, VirtualObjectNode> virtualObjects) {
+        FrameState outterFrameState = null;
+        if (lowLevelFrame.caller() != null) {
+            outterFrameState = createFrameState(lowLevelFrame.caller(), hsailFrame, providers, config, numSRegs, numDRegs, virtualObjects);
+        }
         StructuredGraph hostGraph = hsailFrame.graph();
+        Function<? super Value, ? extends ValueNode> lirValueToHirNode = v -> getNodeForValueFromFrame(v, hsailFrame, hostGraph, providers, config, numSRegs, numDRegs, virtualObjects);
         ValueNode[] locals = new ValueNode[lowLevelFrame.numLocals];
         for (int i = 0; i < lowLevelFrame.numLocals; i++) {
-            locals[i] = getNodeForValueFromFrame(lowLevelFrame.getLocalValue(i), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs);
+            locals[i] = lirValueToHirNode.apply(lowLevelFrame.getLocalValue(i));
         }
         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, numSRegs, numDRegs));
+            stack.add(lirValueToHirNode.apply(lowLevelFrame.getStackValue(i)));
         }
         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, numSRegs, numDRegs);
+            locks[i] = lirValueToHirNode.apply(lockValue);
             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, numSRegs, numDRegs));
+        if (outterFrameState != null) {
+            frameState.setOuterFrameState(outterFrameState);
         }
+        Map<VirtualObject, VirtualObjectNode> virtualObjectsCopy;
+        // TODO this could be implemented more efficiently with a mark into the map
+        // unfortunately LinkedHashMap doesn't seem to provide that.
+        List<VirtualObjectState> virtualStates = new ArrayList<>(virtualObjects.size());
+        do {
+            virtualObjectsCopy = new HashMap<>(virtualObjects);
+            virtualStates.clear();
+            for (Entry<VirtualObject, VirtualObjectNode> entry : virtualObjectsCopy.entrySet()) {
+                VirtualObject virtualObject = entry.getKey();
+                VirtualObjectNode virtualObjectNode = entry.getValue();
+                List<ValueNode> fieldValues = Arrays.stream(virtualObject.getValues()).map(lirValueToHirNode).collect(Collectors.toList());
+                virtualStates.add(new VirtualObjectState(virtualObjectNode, fieldValues));
+            }
+            // New virtual objects may have been discovered while processing the previous set.
+            // Wait until a fixed point is reached
+        } while (virtualObjectsCopy.size() < virtualObjects.size());
+        virtualStates.forEach(vos -> frameState.addVirtualObjectMapping(hostGraph.unique(vos)));
         return frameState;
     }
 
@@ -1122,18 +1165,18 @@
     }
 
     private static ValueNode getNodeForValueFromFrame(Value localValue, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs,
-                    int numDRegs) {
+                    int numDRegs, Map<VirtualObject, VirtualObjectNode> virtualObjects) {
         ValueNode valueNode;
         if (localValue instanceof Constant) {
             valueNode = ConstantNode.forConstant((Constant) localValue, providers.getMetaAccess(), hostGraph);
         } else if (localValue instanceof VirtualObject) {
-            throw GraalInternalError.unimplemented();
+            valueNode = getNodeForVirtualObjectFromFrame((VirtualObject) localValue, virtualObjects, hostGraph);
         } else if (localValue instanceof StackSlot) {
             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, numSRegs, numDRegs);
+            return getNodeForValueFromFrame(hotSpotMonitorValue.getOwner(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs, virtualObjects);
         } else if (localValue instanceof RegisterValue) {
             RegisterValue registerValue = (RegisterValue) localValue;
             int regNumber = registerValue.getRegister().number;
@@ -1146,6 +1189,16 @@
         return valueNode;
     }
 
+    private static ValueNode getNodeForVirtualObjectFromFrame(VirtualObject virtualObject, Map<VirtualObject, VirtualObjectNode> virtualObjects, StructuredGraph hostGraph) {
+        return virtualObjects.computeIfAbsent(virtualObject, vo -> {
+            if (vo.getType().isArray()) {
+                return hostGraph.add(new VirtualArrayNode(vo.getType().getComponentType(), vo.getValues().length));
+            } else {
+                return hostGraph.add(new VirtualInstanceNode(vo.getType(), true));
+            }
+        });
+    }
+
     private static ValueNode getNodeForRegisterFromFrame(int regNumber, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config,
                     int numSRegs) {
         ValueNode valueNode;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot.jfr/src/com/oracle/graal/hotspot/jfr/events/JFREventProvider.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,158 @@
+/*
+ * 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.hotspot.jfr.events;
+
+import java.net.*;
+
+import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.hotspot.events.*;
+import com.oracle.jrockit.jfr.*;
+
+/**
+ * A JFR implementation for {@link EventProvider}. This implementation is used when Flight Recorder
+ * is turned on.
+ */
+@ServiceProvider(EventProvider.class)
+public final class JFREventProvider implements EventProvider {
+
+    @SuppressWarnings("deprecation") private final Producer producer;
+
+    @SuppressWarnings("deprecation")
+    public JFREventProvider() {
+        try {
+            /*
+             * The "HotSpot JVM" producer is a native producer and we cannot use it. So we create
+             * our own. This has the downside that Mission Control is confused and doesn't show
+             * Graal's events in the "Code" tab. There are plans to revise the JFR code for JDK 9.
+             */
+            producer = new Producer("HotSpot JVM", "Oracle Hotspot JVM", "http://www.oracle.com/hotspot/jvm/");
+            producer.register();
+        } catch (URISyntaxException e) {
+            throw new InternalError(e);
+        }
+
+        // Register event classes with Producer.
+        for (Class<?> c : JFREventProvider.class.getDeclaredClasses()) {
+            if (c.isAnnotationPresent(EventDefinition.class)) {
+                assert com.oracle.jrockit.jfr.InstantEvent.class.isAssignableFrom(c) : c;
+                registerEvent(c);
+            }
+        }
+    }
+
+    /**
+     * Register an event class with the {@link Producer}.
+     *
+     * @param c event class
+     * @return the {@link EventToken event token}
+     */
+    @SuppressWarnings({"deprecation", "javadoc", "unchecked"})
+    private final EventToken registerEvent(Class<?> c) {
+        try {
+            return producer.addEvent((Class<? extends com.oracle.jrockit.jfr.InstantEvent>) c);
+        } catch (InvalidEventDefinitionException | InvalidValueException e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public CompilationEvent newCompilationEvent() {
+        return new JFRCompilationEvent();
+    }
+
+    /**
+     * A JFR compilation event.
+     *
+     * <p>
+     * See: event {@code Compilation} in {@code src/share/vm/trace/trace.xml}
+     */
+    @SuppressWarnings("deprecation")
+    @EventDefinition(name = "Compilation", path = "vm/compiler/compilation")
+    public static class JFRCompilationEvent extends com.oracle.jrockit.jfr.DurationEvent implements CompilationEvent {
+
+        /*
+         * FIXME method should be a Method* but we can't express that in Java.
+         */
+        @ValueDefinition(name = "Java Method") public String method;
+        @ValueDefinition(name = "Compilation ID", relationKey = "COMP_ID") public int compileId;
+        @ValueDefinition(name = "Compilation Level") public short compileLevel;
+        @ValueDefinition(name = "Succeeded") public boolean succeeded;
+        @ValueDefinition(name = "On Stack Replacement") public boolean isOsr;
+        @ValueDefinition(name = "Compiled Code Size", contentType = ContentType.Bytes) public int codeSize;
+        @ValueDefinition(name = "Inlined Code Size", contentType = ContentType.Bytes) public int inlinedBytes;
+
+        public void setMethod(String method) {
+            this.method = method;
+        }
+
+        public void setCompileId(int id) {
+            this.compileId = id;
+        }
+
+        public void setCompileLevel(int compileLevel) {
+            this.compileLevel = (short) compileLevel;
+        }
+
+        public void setSucceeded(boolean succeeded) {
+            this.succeeded = succeeded;
+        }
+
+        public void setIsOsr(boolean isOsr) {
+            this.isOsr = isOsr;
+        }
+
+        public void setCodeSize(int codeSize) {
+            this.codeSize = codeSize;
+        }
+
+        public void setInlinedBytes(int inlinedBytes) {
+            this.inlinedBytes = inlinedBytes;
+        }
+    }
+
+    public CompilerFailureEvent newCompilerFailureEvent() {
+        return new JFRCompilerFailureEvent();
+    }
+
+    /**
+     * A JFR compiler failure event.
+     *
+     * <p>
+     * See: event {@code CompilerFailure} in {@code src/share/vm/trace/trace.xml}
+     */
+    @SuppressWarnings("deprecation")
+    @EventDefinition(name = "Compilation Failure", path = "vm/compiler/failure")
+    public static class JFRCompilerFailureEvent extends com.oracle.jrockit.jfr.InstantEvent implements CompilerFailureEvent {
+
+        @ValueDefinition(name = "Compilation ID", relationKey = "COMP_ID") public int compileId;
+        @ValueDefinition(name = "Message", description = "The failure message") public String failure;
+
+        public void setCompileId(int id) {
+            this.compileId = id;
+        }
+
+        public void setMessage(String message) {
+            this.failure = message;
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java	Mon May 19 17:21:30 2014 -0700
@@ -26,6 +26,7 @@
 import java.lang.ref.*;
 import java.lang.reflect.*;
 
+import com.oracle.graal.phases.common.inlining.policy.InlineEverythingPolicy;
 import org.junit.*;
 
 import com.oracle.graal.api.code.*;
@@ -249,7 +250,7 @@
             StructuredGraph graph = parse(snippet);
             HighTierContext highContext = new HighTierContext(getProviders(), new Assumptions(false), null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
             MidTierContext midContext = new MidTierContext(getProviders(), new Assumptions(false), getCodeCache().getTarget(), OptimisticOptimizations.ALL, graph.method().getProfilingInfo(), null);
-            new InliningPhase(new InliningPhase.InlineEverythingPolicy(), new CanonicalizerPhase(true)).apply(graph, highContext);
+            new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase(true)).apply(graph, highContext);
             new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext);
             new GuardLoweringPhase().apply(graph, midContext);
             new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Mon May 19 17:21:30 2014 -0700
@@ -40,6 +40,7 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.code.CallingConvention.Type;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.runtime.*;
 import com.oracle.graal.baseline.*;
 import com.oracle.graal.compiler.*;
 import com.oracle.graal.compiler.common.*;
@@ -47,6 +48,9 @@
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.debug.internal.*;
 import com.oracle.graal.hotspot.bridge.*;
+import com.oracle.graal.hotspot.events.*;
+import com.oracle.graal.hotspot.events.EventProvider.CompilationEvent;
+import com.oracle.graal.hotspot.events.EventProvider.CompilerFailureEvent;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.phases.*;
 import com.oracle.graal.java.*;
@@ -103,6 +107,13 @@
     private final int id;
     private final AtomicReference<CompilationStatus> status;
 
+    /**
+     * The executor processing the Graal compilation queue this task was placed on. This will be
+     * null for blocking compilations or if compilations are scheduled as native HotSpot
+     * {@linkplain #ctask CompileTask}s.
+     */
+    private final ExecutorService executor;
+
     private StructuredGraph graph;
 
     /**
@@ -116,7 +127,7 @@
      * A {@link com.sun.management.ThreadMXBean} to be able to query some information about the
      * current compiler thread, e.g. total allocated bytes.
      */
-    private final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) ManagementFactory.getThreadMXBean();
+    private static final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) ManagementFactory.getThreadMXBean();
 
     /**
      * The address of the native CompileTask associated with this compilation. If 0L, then this
@@ -125,7 +136,8 @@
      */
     private final long ctask;
 
-    public CompilationTask(HotSpotBackend backend, HotSpotResolvedJavaMethod method, int entryBCI, long ctask, boolean blocking) {
+    public CompilationTask(ExecutorService executor, HotSpotBackend backend, HotSpotResolvedJavaMethod method, int entryBCI, long ctask, boolean blocking) {
+        this.executor = executor;
         this.backend = backend;
         this.method = method;
         this.entryBCI = entryBCI;
@@ -144,6 +156,11 @@
         return method;
     }
 
+    /**
+     * Returns the compilation id of this task.
+     *
+     * @return compile id
+     */
     public int getId() {
         return id;
     }
@@ -156,7 +173,7 @@
     public void run() {
         withinEnqueue.set(Boolean.FALSE);
         try {
-            runCompilation(true);
+            runCompilation();
         } finally {
             withinEnqueue.set(Boolean.TRUE);
             status.set(CompilationStatus.Finished);
@@ -226,7 +243,14 @@
         return method.getCompilationProfilingInfo(osrCompilation);
     }
 
-    public void runCompilation(boolean clearFromCompilationQueue) {
+    public void runCompilation() {
+        if (executor != null && executor.isShutdown()) {
+            // We don't want to do any unnecessary compilation if the Graal compilation
+            // queue has been shutdown. Note that we leave the JVM_ACC_QUEUED bit set
+            // for the method so that it won't be re-scheduled for compilation.
+            return;
+        }
+
         /*
          * no code must be outside this try/finally because it could happen otherwise that
          * clearQueuedForCompilation() is not executed
@@ -237,12 +261,16 @@
         long previousInlinedBytecodes = InlinedBytecodes.getCurrentValue();
         long previousCompilationTime = CompilationTime.getCurrentValue();
         HotSpotInstalledCode installedCode = null;
+        final boolean isOSR = entryBCI != StructuredGraph.INVOCATION_ENTRY_BCI;
+
+        // Log a compilation event.
+        EventProvider eventProvider = Graal.getRequiredCapability(EventProvider.class);
+        CompilationEvent compilationEvent = eventProvider.newCompilationEvent();
 
         try (TimerCloseable a = CompilationTime.start()) {
             if (!tryToChangeStatus(CompilationStatus.Queued, CompilationStatus.Running)) {
                 return;
             }
-            boolean isOSR = entryBCI != StructuredGraph.INVOCATION_ENTRY_BCI;
 
             // If there is already compiled code for this method on our level we simply return.
             // Graal compiles are always at the highest compile level, even in non-tiered mode so we
@@ -266,6 +294,8 @@
             final long allocatedBytesBefore = threadMXBean.getThreadAllocatedBytes(threadId);
 
             try (Scope s = Debug.scope("Compiling", new DebugDumpScope(String.valueOf(id), true))) {
+                // Begin the compilation event.
+                compilationEvent.begin();
 
                 if (UseBaselineCompiler.getValue() == true) {
                     HotSpotProviders providers = backend.getProviders();
@@ -307,6 +337,9 @@
             } catch (Throwable e) {
                 throw Debug.handle(e);
             } finally {
+                // End the compilation event.
+                compilationEvent.end();
+
                 filter.remove();
                 final boolean printAfterCompilation = PrintAfterCompilation.getValue() && !TTY.isSuppressed();
 
@@ -346,16 +379,37 @@
             if (PrintStackTraceOnException.getValue() || ExitVMOnException.getValue()) {
                 t.printStackTrace(TTY.cachedOut);
             }
+
+            // Log a failure event.
+            CompilerFailureEvent event = eventProvider.newCompilerFailureEvent();
+            if (event.shouldWrite()) {
+                event.setCompileId(getId());
+                event.setMessage(t.getMessage());
+                event.commit();
+            }
+
             if (ExitVMOnException.getValue()) {
                 System.exit(-1);
             }
         } finally {
-            int processedBytes = (int) (InlinedBytecodes.getCurrentValue() - previousInlinedBytecodes);
+            final int processedBytes = (int) (InlinedBytecodes.getCurrentValue() - previousInlinedBytecodes);
+
+            // Log a compilation event.
+            if (compilationEvent.shouldWrite()) {
+                compilationEvent.setMethod(MetaUtil.format("%H.%n(%p)", method));
+                compilationEvent.setCompileId(getId());
+                compilationEvent.setCompileLevel(config.compilationLevelFullOptimization);
+                compilationEvent.setSucceeded(true);
+                compilationEvent.setIsOsr(isOSR);
+                compilationEvent.setCodeSize(installedCode.getSize());
+                compilationEvent.setInlinedBytes(processedBytes);
+                compilationEvent.commit();
+            }
+
             if (ctask != 0L) {
                 unsafe.putInt(ctask + config.compileTaskNumInlinedBytecodesOffset, processedBytes);
             }
             if ((config.ciTime || config.ciTimeEach || PrintCompRate.getValue() != 0) && installedCode != null) {
-
                 long time = CompilationTime.getCurrentValue() - previousCompilationTime;
                 TimeUnit timeUnit = CompilationTime.getTimeUnit();
                 long timeUnitsPerSecond = timeUnit.convert(1, TimeUnit.SECONDS);
@@ -363,7 +417,7 @@
                 c2vm.notifyCompilationStatistics(id, method, entryBCI != INVOCATION_ENTRY_BCI, processedBytes, time, timeUnitsPerSecond, installedCode);
             }
 
-            if (clearFromCompilationQueue) {
+            if (executor != null) {
                 assert method.isQueuedForCompilation();
                 method.clearQueuedForCompilation();
             }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java	Mon May 19 17:21:30 2014 -0700
@@ -317,7 +317,7 @@
     class CTWCompilationTask extends CompilationTask {
 
         CTWCompilationTask(HotSpotBackend backend, HotSpotResolvedJavaMethod method) {
-            super(backend, method, INVOCATION_ENTRY_BCI, 0L, false);
+            super(null, backend, method, INVOCATION_ENTRY_BCI, 0L, false);
         }
 
         /**
@@ -349,7 +349,7 @@
 
             HotSpotBackend backend = runtime.getHostBackend();
             CompilationTask task = new CTWCompilationTask(backend, method);
-            task.runCompilation(false);
+            task.runCompilation();
 
             compileTime += (System.currentTimeMillis() - start);
             compiledMethodsCounter++;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java	Mon May 19 17:21:30 2014 -0700
@@ -43,6 +43,7 @@
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.bridge.*;
+import com.oracle.graal.hotspot.events.*;
 import com.oracle.graal.hotspot.logging.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.options.*;
@@ -274,6 +275,8 @@
             }
             registerBackend(factory.createBackend(this, hostBackend));
         }
+
+        eventProvider = createEventProvider();
     }
 
     private HotSpotBackend registerBackend(HotSpotBackend backend) {
@@ -379,6 +382,22 @@
 
     private final NodeCollectionsProvider nodeCollectionsProvider = new DefaultNodeCollectionsProvider();
 
+    private final EventProvider eventProvider;
+
+    private EventProvider createEventProvider() {
+        if (config.flightRecorder) {
+            ServiceLoader<EventProvider> sl = ServiceLoader.loadInstalled(EventProvider.class);
+            EventProvider singleProvider = null;
+            for (EventProvider ep : sl) {
+                assert singleProvider == null : String.format("multiple %s service implementations found: %s and %s", EventProvider.class.getName(), singleProvider.getClass().getName(),
+                                ep.getClass().getName());
+                singleProvider = ep;
+            }
+            return singleProvider;
+        }
+        return new EmptyEventProvider();
+    }
+
     @SuppressWarnings("unchecked")
     @Override
     public <T> T getCapability(Class<T> clazz) {
@@ -392,6 +411,8 @@
             return (T) getHostProviders().getSnippetReflection();
         } else if (clazz == MethodHandleAccessProvider.class) {
             return (T) getHostProviders().getMethodHandleAccess();
+        } else if (clazz == EventProvider.class) {
+            return (T) eventProvider;
         }
         return null;
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Mon May 19 17:21:30 2014 -0700
@@ -737,6 +737,8 @@
     @HotSpotVMFlag(name = "AllocatePrefetchStepSize") @Stable public int allocatePrefetchStepSize;
     @HotSpotVMFlag(name = "AllocatePrefetchDistance") @Stable public int allocatePrefetchDistance;
 
+    @HotSpotVMFlag(name = "FlightRecorder", optional = true) @Stable public boolean flightRecorder;
+
     @HotSpotVMField(name = "Universe::_collectedHeap", type = "CollectedHeap*", get = HotSpotVMField.Type.VALUE) @Stable private long universeCollectedHeap;
     @HotSpotVMField(name = "CollectedHeap::_total_collections", type = "unsigned int", get = HotSpotVMField.Type.OFFSET) @Stable private int collectedHeapTotalCollectionsOffset;
 
@@ -1064,6 +1066,7 @@
     @HotSpotVMConstant(name = "Method::_dont_inline") @Stable public int methodFlagsDontInline;
     @HotSpotVMConstant(name = "Method::_hidden") @Stable public int methodFlagsHidden;
     @HotSpotVMConstant(name = "Method::nonvirtual_vtable_index") @Stable public int nonvirtualVtableIndex;
+    @HotSpotVMConstant(name = "Method::invalid_vtable_index") @Stable public int invalidVtableIndex;
 
     @HotSpotVMConstant(name = "JVM_ACC_MONITOR_MATCH") @Stable public int jvmAccMonitorMatch;
     @HotSpotVMConstant(name = "JVM_ACC_HAS_MONITOR_BYTECODES") @Stable public int jvmAccHasMonitorBytecodes;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Mon May 19 17:21:30 2014 -0700
@@ -248,7 +248,7 @@
 
     void initializeConfiguration(HotSpotVMConfig config);
 
-    long resolveMethod(long metaspaceKlass, String name, String signature);
+    long resolveMethod(long metaspaceKlassExactReceiver, long metaspaceMethod, long metaspaceKlassCaller);
 
     long getClassInitializer(long metaspaceKlass);
 
@@ -353,4 +353,6 @@
     void materializeVirtualObjects(HotSpotStackFrameReference stackFrame, boolean invalidate);
 
     void resolveInvokeDynamic(long metaspaceConstantPool, int index);
+
+    int getVtableIndexForInterface(long metaspaceKlass, long metaspaceMethod);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Mon May 19 17:21:30 2014 -0700
@@ -99,7 +99,7 @@
     public native void initializeConfiguration(HotSpotVMConfig config);
 
     @Override
-    public native long resolveMethod(long metaspaceKlass, String name, String signature);
+    public native long resolveMethod(long metaspaceKlassExactReceiver, long metaspaceMethod, long metaspaceKlassCaller);
 
     @Override
     public native boolean hasFinalizableSubclass(long metaspaceKlass);
@@ -182,4 +182,6 @@
     public native long getTimeStamp();
 
     public native void resolveInvokeDynamic(long metaspaceConstantPool, int index);
+
+    public native int getVtableIndexForInterface(long metaspaceKlass, long metaspaceMethod);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Mon May 19 17:21:30 2014 -0700
@@ -91,7 +91,7 @@
      * is in the proper state.
      */
     static class Queue {
-        private ThreadPoolExecutor executor;
+        private final ThreadPoolExecutor executor;
 
         Queue(CompilerThreadFactory factory) {
             executor = new ThreadPoolExecutor(Threads.getValue(), Threads.getValue(), 0L, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(), factory);
@@ -117,11 +117,19 @@
             executor.execute(task);
         }
 
+        /**
+         * @see ExecutorService#isShutdown()
+         */
+        public boolean isShutdown() {
+            return executor.isShutdown();
+        }
+
         public void shutdown() throws InterruptedException {
             assert CompilationTask.isWithinEnqueue();
-            executor.shutdown();
-            if (Debug.isEnabled() && Dump.getValue() != null) {
-                // Wait 2 seconds to flush out all graph dumps that may be of interest
+            executor.shutdownNow();
+            if (Debug.isEnabled() && (Dump.getValue() != null || areMetricsOrTimersEnabled())) {
+                // Wait up to 2 seconds to flush out all graph dumps and stop metrics/timers
+                // being updated.
                 executor.awaitTermination(2, TimeUnit.SECONDS);
             }
         }
@@ -472,18 +480,6 @@
         printMap(new DebugValueScope(null, result), debugValues);
     }
 
-    static long collectTotal(DebugValue value) {
-        List<DebugValueMap> maps = DebugValueMap.getTopLevelMaps();
-        long total = 0;
-        for (int i = 0; i < maps.size(); i++) {
-            DebugValueMap map = maps.get(i);
-            int index = value.getIndex();
-            total += map.getCurrentValue(index);
-            total += collectTotal(map.getChildren(), index);
-        }
-        return total;
-    }
-
     private static long collectTotal(List<DebugValueMap> maps, int index) {
         long total = 0;
         for (int i = 0; i < maps.size(); i++) {
@@ -570,8 +566,8 @@
     void compileMethod(final HotSpotResolvedJavaMethod method, final int entryBCI, long ctask, final boolean blocking) {
         if (ctask != 0L) {
             HotSpotBackend backend = runtime.getHostBackend();
-            CompilationTask task = new CompilationTask(backend, method, entryBCI, ctask, false);
-            task.runCompilation(false);
+            CompilationTask task = new CompilationTask(null, backend, method, entryBCI, ctask, false);
+            task.runCompilation();
             return;
         }
 
@@ -596,13 +592,14 @@
             if (method.tryToQueueForCompilation()) {
                 assert method.isQueuedForCompilation();
 
-                HotSpotBackend backend = runtime.getHostBackend();
-                CompilationTask task = new CompilationTask(backend, method, entryBCI, ctask, block);
-
                 try {
-                    compileQueue.execute(task);
-                    if (block) {
-                        task.block();
+                    if (!compileQueue.executor.isShutdown()) {
+                        HotSpotBackend backend = runtime.getHostBackend();
+                        CompilationTask task = new CompilationTask(compileQueue.executor, backend, method, entryBCI, ctask, block);
+                        compileQueue.execute(task);
+                        if (block) {
+                            task.block();
+                        }
                     }
                 } catch (RejectedExecutionException e) {
                     // The compile queue was already shut down.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/events/EmptyEventProvider.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,105 @@
+/*
+ * 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.hotspot.events;
+
+import com.oracle.graal.compiler.common.*;
+
+/**
+ * An empty implementation for {@link EventProvider}. This implementation is used when no logging is
+ * requested.
+ */
+public final class EmptyEventProvider implements EventProvider {
+
+    public CompilationEvent newCompilationEvent() {
+        return new EmptyCompilationEvent();
+    }
+
+    class EmptyCompilationEvent implements CompilationEvent {
+        public void commit() {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public boolean shouldWrite() {
+            // Events of this class should never been written.
+            return false;
+        }
+
+        public void begin() {
+        }
+
+        public void end() {
+        }
+
+        public void setMethod(String method) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public void setCompileId(int compileId) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public void setCompileLevel(int compileLevel) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public void setSucceeded(boolean succeeded) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public void setIsOsr(boolean isOsr) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public void setCodeSize(int codeSize) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public void setInlinedBytes(int inlinedBytes) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    public CompilerFailureEvent newCompilerFailureEvent() {
+        return new EmptyCompilerFailureEvent();
+    }
+
+    class EmptyCompilerFailureEvent implements CompilerFailureEvent {
+        public void commit() {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public boolean shouldWrite() {
+            // Events of this class should never been written.
+            return false;
+        }
+
+        public void setCompileId(int compileId) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+
+        public void setMessage(String message) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/events/EventProvider.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,105 @@
+/*
+ * 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.hotspot.events;
+
+/**
+ * A provider that provides a specific implementation for events that can be logged in the compiler.
+ */
+public interface EventProvider {
+
+    /**
+     * An instant event is an event that is not considered to have taken any time.
+     */
+    interface InstantEvent {
+        /**
+         * Commits the event.
+         */
+        void commit();
+
+        /**
+         * Determines if this particular event instance would be committed to the data stream right
+         * now if application called {@link #commit()}. This in turn depends on whether the event is
+         * enabled and possible other factors.
+         *
+         * @return if this event would be committed on a call to {@link #commit()}.
+         */
+        boolean shouldWrite();
+    }
+
+    /**
+     * Timed events describe an operation that somehow consumes time.
+     */
+    interface TimedEvent extends InstantEvent {
+        /**
+         * Starts the timing for this event.
+         */
+        void begin();
+
+        /**
+         * Ends the timing period for this event.
+         */
+        void end();
+    }
+
+    /**
+     * Creates a new {@link CompilationEvent}.
+     *
+     * @return a compilation event
+     */
+    CompilationEvent newCompilationEvent();
+
+    /**
+     * A compilation event.
+     */
+    interface CompilationEvent extends TimedEvent {
+        void setMethod(String method);
+
+        void setCompileId(int compileId);
+
+        void setCompileLevel(int compileLevel);
+
+        void setSucceeded(boolean succeeded);
+
+        void setIsOsr(boolean isOsr);
+
+        void setCodeSize(int codeSize);
+
+        void setInlinedBytes(int inlinedBytes);
+    }
+
+    /**
+     * Creates a new {@link CompilerFailureEvent}.
+     *
+     * @return a compiler failure event
+     */
+    CompilerFailureEvent newCompilerFailureEvent();
+
+    /**
+     * A compiler failure event.
+     */
+    interface CompilerFailureEvent extends InstantEvent {
+        void setCompileId(int compileId);
+
+        void setMessage(String message);
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java	Mon May 19 17:21:30 2014 -0700
@@ -230,29 +230,26 @@
             JavaType[] signature = MetaUtil.signatureToTypes(callTarget.targetMethod().getSignature(), callTarget.isStatic() ? null : callTarget.targetMethod().getDeclaringClass());
 
             LoweredCallTargetNode loweredCallTarget = null;
-            if (callTarget.invokeKind() == InvokeKind.Virtual && InlineVTableStubs.getValue() && (AlwaysInlineVTableStubs.getValue() || invoke.isPolymorphic())) {
-
+            boolean isVirtualOrInterface = callTarget.invokeKind() == InvokeKind.Virtual || callTarget.invokeKind() == InvokeKind.Interface;
+            if (InlineVTableStubs.getValue() && isVirtualOrInterface && (AlwaysInlineVTableStubs.getValue() || invoke.isPolymorphic())) {
                 HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) callTarget.targetMethod();
-                if (!hsMethod.getDeclaringClass().isInterface()) {
-                    if (hsMethod.isInVirtualMethodTable()) {
-                        int vtableEntryOffset = hsMethod.vtableEntryOffset();
-                        assert vtableEntryOffset > 0;
-                        Kind wordKind = runtime.getTarget().wordKind;
-                        ValueNode hub = createReadHub(graph, wordKind, receiver, receiverNullCheck);
+                ResolvedJavaType receiverType = invoke.getReceiverType();
+                if (hsMethod.isInVirtualMethodTable(receiverType)) {
+                    Kind wordKind = runtime.getTarget().wordKind;
+                    ValueNode hub = createReadHub(graph, wordKind, receiver, receiverNullCheck);
 
-                        ReadNode metaspaceMethod = createReadVirtualMethod(graph, wordKind, hub, hsMethod);
-                        // We use LocationNode.ANY_LOCATION for the reads that access the
-                        // compiled code entry as HotSpot does not guarantee they are final
-                        // values.
-                        ReadNode compiledEntry = graph.add(new ReadNode(metaspaceMethod, ConstantLocationNode.create(ANY_LOCATION, wordKind, runtime.getConfig().methodCompiledEntryOffset, graph),
-                                        StampFactory.forKind(wordKind), BarrierType.NONE, false));
+                    ReadNode metaspaceMethod = createReadVirtualMethod(graph, wordKind, hub, hsMethod, receiverType);
+                    // We use LocationNode.ANY_LOCATION for the reads that access the
+                    // compiled code entry as HotSpot does not guarantee they are final
+                    // values.
+                    ReadNode compiledEntry = graph.add(new ReadNode(metaspaceMethod, ConstantLocationNode.create(ANY_LOCATION, wordKind, runtime.getConfig().methodCompiledEntryOffset, graph),
+                                    StampFactory.forKind(wordKind), BarrierType.NONE, false));
 
-                        loweredCallTarget = graph.add(new HotSpotIndirectCallTargetNode(metaspaceMethod, compiledEntry, parameters, invoke.asNode().stamp(), signature, callTarget.targetMethod(),
-                                        CallingConvention.Type.JavaCall));
+                    loweredCallTarget = graph.add(new HotSpotIndirectCallTargetNode(metaspaceMethod, compiledEntry, parameters, invoke.asNode().stamp(), signature, callTarget.targetMethod(),
+                                    CallingConvention.Type.JavaCall));
 
-                        graph.addBeforeFixed(invoke.asNode(), metaspaceMethod);
-                        graph.addAfterFixed(metaspaceMethod, compiledEntry);
-                    }
+                    graph.addBeforeFixed(invoke.asNode(), metaspaceMethod);
+                    graph.addAfterFixed(metaspaceMethod, compiledEntry);
                 }
             }
 
@@ -554,8 +551,8 @@
 
     private void lowerLoadMethodNode(LoadMethodNode loadMethodNode) {
         StructuredGraph graph = loadMethodNode.graph();
-        ResolvedJavaMethod method = loadMethodNode.getMethod();
-        ReadNode metaspaceMethod = createReadVirtualMethod(graph, runtime.getTarget().wordKind, loadMethodNode.getHub(), method);
+        HotSpotResolvedJavaMethod method = (HotSpotResolvedJavaMethod) loadMethodNode.getMethod();
+        ReadNode metaspaceMethod = createReadVirtualMethod(graph, runtime.getTarget().wordKind, loadMethodNode.getHub(), method, loadMethodNode.getReceiverType());
         graph.replaceFixed(loadMethodNode, metaspaceMethod);
     }
 
@@ -816,12 +813,11 @@
         return false;
     }
 
-    private static ReadNode createReadVirtualMethod(StructuredGraph graph, Kind wordKind, ValueNode hub, ResolvedJavaMethod method) {
-        HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method;
-        assert !hsMethod.getDeclaringClass().isInterface();
-        assert hsMethod.isInVirtualMethodTable();
+    private static ReadNode createReadVirtualMethod(StructuredGraph graph, Kind wordKind, ValueNode hub, HotSpotResolvedJavaMethod method, ResolvedJavaType receiverType) {
+        return createReadVirtualMethod(graph, wordKind, hub, method.vtableEntryOffset(receiverType));
+    }
 
-        int vtableEntryOffset = hsMethod.vtableEntryOffset();
+    private static ReadNode createReadVirtualMethod(StructuredGraph graph, Kind wordKind, ValueNode hub, int vtableEntryOffset) {
         assert vtableEntryOffset > 0;
         // We use LocationNode.ANY_LOCATION for the reads that access the vtable
         // entry as HotSpot does not guarantee that this is a final value.
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java	Mon May 19 17:21:30 2014 -0700
@@ -416,7 +416,7 @@
         if ((metaspacePointer & config.compilerToVMSymbolTag) != 0) {
             final long metaspaceSymbol = metaspacePointer & ~config.compilerToVMSymbolTag;
             String name = new HotSpotSymbol(metaspaceSymbol).asString();
-            return HotSpotUnresolvedJavaType.create(name);
+            return HotSpotUnresolvedJavaType.create("L" + name + ";");
         } else {
             assert (metaspacePointer & config.compilerToVMKlassTag) == 0;
             return HotSpotResolvedObjectType.fromMetaspaceKlass(metaspacePointer);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Mon May 19 17:21:30 2014 -0700
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.hotspot.meta;
 
+import static com.oracle.graal.compiler.common.GraalInternalError.*;
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static com.oracle.graal.compiler.common.UnsafeAccess.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
@@ -33,7 +34,6 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.meta.ProfilingInfo.TriState;
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.debug.*;
@@ -589,22 +589,34 @@
 
     /**
      * Returns the offset of this method into the v-table. The method must have a v-table entry as
-     * indicated by {@link #isInVirtualMethodTable()}, otherwise an exception is thrown.
+     * indicated by {@link #isInVirtualMethodTable(ResolvedJavaType)}, otherwise an exception is
+     * thrown.
      *
      * @return the offset of this method into the v-table
      */
-    public int vtableEntryOffset() {
-        if (!isInVirtualMethodTable() || !holder.isInitialized()) {
-            throw new GraalInternalError("%s does not have a vtable entry", this);
-        }
+    public int vtableEntryOffset(ResolvedJavaType resolved) {
+        guarantee(isInVirtualMethodTable(resolved), "%s does not have a vtable entry", this);
         HotSpotVMConfig config = runtime().getConfig();
-        final int vtableIndex = getVtableIndex();
+        final int vtableIndex = getVtableIndex(resolved);
         return config.instanceKlassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset;
     }
 
     @Override
-    public boolean isInVirtualMethodTable() {
-        return getVtableIndex() >= 0;
+    public boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
+        return getVtableIndex(resolved) >= 0;
+    }
+
+    private int getVtableIndex(ResolvedJavaType resolved) {
+        if (!holder.isLinked()) {
+            return runtime().getConfig().invalidVtableIndex;
+        }
+        if (holder.isInterface()) {
+            if (resolved.isArray() || resolved.isInterface()) {
+                return runtime().getConfig().invalidVtableIndex;
+            }
+            return getVtableIndexForInterface(resolved);
+        }
+        return getVtableIndex();
     }
 
     /**
@@ -620,6 +632,11 @@
         return result;
     }
 
+    private int getVtableIndexForInterface(ResolvedJavaType resolved) {
+        HotSpotResolvedObjectType hotspotType = (HotSpotResolvedObjectType) resolved;
+        return runtime().getCompilerToVM().getVtableIndexForInterface(hotspotType.getMetaspaceKlass(), getMetaspaceMethod());
+    }
+
     /**
      * The {@link SpeculationLog} for methods compiled by Graal hang off this per-declaring-type
      * {@link ClassValue}. The raw Method* value is safe to use as a key in the map as a) it is
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Mon May 19 17:21:30 2014 -0700
@@ -25,6 +25,7 @@
 import static com.oracle.graal.api.meta.MetaUtil.*;
 import static com.oracle.graal.compiler.common.UnsafeAccess.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+
 import java.lang.annotation.*;
 import java.lang.reflect.*;
 import java.net.*;
@@ -107,7 +108,7 @@
     /**
      * Gets the metaspace Klass for this type.
      */
-    private long metaspaceKlass() {
+    public long getMetaspaceKlass() {
         return HotSpotGraalRuntime.unsafeReadWord(javaClass, runtime().getConfig().klassOffset);
     }
 
@@ -118,7 +119,7 @@
 
     public int getAccessFlags() {
         HotSpotVMConfig config = runtime().getConfig();
-        return unsafe.getInt(metaspaceKlass() + config.klassAccessFlagsOffset);
+        return unsafe.getInt(getMetaspaceKlass() + config.klassAccessFlagsOffset);
     }
 
     @Override
@@ -141,7 +142,7 @@
         if (isArray()) {
             return getElementalType(this).isFinal() ? this : null;
         } else if (isInterface()) {
-            final long implementorMetaspaceKlass = runtime().getCompilerToVM().getKlassImplementor(metaspaceKlass());
+            final long implementorMetaspaceKlass = runtime().getCompilerToVM().getKlassImplementor(getMetaspaceKlass());
 
             // No implementor.
             if (implementorMetaspaceKlass == 0) {
@@ -192,7 +193,7 @@
      * @return value of the subklass field as metaspace klass pointer
      */
     private long getSubklass() {
-        return unsafeReadWord(metaspaceKlass() + runtime().getConfig().subklassOffset);
+        return unsafeReadWord(getMetaspaceKlass() + runtime().getConfig().subklassOffset);
     }
 
     @Override
@@ -271,7 +272,7 @@
     @Override
     public boolean hasFinalizableSubclass() {
         assert !isArray();
-        return runtime().getCompilerToVM().hasFinalizableSubclass(metaspaceKlass());
+        return runtime().getCompilerToVM().hasFinalizableSubclass(getMetaspaceKlass());
     }
 
     @Override
@@ -309,7 +310,7 @@
      * @return state field value of this type
      */
     private int getState() {
-        return unsafe.getByte(metaspaceKlass() + runtime().getConfig().klassStateOffset) & 0xFF;
+        return unsafe.getByte(getMetaspaceKlass() + runtime().getConfig().klassStateOffset) & 0xFF;
     }
 
     @Override
@@ -354,13 +355,17 @@
     }
 
     @Override
-    public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method) {
-        assert method instanceof HotSpotMethod;
-        if (!method.isAbstract() && method.getDeclaringClass().equals(this)) {
+    public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
+        assert !callerType.isArray();
+        if (!method.isAbstract() && method.getDeclaringClass().equals(this) && method.isPublic()) {
             return method;
         }
-
-        final long resolvedMetaspaceMethod = runtime().getCompilerToVM().resolveMethod(metaspaceKlass(), method.getName(), ((HotSpotSignature) method.getSignature()).getMethodDescriptor());
+        if (!method.getDeclaringClass().isAssignableFrom(this)) {
+            return null;
+        }
+        HotSpotResolvedJavaMethod hotSpotMethod = (HotSpotResolvedJavaMethod) method;
+        HotSpotResolvedObjectType hotSpotCallerType = (HotSpotResolvedObjectType) callerType;
+        final long resolvedMetaspaceMethod = runtime().getCompilerToVM().resolveMethod(getMetaspaceKlass(), hotSpotMethod.getMetaspaceMethod(), hotSpotCallerType.getMetaspaceKlass());
         if (resolvedMetaspaceMethod == 0) {
             return null;
         }
@@ -373,7 +378,7 @@
 
     public ConstantPool constantPool() {
         if (constantPool == null) {
-            final long metaspaceConstantPool = unsafe.getAddress(metaspaceKlass() + runtime().getConfig().instanceKlassConstantsOffset);
+            final long metaspaceConstantPool = unsafe.getAddress(getMetaspaceKlass() + runtime().getConfig().instanceKlassConstantsOffset);
             constantPool = new HotSpotConstantPool(metaspaceConstantPool);
         }
         return constantPool;
@@ -389,7 +394,7 @@
         assert !isInterface();
 
         HotSpotVMConfig config = runtime().getConfig();
-        final int layoutHelper = unsafe.getInt(metaspaceKlass() + config.klassLayoutHelperOffset);
+        final int layoutHelper = unsafe.getInt(getMetaspaceKlass() + config.klassLayoutHelperOffset);
         assert layoutHelper > config.klassLayoutHelperNeutralValue : "must be instance";
 
         // See: Klass::layout_helper_size_in_bytes
@@ -475,7 +480,7 @@
         public FieldInfo(int index) {
             HotSpotVMConfig config = runtime().getConfig();
             // Get Klass::_fields
-            final long metaspaceFields = unsafe.getAddress(metaspaceKlass() + config.instanceKlassFieldsOffset);
+            final long metaspaceFields = unsafe.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
             assert config.fieldInfoFieldSlots == 6 : "revisit the field parsing code";
             metaspaceData = metaspaceFields + config.arrayU2DataOffset + config.fieldInfoFieldSlots * Short.BYTES * index;
         }
@@ -611,7 +616,7 @@
      */
     private int getFieldCount() {
         HotSpotVMConfig config = runtime().getConfig();
-        final long metaspaceFields = unsafe.getAddress(metaspaceKlass() + config.instanceKlassFieldsOffset);
+        final long metaspaceFields = unsafe.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
         int metaspaceFieldsLength = unsafe.getInt(metaspaceFields + config.arrayU1LengthOffset);
         int fieldCount = 0;
 
@@ -633,7 +638,7 @@
     @Override
     public String getSourceFileName() {
         HotSpotVMConfig config = runtime().getConfig();
-        final int sourceFileNameIndex = unsafe.getChar(metaspaceKlass() + config.klassSourceFileNameIndexOffset);
+        final int sourceFileNameIndex = unsafe.getChar(getMetaspaceKlass() + config.klassSourceFileNameIndexOffset);
         if (sourceFileNameIndex == 0) {
             return null;
         }
@@ -654,7 +659,7 @@
      * Gets the metaspace Klass boxed in a {@link Constant}.
      */
     public Constant klass() {
-        return HotSpotMetaspaceConstant.forMetaspaceObject(runtime().getTarget().wordKind, metaspaceKlass(), this);
+        return HotSpotMetaspaceConstant.forMetaspaceObject(runtime().getTarget().wordKind, getMetaspaceKlass(), this);
     }
 
     public boolean isPrimaryType() {
@@ -663,7 +668,7 @@
 
     public int superCheckOffset() {
         HotSpotVMConfig config = runtime().getConfig();
-        return unsafe.getInt(metaspaceKlass() + config.superCheckOffsetOffset);
+        return unsafe.getInt(getMetaspaceKlass() + config.superCheckOffsetOffset);
     }
 
     public long prototypeMarkWord() {
@@ -671,7 +676,7 @@
         if (isArray()) {
             return config.arrayPrototypeMarkWord();
         } else {
-            return unsafeReadWord(metaspaceKlass() + config.prototypeMarkWordOffset);
+            return unsafeReadWord(getMetaspaceKlass() + config.prototypeMarkWordOffset);
         }
     }
 
@@ -731,7 +736,7 @@
     }
 
     public ResolvedJavaMethod getClassInitializer() {
-        final long metaspaceMethod = runtime().getCompilerToVM().getClassInitializer(metaspaceKlass());
+        final long metaspaceMethod = runtime().getCompilerToVM().getClassInitializer(getMetaspaceKlass());
         if (metaspaceMethod != 0L) {
             return createMethod(metaspaceMethod);
         }
@@ -745,12 +750,6 @@
 
     @Override
     public String toString() {
-        String simpleName;
-        if (isArray() || isInterface()) {
-            simpleName = getName();
-        } else {
-            simpleName = getName().substring(1, getName().length() - 1);
-        }
-        return "HotSpotType<" + simpleName + ", resolved>";
+        return "HotSpotType<" + getName() + ", resolved>";
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedPrimitiveType.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedPrimitiveType.java	Mon May 19 17:21:30 2014 -0700
@@ -163,7 +163,7 @@
     }
 
     @Override
-    public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method) {
+    public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
         return null;
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java	Mon May 19 17:21:30 2014 -0700
@@ -32,56 +32,28 @@
 public class HotSpotUnresolvedJavaType extends HotSpotJavaType {
 
     private static final long serialVersionUID = -2320936267633521314L;
-    public final String simpleName;
-    public final int dimensions;
 
-    public HotSpotUnresolvedJavaType(String name, String simpleName, int dimensions) {
+    public HotSpotUnresolvedJavaType(String name) {
         super(name);
-        assert dimensions >= 0;
-        this.simpleName = simpleName;
-        this.dimensions = dimensions;
     }
 
     /**
      * Creates an unresolved type for a valid {@link JavaType#getName() type name}.
      */
     public static HotSpotUnresolvedJavaType create(String name) {
-        int dims = 0;
-        int startIndex = 0;
-        while (name.charAt(startIndex) == '[') {
-            startIndex++;
-            dims++;
-        }
-
-        // Decode name if necessary.
-        if (name.charAt(name.length() - 1) == ';') {
-            assert name.charAt(startIndex) == 'L';
-            return new HotSpotUnresolvedJavaType(name, name.substring(startIndex + 1, name.length() - 1), dims);
-        } else {
-            return new HotSpotUnresolvedJavaType(HotSpotUnresolvedJavaType.getFullName(name, dims), name, dims);
-        }
-    }
-
-    public static String getFullName(String name, int dimensions) {
-        StringBuilder str = new StringBuilder(name.length() + dimensions + 2);
-        for (int i = 0; i < dimensions; i++) {
-            str.append('[');
-        }
-        str.append('L').append(name).append(';');
-        return str.toString();
+        assert name.charAt(name.length() - 1) == ';' : name;
+        return new HotSpotUnresolvedJavaType(name);
     }
 
     @Override
     public JavaType getComponentType() {
-        assert dimensions > 0 : "no array class" + getName();
-        String name = getFullName(getName(), dimensions - 1);
-        return new HotSpotUnresolvedJavaType(name, simpleName, dimensions - 1);
+        assert getName().charAt(0) == '[' : "no array class" + getName();
+        return new HotSpotUnresolvedJavaType(getName().substring(1));
     }
 
     @Override
     public JavaType getArrayClass() {
-        String name = getFullName(getName(), dimensions + 1);
-        return new HotSpotUnresolvedJavaType(name, simpleName, dimensions + 1);
+        return new HotSpotUnresolvedJavaType('[' + getName());
     }
 
     @Override
@@ -91,7 +63,7 @@
 
     @Override
     public int hashCode() {
-        return simpleName.hashCode();
+        return getName().hashCode();
     }
 
     @Override
@@ -103,12 +75,12 @@
             return false;
         }
         HotSpotUnresolvedJavaType that = (HotSpotUnresolvedJavaType) obj;
-        return this.simpleName.equals(that.simpleName) && this.dimensions == that.dimensions;
+        return this.getName().equals(that.getName());
     }
 
     @Override
     public String toString() {
-        return "HotSpotType<" + simpleName + ", unresolved>";
+        return "HotSpotType<" + getName() + ", unresolved>";
     }
 
     @Override
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Mon May 19 17:21:30 2014 -0700
@@ -179,15 +179,11 @@
     @Snippet
     public static Object allocateArray(Word hub, int length, Word prototypeMarkWord, @ConstantParameter int headerSize, @ConstantParameter int log2ElementSize,
                     @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister, @ConstantParameter boolean maybeUnroll, @ConstantParameter String typeContext) {
-        if (!belowThan(length, MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH)) {
-            // This handles both negative array sizes and very large array sizes
-            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
-        }
-        return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, maybeUnroll, typeContext);
+        return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, maybeUnroll, typeContext, false);
     }
 
     private static Object allocateArrayImpl(Word hub, int length, Word prototypeMarkWord, int headerSize, int log2ElementSize, boolean fillContents, @ConstantParameter Register threadRegister,
-                    @ConstantParameter boolean maybeUnroll, String typeContext) {
+                    @ConstantParameter boolean maybeUnroll, String typeContext, boolean skipNegativeCheck) {
         Object result;
         int alignment = wordSize();
         int allocationSize = computeArrayAllocationSize(length, alignment, headerSize, log2ElementSize);
@@ -195,7 +191,7 @@
         Word top = readTlabTop(thread);
         Word end = readTlabEnd(thread);
         Word newTop = top.add(allocationSize);
-        if (useTLAB() && probability(FAST_PATH_PROBABILITY, newTop.belowOrEqual(end))) {
+        if ((skipNegativeCheck || belowThan(length, MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH)) && useTLAB() && probability(FAST_PATH_PROBABILITY, newTop.belowOrEqual(end))) {
             writeTlabTop(thread, newTop);
             emitPrefetchAllocate(newTop, true);
             newarray_loopInit.inc();
@@ -246,7 +242,7 @@
         int log2ElementSize = (layoutHelper >> layoutHelperLog2ElementSizeShift()) & layoutHelperLog2ElementSizeMask();
         Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
 
-        return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, false, "dynamic type");
+        return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, false, "dynamic type", true);
     }
 
     /**
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java	Mon May 19 17:21:30 2014 -0700
@@ -97,7 +97,7 @@
 
         // check that array length is small enough for fast path.
         Word thread = registerAsWord(threadRegister);
-        if (length <= MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH) {
+        if (length >= 0 && length <= MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH) {
             Word memory = refillAllocate(thread, intArrayHub, sizeInBytes, logging());
             if (memory.notEqual(0)) {
                 if (logging()) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java	Mon May 19 17:21:30 2014 -0700
@@ -22,7 +22,6 @@
  */
 package com.oracle.graal.hotspot.stubs;
 
-import static com.oracle.graal.api.meta.DeoptimizationAction.*;
 import static com.oracle.graal.api.meta.DeoptimizationReason.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
 import static com.oracle.graal.hotspot.nodes.CStringNode.*;
@@ -78,7 +77,7 @@
             if (isObjectResult) {
                 getAndClearObjectResult(thread);
             }
-            DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
+            DeoptimizeCallerNode.deopt(DeoptimizationAction.None, RuntimeConstraint);
         }
     }
 
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Mon May 19 17:21:30 2014 -0700
@@ -39,8 +39,8 @@
 
     // @formatter:off
 
-    IADD, ISUB, IMUL, IDIV, IDIVREM, IREM, IUDIV, IUREM, IAND, IOR, IXOR, ISHL, ISHR, IUSHR, IROL, IROR,
-    LADD, LSUB, LMUL, LDIV, LDIVREM, LREM, LUDIV, LUREM, LAND, LOR, LXOR, LSHL, LSHR, LUSHR, LROL, LROR,
+    IADD, ISUB, IMUL, IUMUL, IDIV, IDIVREM, IREM, IUDIV, IUREM, IAND, IOR, IXOR, ISHL, ISHR, IUSHR, IROL, IROR,
+    LADD, LSUB, LMUL, LUMUL, LDIV, LDIVREM, LREM, LUDIV, LUREM, LAND, LOR, LXOR, LSHL, LSHR, LUSHR, LROL, LROR,
     FADD, FSUB, FMUL, FDIV, FREM, FAND, FOR, FXOR,
     DADD, DSUB, DMUL, DDIV, DREM, DAND, DOR, DXOR,
     INEG, LNEG, INOT, LNOT,
@@ -342,6 +342,64 @@
         }
     }
 
+    public static class MulHighOp extends AMD64LIRInstruction {
+
+        @Opcode private final AMD64Arithmetic opcode;
+        @Def({REG}) public AllocatableValue lowResult;
+        @Def({REG}) public AllocatableValue highResult;
+        @Use({REG}) public AllocatableValue x;
+        @Use({REG, STACK}) public AllocatableValue y;
+
+        public MulHighOp(AMD64Arithmetic opcode, AllocatableValue y) {
+            PlatformKind kind = y.getPlatformKind();
+
+            this.opcode = opcode;
+            this.x = AMD64.rax.asValue(kind);
+            this.y = y;
+            this.lowResult = AMD64.rax.asValue(kind);
+            this.highResult = AMD64.rdx.asValue(kind);
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
+            if (isRegister(y)) {
+                switch (opcode) {
+                    case IMUL:
+                        masm.imull(asRegister(y));
+                        break;
+                    case IUMUL:
+                        masm.mull(asRegister(y));
+                        break;
+                    case LMUL:
+                        masm.imulq(asRegister(y));
+                        break;
+                    case LUMUL:
+                        masm.mulq(asRegister(y));
+                        break;
+                    default:
+                        throw GraalInternalError.shouldNotReachHere();
+                }
+            } else {
+                switch (opcode) {
+                    case IMUL:
+                        masm.imull((AMD64Address) crb.asAddress(y));
+                        break;
+                    case IUMUL:
+                        masm.mull((AMD64Address) crb.asAddress(y));
+                        break;
+                    case LMUL:
+                        masm.imulq((AMD64Address) crb.asAddress(y));
+                        break;
+                    case LUMUL:
+                        masm.mulq((AMD64Address) crb.asAddress(y));
+                        break;
+                    default:
+                        throw GraalInternalError.shouldNotReachHere();
+                }
+            }
+        }
+    }
+
     public static class DivRemOp extends AMD64LIRInstruction {
 
         @Opcode private final AMD64Arithmetic opcode;
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java	Mon May 19 17:21:30 2014 -0700
@@ -142,6 +142,7 @@
         protected void verify() {
             super.verify();
             assert y instanceof Variable || y instanceof Constant;
+            assert kind != Kind.Long || !(y instanceof Constant) || NumUtil.isInt(((Constant) y).asLong());
         }
     }
 
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Mon May 19 17:21:30 2014 -0700
@@ -84,11 +84,6 @@
         /**
          * Generates the code for this switch op.
          *
-         * The keys for switch statements in Java bytecode for of type int. However, Graal also
-         * generates a TypeSwitchNode (for method dispatch) which triggers the invocation of these
-         * routines with keys of type Long or Object. Currently we only support the
-         * IntegerSwitchNode so we throw an exception if the key isn't of type int.
-         *
          * @param crb the CompilationResultBuilder
          * @param masm the HSAIL assembler
          */
@@ -100,13 +95,13 @@
                     switch (key.getKind()) {
                         case Int:
                         case Long:
+                        case Object:
                             // Generate cascading compare and branches for each case.
                             masm.emitCompare(key.getKind(), key, keyConstants[index], HSAILCompare.conditionToString(condition), false, false);
                             masm.cbr(masm.nameOf(target));
                             break;
-                        case Object:
                         default:
-                            throw new GraalInternalError("switch only supported for int");
+                            throw new GraalInternalError("switch not supported for kind " + key.getKind());
                     }
                 }
             };
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/ArithmeticLIRGenerator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/ArithmeticLIRGenerator.java	Mon May 19 17:21:30 2014 -0700
@@ -42,6 +42,10 @@
 
     Value emitMul(Value a, Value b);
 
+    Value emitMulHigh(Value a, Value b);
+
+    Value emitUMulHigh(Value a, Value b);
+
     Value emitDiv(Value a, Value b, LIRFrameState state);
 
     Value emitRem(Value a, Value b, LIRFrameState state);
--- a/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampJoinTest.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampJoinTest.java	Mon May 19 17:21:30 2014 -0700
@@ -26,34 +26,16 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.nodes.type.*;
 
-public class ObjectStampJoinTest extends GraalCompilerTest {
-
-    private static class A {
-
-    }
-
-    private static class B extends A {
-
-    }
-
-    private static class C extends B implements I {
+public class ObjectStampJoinTest extends ObjectStampTest {
 
-    }
-
-    private static class D extends A {
-
-    }
-
-    private abstract static class E extends A {
-
-    }
-
-    private interface I {
-
-    }
+    // class A
+    // class B extends A
+    // class C extends B implements I
+    // class D extends A
+    // abstract class E extends A
+    // interface I
 
     @Test
     public void testJoin0() {
@@ -142,8 +124,8 @@
     @Test
     public void testJoinInterface0() {
         Stamp a = StampFactory.declared(getType(A.class));
-        Stamp b = StampFactory.declared(getType(I.class));
-        Assert.assertNotSame(StampFactory.illegal(Kind.Object), join(a, b));
+        Stamp i = StampFactory.declared(getType(I.class));
+        Assert.assertNotSame(StampFactory.illegal(Kind.Object), join(a, i));
     }
 
     @Test
@@ -163,14 +145,4 @@
         Assert.assertEquals(StampFactory.illegal(Kind.Object), join);
     }
 
-    private static Stamp join(Stamp a, Stamp b) {
-        Stamp ab = a.join(b);
-        Stamp ba = b.join(a);
-        Assert.assertEquals(ab, ba);
-        return ab;
-    }
-
-    private ResolvedJavaType getType(Class<?> clazz) {
-        return getMetaAccess().lookupJavaType(clazz);
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampMeetTest.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013, 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.nodes.test;
+
+import org.junit.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
+
+public class ObjectStampMeetTest extends ObjectStampTest {
+
+    // class A
+    // class B extends A
+    // class C extends B implements I
+    // class D extends A
+    // abstract class E extends A
+    // interface I
+
+    @Test
+    public void testMeet0() {
+        Stamp a = StampFactory.declared(getType(A.class));
+        Stamp b = StampFactory.declared(getType(B.class));
+        Assert.assertEquals(a, meet(a, b));
+    }
+
+    @Test
+    public void testMeet1() {
+        Stamp a = StampFactory.declared(getType(A.class));
+        Stamp aNonNull = StampFactory.declaredNonNull(getType(A.class));
+        Stamp b = StampFactory.declared(getType(B.class));
+        Stamp bNonNull = StampFactory.declaredNonNull(getType(B.class));
+        Assert.assertEquals(a, meet(aNonNull, b));
+        Assert.assertEquals(aNonNull, meet(aNonNull, bNonNull));
+    }
+
+    @Test
+    public void testMeet2() {
+        Stamp a = StampFactory.declared(getType(A.class));
+        Stamp aExact = StampFactory.exactNonNull(getType(A.class));
+        Stamp b = StampFactory.declared(getType(B.class));
+        Assert.assertEquals(a, meet(aExact, b));
+    }
+
+    @Test
+    public void testMeet3() {
+        Stamp a = StampFactory.declared(getType(A.class));
+        Stamp d = StampFactory.declared(getType(D.class));
+        Stamp c = StampFactory.declared(getType(C.class));
+        Assert.assertEquals(a, meet(c, d));
+    }
+
+    @Test
+    public void testMeet4() {
+        Stamp dExactNonNull = StampFactory.exactNonNull(getType(D.class));
+        Stamp cExactNonNull = StampFactory.exactNonNull(getType(C.class));
+        Stamp aNonNull = StampFactory.declaredNonNull(getType(A.class));
+        Assert.assertEquals(aNonNull, meet(cExactNonNull, dExactNonNull));
+    }
+
+    @Test
+    public void testMeet() {
+        Stamp dExact = StampFactory.exact(getType(D.class));
+        Stamp c = StampFactory.declared(getType(C.class));
+        Stamp a = StampFactory.declared(getType(A.class));
+        Assert.assertEquals(a, meet(dExact, c));
+    }
+
+    @Test
+    public void testMeet6() {
+        Stamp dExactNonNull = StampFactory.exactNonNull(getType(D.class));
+        Stamp alwaysNull = StampFactory.alwaysNull();
+        Stamp dExact = StampFactory.exact(getType(D.class));
+        Assert.assertEquals(dExact, meet(dExactNonNull, alwaysNull));
+    }
+
+    @Test
+    public void testMeet7() {
+        Stamp aExact = StampFactory.exact(getType(A.class));
+        Stamp e = StampFactory.declared(getType(E.class));
+        Stamp a = StampFactory.declared(getType(A.class));
+        Assert.assertEquals(a, meet(aExact, e));
+    }
+
+    @Test
+    public void testMeetInterface0() {
+        Stamp a = StampFactory.declared(getType(A.class));
+        Stamp i = StampFactory.declared(getType(I.class));
+        Assert.assertNotSame(StampFactory.object(), meet(a, i));
+    }
+
+    @Test
+    public void testMeetIllegal1() {
+        for (Class<?> clazz : new Class<?>[]{A.class, B.class, C.class, D.class, E.class, I.class, Object.class}) {
+            ResolvedJavaType type = getType(clazz);
+            for (Stamp test : new Stamp[]{StampFactory.declared(type), StampFactory.declaredNonNull(type), StampFactory.exact(type), StampFactory.exactNonNull(type)}) {
+                if (!type.isAbstract() || !((ObjectStamp) test).isExactType()) {
+                    Assert.assertEquals("meeting illegal and " + test, test, meet(StampFactory.illegal(Kind.Object), test));
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampTest.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 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.nodes.test;
+
+import org.junit.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.compiler.test.*;
+
+public class ObjectStampTest extends GraalCompilerTest {
+
+    protected static class A {
+
+    }
+
+    protected static class B extends A {
+
+    }
+
+    protected static class C extends B implements I {
+
+    }
+
+    protected static class D extends A {
+
+    }
+
+    protected abstract static class E extends A {
+
+    }
+
+    protected interface I {
+
+    }
+
+    protected static Stamp join(Stamp a, Stamp b) {
+        Stamp ab = a.join(b);
+        Stamp ba = b.join(a);
+        Assert.assertEquals(ab, ba);
+        return ab;
+    }
+
+    protected static Stamp meet(Stamp a, Stamp b) {
+        Stamp ab = a.meet(b);
+        Stamp ba = b.meet(a);
+        Assert.assertEquals(ab, ba);
+        return ab;
+    }
+
+    protected ResolvedJavaType getType(Class<?> clazz) {
+        return getMetaAccess().lookupJavaType(clazz);
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java	Mon May 19 17:21:30 2014 -0700
@@ -38,10 +38,12 @@
 public class GuardedValueNode extends FloatingGuardedNode implements LIRLowerable, Virtualizable, IterableNodeType, Canonicalizable, ValueProxy {
 
     @Input private ValueNode object;
+    private final Stamp piStamp;
 
     public GuardedValueNode(ValueNode object, GuardingNode guard, Stamp stamp) {
         super(stamp, guard);
         this.object = object;
+        this.piStamp = stamp;
     }
 
     public GuardedValueNode(ValueNode object, GuardingNode guard) {
@@ -61,10 +63,10 @@
 
     @Override
     public boolean inferStamp() {
-        if (stamp() instanceof ObjectStamp && object().stamp() instanceof ObjectStamp) {
-            return updateStamp(((ObjectStamp) object().stamp()).castTo((ObjectStamp) stamp()));
+        if (piStamp instanceof ObjectStamp && object().stamp() instanceof ObjectStamp) {
+            return updateStamp(((ObjectStamp) object().stamp()).castTo((ObjectStamp) piStamp));
         }
-        return updateStamp(object().stamp().join(stamp()));
+        return updateStamp(object().stamp().join(piStamp));
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java	Mon May 19 17:21:30 2014 -0700
@@ -41,6 +41,7 @@
     @Input(InputType.Condition) private LogicNode condition;
     private final DeoptimizationReason reason;
     private final DeoptimizationAction action;
+    private final Stamp piStamp;
     private boolean negated;
 
     public ValueNode object() {
@@ -81,6 +82,7 @@
     public GuardingPiNode(ValueNode object, ValueNode condition, boolean negateCondition, DeoptimizationReason reason, DeoptimizationAction action, Stamp stamp) {
         super(stamp);
         assert stamp != null;
+        this.piStamp = stamp;
         this.object = object;
         this.condition = (LogicNode) condition;
         this.reason = reason;
@@ -107,7 +109,7 @@
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(stamp().join(object().stamp()));
+        return updateStamp(piStamp.join(object().stamp()));
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Mon May 19 17:21:30 2014 -0700
@@ -187,6 +187,10 @@
             } while (false);
         }
 
+        if (checkForUnsignedCompare(tool)) {
+            return;
+        }
+
         if (condition() instanceof LogicConstantNode) {
             LogicConstantNode c = (LogicConstantNode) condition();
             if (c.getValue()) {
@@ -219,7 +223,7 @@
                 // Reordering of those two if statements is beneficial from the point of view of
                 // their probabilities.
                 if (prepareForSwap(tool.getConstantReflection(), condition(), nextIf.condition(), this.trueSuccessorProbability, probabilityB)) {
-                    // Reording is allowed from (if1 => begin => if2) to (if2 => begin => if1).
+                    // Reordering is allowed from (if1 => begin => if2) to (if2 => begin => if1).
                     assert intermediateBegin.next() == nextIf;
                     BeginNode bothFalseBegin = nextIf.falseSuccessor();
                     nextIf.setFalseSuccessor(null);
@@ -243,6 +247,106 @@
         }
     }
 
+    /**
+     * Recognize a couple patterns that can be merged into an unsigned compare.
+     *
+     * @param tool
+     * @return true if a replacement was done.
+     */
+    private boolean checkForUnsignedCompare(SimplifierTool tool) {
+        if (condition() instanceof IntegerLessThanNode && trueSuccessor().usages().isEmpty() && falseSuccessor().usages().isEmpty()) {
+            IntegerLessThanNode lessThan = (IntegerLessThanNode) condition();
+            Constant y = lessThan.y().stamp().asConstant();
+            if (y != null && y.asLong() == 0 && falseSuccessor().next() instanceof IfNode) {
+                IfNode ifNode2 = (IfNode) falseSuccessor().next();
+                if (ifNode2.condition() instanceof IntegerLessThanNode) {
+                    IntegerLessThanNode lessThan2 = (IntegerLessThanNode) ifNode2.condition();
+                    BeginNode falseSucc = ifNode2.falseSuccessor();
+                    BeginNode trueSucc = ifNode2.trueSuccessor();
+                    IntegerBelowThanNode below = null;
+                    /*
+                     * Convert x >= 0 && x < positive which is represented as !(x < 0) && x <
+                     * <positive> into an unsigned compare.
+                     */
+                    if (lessThan2.x() == lessThan.x() && lessThan2.y().stamp() instanceof IntegerStamp && ((IntegerStamp) lessThan2.y().stamp()).isPositive() &&
+                                    sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) {
+                        below = graph().unique(new IntegerBelowThanNode(lessThan2.x(), lessThan2.y()));
+                        // swap direction
+                        BeginNode tmp = falseSucc;
+                        falseSucc = trueSucc;
+                        trueSucc = tmp;
+                    } else if (lessThan2.y() == lessThan.x() && sameDestination(trueSuccessor(), ifNode2.trueSuccessor)) {
+                        /*
+                         * Convert x >= 0 && x <= positive which is represented as !(x < 0) &&
+                         * !(<positive> > x), into x <| positive + 1. This can only be done for
+                         * constants since there isn't a IntegerBelowEqualThanNode but that doesn't
+                         * appear to be interesting.
+                         */
+                        Constant positive = lessThan2.x().asConstant();
+                        if (positive != null && positive.asLong() > 0 && positive.asLong() < positive.getKind().getMaxValue()) {
+                            ConstantNode newLimit = ConstantNode.forIntegerKind(positive.getKind(), positive.asLong() + 1, graph());
+                            below = graph().unique(new IntegerBelowThanNode(lessThan.x(), newLimit));
+                        }
+                    }
+                    if (below != null) {
+                        ifNode2.setTrueSuccessor(null);
+                        ifNode2.setFalseSuccessor(null);
+
+                        IfNode newIfNode = graph().add(new IfNode(below, falseSucc, trueSucc, 1 - trueSuccessorProbability));
+                        // Remove the < 0 test.
+                        tool.deleteBranch(trueSuccessor);
+                        graph().removeSplit(this, falseSuccessor);
+
+                        // Replace the second test with the new one.
+                        ifNode2.predecessor().replaceFirstSuccessor(ifNode2, newIfNode);
+                        ifNode2.safeDelete();
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check it these two blocks end up at the same place. Meeting at the same merge, or
+     * deoptimizing in the same way.
+     */
+    private static boolean sameDestination(BeginNode succ1, BeginNode succ2) {
+        Node next1 = succ1.next();
+        Node next2 = succ2.next();
+        if (next1 instanceof EndNode && next2 instanceof EndNode) {
+            EndNode end1 = (EndNode) next1;
+            EndNode end2 = (EndNode) next2;
+            if (end1.merge() == end2.merge()) {
+                // They go to the same MergeNode
+                return true;
+            }
+        } else if (next1 instanceof DeoptimizeNode && next2 instanceof DeoptimizeNode) {
+            DeoptimizeNode deopt1 = (DeoptimizeNode) next1;
+            DeoptimizeNode deopt2 = (DeoptimizeNode) next2;
+            if (deopt1.reason() == deopt2.reason() && deopt1.action() == deopt2.action()) {
+                // Same deoptimization reason and action.
+                return true;
+            }
+        } else if (next1 instanceof LoopExitNode && next2 instanceof LoopExitNode) {
+            LoopExitNode exit1 = (LoopExitNode) next1;
+            LoopExitNode exit2 = (LoopExitNode) next2;
+            if (exit1.loopBegin() == exit2.loopBegin() && exit1.stateAfter() == exit2.stateAfter() && exit1.stateAfter() == null && sameDestination(exit1, exit2)) {
+                // Exit the same loop and end up at the same place.
+                return true;
+            }
+        } else if (next1 instanceof ReturnNode && next2 instanceof ReturnNode) {
+            ReturnNode exit1 = (ReturnNode) next1;
+            ReturnNode exit2 = (ReturnNode) next2;
+            if (exit1.result() == exit2.result()) {
+                // Exit the same loop and end up at the same place.
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static boolean prepareForSwap(ConstantReflectionProvider constantReflection, LogicNode a, LogicNode b, double probabilityA, double probabilityB) {
         if (a instanceof InstanceOfNode) {
             InstanceOfNode instanceOfA = (InstanceOfNode) a;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java	Mon May 19 17:21:30 2014 -0700
@@ -25,7 +25,9 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
 
 public interface Invoke extends StateSplit, Lowerable, DeoptimizingNode.DeoptDuring, GuardedNode {
 
@@ -85,4 +87,16 @@
         newStateDuring.setDuringCall(true);
         setStateDuring(newStateDuring);
     }
+
+    default ValueNode getReceiver() {
+        return callTarget().arguments().get(0);
+    }
+
+    default ResolvedJavaType getReceiverType() {
+        ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver());
+        if (receiverType == null) {
+            receiverType = ((MethodCallTargetNode) callTarget()).targetMethod().getDeclaringClass();
+        }
+        return receiverType;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Mon May 19 17:21:30 2014 -0700
@@ -43,6 +43,7 @@
 public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtualizable, IterableNodeType, Canonicalizable, ValueProxy {
 
     @Input private ValueNode object;
+    private final Stamp piStamp;
 
     public ValueNode object() {
         return object;
@@ -50,12 +51,14 @@
 
     public PiNode(ValueNode object, Stamp stamp) {
         super(stamp);
+        this.piStamp = stamp;
         this.object = object;
     }
 
     public PiNode(ValueNode object, Stamp stamp, ValueNode anchor) {
         super(stamp, (GuardingNode) anchor);
         this.object = object;
+        this.piStamp = stamp;
     }
 
     public PiNode(ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) {
@@ -71,13 +74,13 @@
 
     @Override
     public boolean inferStamp() {
-        if (stamp() == StampFactory.forNodeIntrinsic()) {
+        if (piStamp == StampFactory.forNodeIntrinsic()) {
             return false;
         }
-        if (stamp() instanceof ObjectStamp && object.stamp() instanceof ObjectStamp) {
-            return updateStamp(((ObjectStamp) object.stamp()).castTo((ObjectStamp) stamp()));
+        if (piStamp instanceof ObjectStamp && object.stamp() instanceof ObjectStamp) {
+            return updateStamp(((ObjectStamp) object.stamp()).castTo((ObjectStamp) piStamp));
         }
-        return updateStamp(stamp().join(object().stamp()));
+        return updateStamp(piStamp.join(object().stamp()));
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValuePhiNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValuePhiNode.java	Mon May 19 17:21:30 2014 -0700
@@ -54,10 +54,6 @@
 
     @Override
     public boolean inferStamp() {
-        return inferPhiStamp();
-    }
-
-    public boolean inferPhiStamp() {
         return updateStamp(StampTool.meet(values()));
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java	Mon May 19 17:21:30 2014 -0700
@@ -82,7 +82,7 @@
                 IntegerStamp equalsXStamp = (IntegerStamp) equals.x().stamp();
                 if (equalsXStamp.upMask() == 1) {
                     if (x().asConstant().equals(Constant.INT_0) && y().asConstant().equals(Constant.INT_1)) {
-                        return equals.x();
+                        return IntegerConvertNode.convertUnsigned(equals.x(), stamp());
                     }
                 }
             }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java	Mon May 19 17:21:30 2014 -0700
@@ -75,6 +75,14 @@
     }
 
     public static ValueNode convert(ValueNode input, Stamp stamp) {
+        return convert(input, stamp, false);
+    }
+
+    public static ValueNode convertUnsigned(ValueNode input, Stamp stamp) {
+        return convert(input, stamp, true);
+    }
+
+    public static ValueNode convert(ValueNode input, Stamp stamp, boolean zeroExtend) {
         StructuredGraph graph = input.graph();
         IntegerStamp fromStamp = (IntegerStamp) input.stamp();
         IntegerStamp toStamp = (IntegerStamp) stamp;
@@ -84,6 +92,9 @@
             result = input;
         } else if (toStamp.getBits() < fromStamp.getBits()) {
             result = graph.unique(new NarrowNode(input, toStamp.getBits()));
+        } else if (zeroExtend) {
+            // toStamp.getBits() > fromStamp.getBits()
+            result = graph.unique(new ZeroExtendNode(input, toStamp.getBits()));
         } else {
             // toStamp.getBits() > fromStamp.getBits()
             result = graph.unique(new SignExtendNode(input, toStamp.getBits()));
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java	Mon May 19 17:21:30 2014 -0700
@@ -34,18 +34,20 @@
 
     @Input private ValueNode hub;
     private final ResolvedJavaMethod method;
+    private final ResolvedJavaType receiverType;
 
     public ValueNode getHub() {
         return hub;
     }
 
-    public LoadMethodNode(ResolvedJavaMethod method, ValueNode hub, Kind kind) {
+    public LoadMethodNode(ResolvedJavaMethod method, ResolvedJavaType receiverType, ValueNode hub, Kind kind) {
         super(kind == Kind.Object ? StampFactory.objectNonNull() : StampFactory.forKind(kind));
+        this.receiverType = receiverType;
         this.hub = hub;
         this.method = method;
         assert !method.isAbstract() : "Cannot load abstract method from a hub";
         assert !method.isStatic() : "Cannot load a static method from a hub";
-        assert method.isInVirtualMethodTable();
+        assert method.isInVirtualMethodTable(receiverType);
     }
 
     @Override
@@ -56,4 +58,8 @@
     public ResolvedJavaMethod getMethod() {
         return method;
     }
+
+    public ResolvedJavaType getReceiverType() {
+        return receiverType;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java	Mon May 19 17:21:30 2014 -0700
@@ -23,10 +23,12 @@
 package com.oracle.graal.nodes.extended;
 
 import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 
-public class NullCheckNode extends DeoptimizingFixedWithNextNode implements LIRLowerable {
+@NodeInfo(allowedUsageTypes = {InputType.Guard})
+public class NullCheckNode extends DeoptimizingFixedWithNextNode implements LIRLowerable, GuardingNode {
 
     @Input private ValueNode object;
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Mon May 19 17:21:30 2014 -0700
@@ -132,8 +132,9 @@
 
     @Override
     public boolean inferStamp() {
-        if (stamp() instanceof ObjectStamp && object().stamp() instanceof ObjectStamp) {
-            return updateStamp(((ObjectStamp) object().stamp()).castTo((ObjectStamp) stamp()));
+        if (object().stamp() instanceof ObjectStamp) {
+            ObjectStamp castStamp = (ObjectStamp) StampFactory.declared(type);
+            return updateStamp(((ObjectStamp) object().stamp()).castTo(castStamp));
         }
         return false;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Mon May 19 17:21:30 2014 -0700
@@ -144,12 +144,32 @@
             ResolvedJavaType type = StampTool.typeOrNull(receiver);
             if (type != null) {
                 // either the holder class is exact, or the receiver object has an exact type
-                ResolvedJavaMethod resolvedMethod = type.resolveMethod(targetMethod);
+                ResolvedJavaMethod resolvedMethod = type.resolveMethod(targetMethod, invoke().getContextType());
                 if (resolvedMethod != null && (resolvedMethod.canBeStaticallyBound() || StampTool.isExactType(receiver))) {
                     invokeKind = InvokeKind.Special;
                     targetMethod = resolvedMethod;
                     return this;
                 }
+                if (tool.assumptions() != null && tool.assumptions().useOptimisticAssumptions()) {
+                    ResolvedJavaType uniqueConcreteType = type.findUniqueConcreteSubtype();
+                    if (uniqueConcreteType != null) {
+                        ResolvedJavaMethod methodFromUniqueType = uniqueConcreteType.resolveMethod(targetMethod, invoke().getContextType());
+                        if (methodFromUniqueType != null) {
+                            tool.assumptions().recordConcreteSubtype(type, uniqueConcreteType);
+                            invokeKind = InvokeKind.Special;
+                            targetMethod = methodFromUniqueType;
+                            return this;
+                        }
+                    }
+
+                    ResolvedJavaMethod uniqueConcreteMethod = type.findUniqueConcreteMethod(targetMethod);
+                    if (uniqueConcreteMethod != null) {
+                        tool.assumptions().recordConcreteMethod(targetMethod, type, uniqueConcreteMethod);
+                        invokeKind = InvokeKind.Special;
+                        targetMethod = uniqueConcreteMethod;
+                        return this;
+                    }
+                }
             }
         }
         return this;
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java	Mon May 19 17:21:30 2014 -0700
@@ -815,7 +815,7 @@
                     if (receiver != null && (callTarget.invokeKind() == InvokeKind.Interface || callTarget.invokeKind() == InvokeKind.Virtual)) {
                         ResolvedJavaType type = state.getNodeType(receiver);
                         if (!Objects.equals(type, StampTool.typeOrNull(receiver))) {
-                            ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod());
+                            ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod(), invoke.getContextType());
                             if (method != null) {
                                 if (method.canBeStaticallyBound() || type.isFinal()) {
                                     callTarget.setInvokeKind(InvokeKind.Special);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeoptimizationGroupingPhase.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeoptimizationGroupingPhase.java	Mon May 19 17:21:30 2014 -0700
@@ -62,7 +62,7 @@
                         merge.addForwardEnd(firstEnd);
                         reasonActionPhi.addInput(((AbstractDeoptimizeNode) target).getActionAndReason(context.getMetaAccess()));
                         speculationPhi.addInput(((AbstractDeoptimizeNode) target).getSpeculation(context.getMetaAccess()));
-                        target.predecessor().replaceFirstSuccessor(target, firstEnd);
+                        target.replaceAtPredecessor(firstEnd);
 
                         exitLoops((AbstractDeoptimizeNode) target, firstEnd, cfg);
 
@@ -77,7 +77,7 @@
                     merge.addForwardEnd(newEnd);
                     reasonActionPhi.addInput(deopt.getActionAndReason(context.getMetaAccess()));
                     speculationPhi.addInput(deopt.getSpeculation(context.getMetaAccess()));
-                    deopt.predecessor().replaceFirstSuccessor(deopt, newEnd);
+                    deopt.replaceAtPredecessor(newEnd);
                     exitLoops(deopt, newEnd, cfg);
                     obsoletes.add(deopt);
                 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java	Mon May 19 17:21:30 2014 -0700
@@ -22,7 +22,10 @@
  */
 package com.oracle.graal.phases.common;
 
+import java.util.*;
+
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
@@ -34,6 +37,10 @@
 
 public class UseTrappingNullChecksPhase extends BasePhase<LowTierContext> {
 
+    private static final DebugMetric metricTrappingNullCheck = Debug.metric("TrappingNullCheck");
+    private static final DebugMetric metricTrappingNullCheckUnreached = Debug.metric("TrappingNullCheckUnreached");
+    private static final DebugMetric metricTrappingNullCheckDynamicDeoptimize = Debug.metric("TrappingNullCheckDynamicDeoptimize");
+
     @Override
     protected void run(StructuredGraph graph, LowTierContext context) {
         if (context.getTarget().implicitNullCheckLimit <= 0) {
@@ -42,36 +49,85 @@
         assert graph.getGuardsStage().ordinal() >= GuardsStage.AFTER_FSA.ordinal();
 
         for (DeoptimizeNode deopt : graph.getNodes(DeoptimizeNode.class)) {
-            tryUseTrappingNullCheck(deopt);
+            tryUseTrappingNullCheck(deopt, deopt.predecessor(), deopt.reason(), deopt.getSpeculation());
+        }
+        for (DynamicDeoptimizeNode deopt : graph.getNodes(DynamicDeoptimizeNode.class)) {
+            tryUseTrappingNullCheck(context.getMetaAccess(), deopt);
         }
     }
 
-    private static void tryUseTrappingNullCheck(DeoptimizeNode deopt) {
-        if (deopt.reason() != DeoptimizationReason.NullCheckException) {
-            return;
-        }
-        if (deopt.getSpeculation() != null && !deopt.getSpeculation().equals(Constant.NULL_OBJECT)) {
+    private static void tryUseTrappingNullCheck(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt) {
+        ValueNode speculation = deopt.getSpeculation();
+        if (!speculation.isConstant() || !speculation.asConstant().equals(Constant.NULL_OBJECT)) {
             return;
         }
         Node predecessor = deopt.predecessor();
+        if (predecessor instanceof MergeNode) {
+            MergeNode merge = (MergeNode) predecessor;
+
+            if (merge.phis().isEmpty()) {
+                // Process each predecessor at the merge, unpacking the reasons as needed.
+                ValueNode reason = deopt.getActionAndReason();
+                List<ValueNode> values = reason instanceof ValuePhiNode ? ((ValuePhiNode) reason).values().snapshot() : null;
+
+                int index = 0;
+                for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
+                    ValueNode thisReason = values != null ? values.get(index++) : reason;
+                    if (thisReason.isConstant()) {
+                        DeoptimizationReason deoptimizationReason = metaAccessProvider.decodeDeoptReason(thisReason.asConstant());
+                        tryUseTrappingNullCheck(deopt, end.predecessor(), deoptimizationReason, null);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void tryUseTrappingNullCheck(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason, Constant speculation) {
+        if (deoptimizationReason != DeoptimizationReason.NullCheckException && deoptimizationReason != DeoptimizationReason.UnreachedCode) {
+            return;
+        }
+        if (speculation != null && !speculation.equals(Constant.NULL_OBJECT)) {
+            return;
+        }
+        if (predecessor instanceof MergeNode) {
+            MergeNode merge = (MergeNode) predecessor;
+            if (merge.phis().isEmpty()) {
+                for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
+                    checkPredecessor(deopt, end.predecessor(), deoptimizationReason);
+                }
+            }
+        } else if (predecessor instanceof BeginNode) {
+            checkPredecessor(deopt, predecessor, deoptimizationReason);
+        }
+    }
+
+    private static void checkPredecessor(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason) {
+        Node current = predecessor;
         Node branch = null;
-        while (predecessor instanceof BeginNode) {
-            branch = predecessor;
-            predecessor = predecessor.predecessor();
+        while (current instanceof BeginNode) {
+            branch = current;
+            current = current.predecessor();
         }
-        if (predecessor instanceof IfNode) {
-            IfNode ifNode = (IfNode) predecessor;
+        if (current instanceof IfNode) {
+            IfNode ifNode = (IfNode) current;
             if (branch != ifNode.trueSuccessor()) {
                 return;
             }
             LogicNode condition = ifNode.condition();
             if (condition instanceof IsNullNode) {
-                replaceWithTrappingNullCheck(deopt, ifNode, condition);
+                replaceWithTrappingNullCheck(deopt, ifNode, condition, deoptimizationReason);
             }
         }
     }
 
-    private static void replaceWithTrappingNullCheck(DeoptimizeNode deopt, IfNode ifNode, LogicNode condition) {
+    private static void replaceWithTrappingNullCheck(AbstractDeoptimizeNode deopt, IfNode ifNode, LogicNode condition, DeoptimizationReason deoptimizationReason) {
+        metricTrappingNullCheck.increment();
+        if (deopt instanceof DynamicDeoptimizeNode) {
+            metricTrappingNullCheckDynamicDeoptimize.increment();
+        }
+        if (deoptimizationReason == DeoptimizationReason.UnreachedCode) {
+            metricTrappingNullCheckUnreached.increment();
+        }
         IsNullNode isNullNode = (IsNullNode) condition;
         BeginNode nonTrappingContinuation = ifNode.falseSuccessor();
         BeginNode trappingContinuation = ifNode.trueSuccessor();
@@ -79,6 +135,17 @@
         trappingNullCheck.setStateBefore(deopt.stateBefore());
         deopt.graph().replaceSplit(ifNode, trappingNullCheck, nonTrappingContinuation);
 
+        /*
+         * We now have the pattern NullCheck/BeginNode/... It's possible some node is using the
+         * BeginNode as a guard input, so replace guard users of the Begin with the NullCheck and
+         * then remove the Begin from the graph.
+         */
+        nonTrappingContinuation.replaceAtUsages(InputType.Guard, trappingNullCheck);
+        FixedNode next = nonTrappingContinuation.next();
+        nonTrappingContinuation.clearSuccessors();
+        trappingNullCheck.setNext(next);
+        nonTrappingContinuation.safeDelete();
+
         GraphUtil.killCFG(trappingContinuation);
         if (isNullNode.usages().isEmpty()) {
             GraphUtil.killWithUnusedFloatingInputs(isNullNode);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java	Mon May 19 17:21:30 2014 -0700
@@ -594,7 +594,7 @@
         if (type == null) {
             return;
         }
-        ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod());
+        ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod(), invoke.getContextType());
         if (method == null) {
             return;
         }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java	Mon May 19 17:21:30 2014 -0700
@@ -191,8 +191,8 @@
      * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction} determined to be
      * unreachable will be eliminated by canonicalization and dead code elimination. For now they
      * still exist, thus polluting the result of
-     * {@link com.oracle.graal.nodes.ValuePhiNode#inferPhiStamp()} but we are careful to skip them
-     * when merging type-witnesses and known-null maps.
+     * {@link com.oracle.graal.nodes.ValuePhiNode#inferStamp()} but we are careful to skip them when
+     * merging type-witnesses and known-null maps.
      * </p>
      */
     private void mergePhis(MergeNode merge, List<State> withStates, Map<ValueNode, Witness> newKnownPhiTypes) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java	Mon May 19 17:14:36 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,324 +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.common.inlining;
-
-import static com.oracle.graal.graph.util.CollectionsAccess.*;
-
-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 Map<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 = newNodeIdentityMap(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO);
-            }
-            NodeWorkList workList = graph.createNodeWorkList();
-            Map<LoopBeginNode, Scope> loops = newNodeIdentityMap(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, Map<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) : invoke + ": " + relevance + " / " + invokeProbability + " / " + getFastPathMinProbability() + " / " + getScopeRelevanceWithinParent();
-            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);
-        }
-    }
-}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/DepthSearchUtil.java	Mon May 19 17:14:36 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +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.inlining;
-
-import com.oracle.graal.api.meta.Constant;
-import com.oracle.graal.api.meta.ResolvedJavaMethod;
-import com.oracle.graal.compiler.common.type.Stamp;
-import com.oracle.graal.debug.Debug;
-import com.oracle.graal.graph.NodeInputList;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.phases.common.CanonicalizerPhase;
-import com.oracle.graal.phases.common.DeadCodeEliminationPhase;
-import com.oracle.graal.phases.tiers.HighTierContext;
-
-import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer;
-
-/**
- * The workings of {@link InliningPhase#run(StructuredGraph, HighTierContext)} include delving into
- * a callsite to explore inlining opportunities. The utilities used for that are grouped in this
- * class.
- */
-public class DepthSearchUtil {
-
-    private DepthSearchUtil() {
-        // no instances
-    }
-
-    static InliningUtil.Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context, CanonicalizerPhase canonicalizer) {
-        Class<? extends FixedWithNextNode> macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method);
-        if (macroNodeClass != null) {
-            return new InliningUtil.InlineableMacroNode(macroNodeClass);
-        } else {
-            return new InliningUtil.InlineableGraph(buildGraph(method, invoke, context, canonicalizer));
-        }
-    }
-
-    private static StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) {
-        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 (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) {
-            if (parseBytecodes) {
-                parseBytecodes(newGraph, context, canonicalizer);
-            }
-
-            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;
-    }
-
-    /**
-     * This method builds the IR nodes for <code>newGraph</code> and canonicalizes them. Provided
-     * profiling info is mature, the resulting graph is cached.
-     */
-    private static StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context, CanonicalizerPhase canonicalizer) {
-        final 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;
-    }
-}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningIterator.java	Mon May 19 17:14:36 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +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.inlining;
-
-import com.oracle.graal.graph.Node;
-import com.oracle.graal.graph.NodeBitMap;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.MethodCallTargetNode;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.LinkedList;
-
-/**
- * Given a graph, visit all fixed nodes in dominator-based order, collecting in the process the
- * {@link Invoke} nodes with {@link MethodCallTargetNode}. Such list of callsites is returned by
- * {@link #apply()}
- */
-class InliningIterator {
-
-    private final StartNode start;
-    private final Deque<FixedNode> nodeQueue;
-    private final NodeBitMap queuedNodes;
-
-    public InliningIterator(StructuredGraph graph) {
-        this.start = graph.start();
-        this.nodeQueue = new ArrayDeque<>();
-        this.queuedNodes = graph.createNodeBitMap();
-        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 to do
-            } 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 to do
-            } 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;
-    }
-}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java	Mon May 19 17:21:30 2014 -0700
@@ -22,36 +22,21 @@
  */
 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.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.common.inlining.policy.GreedyInliningPolicy;
+import com.oracle.graal.phases.common.inlining.policy.InliningPolicy;
+import com.oracle.graal.phases.common.inlining.walker.CallsiteHolder;
+import com.oracle.graal.phases.common.inlining.walker.InliningData;
+import com.oracle.graal.phases.common.inlining.walker.MethodInvocation;
 import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.phases.util.*;
 
 public class InliningPhase extends AbstractInliningPhase {
 
-    static class Options {
+    public static class Options {
 
         // @formatter:off
         @Option(help = "Unconditionally inline intrinsics")
@@ -65,12 +50,6 @@
     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);
     }
@@ -95,16 +74,17 @@
     /**
      * <p>
      * The space of inlining decisions is explored depth-first with the help of a stack realized by
-     * {@link InliningData}. At any point in time, its topmost element consist of:
+     * {@link com.oracle.graal.phases.common.inlining.walker.InliningData}. At any point in time,
+     * its topmost element consist of:
      * <ul>
      * <li>
-     * one or more {@link GraphInfo}s of inlining candidates, all of them corresponding to a single
-     * callsite (details below). For example, "exact inline" leads to a single candidate.</li>
+     * one or more {@link CallsiteHolder}s of inlining candidates, all of them corresponding to a
+     * single callsite (details below). For example, "exact inline" leads to a single candidate.</li>
      * <li>
      * the callsite (for the targets above) is tracked as a {@link MethodInvocation}. The difference
-     * between {@link MethodInvocation#totalGraphs()} and {@link MethodInvocation#processedGraphs()}
-     * indicates the topmost {@link GraphInfo}s that might be delved-into to explore inlining
-     * opportunities.</li>
+     * between {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#totalGraphs()}
+     * and {@link MethodInvocation#processedGraphs()} indicates the topmost {@link CallsiteHolder}s
+     * that might be delved-into to explore inlining opportunities.</li>
      * </ul>
      * </p>
      *
@@ -112,10 +92,11 @@
      * The bottom-most element in the stack consists of:
      * <ul>
      * <li>
-     * a single {@link GraphInfo} (the root one, for the method on which inlining was called)</li>
+     * a single {@link CallsiteHolder} (the root one, for the method on which inlining was called)</li>
      * <li>
-     * a single {@link MethodInvocation} (the {@link MethodInvocation#isRoot} one, ie the unknown
-     * caller of the root graph)</li>
+     * a single {@link MethodInvocation} (the
+     * {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#isRoot} one, ie the
+     * unknown caller of the root graph)</li>
      * </ul>
      *
      * </p>
@@ -132,7 +113,8 @@
      * <li>
      * try to inline: move past the current inlining candidate (remove it from the topmost element).
      * If that was the last one then try to inline the callsite that is (still) in the topmost
-     * element of {@link InliningData}, and then remove such callsite.</li>
+     * element of {@link com.oracle.graal.phases.common.inlining.walker.InliningData}, and then
+     * remove such callsite.</li>
      * </ol>
      * </p>
      *
@@ -141,12 +123,12 @@
      * <ul>
      * <li>
      * the first step amounts to backtracking, the 2nd one to delving, and the 3rd one also involves
-     * bakctraking (however after may-be inlining).</li>
+     * backtracking (however after may-be inlining).</li>
      * <li>
      * the choice of abandon-and-backtrack or delve-into is depends on
      * {@link InliningPolicy#isWorthInlining} and {@link InliningPolicy#continueInlining}.</li>
      * <li>
-     * the 3rd choice is picked when both of the previous one aren't picked</li>
+     * the 3rd choice is picked when both of the previous ones aren't picked</li>
      * <li>
      * as part of trying-to-inline, {@link InliningPolicy#isWorthInlining} again sees use, but
      * that's another story.</li>
@@ -156,35 +138,12 @@
      */
     @Override
     protected void run(final StructuredGraph graph, final HighTierContext context) {
-        final InliningData data = new InliningData(graph, context.getAssumptions(), maxMethodPerInlining, canonicalizer);
-        ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache();
+        final InliningData data = new InliningData(graph, context, maxMethodPerInlining, canonicalizer, inliningPolicy);
 
         while (data.hasUnprocessedGraphs()) {
-            final MethodInvocation currentInvocation = data.currentInvocation();
-            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 (data.currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(data.currentGraph().graph())) {
-                data.processNextInvoke(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);
-                        }
-                    }
-                }
+            boolean wasInlined = data.moveForward();
+            if (wasInlined) {
+                inliningCount++;
             }
         }
 
@@ -192,552 +151,4 @@
         assert data.graphCount() == 0;
     }
 
-    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 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;
-        }
-    }
-
-    /**
-     * 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, 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 final int maxMethodPerInlining;
-        private final CanonicalizerPhase canonicalizer;
-
-        private int maxGraphs;
-
-        public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions, int maxMethodPerInlining, CanonicalizerPhase canonicalizer) {
-            this.graphQueue = new ArrayDeque<>();
-            this.invocationQueue = new ArrayDeque<>();
-            this.maxMethodPerInlining = maxMethodPerInlining;
-            this.canonicalizer = canonicalizer;
-            this.maxGraphs = 1;
-
-            invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0));
-            pushGraph(rootGraph, 1.0, 1.0);
-        }
-
-        /**
-         * Process the next invoke and enqueue all its graphs for processing.
-         */
-        void processNextInvoke(HighTierContext context) {
-            GraphInfo graphInfo = currentGraph();
-            Invoke invoke = graphInfo.popInvoke();
-            MethodInvocation callerInvocation = currentInvocation();
-            Assumptions parentAssumptions = callerInvocation.assumptions();
-            InlineInfo info = InliningUtil.getInlineInfo(this, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations());
-
-            if (info != null) {
-                double invokeProbability = graphInfo.invokeProbability(invoke);
-                double invokeRelevance = graphInfo.invokeRelevance(invoke);
-                MethodInvocation calleeInvocation = pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance);
-
-                for (int i = 0; i < info.numberOfMethods(); i++) {
-                    Inlineable elem = DepthSearchUtil.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()), canonicalizer);
-                    info.setInlinableElement(i, elem);
-                    if (elem instanceof InlineableGraph) {
-                        pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
-                    } else {
-                        assert elem instanceof InlineableMacroNode;
-                        pushDummyGraph();
-                    }
-                }
-            }
-        }
-
-        public int graphCount() {
-            return graphQueue.size();
-        }
-
-        public void pushGraph(StructuredGraph graph, double probability, double relevance) {
-            assert !contains(graph);
-            graphQueue.push(new GraphInfo(graph, 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 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, double probability, double relevance) {
-            this.graph = graph;
-            if (graph == null) {
-                this.remainingInvokes = new LinkedList<>();
-            } else {
-                LinkedList<Invoke> invokes = new InliningIterator(graph).apply();
-                assert invokes.size() == count(graph.getInvokes());
-                this.remainingInvokes = invokes;
-            }
-            this.probability = probability;
-            this.relevance = relevance;
-
-            if (graph != null && !remainingInvokes.isEmpty()) {
-                probabilities = new FixedNodeProbabilityCache();
-                computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities);
-                computeProbabilities();
-            } else {
-                probabilities = null;
-                computeInliningRelevance = null;
-            }
-        }
-
-        private static int count(Iterable<Invoke> invokes) {
-            int count = 0;
-            Iterator<Invoke> iterator = invokes.iterator();
-            while (iterator.hasNext()) {
-                iterator.next();
-                count++;
-            }
-            return count;
-        }
-
-        /**
-         * 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;
-        }
-    }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Mon May 19 17:21:30 2014 -0700
@@ -28,15 +28,10 @@
 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;
@@ -52,14 +47,11 @@
 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.*;
+import com.oracle.graal.phases.common.inlining.info.*;
+import com.oracle.graal.phases.common.inlining.walker.InliningData;
 
 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
@@ -68,13 +60,6 @@
      */
     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();
@@ -155,22 +140,21 @@
         }
     }
 
-    public static boolean logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) {
-        return logInliningDecision(info, inliningDepth, allowLogging, true, msg, args);
+    public static void logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) {
+        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 void logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) {
+        logInliningDecision(info, inliningDepth, true, false, msg, args);
     }
 
-    public static boolean logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) {
+    public static void 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) {
@@ -182,34 +166,23 @@
         }
     }
 
-    private static boolean logNotInlinedMethod(Invoke invoke, String msg) {
+    public static void 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]);
+    public static void logNotInlined(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) {
+        logNotInlinedInvoke(invoke, inliningDepth, method, msg, new Object[0]);
     }
 
-    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) {
+    public static void logNotInlinedInvoke(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) {
@@ -255,995 +228,13 @@
         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) {
+    public 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));
     }
 
@@ -1252,44 +243,56 @@
         return graph.unique(new GuardedValueNode(receiver, anchor, stamp));
     }
 
-    // TODO (chaeubl): cleanup this method
-    private static boolean checkInvokeConditions(Invoke invoke) {
+    /**
+     * @return null iff the check succeeds, otherwise a (non-null) descriptive message.
+     */
+    public static String 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) {
+            return "the invoke is dead code";
+        }
+        if (!(invoke.callTarget() instanceof MethodCallTargetNode)) {
+            return "the invoke has already been lowered, or has been created as a low-level node";
+        }
+        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+        if (callTarget.targetMethod() == null) {
+            return "target method is null";
+        }
+        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;
+            return "the invoke has no after state";
+        }
+        if (!invoke.useForInlining()) {
+            return "the invoke is marked to be not used for inlining";
         }
+        ValueNode receiver = callTarget.receiver();
+        if (receiver != null && receiver.isConstant() && receiver.asConstant().isNull()) {
+            return "receiver is null";
+        }
+        return null;
     }
 
-    private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) {
+    public static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) {
+        String failureMessage = null;
         if (method == null) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved");
+            failureMessage = "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");
+            failureMessage = "it is a non-intrinsic native method";
         } else if (method.isAbstract()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method");
+            failureMessage = "it is an abstract method";
         } else if (!method.getDeclaringClass().isInitialized()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized");
+            failureMessage = "the method's class is not initialized";
         } else if (!method.canBeInlined()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable");
+            failureMessage = "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");
+            failureMessage = "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");
+            failureMessage = "the callee uses less optimistic optimizations than caller";
+        }
+        if (failureMessage == null) {
+            return true;
         } else {
-            return true;
+            logNotInlined(invoke, data.inliningDepth(), method, failureMessage);
+            return false;
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/AbstractInlineInfo.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,68 @@
+/*
+ * 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.info;
+
+import com.oracle.graal.api.code.Assumptions;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.FixedWithNextNode;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+
+import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable;
+import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableMacroNode;
+import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableGraph;
+
+public abstract 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();
+            InliningUtil.inlineMacroNode(invoke, concrete, macroNodeClass);
+        }
+
+        InliningUtil.InlinedBytecodes.add(concrete.getCodeSize());
+        assumptions.recordMethodContents(concrete);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/AssumptionInlineInfo.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,64 @@
+/*
+ * 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.info;
+
+import com.oracle.graal.api.code.Assumptions;
+import com.oracle.graal.api.meta.MetaAccessProvider;
+import com.oracle.graal.api.meta.MetaUtil;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.util.Providers;
+import com.oracle.graal.api.code.Assumptions.Assumption;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+
+/**
+ * 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.
+ */
+public 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);
+        InliningUtil.replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete);
+    }
+
+    @Override
+    public String toString() {
+        return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/ExactInlineInfo.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,106 @@
+/*
+ * 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.info;
+
+import com.oracle.graal.api.code.Assumptions;
+import com.oracle.graal.api.meta.MetaAccessProvider;
+import com.oracle.graal.api.meta.MetaUtil;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.phases.util.Providers;
+import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable;
+
+/**
+ * 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 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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/InlineInfo.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,80 @@
+/*
+ * 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.info;
+
+import com.oracle.graal.api.code.Assumptions;
+import com.oracle.graal.api.meta.MetaAccessProvider;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.util.Providers;
+
+/**
+ * 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);
+
+    InliningUtil.Inlineable inlineableElementAt(int index);
+
+    double probabilityAt(int index);
+
+    double relevanceAt(int index);
+
+    void setInlinableElement(int index, InliningUtil.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();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/MultiTypeGuardInlineInfo.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,551 @@
+/*
+ * 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.info;
+
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType;
+import com.oracle.graal.compiler.common.calc.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.graph.*;
+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.util.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.common.inlining.*;
+import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable;
+import com.oracle.graal.phases.tiers.*;
+import com.oracle.graal.phases.util.*;
+
+/**
+ * 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.
+ */
+public class MultiTypeGuardInlineInfo extends AbstractInlineInfo {
+
+    private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication");
+
+    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 = InliningUtil.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 = InliningUtil.nonNullReceiver(invoke);
+        Kind hubKind = ((MethodCallTargetNode) invoke.callTarget()).targetMethod().getDeclaringClass().getEncoding(ResolvedJavaType.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)));
+                    }
+                }
+            }
+
+            ResolvedJavaType receiverType = invoke.getReceiverType();
+            FixedNode lastSucc = successors[concretes.size()];
+            for (int i = concretes.size() - 1; i >= 0; --i) {
+                LoadMethodNode method = graph.add(new LoadMethodNode(concretes.get(i), receiverType, 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() {
+        ResolvedJavaType receiverType = invoke.getReceiverType();
+        for (ResolvedJavaMethod concrete : concretes) {
+            if (!concrete.isInVirtualMethodTable(receiverType)) {
+                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();
+            ResolvedJavaType contextType = invoke.getContextType();
+            // 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, contextType);
+                if (baseClassTargetMethod != null) {
+                    devirtualizeWithTypeSwitch(graph, InvokeKind.Virtual, leastCommonType.resolveMethod(targetMethod, contextType), 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 = InliningUtil.createAnchoredReceiver(graph, invocationEntry, target.getDeclaringClass(), receiver, false);
+        invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver);
+        InliningUtil.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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/TypeGuardInlineInfo.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,124 @@
+/*
+ * 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.info;
+
+import com.oracle.graal.api.code.Assumptions;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.calc.Condition;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.CompareNode;
+import com.oracle.graal.nodes.extended.LoadHubNode;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.util.Providers;
+import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+
+/**
+ * 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.
+ */
+public 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);
+        InliningUtil.replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete);
+    }
+
+    private void createGuard(StructuredGraph graph, MetaAccessProvider metaAccess) {
+        ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
+        ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(ResolvedJavaType.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 = InliningUtil.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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/AbstractInliningPolicy.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,126 @@
+/*
+ * 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.policy;
+
+import com.oracle.graal.api.meta.ProfilingInfo;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.FixedNode;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.nodes.spi.Replacements;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.common.inlining.info.InlineInfo;
+
+import java.util.Map;
+import java.util.function.ToDoubleFunction;
+
+import static com.oracle.graal.compiler.common.GraalOptions.RelevanceCapForInlining;
+import static com.oracle.graal.phases.common.inlining.InliningPhase.Options.AlwaysInlineIntrinsics;
+
+public abstract 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++) {
+            InliningUtil.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++) {
+            InliningUtil.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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/GreedyInliningPolicy.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,112 @@
+/*
+ * 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.policy;
+
+import com.oracle.graal.debug.Debug;
+import com.oracle.graal.debug.DebugMetric;
+import com.oracle.graal.nodes.FixedNode;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.nodes.spi.Replacements;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.common.inlining.info.InlineInfo;
+
+import java.util.Map;
+import java.util.function.ToDoubleFunction;
+
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+
+public class GreedyInliningPolicy extends AbstractInliningPolicy {
+
+    private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize");
+
+    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()) {
+            InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything");
+            return true;
+        }
+
+        if (isIntrinsic(replacements, info)) {
+            InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic");
+            return true;
+        }
+
+        if (info.shouldInline()) {
+            InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining");
+            return true;
+        }
+
+        double inliningBonus = getInliningBonus(info);
+        int nodes = determineNodeCount(info);
+        int lowLevelGraphSize = previousLowLevelGraphSize(info);
+
+        if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) {
+            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);
+            return false;
+        }
+
+        if (nodes < TrivialInliningSize.getValue() * inliningBonus) {
+            InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes);
+            return true;
+        }
+
+        /*
+         * 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) {
+            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);
+            return false;
+        }
+
+        double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus));
+        if (nodes <= maximumNodes) {
+            InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability, inliningBonus,
+                            nodes, maximumNodes);
+            return true;
+        }
+
+        InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes, maximumNodes);
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InlineEverythingPolicy.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,48 @@
+/*
+ * 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.policy;
+
+import com.oracle.graal.api.code.BailoutException;
+import com.oracle.graal.nodes.FixedNode;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.nodes.spi.Replacements;
+import com.oracle.graal.phases.common.inlining.info.InlineInfo;
+
+import java.util.function.ToDoubleFunction;
+
+import static com.oracle.graal.compiler.common.GraalOptions.MaximumDesiredSize;
+
+public 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InliningPolicy.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,37 @@
+/*
+ * 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.policy;
+
+import com.oracle.graal.nodes.FixedNode;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.nodes.spi.Replacements;
+import com.oracle.graal.phases.common.inlining.info.InlineInfo;
+
+import java.util.function.ToDoubleFunction;
+
+public interface InliningPolicy {
+
+    boolean continueInlining(StructuredGraph graph);
+
+    boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/CallsiteHolder.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,126 @@
+/*
+ * 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.walker;
+
+import com.oracle.graal.api.meta.MetaUtil;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.FixedNode;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.phases.graph.FixedNodeProbabilityCache;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.function.ToDoubleFunction;
+
+import static com.oracle.graal.compiler.common.GraalOptions.CapInheritedRelevance;
+
+/**
+ * Information about a graph that will potentially be inlined. This includes tracking the
+ * invocations in graph that will subject to inlining themselves.
+ */
+public class CallsiteHolder {
+
+    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 CallsiteHolder(StructuredGraph graph, double probability, double relevance) {
+        this.graph = graph;
+        if (graph == null) {
+            this.remainingInvokes = new LinkedList<>();
+        } else {
+            LinkedList<Invoke> invokes = new InliningIterator(graph).apply();
+            assert invokes.size() == count(graph.getInvokes());
+            this.remainingInvokes = invokes;
+        }
+        this.probability = probability;
+        this.relevance = relevance;
+
+        if (graph != null && !remainingInvokes.isEmpty()) {
+            probabilities = new FixedNodeProbabilityCache();
+            computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities);
+            computeProbabilities();
+        } else {
+            probabilities = null;
+            computeInliningRelevance = null;
+        }
+    }
+
+    private static int count(Iterable<Invoke> invokes) {
+        int count = 0;
+        Iterator<Invoke> iterator = invokes.iterator();
+        while (iterator.hasNext()) {
+            iterator.next();
+            count++;
+        }
+        return count;
+    }
+
+    /**
+     * Gets the method associated with the {@linkplain #graph() graph} represented by this object.
+     */
+    public ResolvedJavaMethod method() {
+        return graph == null ? null : 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/walker/ComputeInliningRelevance.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,324 @@
+/*
+ * 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.walker;
+
+import static com.oracle.graal.graph.util.CollectionsAccess.*;
+
+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 Map<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 = newNodeIdentityMap(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO);
+            }
+            NodeWorkList workList = graph.createNodeWorkList();
+            Map<LoopBeginNode, Scope> loops = newNodeIdentityMap(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, Map<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) : invoke + ": " + relevance + " / " + invokeProbability + " / " + getFastPathMinProbability() + " / " + getScopeRelevanceWithinParent();
+            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/walker/DepthSearchUtil.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,149 @@
+/*
+ * 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.walker;
+
+import com.oracle.graal.api.meta.Constant;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.compiler.common.type.Stamp;
+import com.oracle.graal.debug.Debug;
+import com.oracle.graal.graph.NodeInputList;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.phases.common.CanonicalizerPhase;
+import com.oracle.graal.phases.common.DeadCodeEliminationPhase;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.tiers.HighTierContext;
+
+import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer;
+
+/**
+ * The workings of {@link InliningData} include delving into a callsite to explore inlining
+ * opportunities. The utilities used for that are grouped in this class.
+ */
+public class DepthSearchUtil {
+
+    private DepthSearchUtil() {
+        // no instances
+    }
+
+    public static InliningUtil.Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context, CanonicalizerPhase canonicalizer) {
+        Class<? extends FixedWithNextNode> macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method);
+        if (macroNodeClass != null) {
+            return new InliningUtil.InlineableMacroNode(macroNodeClass);
+        } else {
+            return new InliningUtil.InlineableGraph(buildGraph(method, invoke, context, canonicalizer));
+        }
+    }
+
+    private static StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) {
+        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 (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) {
+            if (parseBytecodes) {
+                parseBytecodes(newGraph, context, canonicalizer);
+            }
+
+            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;
+    }
+
+    /**
+     * This method builds the IR nodes for <code>newGraph</code> and canonicalizes them. Provided
+     * profiling info is mature, the resulting graph is cached.
+     */
+    private static StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context, CanonicalizerPhase canonicalizer) {
+        final 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningData.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,553 @@
+/*
+ * 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.walker;
+
+import com.oracle.graal.api.code.Assumptions;
+import com.oracle.graal.api.code.BailoutException;
+import com.oracle.graal.api.meta.JavaTypeProfile;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.api.meta.ResolvedJavaType;
+import com.oracle.graal.compiler.common.GraalInternalError;
+import com.oracle.graal.compiler.common.type.ObjectStamp;
+import com.oracle.graal.debug.Debug;
+import com.oracle.graal.debug.DebugMetric;
+import com.oracle.graal.graph.Graph;
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode;
+import com.oracle.graal.phases.OptimisticOptimizations;
+import com.oracle.graal.phases.common.CanonicalizerPhase;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.common.inlining.info.*;
+import com.oracle.graal.phases.common.inlining.policy.InliningPolicy;
+import com.oracle.graal.phases.graph.FixedNodeProbabilityCache;
+import com.oracle.graal.phases.tiers.HighTierContext;
+import com.oracle.graal.phases.util.Providers;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.ToDoubleFunction;
+
+import static com.oracle.graal.compiler.common.GraalOptions.MegamorphicInliningMinMethodProbability;
+import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer;
+
+/**
+ * Holds the data for building the callee graphs recursively: graphs and invocations (each
+ * invocation can have multiple graphs).
+ */
+public class InliningData {
+
+    private static final CallsiteHolder DUMMY_CALLSITE_HOLDER = new CallsiteHolder(null, 1.0, 1.0);
+    // Metrics
+    private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed");
+    private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns");
+    private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered");
+
+    /**
+     * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee.
+     */
+    private final ArrayDeque<CallsiteHolder> graphQueue = new ArrayDeque<>();
+    private final ArrayDeque<MethodInvocation> invocationQueue = new ArrayDeque<>();
+    private final ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache();
+
+    private final HighTierContext context;
+    private final int maxMethodPerInlining;
+    private final CanonicalizerPhase canonicalizer;
+    private final InliningPolicy inliningPolicy;
+
+    private int maxGraphs;
+
+    public InliningData(StructuredGraph rootGraph, HighTierContext context, int maxMethodPerInlining, CanonicalizerPhase canonicalizer, InliningPolicy inliningPolicy) {
+        assert rootGraph != null;
+        this.context = context;
+        this.maxMethodPerInlining = maxMethodPerInlining;
+        this.canonicalizer = canonicalizer;
+        this.inliningPolicy = inliningPolicy;
+        this.maxGraphs = 1;
+
+        Assumptions rootAssumptions = context.getAssumptions();
+        invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0));
+        pushGraph(rootGraph, 1.0, 1.0);
+    }
+
+    /**
+     * 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
+     */
+    private InlineInfo getInlineInfo(Invoke invoke, Assumptions assumptions) {
+        final String failureMessage = InliningUtil.checkInvokeConditions(invoke);
+        if (failureMessage != null) {
+            InliningUtil.logNotInlinedMethod(invoke, failureMessage);
+            return null;
+        }
+        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
+        ResolvedJavaMethod targetMethod = callTarget.targetMethod();
+
+        if (callTarget.invokeKind() == MethodCallTargetNode.InvokeKind.Special || targetMethod.canBeStaticallyBound()) {
+            return getExactInlineInfo(invoke, targetMethod);
+        }
+
+        assert callTarget.invokeKind() == MethodCallTargetNode.InvokeKind.Virtual || callTarget.invokeKind() == MethodCallTargetNode.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;
+        }
+        ResolvedJavaType contextType = invoke.getContextType();
+        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, contextType);
+                    if (resolvedMethod != null) {
+                        return getExactInlineInfo(invoke, resolvedMethod);
+                    }
+                }
+            }
+        }
+
+        if (holder.isArray()) {
+            // arrays can be treated as Objects
+            ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod, contextType);
+            if (resolvedMethod != null) {
+                return getExactInlineInfo(invoke, resolvedMethod);
+            }
+        }
+
+        if (assumptions.useOptimisticAssumptions()) {
+            ResolvedJavaType uniqueSubtype = holder.findUniqueConcreteSubtype();
+            if (uniqueSubtype != null) {
+                ResolvedJavaMethod resolvedMethod = uniqueSubtype.resolveMethod(targetMethod, contextType);
+                if (resolvedMethod != null) {
+                    return getAssumptionInlineInfo(invoke, resolvedMethod, new Assumptions.ConcreteSubtype(holder, uniqueSubtype));
+                }
+            }
+
+            ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod);
+            if (concrete != null) {
+                return getAssumptionInlineInfo(invoke, concrete, new Assumptions.ConcreteMethod(targetMethod, holder, concrete));
+            }
+        }
+
+        // type check based inlining
+        return getTypeCheckedInlineInfo(invoke, targetMethod);
+    }
+
+    private InlineInfo getTypeCheckedInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) {
+        JavaTypeProfile typeProfile;
+        ValueNode receiver = invoke.callTarget().arguments().get(0);
+        if (receiver instanceof TypeProfileProxyNode) {
+            TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver;
+            typeProfile = typeProfileProxyNode.getProfile();
+        } else {
+            InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no type profile exists");
+            return null;
+        }
+
+        JavaTypeProfile.ProfiledType[] ptypes = typeProfile.getTypes();
+        if (ptypes == null || ptypes.length <= 0) {
+            InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no types in profile");
+            return null;
+        }
+        ResolvedJavaType contextType = invoke.getContextType();
+        double notRecordedTypeProbability = typeProfile.getNotRecordedProbability();
+        final OptimisticOptimizations optimisticOpts = context.getOptimisticOptimizations();
+        if (ptypes.length == 1 && notRecordedTypeProbability == 0) {
+            if (!optimisticOpts.inlineMonomorphicCalls()) {
+                InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "inlining monomorphic calls is disabled");
+                return null;
+            }
+
+            ResolvedJavaType type = ptypes[0].getType();
+            assert type.isArray() || !type.isAbstract();
+            ResolvedJavaMethod concrete = type.resolveMethod(targetMethod, contextType);
+            if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, concrete, optimisticOpts)) {
+                return null;
+            }
+            return new TypeGuardInlineInfo(invoke, concrete, type);
+        } else {
+            invoke.setPolymorphic(true);
+
+            if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) {
+                InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length);
+                return null;
+            }
+            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
+                InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length,
+                                notRecordedTypeProbability * 100);
+                return null;
+            }
+
+            // 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, contextType);
+                if (concrete == null) {
+                    InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "could not resolve method");
+                    return null;
+                }
+                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() > maxMethodPerInlining) {
+                InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxMethodPerInlining);
+                return null;
+            }
+
+            // 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.
+                    InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)",
+                                    concreteMethods.size());
+                    return null;
+                }
+
+                concreteMethods = newConcreteMethods;
+                concreteMethodsProbabilities = newConcreteMethodsProbabilities;
+            }
+
+            // Clean out types whose methods are no longer available.
+            ArrayList<JavaTypeProfile.ProfiledType> usedTypes = new ArrayList<>();
+            ArrayList<Integer> typesToConcretes = new ArrayList<>();
+            for (JavaTypeProfile.ProfiledType type : ptypes) {
+                ResolvedJavaMethod concrete = type.getType().resolveMethod(targetMethod, contextType);
+                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.
+                InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length);
+                return null;
+            }
+
+            for (ResolvedJavaMethod concrete : concreteMethods) {
+                if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, concrete, optimisticOpts)) {
+                    InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined");
+                    return null;
+                }
+            }
+            return new MultiTypeGuardInlineInfo(invoke, concreteMethods, concreteMethodsProbabilities, usedTypes, typesToConcretes, notRecordedTypeProbability);
+        }
+    }
+
+    private InlineInfo getAssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumptions.Assumption takenAssumption) {
+        assert !concrete.isAbstract();
+        if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, concrete, context.getOptimisticOptimizations())) {
+            return null;
+        }
+        return new AssumptionInlineInfo(invoke, concrete, takenAssumption);
+    }
+
+    private InlineInfo getExactInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) {
+        assert !targetMethod.isAbstract();
+        if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, targetMethod, context.getOptimisticOptimizations())) {
+            return null;
+        }
+        return new ExactInlineInfo(invoke, targetMethod);
+    }
+
+    private void doInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInfo, Assumptions callerAssumptions) {
+        StructuredGraph callerGraph = callerCallsiteHolder.graph();
+        Graph.Mark markBeforeInlining = callerGraph.getMark();
+        InlineInfo callee = calleeInfo.callee();
+        try {
+            try (Debug.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()) {
+                    Graph.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) {
+                            callerCallsiteHolder.pushInvoke((Invoke) newNode);
+                        }
+                    }
+                }
+
+                callerCallsiteHolder.computeProbabilities();
+
+                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());
+        }
+    }
+
+    /**
+     * @return true iff inlining was actually performed
+     */
+    private boolean tryToInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth) {
+        InlineInfo callee = calleeInfo.callee();
+        Assumptions callerAssumptions = parentInvocation.assumptions();
+        metricInliningConsidered.increment();
+
+        if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) {
+            doInline(callerCallsiteHolder, calleeInfo, callerAssumptions);
+            return true;
+        }
+
+        if (context.getOptimisticOptimizations().devirtualizeInvokes()) {
+            callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions);
+        }
+
+        return false;
+    }
+
+    /**
+     * Process the next invoke and enqueue all its graphs for processing.
+     */
+    private void processNextInvoke() {
+        CallsiteHolder callsiteHolder = currentGraph();
+        Invoke invoke = callsiteHolder.popInvoke();
+        MethodInvocation callerInvocation = currentInvocation();
+        Assumptions parentAssumptions = callerInvocation.assumptions();
+        InlineInfo info = getInlineInfo(invoke, parentAssumptions);
+
+        if (info != null) {
+            double invokeProbability = callsiteHolder.invokeProbability(invoke);
+            double invokeRelevance = callsiteHolder.invokeRelevance(invoke);
+            MethodInvocation calleeInvocation = pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance);
+
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                InliningUtil.Inlineable elem = DepthSearchUtil.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()), canonicalizer);
+                info.setInlinableElement(i, elem);
+                if (elem instanceof InliningUtil.InlineableGraph) {
+                    pushGraph(((InliningUtil.InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
+                } else {
+                    assert elem instanceof InliningUtil.InlineableMacroNode;
+                    pushDummyGraph();
+                }
+            }
+        }
+    }
+
+    public int graphCount() {
+        return graphQueue.size();
+    }
+
+    private void pushGraph(StructuredGraph graph, double probability, double relevance) {
+        assert graph != null;
+        assert !contains(graph);
+        graphQueue.push(new CallsiteHolder(graph, probability, relevance));
+        assert graphQueue.size() <= maxGraphs;
+    }
+
+    private void pushDummyGraph() {
+        graphQueue.push(DUMMY_CALLSITE_HOLDER);
+    }
+
+    public boolean hasUnprocessedGraphs() {
+        return !graphQueue.isEmpty();
+    }
+
+    private CallsiteHolder currentGraph() {
+        return graphQueue.peek();
+    }
+
+    private void popGraph() {
+        graphQueue.pop();
+        assert graphQueue.size() <= maxGraphs;
+    }
+
+    private 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.
+     */
+    private Object[] inliningContext() {
+        if (!Debug.isDumpEnabled()) {
+            return NO_CONTEXT;
+        }
+        Object[] result = new Object[graphQueue.size()];
+        int i = 0;
+        for (CallsiteHolder g : graphQueue) {
+            result[i++] = g.method();
+        }
+        return result;
+    }
+
+    private MethodInvocation currentInvocation() {
+        return invocationQueue.peekFirst();
+    }
+
+    private 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;
+    }
+
+    private void popInvocation() {
+        maxGraphs -= invocationQueue.peekFirst().callee().numberOfMethods();
+        assert graphQueue.size() <= maxGraphs;
+        invocationQueue.removeFirst();
+    }
+
+    public int countRecursiveInlining(ResolvedJavaMethod method) {
+        int count = 0;
+        for (CallsiteHolder callsiteHolder : graphQueue) {
+            if (method.equals(callsiteHolder.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 (CallsiteHolder graph : graphQueue) {
+            result.append(graph.graph());
+            result.append("; ");
+        }
+
+        return result.toString();
+    }
+
+    private boolean contains(StructuredGraph graph) {
+        for (CallsiteHolder info : graphQueue) {
+            if (info.graph() == graph) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return true iff inlining was actually performed
+     */
+    public boolean moveForward() {
+
+        final MethodInvocation currentInvocation = currentInvocation();
+
+        final boolean backtrack = (!currentInvocation.isRoot() && !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), inliningDepth(),
+                        currentInvocation.probability(), currentInvocation.relevance(), false));
+        if (backtrack) {
+            int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs();
+            assert remainingGraphs > 0;
+            popGraphs(remainingGraphs);
+            popInvocation();
+            return false;
+        }
+
+        final boolean delve = currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(currentGraph().graph());
+        if (delve) {
+            processNextInvoke();
+            return false;
+        }
+
+        popGraph();
+        if (currentInvocation.isRoot()) {
+            return false;
+        }
+
+        // try to inline
+        assert currentInvocation.callee().invoke().asNode().isAlive();
+        currentInvocation.incrementProcessedGraphs();
+        if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) {
+            popInvocation();
+            final MethodInvocation parentInvoke = currentInvocation();
+            try (Debug.Scope s = Debug.scope("Inlining", inliningContext())) {
+                return tryToInline(currentGraph(), currentInvocation, parentInvoke, inliningDepth() + 1);
+            } catch (Throwable e) {
+                throw Debug.handle(e);
+            }
+        }
+
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningIterator.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,130 @@
+/*
+ * 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.walker;
+
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.graph.NodeBitMap;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.LinkedList;
+
+/**
+ * Given a graph, visit all fixed nodes in dominator-based order, collecting in the process the
+ * {@link Invoke} nodes with {@link MethodCallTargetNode}. Such list of callsites is returned by
+ * {@link #apply()}
+ */
+public class InliningIterator {
+
+    private final StartNode start;
+    private final Deque<FixedNode> nodeQueue;
+    private final NodeBitMap queuedNodes;
+
+    public InliningIterator(StructuredGraph graph) {
+        this.start = graph.start();
+        this.nodeQueue = new ArrayDeque<>();
+        this.queuedNodes = graph.createNodeBitMap();
+        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 to do
+            } 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 to do
+            } 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/MethodInvocation.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,95 @@
+/*
+ * 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.walker;
+
+import com.oracle.graal.api.code.Assumptions;
+import com.oracle.graal.api.meta.MetaUtil;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.CallTargetNode;
+import com.oracle.graal.nodes.java.MethodCallTargetNode;
+import com.oracle.graal.phases.common.inlining.info.InlineInfo;
+
+public 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();
+        }
+    }
+}
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java	Mon May 19 17:21:30 2014 -0700
@@ -28,6 +28,7 @@
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.debug.DebugMemUseTracker.Closeable;
 import com.oracle.graal.debug.internal.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 
 /**
@@ -39,9 +40,26 @@
 
     private CharSequence name;
 
-    private final DebugTimer phaseTimer;
-    private final DebugMetric phaseMetric;
-    private final DebugMemUseTracker phaseMemUseTracker;
+    /**
+     * Records time spent in {@link #apply(StructuredGraph, Object, boolean)}.
+     */
+    private final DebugTimer timer;
+
+    /**
+     * Counts calls to {@link #apply(StructuredGraph, Object, boolean)}.
+     */
+    private final DebugMetric executionCount;
+
+    /**
+     * Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to
+     * {@link #apply(StructuredGraph, Object, boolean)}.
+     */
+    private final DebugMetric inputNodesCount;
+
+    /**
+     * Records memory usage within {@link #apply(StructuredGraph, Object, boolean)}.
+     */
+    private final DebugMemUseTracker memUseTracker;
 
     private static final Pattern NAME_PATTERN = Pattern.compile("[A-Z][A-Za-z0-9]+");
 
@@ -51,17 +69,19 @@
     }
 
     protected BasePhase() {
-        phaseTimer = Debug.timer("PhaseTime_%s", getClass());
-        phaseMetric = Debug.metric("PhaseCount_%s", getClass());
-        phaseMemUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass());
+        timer = Debug.timer("PhaseTime_%s", getClass());
+        executionCount = Debug.metric("PhaseCount_%s", getClass());
+        memUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass());
+        inputNodesCount = Debug.metric("PhaseNodes_%s", getClass());
     }
 
     protected BasePhase(String name) {
         assert checkName(name);
         this.name = name;
-        phaseTimer = Debug.timer("PhaseTime_%s", getClass());
-        phaseMetric = Debug.metric("PhaseCount_%s", getClass());
-        phaseMemUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass());
+        timer = Debug.timer("PhaseTime_%s", getClass());
+        executionCount = Debug.metric("PhaseCount_%s", getClass());
+        memUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass());
+        inputNodesCount = Debug.metric("PhaseNodes_%s", getClass());
     }
 
     protected CharSequence getDetailedName() {
@@ -73,9 +93,10 @@
     }
 
     public final void apply(final StructuredGraph graph, final C context, final boolean dumpGraph) {
-        try (TimerCloseable a = phaseTimer.start(); Scope s = Debug.scope(getClass(), this); Closeable c = phaseMemUseTracker.start()) {
+        try (TimerCloseable a = timer.start(); Scope s = Debug.scope(getClass(), this); Closeable c = memUseTracker.start()) {
             BasePhase.this.run(graph, context);
-            phaseMetric.increment();
+            executionCount.increment();
+            inputNodesCount.add(graph.getNodeCount());
             if (dumpGraph && Debug.isDumpEnabled()) {
                 Debug.dump(graph, "After phase %s", getName());
             }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/SinglePassNodeIterator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/SinglePassNodeIterator.java	Mon May 19 17:21:30 2014 -0700
@@ -58,9 +58,9 @@
     private final NodeBitMap visitedEnds;
 
     /**
-     * @see SinglePassNodeIterator.QElem
+     * @see SinglePassNodeIterator.PathStart
      */
-    private final Deque<QElem<T>> nodeQueue;
+    private final Deque<PathStart<T>> nodeQueue;
 
     /**
      * The keys in this map may be:
@@ -104,23 +104,32 @@
      * <ul>
      * <li>a {@link MergeNode} whose pre-state results from merging those of its forward-ends, see
      * {@link #nextQueuedNode()}</li>
-     * <li>the successor of a control-split node, in which case the pre-state of the successor in
-     * question is also stored in the item, see {@link #nextQueuedNode()}</li>
+     * <li>a successor of a control-split node, in which case the state on entry to it (the
+     * successor) is also stored in the item, see {@link #nextQueuedNode()}</li>
      * </ul>
      * </p>
      */
-    private static class QElem<U> {
-        private final FixedNode node;
-        private final U preState;
+    private static class PathStart<U> {
+        private final BeginNode node;
+        private final U stateOnEntry;
 
-        private QElem(FixedNode node, U preState) {
+        private PathStart(BeginNode node, U stateOnEntry) {
             this.node = node;
-            this.preState = preState;
+            this.stateOnEntry = stateOnEntry;
             assert repOK();
         }
 
+        /**
+         * @return true iff this instance is internally consistent (ie, its "representation is OK")
+         */
         private boolean repOK() {
-            return (node instanceof MergeNode && preState == null) || (node instanceof BeginNode && preState != null);
+            if (node == null) {
+                return false;
+            }
+            if (node instanceof MergeNode) {
+                return stateOnEntry == null;
+            }
+            return (stateOnEntry != null);
         }
     }
 
@@ -188,11 +197,29 @@
         finished();
     }
 
+    /**
+     * Two methods enqueue items in {@link #nodeQueue}. Of them, only this method enqueues items
+     * with non-null state (the other method being {@link #queueMerge(EndNode)}).
+     *
+     * <p>
+     * A space optimization is made: the state is cloned for all successors except the first. Given
+     * that right after invoking this method, {@link #nextQueuedNode()} is invoked, that single
+     * non-cloned state instance is in effect "handed over" to its next owner (thus realizing an
+     * owner-is-mutator access protocol).
+     * </p>
+     */
     private void queueSuccessors(FixedNode x) {
-        for (Node node : x.successors()) {
-            if (node != null) {
-                nodeQueue.addFirst(new QElem<>((BeginNode) node, state));
-            }
+        Iterator<Node> iter = x.successors().nonNull().iterator();
+        if (iter.hasNext()) {
+            BeginNode begin = (BeginNode) iter.next();
+            // the current state isn't cloned for the first successor
+            // conceptually, the state is handed over to it
+            nodeQueue.addFirst(new PathStart<>(begin, state));
+        }
+        while (iter.hasNext()) {
+            BeginNode begin = (BeginNode) iter.next();
+            // for all other successors it is cloned
+            nodeQueue.addFirst(new PathStart<>(begin, state.clone()));
         }
     }
 
@@ -210,10 +237,10 @@
         if (nodeQueue.isEmpty()) {
             return null;
         }
-        QElem<T> elem = nodeQueue.removeFirst();
+        PathStart<T> elem = nodeQueue.removeFirst();
         if (elem.node instanceof MergeNode) {
             MergeNode merge = (MergeNode) elem.node;
-            state = pruneEntry(merge.forwardEndAt(0)).clone();
+            state = pruneEntry(merge.forwardEndAt(0));
             ArrayList<T> states = new ArrayList<>(merge.forwardEndCount() - 1);
             for (int i = 1; i < merge.forwardEndCount(); i++) {
                 T other = pruneEntry(merge.forwardEndAt(i));
@@ -223,9 +250,9 @@
             assert ready : "Not a single-pass iterator after all";
             return merge;
         } else {
-            BeginNode begin = (BeginNode) elem.node;
+            BeginNode begin = elem.node;
             assert begin.predecessor() != null;
-            state = elem.preState.clone();
+            state = elem.stateOnEntry;
             state.afterSplit(begin);
             return begin;
         }
@@ -292,7 +319,7 @@
             }
         }
         if (endsVisited) {
-            nodeQueue.add(new QElem<>(merge, null));
+            nodeQueue.add(new PathStart<>(merge, null));
         }
     }
 
@@ -338,6 +365,7 @@
     private void keepForLater(FixedNode x, T s) {
         assert !nodeStates.containsKey(x);
         assert (x instanceof LoopBeginNode) || (x instanceof LoopEndNode) || (x instanceof EndNode);
+        assert s != null;
         nodeStates.put(x, s);
     }
 
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Mon May 19 17:21:30 2014 -0700
@@ -224,7 +224,8 @@
                     if (pendingStateAfter != null) {
                         pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() {
                             public void apply(Node usage, Node nonVirtualNode) {
-                                assert currentState.isMarked(nonVirtualNode) : nonVirtualNode + " not available at virtualstate " + usage + " at end of block " + block + " \n" + list;
+                                assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode : nonVirtualNode + " not available at virtualstate " + usage +
+                                                " at end of block " + block + " \n" + list;
                             }
                         });
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/AnsiTerminalDecorator.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,59 @@
+/*
+ * 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.test;
+
+import org.junit.runner.*;
+import org.junit.runner.notification.*;
+
+import static com.oracle.graal.debug.AnsiColor.*;
+
+/**
+ * Color support for JUnit test output using ANSI escapes codes.
+ */
+public class AnsiTerminalDecorator extends GraalJUnitRunListenerDecorator {
+
+    public AnsiTerminalDecorator(GraalJUnitRunListener l) {
+        super(l);
+    }
+
+    @Override
+    public void testSucceeded(Description description) {
+        getWriter().print(GREEN);
+        super.testSucceeded(description);
+        getWriter().print(RESET);
+    }
+
+    @Override
+    public void testFailed(Failure failure) {
+        getWriter().print(RED);
+        super.testFailed(failure);
+        getWriter().print(RESET);
+    }
+
+    @Override
+    public void testIgnored(Description description) {
+        getWriter().print(MAGENTA);
+        super.testIgnored(description);
+        getWriter().print(RESET);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/EagerStackTraceDecorator.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,39 @@
+/*
+ * 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.test;
+
+import org.junit.runner.notification.*;
+
+public class EagerStackTraceDecorator extends GraalJUnitRunListenerDecorator {
+
+    public EagerStackTraceDecorator(GraalJUnitRunListener l) {
+        super(l);
+    }
+
+    @Override
+    public void testFailed(Failure failure) {
+        super.testFailed(failure);
+        failure.getException().printStackTrace(getWriter());
+    }
+
+}
--- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java	Mon May 19 17:21:30 2014 -0700
@@ -49,6 +49,8 @@
         List<Failure> missingClasses = new ArrayList<>();
         boolean verbose = false;
         boolean enableTiming = false;
+        boolean color = false;
+        boolean eagerStackTrace = false;
         for (String each : args) {
             if (each.charAt(0) == '-') {
                 // command line arguments
@@ -56,6 +58,10 @@
                     verbose = true;
                 } else if (each.contentEquals("-JUnitEnableTiming")) {
                     enableTiming = true;
+                } else if (each.contentEquals("-JUnitColor")) {
+                    color = true;
+                } else if (each.contentEquals("-JUnitEagerStackTrace")) {
+                    eagerStackTrace = true;
                 } else {
                     system.out().println("Unknown command line argument: " + each);
                 }
@@ -80,6 +86,12 @@
         if (enableTiming) {
             graalListener = new TimingDecorator(graalListener);
         }
+        if (color) {
+            graalListener = new AnsiTerminalDecorator(graalListener);
+        }
+        if (eagerStackTrace) {
+            graalListener = new EagerStackTraceDecorator(graalListener);
+        }
         junitCore.addListener(GraalTextListener.createRunListener(graalListener));
         Result result = junitCore.run(classes.toArray(new Class[0]));
         for (Failure each : missingClasses) {
--- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java	Mon May 19 17:21:30 2014 -0700
@@ -136,6 +136,8 @@
                 Assert.assertEquals((double) expected, (double) actual, delta);
             } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) {
                 Assert.assertEquals((float) expected, (float) actual, delta);
+            } else {
+                Assert.assertEquals(message, expected, actual);
             }
         } else {
             Assert.assertEquals(message, expected, actual);
--- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java	Mon May 19 17:21:30 2014 -0700
@@ -65,8 +65,7 @@
 
     @Override
     public void testFailed(Failure failure) {
-        getWriter().println("FAILED");
-        failure.getException().printStackTrace(getWriter());
+        getWriter().print("FAILED");
     }
 
     @Override
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Mon May 19 17:21:30 2014 -0700
@@ -284,7 +284,6 @@
         if (frames.hasNext()) {
             return new HotSpotFrameInstance.CallTargetFrame(frames.next(), true);
         } else {
-            System.out.println("no current frame found");
             return null;
         }
     }
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java	Mon May 19 17:21:30 2014 -0700
@@ -62,6 +62,20 @@
     }
 
     @Test
+    public void testMulHigh() {
+        test("mulHigh", 7, 15);
+        test("mulHigh", Integer.MAX_VALUE, 15);
+        test("mulHigh", Integer.MIN_VALUE, 15);
+    }
+
+    @Test
+    public void testMulHighUnsigned() {
+        test("mulHighUnsigned", 7, 15);
+        test("mulHighUnsigned", Integer.MAX_VALUE, 15);
+        test("mulHighUnsigned", Integer.MIN_VALUE, 15);
+    }
+
+    @Test
     public void testLongAdd() {
         test("longAdd", (long) Integer.MAX_VALUE, 2L);
         test("longAdd", Long.MAX_VALUE, 2L);
@@ -80,6 +94,20 @@
         test("longSub", Long.MIN_VALUE, 2L);
     }
 
+    @Test
+    public void testLongMulHigh() {
+        test("longMulHigh", 7L, 15L);
+        test("longMulHigh", Long.MAX_VALUE, 15L);
+        test("longMulHigh", Long.MIN_VALUE, 15L);
+    }
+
+    @Test
+    public void testLongMulHighUnsigned() {
+        test("longMulHighUnsigned", 7L, 15L);
+        test("longMulHighUnsigned", Long.MAX_VALUE, 15L);
+        test("longMulHighUnsigned", Long.MIN_VALUE, 15L);
+    }
+
     public static int add(int a, int b) {
         return ExactMath.addExact(a, b);
     }
@@ -92,6 +120,14 @@
         return ExactMath.subtractExact(a, b);
     }
 
+    public static int mulHigh(int a, int b) {
+        return ExactMath.multiplyHigh(a, b);
+    }
+
+    public static int mulHighUnsigned(int a, int b) {
+        return ExactMath.multiplyHighUnsigned(a, b);
+    }
+
     public static long longAdd(long a, long b) {
         return ExactMath.addExact(a, b);
     }
@@ -103,4 +139,12 @@
     public static long longSub(long a, long b) {
         return ExactMath.subtractExact(a, b);
     }
+
+    public static long longMulHigh(long a, long b) {
+        return ExactMath.multiplyHigh(a, b);
+    }
+
+    public static long longMulHighUnsigned(long a, long b) {
+        return ExactMath.multiplyHighUnsigned(a, b);
+    }
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Mon May 19 17:21:30 2014 -0700
@@ -108,7 +108,7 @@
             canonicalizer.apply(graph, baseContext);
 
             // Intrinsify methods.
-            new ReplaceIntrinsicsPhase(providers.getReplacements()).apply(graph);
+            new IncrementalCanonicalizerPhase<>(canonicalizer, new ReplaceIntrinsicsPhase(providers.getReplacements())).apply(graph, baseContext);
 
             Debug.dump(graph, "Before inlining");
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/arithmetic/IntegerMulHighNode.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,75 @@
+/*
+ * 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.truffle.nodes.arithmetic;
+
+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.lir.gen.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.truffle.api.*;
+
+@NodeInfo(shortName = "*H")
+public class IntegerMulHighNode extends IntegerArithmeticNode {
+
+    public IntegerMulHighNode(ValueNode x, ValueNode y) {
+        this(x.stamp().unrestricted(), x, y);
+    }
+
+    public IntegerMulHighNode(Stamp stamp, ValueNode x, ValueNode y) {
+        super(stamp, x, y);
+    }
+
+    @Override
+    public Constant evalConst(Constant... inputs) {
+        assert inputs.length == 2 && inputs[0].getKind() == inputs[1].getKind();
+        switch (inputs[0].getKind()) {
+            case Int:
+                return Constant.forInt(ExactMath.multiplyHigh(inputs[0].asInt(), inputs[1].asInt()));
+            case Long:
+                return Constant.forLong(ExactMath.multiplyHigh(inputs[0].asLong(), inputs[1].asLong()));
+            default:
+                throw GraalInternalError.unimplemented();
+        }
+    }
+
+    @Override
+    public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
+        Value a = builder.operand(x());
+        Value b = builder.operand(y());
+        builder.setResult(this, gen.emitMulHigh(a, b));
+    }
+
+    @NodeIntrinsic
+    public static int multiplyHigh(int a, int b) {
+        return ExactMath.multiplyHigh(a, b);
+    }
+
+    @NodeIntrinsic
+    public static long multiplyHigh(long a, long b) {
+        return ExactMath.multiplyHigh(a, b);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/arithmetic/UnsignedMulHighNode.java	Mon May 19 17:21:30 2014 -0700
@@ -0,0 +1,75 @@
+/*
+ * 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.truffle.nodes.arithmetic;
+
+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.lir.gen.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.truffle.api.*;
+
+@NodeInfo(shortName = "|*H|")
+public class UnsignedMulHighNode extends IntegerArithmeticNode {
+
+    public UnsignedMulHighNode(ValueNode x, ValueNode y) {
+        this(x.stamp().unrestricted(), x, y);
+    }
+
+    public UnsignedMulHighNode(Stamp stamp, ValueNode x, ValueNode y) {
+        super(stamp, x, y);
+    }
+
+    @Override
+    public Constant evalConst(Constant... inputs) {
+        assert inputs.length == 2 && inputs[0].getKind() == inputs[1].getKind();
+        switch (inputs[0].getKind()) {
+            case Int:
+                return Constant.forInt(ExactMath.multiplyHighUnsigned(inputs[0].asInt(), inputs[1].asInt()));
+            case Long:
+                return Constant.forLong(ExactMath.multiplyHighUnsigned(inputs[0].asLong(), inputs[1].asLong()));
+            default:
+                throw GraalInternalError.unimplemented();
+        }
+    }
+
+    @Override
+    public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
+        Value a = builder.operand(x());
+        Value b = builder.operand(y());
+        builder.setResult(this, gen.emitUMulHigh(a, b));
+    }
+
+    @NodeIntrinsic
+    public static int multiplyHighUnsigned(int a, int b) {
+        return ExactMath.multiplyHighUnsigned(a, b);
+    }
+
+    @NodeIntrinsic
+    public static long multiplyHighUnsigned(long a, long b) {
+        return ExactMath.multiplyHighUnsigned(a, b);
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/ExactMathSubstitutions.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/ExactMathSubstitutions.java	Mon May 19 17:21:30 2014 -0700
@@ -62,4 +62,24 @@
     public static long multiplyExact(long x, long y) {
         return IntegerMulExactNode.multiplyExact(x, y);
     }
+
+    @MethodSubstitution
+    public static int multiplyHigh(int x, int y) {
+        return IntegerMulHighNode.multiplyHigh(x, y);
+    }
+
+    @MethodSubstitution
+    public static int multiplyHighUnsigned(int x, int y) {
+        return UnsignedMulHighNode.multiplyHighUnsigned(x, y);
+    }
+
+    @MethodSubstitution
+    public static long multiplyHigh(long x, long y) {
+        return IntegerMulHighNode.multiplyHigh(x, y);
+    }
+
+    @MethodSubstitution
+    public static long multiplyHighUnsigned(long x, long y) {
+        return UnsignedMulHighNode.multiplyHighUnsigned(x, y);
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/VirtualObjectState.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/VirtualObjectState.java	Mon May 19 17:21:30 2014 -0700
@@ -45,7 +45,7 @@
         this.fieldValues = new NodeInputList<>(this, fieldValues);
     }
 
-    private VirtualObjectState(VirtualObjectNode object, List<ValueNode> fieldValues) {
+    public VirtualObjectState(VirtualObjectNode object, List<ValueNode> fieldValues) {
         super(object);
         assert object.entryCount() == fieldValues.size();
         this.fieldValues = new NodeInputList<>(this, fieldValues);
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java	Mon May 19 17:21:30 2014 -0700
@@ -178,15 +178,15 @@
             return;
         }
 
+        Invoke invoke = callTargetNode.invoke();
         if (!callTargetNode.isStatic()) {
             assert callTargetNode.receiver().getKind() == wordKind : "changeToWord() missed the receiver";
-            targetMethod = wordImplType.resolveMethod(targetMethod);
+            targetMethod = wordImplType.resolveMethod(targetMethod, invoke.getContextType());
         }
         Operation operation = targetMethod.getAnnotation(Word.Operation.class);
         assert operation != null : targetMethod;
 
         NodeInputList<ValueNode> arguments = callTargetNode.arguments();
-        Invoke invoke = callTargetNode.invoke();
 
         switch (operation.opcode()) {
             case NODE_CLASS:
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java	Mon May 19 17:21:30 2014 -0700
@@ -113,7 +113,7 @@
             if (!isStatic) {
                 ValueNode receiver = arguments.get(argc);
                 if (receiver == node && isWord(node)) {
-                    ResolvedJavaMethod resolvedMethod = wordAccess.wordImplType.resolveMethod(method);
+                    ResolvedJavaMethod resolvedMethod = wordAccess.wordImplType.resolveMethod(method, invoke.getContextType());
                     verify(resolvedMethod != null, node, invoke.asNode(), "cannot resolve method on Word class: " + MetaUtil.format("%H.%n(%P) %r", method));
                     Operation operation = resolvedMethod.getAnnotation(Word.Operation.class);
                     verify(operation != null, node, invoke.asNode(), "cannot dispatch on word value to non @Operation annotated method " + resolvedMethod);
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExactMath.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExactMath.java	Mon May 19 17:21:30 2014 -0700
@@ -91,4 +91,54 @@
         }
         return r;
     }
+
+    public static int multiplyHigh(int x, int y) {
+        long r = (long) x * (long) y;
+        return (int) (r >> 32);
+    }
+
+    public static int multiplyHighUnsigned(int x, int y) {
+        long xl = x & 0xFFFFFFFFL;
+        long yl = y & 0xFFFFFFFFL;
+        long r = xl * yl;
+        return (int) (r >> 32);
+    }
+
+    public static long multiplyHigh(long x, long y) {
+        long x0, y0, z0;
+        long x1, y1, z1, z2, t;
+
+        x0 = x & 0xFFFFFFFFL;
+        x1 = x >> 32;
+
+        y0 = y & 0xFFFFFFFFL;
+        y1 = y >> 32;
+
+        z0 = x0 * y0;
+        t = x1 * y0 + (z0 >>> 32);
+        z1 = t & 0xFFFFFFFFL;
+        z2 = t >> 32;
+        z1 += x0 * y1;
+
+        return x1 * y1 + z2 + (z1 >> 32);
+    }
+
+    public static long multiplyHighUnsigned(long x, long y) {
+        long x0, y0, z0;
+        long x1, y1, z1, z2, t;
+
+        x0 = x & 0xFFFFFFFFL;
+        x1 = x >>> 32;
+
+        y0 = y & 0xFFFFFFFFL;
+        y1 = y >>> 32;
+
+        z0 = x0 * y0;
+        t = x1 * y0 + (z0 >>> 32);
+        z1 = t & 0xFFFFFFFFL;
+        z2 = t >>> 32;
+        z1 += x0 * y1;
+
+        return x1 * y1 + z2 + (z1 >>> 32);
+    }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon May 19 17:21:30 2014 -0700
@@ -506,4 +506,31 @@
             throw new RuntimeException(e);
         }
     }
+
+    /**
+     * Returns a user-readable description of the purpose of the Node, or "" if no description is
+     * available.
+     */
+    public String getDescription() {
+        NodeInfo info = getClass().getAnnotation(NodeInfo.class);
+        if (info != null) {
+            return info.description();
+        }
+        return "";
+    }
+
+    /**
+     * Returns a string representing the language this node has been implemented for. If the
+     * language is unknown, returns "".
+     */
+    public String getLanguage() {
+        NodeInfo info = getClass().getAnnotation(NodeInfo.class);
+        if (info != null && info.language() != null && info.language().length() > 0) {
+            return info.language();
+        }
+        if (parent != null) {
+            return parent.getLanguage();
+        }
+        return "";
+    }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeInfo.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeInfo.java	Mon May 19 17:21:30 2014 -0700
@@ -35,7 +35,7 @@
 
     /**
      * Short name representing the node that can be used for debugging.
-     * 
+     *
      * @return the short name
      */
     String shortName() default "";
@@ -43,10 +43,26 @@
     /**
      * Provides a rough estimate for the cost of the annotated {@link Node}. This estimate can be
      * used by runtime systems or guest languages to implement heuristics based on Truffle ASTs.
-     * 
+     *
      * @see Node#getCost()
      * @see NodeCost
      */
     NodeCost cost() default NodeCost.MONOMORPHIC;
 
+    /**
+     * A human readable explanation of the purpose of the annotated {@link Node}. Can be used e.g.
+     * for debugging or visualization purposes.
+     *
+     * @return the description
+     */
+    String description() default "";
+
+    /**
+     * A description, providing a user-readable explanation of the source language of the annotated
+     * {@link Node}. Can be used e.g. for debugging or visualization purposes. Typically this
+     * information is set only in an abstract base node for the language implementation.
+     *
+     * @return the description
+     */
+    String language() default "";
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java	Mon May 19 17:21:30 2014 -0700
@@ -112,7 +112,7 @@
 
     /**
      * Support JDK8 langtools.
-     * 
+     *
      * @param annotationType
      */
     public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
@@ -121,7 +121,7 @@
 
     /**
      * Support for some JDK8 builds. (remove after jdk8 is released)
-     * 
+     *
      * @param annotationType
      */
     public <A extends Annotation> A[] getAnnotations(Class<A> annotationType) {
@@ -130,7 +130,7 @@
 
     /**
      * Support for some JDK8 builds. (remove after jdk8 is released)
-     * 
+     *
      * @param annotationType
      */
     public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Mon May 19 17:21:30 2014 -0700
@@ -35,6 +35,7 @@
  * type system for all subclasses.
  */
 @TypeSystemReference(SLTypes.class)
+@NodeInfo(description = "The abstract base node for all expressions")
 public abstract class SLExpressionNode extends SLStatementNode {
 
     /**
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Mon May 19 17:21:30 2014 -0700
@@ -33,6 +33,7 @@
  * builtin functions, the {@link #bodyNode} is a subclass of {@link SLBuiltinNode}. For user-defined
  * functions, the {@link #bodyNode} is a {@link SLFunctionBodyNode}.
  */
+@NodeInfo(language = "Simple Language", description = "The root of all Simple Language execution trees")
 public final class SLRootNode extends RootNode {
 
     /** The function body that is executed, and specialized during execution. */
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Mon May 19 17:21:30 2014 -0700
@@ -30,6 +30,7 @@
  * statements, i.e., without returning a value. The {@link VirtualFrame} provides access to the
  * local variables.
  */
+@NodeInfo(language = "Simple Language", description = "The abstract base node for all statements")
 public abstract class SLStatementNode extends Node {
 
     /**
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBlockNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBlockNode.java	Mon May 19 17:21:30 2014 -0700
@@ -30,7 +30,7 @@
 /**
  * A statement node that just executes a list of other statements.
  */
-@NodeInfo(shortName = "block")
+@NodeInfo(shortName = "block", description = "The node implementing a source code block")
 public final class SLBlockNode extends SLStatementNode {
 
     /**
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBreakNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBreakNode.java	Mon May 19 17:21:30 2014 -0700
@@ -32,7 +32,7 @@
  * breaking out. This is done by throwing an {@link SLBreakException exception} that is caught by
  * the {@link SLWhileNode#executeVoid loop node}.
  */
-@NodeInfo(shortName = "break")
+@NodeInfo(shortName = "break", description = "The node implementing a break statement")
 public final class SLBreakNode extends SLStatementNode {
 
     @Override
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLContinueNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLContinueNode.java	Mon May 19 17:21:30 2014 -0700
@@ -32,7 +32,7 @@
  * are continuing. This is done by throwing an {@link SLContinueException exception} that is caught
  * by the {@link SLWhileNode#executeVoid loop node}.
  */
-@NodeInfo(shortName = "continue")
+@NodeInfo(shortName = "continue", description = "The node implementing a continue statement")
 public final class SLContinueNode extends SLStatementNode {
 
     @Override
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLIfNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLIfNode.java	Mon May 19 17:21:30 2014 -0700
@@ -28,7 +28,7 @@
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.sl.nodes.*;
 
-@NodeInfo(shortName = "if")
+@NodeInfo(shortName = "if", description = "The node implementing a condional statement")
 public final class SLIfNode extends SLStatementNode {
 
     /**
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java	Mon May 19 17:21:30 2014 -0700
@@ -34,7 +34,7 @@
  * caught by the {@link SLFunctionBodyNode#executeGeneric function body}. The exception transports
  * the return value.
  */
-@NodeInfo(shortName = "return")
+@NodeInfo(shortName = "return", description = "The node implementing a return statement")
 public final class SLReturnNode extends SLStatementNode {
 
     @Child private SLExpressionNode valueNode;
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLWhileNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLWhileNode.java	Mon May 19 17:21:30 2014 -0700
@@ -29,7 +29,7 @@
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.sl.nodes.*;
 
-@NodeInfo(shortName = "while")
+@NodeInfo(shortName = "while", description = "The node implementing a while loop")
 public final class SLWhileNode extends SLStatementNode {
 
     /**
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java	Mon May 19 17:14:36 2014 -0700
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java	Mon May 19 17:21:30 2014 -0700
@@ -25,6 +25,7 @@
 import java.math.*;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.sl.nodes.*;
@@ -41,6 +42,7 @@
     }
 
     @Specialization
+    @SlowPath
     protected BigInteger mul(BigInteger left, BigInteger right) {
         return left.multiply(right);
     }
--- a/mx/mx_graal.py	Mon May 19 17:14:36 2014 -0700
+++ b/mx/mx_graal.py	Mon May 19 17:21:30 2014 -0700
@@ -957,7 +957,7 @@
         f_testfile.close()
         harness(projectscp, vmArgs)
 
-def _unittest(args, annotations, prefixcp="", whitelist=None, verbose=False, enable_timing=False, regex=None):
+def _unittest(args, annotations, prefixcp="", whitelist=None, verbose=False, enable_timing=False, regex=None, color=False, eager_stacktrace=False):
     mxdir = dirname(__file__)
     name = 'JUnitWrapper'
     javaSource = join(mxdir, name + '.java')
@@ -976,6 +976,10 @@
         coreArgs.append('-JUnitVerbose')
     if enable_timing:
         coreArgs.append('-JUnitEnableTiming')
+    if color:
+        coreArgs.append('-JUnitColor')
+    if eager_stacktrace:
+        coreArgs.append('-JUnitEagerStackTrace')
 
 
     def harness(projectscp, vmArgs):
@@ -1006,6 +1010,8 @@
       --verbose              enable verbose JUnit output
       --enable-timing        enable JUnit test timing
       --regex <regex>        run only testcases matching a regular expression
+      --color                enable colors output
+      --eager-stacktrace     print stacktrace eagerly
 
     To avoid conflicts with VM options '--' can be used as delimiter.
 
@@ -1046,6 +1052,8 @@
     parser.add_argument('--verbose', help='enable verbose JUnit output', action='store_true')
     parser.add_argument('--enable-timing', help='enable JUnit test timing', action='store_true')
     parser.add_argument('--regex', help='run only testcases matching a regular expression', metavar='<regex>')
+    parser.add_argument('--color', help='enable color output', action='store_true')
+    parser.add_argument('--eager-stacktrace', help='print stacktrace eagerly', action='store_true')
 
     ut_args = []
     delimiter = False
@@ -1064,15 +1072,14 @@
         # parse all know arguments
         parsed_args, args = parser.parse_known_args(ut_args)
 
-    whitelist = None
     if parsed_args.whitelist:
         try:
             with open(join(_graal_home, parsed_args.whitelist)) as fp:
-                whitelist = [re.compile(fnmatch.translate(l.rstrip())) for l in fp.readlines() if not l.startswith('#')]
+                parsed_args.whitelist = [re.compile(fnmatch.translate(l.rstrip())) for l in fp.readlines() if not l.startswith('#')]
         except IOError:
             mx.log('warning: could not read whitelist: ' + parsed_args.whitelist)
 
-    _unittest(args, ['@Test', '@Parameters'], whitelist=whitelist, verbose=parsed_args.verbose, enable_timing=parsed_args.enable_timing, regex=parsed_args.regex)
+    _unittest(args, ['@Test', '@Parameters'], **parsed_args.__dict__)
 
 def shortunittest(args):
     """alias for 'unittest --whitelist test/whitelist_shortunittest.txt'{0}"""
@@ -1535,7 +1542,7 @@
                     if not mx.library(name, fatalIfMissing=False):
                         mx.log('Skipping ' + groupId + '.' + artifactId + '.jar as ' + name + ' cannot be resolved')
                         return
-        d = mx.Distribution(graalSuite, name=artifactId, path=path, sourcesPath=path, deps=deps, excludedDependencies=[])
+        d = mx.Distribution(graalSuite, name=artifactId, path=path, sourcesPath=path, deps=deps, excludedDependencies=[], distDependencies=[])
         d.make_archive()
         cmd = ['mvn', 'install:install-file', '-DgroupId=' + groupId, '-DartifactId=' + artifactId,
                '-Dversion=1.0-SNAPSHOT', '-Dpackaging=jar', '-Dfile=' + d.path]
--- a/mx/projects	Mon May 19 17:14:36 2014 -0700
+++ b/mx/projects	Mon May 19 17:21:30 2014 -0700
@@ -2,8 +2,7 @@
 mxversion=1.0
 suite=graal
 
-library@JDK_TOOLS@path=${JAVA_HOME}/lib/tools.jar
-library@JDK_TOOLS@optional=true
+jrelibrary@JFR@jar=jfr.jar
 
 library@JUNIT@path=lib/junit-4.11.jar
 library@JUNIT@urls=http://repo1.maven.org/maven2/junit/junit/4.11/junit-4.11.jar
@@ -75,6 +74,7 @@
 com.oracle.graal.truffle.hotspot.amd64,\
 com.oracle.graal.hotspot.sparc,\
 com.oracle.graal.hotspot,\
+com.oracle.graal.hotspot.jfr,\
 com.oracle.graal.hotspot.hsail
 distribution@GRAAL@exclude=FINDBUGS
 
@@ -87,7 +87,7 @@
 distribution@TRUFFLE-DSL-PROCESSOR@sourcesPath=truffle-dsl-processor-sources.jar
 distribution@TRUFFLE-DSL-PROCESSOR@dependencies=\
 com.oracle.truffle.dsl.processor
-distribution@TRUFFLE-DSL-PROCESSOR@distDependency=TRUFFLE
+distribution@TRUFFLE-DSL-PROCESSOR@distDependencies=TRUFFLE
 
 # graal.api.collections
 project@com.oracle.graal.api.collections@subDir=graal
@@ -191,6 +191,16 @@
 project@com.oracle.graal.hotspot@javaCompliance=1.8
 project@com.oracle.graal.hotspot@workingSets=Graal,HotSpot
 
+# graal.hotspot.jfr
+project@com.oracle.graal.hotspot.jfr@subDir=graal
+project@com.oracle.graal.hotspot.jfr@sourceDirs=src
+project@com.oracle.graal.hotspot.jfr@dependencies=com.oracle.graal.hotspot,JFR
+project@com.oracle.graal.hotspot.jfr@checkstyle=com.oracle.graal.graph
+project@com.oracle.graal.hotspot.jfr@annotationProcessors=com.oracle.graal.service.processor
+project@com.oracle.graal.hotspot.jfr@javaCompliance=1.8
+project@com.oracle.graal.hotspot.jfr@profile=
+project@com.oracle.graal.hotspot.jfr@workingSets=Graal,HotSpot
+
 # graal.hotspot.amd64
 project@com.oracle.graal.hotspot.amd64@subDir=graal
 project@com.oracle.graal.hotspot.amd64@sourceDirs=src
@@ -568,7 +578,7 @@
 # graal.test
 project@com.oracle.graal.test@subDir=graal
 project@com.oracle.graal.test@sourceDirs=src
-project@com.oracle.graal.test@dependencies=JUNIT
+project@com.oracle.graal.test@dependencies=JUNIT,com.oracle.graal.debug
 project@com.oracle.graal.test@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.test@javaCompliance=1.8
 project@com.oracle.graal.test@workingSets=Graal,Test
--- a/mxtool/mx.py	Mon May 19 17:14:36 2014 -0700
+++ b/mxtool/mx.py	Mon May 19 17:21:30 2014 -0700
@@ -49,6 +49,7 @@
 
 _projects = dict()
 _libs = dict()
+_jreLibs = dict()
 _dists = dict()
 _suites = dict()
 _annotationProcessors = None
@@ -62,7 +63,7 @@
 A distribution is a jar or zip file containing the output from one or more Java projects.
 """
 class Distribution:
-    def __init__(self, suite, name, path, sourcesPath, deps, excludedDependencies, distDependency):
+    def __init__(self, suite, name, path, sourcesPath, deps, excludedDependencies, distDependencies):
         self.suite = suite
         self.name = name
         self.path = path.replace('/', os.sep)
@@ -71,7 +72,7 @@
         self.deps = deps
         self.update_listeners = set()
         self.excludedDependencies = excludedDependencies
-        self.distDependency = distDependency
+        self.distDependencies = distDependencies
 
     def sorted_deps(self, includeLibs=False):
         try:
@@ -123,18 +124,22 @@
                             for arcname in lp.namelist():
                                 overwriteCheck(srcArc.zf, arcname, lpath + '!' + arcname)
                                 srcArc.zf.writestr(arcname, lp.read(arcname))
-                else:
+                elif dep.isProject():
                     p = dep
 
-                    if self.distDependency and p in _dists[self.distDependency].sorted_deps():
-                        logv("Excluding {0} from {1} because it's provided by the dependency {2}".format(p.name, self.path, self.distDependency))
+                    isCoveredByDependecy = False
+                    for d in self.distDependencies:
+                        if p in _dists[d].sorted_deps():
+                            logv("Excluding {0} from {1} because it's provided by the dependency {2}".format(p.name, self.path, d))
+                            isCoveredByDependecy = True
+                            break
+
+                    if isCoveredByDependecy:
                         continue
 
                     # skip a  Java project if its Java compliance level is "higher" than the configured JDK
                     jdk = java(p.javaCompliance)
-                    if not jdk:
-                        log('Excluding {0} from {2} (Java compliance level {1} required)'.format(p.name, p.javaCompliance, self.path))
-                        continue
+                    assert jdk
 
                     logv('[' + self.path + ': adding project ' + p.name + ']')
                     outputDir = p.output_dir()
@@ -201,6 +206,9 @@
     def isLibrary(self):
         return isinstance(self, Library)
 
+    def isJreLibrary(self):
+        return isinstance(self, JreLibrary)
+
     def isProject(self):
         return isinstance(self, Project)
 
@@ -229,7 +237,7 @@
             if not exists(s):
                 os.mkdir(s)
 
-    def all_deps(self, deps, includeLibs, includeSelf=True, includeAnnotationProcessors=False):
+    def all_deps(self, deps, includeLibs, includeSelf=True, includeJreLibs=False, includeAnnotationProcessors=False):
         """
         Add the transitive set of dependencies for this project, including
         libraries if 'includeLibs' is true, to the 'deps' list.
@@ -242,8 +250,8 @@
         for name in childDeps:
             assert name != self.name
             dep = dependency(name)
-            if not dep in deps and (includeLibs or not dep.isLibrary()):
-                dep.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors)
+            if not dep in deps and (dep.isProject or (dep.isLibrary() and includeLibs) or (dep.isJreLibrary() and includeJreLibs)):
+                dep.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors)
         if not self in deps and includeSelf:
             deps.append(self)
         return deps
@@ -506,17 +514,74 @@
 
     return path
 
-class Library(Dependency):
-    def __init__(self, suite, name, path, mustExist, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps):
+class BaseLibrary(Dependency):
+    def __init__(self, suite, name, optional):
         Dependency.__init__(self, suite, name)
+        self.optional = optional
+
+    def __ne__(self, other):
+        result = self.__eq__(other)
+        if result is NotImplemented:
+            return result
+        return not result
+
+"""
+A library that will be provided by the JDK but may be absent.
+Any project or normal library that depends on a missing library
+will be removed from the global project and library dictionaries
+(i.e., _projects and _libs).
+
+This mechanism exists primarily to be able to support code
+that may use functionality in one JDK (e.g., Oracle JDK)
+that is not present in another JDK (e.g., OpenJDK). A
+motivating example is the Java Flight Recorder library
+found in the Oracle JDK. 
+"""
+class JreLibrary(BaseLibrary):
+    def __init__(self, suite, name, jar, optional):
+        BaseLibrary.__init__(self, suite, name, optional)
+        self.jar = jar
+
+    def __eq__(self, other):
+        if isinstance(other, JreLibrary):
+            return self.jar == other.jar
+        else:
+            return NotImplemented
+
+    def is_present_in_jdk(self, jdk):
+        for e in jdk.bootclasspath().split(os.pathsep):
+            if basename(e) == self.jar:
+                return True
+        for d in jdk.extdirs().split(os.pathsep):
+            if len(d) and self.jar in os.listdir(d):
+                return True
+        for d in jdk.endorseddirs().split(os.pathsep):
+            if len(d) and self.jar in os.listdir(d):
+                return True
+        return False
+
+    def all_deps(self, deps, includeLibs, includeSelf=True, includeJreLibs=False, includeAnnotationProcessors=False):
+        """
+        Add the transitive set of dependencies for this JRE library to the 'deps' list.
+        """
+        if includeJreLibs and includeSelf and not self in deps:
+            deps.append(self)
+        return deps
+
+class Library(BaseLibrary):
+    def __init__(self, suite, name, path, optional, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps):
+        BaseLibrary.__init__(self, suite, name, optional)
         self.path = path.replace('/', os.sep)
         self.urls = urls
         self.sha1 = sha1
-        self.mustExist = mustExist
         self.sourcePath = sourcePath
         self.sourceUrls = sourceUrls
         self.sourceSha1 = sourceSha1
         self.deps = deps
+        abspath = _make_absolute(self.path, self.suite.dir)
+        if not optional and not exists(abspath):
+            if not len(urls):
+                abort('Non-optional library {} must either exist at {} or specify one or more URLs from which it can be retrieved'.format(name, abspath))
         for url in urls:
             if url.endswith('/') != self.path.endswith(os.sep):
                 abort('Path for dependency directory must have a URL ending with "/": path=' + self.path + ' url=' + url)
@@ -530,14 +595,6 @@
         else:
             return NotImplemented
 
-
-    def __ne__(self, other):
-        result = self.__eq__(other)
-        if result is NotImplemented:
-            return result
-        return not result
-
-
     def get_path(self, resolve):
         path = _make_absolute(self.path, self.suite.dir)
         sha1path = path + '.sha1'
@@ -546,8 +603,7 @@
         if includedInJDK and java().javaCompliance >= JavaCompliance(includedInJDK):
             return None
 
-        return _download_file_with_sha1(self.name, path, self.urls, self.sha1, sha1path, resolve, self.mustExist)
-
+        return _download_file_with_sha1(self.name, path, self.urls, self.sha1, sha1path, resolve, not self.optional)
 
     def get_source_path(self, resolve):
         if self.sourcePath is None:
@@ -562,7 +618,7 @@
         if path and (exists(path) or not resolve):
             cp.append(path)
 
-    def all_deps(self, deps, includeLibs, includeSelf=True, includeAnnotationProcessors=False):
+    def all_deps(self, deps, includeLibs, includeSelf=True, includeJreLibs=False, includeAnnotationProcessors=False):
         """
         Add the transitive set of dependencies for this library to the 'deps' list.
         """
@@ -575,7 +631,7 @@
             assert name != self.name
             dep = library(name)
             if not dep in deps:
-                dep.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors)
+                dep.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors)
         if not self in deps and includeSelf:
             deps.append(self)
         return deps
@@ -631,6 +687,7 @@
         self.mxDir = mxDir
         self.projects = []
         self.libs = []
+        self.jreLibs = []
         self.dists = []
         self.commands = None
         self.primary = primary
@@ -648,6 +705,7 @@
 
     def _load_projects(self):
         libsMap = dict()
+        jreLibsMap = dict()
         projsMap = dict()
         distsMap = dict()
         projectsFile = join(self.mxDir, 'projects')
@@ -697,6 +755,8 @@
                         m = projsMap
                     elif kind == 'library':
                         m = libsMap
+                    elif kind == 'jrelibrary':
+                        m = jreLibsMap
                     elif kind == 'distribution':
                         m = distsMap
                     else:
@@ -737,16 +797,24 @@
             p.__dict__.update(attrs)
             self.projects.append(p)
 
+        for name, attrs in jreLibsMap.iteritems():
+            jar = attrs.pop('jar')
+            # JRE libraries are optional by default
+            optional = attrs.pop('optional', 'true') != 'false'
+            l = JreLibrary(self, name, jar, optional)
+            self.jreLibs.append(l)
+
         for name, attrs in libsMap.iteritems():
             path = attrs.pop('path')
-            mustExist = attrs.pop('optional', 'false') != 'true'
             urls = pop_list(attrs, 'urls')
             sha1 = attrs.pop('sha1', None)
             sourcePath = attrs.pop('sourcePath', None)
             sourceUrls = pop_list(attrs, 'sourceUrls')
             sourceSha1 = attrs.pop('sourceSha1', None)
             deps = pop_list(attrs, 'dependencies')
-            l = Library(self, name, path, mustExist, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps)
+            # Add support optional libraries once we have a good use case
+            optional = False
+            l = Library(self, name, path, optional, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps)
             l.__dict__.update(attrs)
             self.libs.append(l)
 
@@ -755,8 +823,8 @@
             sourcesPath = attrs.pop('sourcesPath', None)
             deps = pop_list(attrs, 'dependencies')
             exclDeps = pop_list(attrs, 'exclude')
-            distDep = attrs.pop('distDependency', None)
-            d = Distribution(self, name, path, sourcesPath, deps, exclDeps, distDep)
+            distDeps = pop_list(attrs, 'distDependencies')
+            d = Distribution(self, name, path, sourcesPath, deps, exclDeps, distDeps)
             d.__dict__.update(attrs)
             self.dists.append(d)
 
@@ -836,6 +904,12 @@
             if existing is not None and existing != l:
                 abort('inconsistent library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir)
             _libs[l.name] = l
+        for l in self.jreLibs:
+            existing = _jreLibs.get(l.name)
+            # Check that suites that define same library are consistent
+            if existing is not None and existing != l:
+                abort('inconsistent JRE library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir)
+            _jreLibs[l.name] = l
         for d in self.dists:
             existing = _dists.get(d.name)
             if existing is not None:
@@ -844,6 +918,54 @@
                 warn('distribution ' + d.name + ' redefined')
                 d.path = existing.path
             _dists[d.name] = d
+
+        # Remove projects and libraries that (recursively) depend on an optional library
+        # whose artifact does not exist or on a JRE library that is not present in the
+        # JDK for a project. Also remove projects whose Java compliance requirement
+        # cannot be satisfied by the configured JDKs.
+        #
+        # Removed projects and libraries are also removed from
+        # distributions in they are listed as dependencies.
+        for d in sorted_deps(includeLibs=True):
+            if d.isLibrary():
+                if d.optional:
+                    try:
+                        d.optional = False
+                        path = d.get_path(resolve=True)
+                    except SystemExit:
+                        path = None
+                    finally:
+                        d.optional = True
+                    if not path:
+                        logv('[omitting optional library {} as {} does not exist]'.format(d, d.path))
+                        del _libs[d.name]
+                        self.libs.remove(d)
+            elif d.isProject():
+                if java(d.javaCompliance) is None:
+                    logv('[omitting project {} as Java compliance {} cannot be satisfied by configured JDKs]'.format(d, d.javaCompliance))
+                    del _projects[d.name]
+                    self.projects.remove(d)
+                else:
+                    for name in list(d.deps):
+                        jreLib = _jreLibs.get(name)
+                        if jreLib:
+                            if not jreLib.is_present_in_jdk(java(d.javaCompliance)):
+                                if jreLib.optional:
+                                    logv('[omitting project {} as dependency {} is missing]'.format(d, name))
+                                    del _projects[d.name]
+                                    self.projects.remove(d)
+                                else:
+                                    abort('JRE library {} required by {} not found'.format(jreLib, d))
+                        elif not dependency(name, fatalIfMissing=False):
+                            logv('[omitting project {} as dependency {} is missing]'.format(d, name))
+                            del _projects[d.name]
+                            self.projects.remove(d)
+        for dist in _dists.values():
+            for name in list(dist.deps):
+                if not dependency(name, fatalIfMissing=False):
+                    logv('[omitting {} from distribution {}]'.format(name, dist))
+                    dist.deps.remove(name)
+
         if hasattr(self, 'mx_post_parse_cmd_line'):
             self.mx_post_parse_cmd_line(opts)
 
@@ -1025,6 +1147,8 @@
     d = _projects.get(name)
     if d is None:
         d = _libs.get(name)
+        if d is None:
+            d = _jreLibs.get(name)
     if d is None and fatalIfMissing:
         if name in _opts.ignored_projects:
             abort('project named ' + name + ' is ignored')
@@ -1381,11 +1505,11 @@
         sub = _addSubprocess(p, args)
         if callable(out):
             t = Thread(target=redirect, args=(p.stdout, out))
-            t.daemon = True  # thread dies with the program
+            # Don't make the reader thread a daemon otherwise output can be droppped
             t.start()
         if callable(err):
             t = Thread(target=redirect, args=(p.stderr, err))
-            t.daemon = True  # thread dies with the program
+            # Don't make the reader thread a daemon otherwise output can be droppped
             t.start()
         if timeout is None or timeout == 0:
             retcode = waitOn(p)
@@ -1540,6 +1664,8 @@
         self.javadoc = exe_suffix(join(self.jdk, 'bin', 'javadoc'))
         self.toolsjar = join(self.jdk, 'lib', 'tools.jar')
         self._bootclasspath = None
+        self._extdirs = None
+        self._endorseddirs = None
 
         if not exists(self.java):
             abort('Java launcher does not exist: ' + self.java)
@@ -1708,8 +1834,6 @@
     if _opts.killwithsigquit:
         _send_sigquit()
 
-    # import traceback
-    # traceback.print_stack()
     for p, args in _currentSubprocesses:
         try:
             if get_os() == 'windows':
@@ -1719,6 +1843,9 @@
         except BaseException as e:
             log('error while killing subprocess {} "{}": {}'.format(p.pid, ' '.join(args), e))
 
+    if _opts and _opts.verbose:
+        import traceback
+        traceback.print_stack()
     raise SystemExit(codeOrMessage)
 
 def download(path, urls, verbose=False):
@@ -2024,9 +2151,7 @@
         # skip building this Java project if its Java compliance level is "higher" than the configured JDK
         requiredCompliance = p.javaCompliance if p.javaCompliance else JavaCompliance(args.compliance) if args.compliance else None
         jdk = java(requiredCompliance)
-        if not jdk:
-            log('Excluding {0} from build (Java compliance level {1} required)'.format(p.name, requiredCompliance))
-            continue
+        assert jdk
 
         outputDir = prepareOutputDirs(p, args.clean)
 
@@ -2622,9 +2747,7 @@
 
         # skip checking this Java project if its Java compliance level is "higher" than the configured JDK
         jdk = java(p.javaCompliance)
-        if not jdk:
-            log('Excluding {0} from checking (Java compliance level {1} required)'.format(p.name, p.javaCompliance))
-            continue
+        assert jdk
 
         for sourceDir in sourceDirs:
             javafilelist = []
@@ -2868,10 +2991,10 @@
             elif dep.get_source_path(resolve=True):
                 memento = XMLDoc().element('archive', {'detectRoot' : 'true', 'path' : dep.get_source_path(resolve=True)}).xml(standalone='no')
                 slm.element('container', {'memento' : memento, 'typeId':'org.eclipse.debug.core.containerType.externalArchive'})
-        else:
+        elif dep.isProject():
             memento = XMLDoc().element('javaProject', {'name' : dep.name}).xml(standalone='no')
             slm.element('container', {'memento' : memento, 'typeId':'org.eclipse.jdt.launching.sourceContainer.javaProject'})
-            if javaCompliance is None or dep.javaCompliance < javaCompliance:
+            if javaCompliance is None or dep.javaCompliance > javaCompliance:
                 javaCompliance = dep.javaCompliance
 
     if javaCompliance:
@@ -3035,9 +3158,7 @@
         if p.native:
             continue
 
-        if not java(p.javaCompliance):
-            log('Excluding {0} (JDK with compliance level {1} not available)'.format(p.name, p.javaCompliance))
-            continue
+        assert java(p.javaCompliance)
 
         if not exists(p.dir):
             os.makedirs(p.dir)
@@ -3078,7 +3199,7 @@
                     libraryDeps -= set(dep.all_deps([], True))
                 else:
                     libraryDeps.add(dep)
-            else:
+            elif dep.isProject():
                 projectDeps.add(dep)
 
         for dep in containerDeps:
@@ -3087,8 +3208,6 @@
         for dep in libraryDeps:
             path = dep.path
             dep.get_path(resolve=True)
-            if not path or (not exists(path) and not dep.mustExist):
-                continue
 
             # Relative paths for "lib" class path entries have various semantics depending on the Eclipse
             # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's
@@ -3246,16 +3365,13 @@
                 for dep in dependency(ap).all_deps([], True):
                     if dep.isLibrary():
                         if not hasattr(dep, 'eclipse.container') and not hasattr(dep, 'eclipse.project'):
-                            if dep.mustExist:
-                                path = dep.get_path(resolve=True)
-                                if path:
-                                    # Relative paths for "lib" class path entries have various semantics depending on the Eclipse
-                                    # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's
-                                    # safest to simply use absolute paths.
-                                    path = _make_absolute(path, p.suite.dir)
-                                    out.element('factorypathentry', {'kind' : 'EXTJAR', 'id' : path, 'enabled' : 'true', 'runInBatchMode' : 'false'})
-                                    files.append(path)
-                    else:
+                            # Relative paths for "lib" class path entries have various semantics depending on the Eclipse
+                            # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's
+                            # safest to simply use absolute paths.
+                            path = _make_absolute(dep.get_path(resolve=True), p.suite.dir)
+                            out.element('factorypathentry', {'kind' : 'EXTJAR', 'id' : path, 'enabled' : 'true', 'runInBatchMode' : 'false'})
+                            files.append(path)
+                    elif dep.isProject():
                         out.element('factorypathentry', {'kind' : 'WKSPJAR', 'id' : '/' + dep.name + '/' + dep.name + '.jar', 'enabled' : 'true', 'runInBatchMode' : 'false'})
             out.close('factorypath')
             update_file(join(p.dir, '.factorypath'), out.xml(indent='\t', newl='\n'))
@@ -3558,10 +3674,7 @@
             os.makedirs(join(p.dir, 'nbproject'))
 
         jdk = java(p.javaCompliance)
-
-        if not jdk:
-            log('Excluding {0} (JDK with compliance level {1} not available)'.format(p.name, p.javaCompliance))
-            continue
+        assert jdk
 
         jdks.add(jdk)
 
@@ -3602,7 +3715,7 @@
             if dep == p:
                 continue
 
-            if not dep.isLibrary():
+            if dep.isProject():
                 n = dep.name.replace('.', '_')
                 if firstDep:
                     out.open('references', {'xmlns' : 'http://www.netbeans.org/ns/ant-project-references/1'})
@@ -3737,8 +3850,6 @@
                 continue
 
             if dep.isLibrary():
-                if not dep.mustExist:
-                    continue
                 path = dep.get_path(resolve=True)
                 if path:
                     if os.sep == '\\':
@@ -3747,7 +3858,7 @@
                     print >> out, ref + '=' + path
                     libFiles.append(path)
 
-            else:
+            elif dep.isProject():
                 n = dep.name.replace('.', '_')
                 relDepPath = os.path.relpath(dep.dir, p.dir).replace(os.sep, '/')
                 ref = 'reference.' + n + '.jar'
@@ -3814,9 +3925,7 @@
         if p.native:
             continue
 
-        if not java(p.javaCompliance):
-            log('Excluding {0} (JDK with compliance level {1} not available)'.format(p.name, p.javaCompliance))
-            continue
+        assert java(p.javaCompliance)
 
         if not exists(p.dir):
             os.makedirs(p.dir)
@@ -3863,10 +3972,9 @@
                 continue
 
             if dep.isLibrary():
-                if dep.mustExist:
-                    libraries.add(dep)
-                    moduleXml.element('orderEntry', attributes={'type': 'library', 'name': dep.name, 'level': 'project'})
-            else:
+                libraries.add(dep)
+                moduleXml.element('orderEntry', attributes={'type': 'library', 'name': dep.name, 'level': 'project'})
+            elif dep.isProject():
                 moduleXml.element('orderEntry', attributes={'type': 'module', 'module-name': dep.name})
 
         moduleXml.close('component')
@@ -3938,7 +4046,7 @@
                 for entry in pDep.all_deps([], True):
                     if entry.isLibrary():
                         compilerXml.element('entry', attributes={'name': '$PROJECT_DIR$/' + os.path.relpath(entry.path, suite.dir)})
-                    else:
+                    elif entry.isProject():
                         assert entry.isProject()
                         compilerXml.element('entry', attributes={'name': '$PROJECT_DIR$/' + os.path.relpath(entry.output_dir(), suite.dir)})
             compilerXml.close('processorPath')
--- a/src/share/vm/graal/graalCodeInstaller.cpp	Mon May 19 17:14:36 2014 -0700
+++ b/src/share/vm/graal/graalCodeInstaller.cpp	Mon May 19 17:21:30 2014 -0700
@@ -605,10 +605,11 @@
   Klass* context = java_lang_Class::as_Klass(HotSpotResolvedObjectType::javaClass(context_handle));
   Klass* subtype = java_lang_Class::as_Klass(HotSpotResolvedObjectType::javaClass(subtype_handle));
 
-  _dependencies->assert_leaf_type(subtype);
   if (context != subtype) {
     assert(context->is_abstract(), "");
     _dependencies->assert_abstract_with_unique_concrete_subtype(context, subtype);
+  } else {
+    _dependencies->assert_leaf_type(subtype);
   }
 }
 
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Mon May 19 17:14:36 2014 -0700
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Mon May 19 17:21:30 2014 -0700
@@ -468,11 +468,76 @@
   return (jlong) (address) result.field_holder();
 C2V_END
 
-C2V_VMENTRY(jlong, resolveMethod, (JNIEnv *, jobject, jlong metaspace_klass, jstring name, jstring signature))
+C2V_VMENTRY(jint, getVtableIndexForInterface, (JNIEnv *, jobject, jlong metaspace_klass, jlong metaspace_method))
   Klass* klass = (Klass*) metaspace_klass;
-  Symbol* name_symbol = java_lang_String::as_symbol(JNIHandles::resolve(name), THREAD);
-  Symbol* signature_symbol = java_lang_String::as_symbol(JNIHandles::resolve(signature), THREAD);
-  return (jlong) (address) klass->lookup_method(name_symbol, signature_symbol);
+  Method* method = (Method*) metaspace_method;
+
+  return LinkResolver::vtable_index_of_interface_method(klass, method);
+C2V_END
+
+C2V_VMENTRY(jlong, resolveMethod, (JNIEnv *, jobject, jlong metaspace_klass_receiver, jlong metaspace_method, jlong metaspace_klass_caller))
+  Klass* recv_klass = (Klass*) metaspace_klass_receiver;
+  Klass* caller_klass = (Klass*) metaspace_klass_caller;
+  Method* method = (Method*) metaspace_method;
+
+  if (recv_klass->oop_is_array() || (InstanceKlass::cast(recv_klass)->is_linked())) {
+    Klass* holder_klass = method->method_holder();
+    Symbol* method_name = method->name();
+    Symbol* method_signature = method->signature();
+
+
+    if (holder_klass->is_interface()) {
+      // do link-time resolution to check all access rules.
+      methodHandle resolved_method;
+      LinkResolver::linktime_resolve_interface_method(resolved_method, holder_klass, method_name, method_signature, caller_klass, true, CHECK_AND_CLEAR_0);
+      if (resolved_method->is_private()) {
+        return (jlong) NULL;
+      }
+      assert(recv_klass->is_subtype_of(holder_klass), "");
+      // do actual lookup
+      methodHandle sel_method;
+      LinkResolver::lookup_instance_method_in_klasses(sel_method, recv_klass,
+                resolved_method->name(),
+                resolved_method->signature(), CHECK_AND_CLEAR_0);
+      return (jlong) (address) sel_method();
+    } else {
+      // do link-time resolution to check all access rules.
+      methodHandle resolved_method;
+      LinkResolver::linktime_resolve_virtual_method(resolved_method, holder_klass, method_name, method_signature, caller_klass, true, CHECK_AND_CLEAR_0);
+      // do actual lookup (see LinkResolver::runtime_resolve_virtual_method)
+      int vtable_index = Method::invalid_vtable_index;
+      Method* selected_method;
+
+      if (resolved_method->method_holder()->is_interface()) { // miranda method
+        vtable_index = LinkResolver::vtable_index_of_interface_method(holder_klass, resolved_method);
+        assert(vtable_index >= 0 , "we should have valid vtable index at this point");
+
+        InstanceKlass* inst = InstanceKlass::cast(recv_klass);
+        selected_method = inst->method_at_vtable(vtable_index);
+      } else {
+        // at this point we are sure that resolved_method is virtual and not
+        // a miranda method; therefore, it must have a valid vtable index.
+        assert(!resolved_method->has_itable_index(), "");
+        vtable_index = resolved_method->vtable_index();
+        // We could get a negative vtable_index for final methods,
+        // because as an optimization they are they are never put in the vtable,
+        // unless they override an existing method.
+        // If we do get a negative, it means the resolved method is the the selected
+        // method, and it can never be changed by an override.
+        if (vtable_index == Method::nonvirtual_vtable_index) {
+          assert(resolved_method->can_be_statically_bound(), "cannot override this method");
+          selected_method = resolved_method();
+        } else {
+          // recv_klass might be an arrayKlassOop but all vtables start at
+          // the same place. The cast is to avoid virtual call and assertion.
+          InstanceKlass* inst = (InstanceKlass*)recv_klass;
+          selected_method = inst->method_at_vtable(vtable_index);
+        }
+      }
+      return (jlong) (address) selected_method;
+    }
+  }
+  return (jlong) NULL;
 C2V_END
 
 C2V_VMENTRY(jboolean, hasFinalizableSubclass,(JNIEnv *, jobject, jlong metaspace_klass))
@@ -1058,58 +1123,59 @@
 #define METASPACE_SYMBOL      "J"
 
 JNINativeMethod CompilerToVM_methods[] = {
-  {CC"initializeBytecode",                           CC"("METASPACE_METHOD"[B)[B",                                     FN_PTR(initializeBytecode)},
-  {CC"exceptionTableStart",                          CC"("METASPACE_METHOD")J",                                        FN_PTR(exceptionTableStart)},
-  {CC"exceptionTableLength",                         CC"("METASPACE_METHOD")I",                                        FN_PTR(exceptionTableLength)},
-  {CC"hasBalancedMonitors",                          CC"("METASPACE_METHOD")Z",                                        FN_PTR(hasBalancedMonitors)},
-  {CC"findUniqueConcreteMethod",                     CC"("METASPACE_METHOD")"METASPACE_METHOD,                         FN_PTR(findUniqueConcreteMethod)},
-  {CC"getKlassImplementor",                          CC"("METASPACE_KLASS")"METASPACE_KLASS,                           FN_PTR(getKlassImplementor)},
-  {CC"getStackTraceElement",                         CC"("METASPACE_METHOD"I)"STACK_TRACE_ELEMENT,                     FN_PTR(getStackTraceElement)},
-  {CC"methodIsIgnoredBySecurityStackWalk",           CC"("METASPACE_METHOD")Z",                                        FN_PTR(methodIsIgnoredBySecurityStackWalk)},
-  {CC"doNotInlineOrCompile",                         CC"("METASPACE_METHOD")V",                                        FN_PTR(doNotInlineOrCompile)},
-  {CC"canInlineMethod",                              CC"("METASPACE_METHOD")Z",                                        FN_PTR(canInlineMethod)},
-  {CC"shouldInlineMethod",                           CC"("METASPACE_METHOD")Z",                                        FN_PTR(shouldInlineMethod)},
-  {CC"lookupType",                                   CC"("STRING CLASS"Z)"METASPACE_KLASS,                             FN_PTR(lookupType)},
-  {CC"resolveConstantInPool",                        CC"("METASPACE_CONSTANT_POOL"I)"OBJECT,                           FN_PTR(resolveConstantInPool)},
-  {CC"resolvePossiblyCachedConstantInPool",          CC"("METASPACE_CONSTANT_POOL"I)"OBJECT,                           FN_PTR(resolvePossiblyCachedConstantInPool)},
-  {CC"lookupNameRefInPool",                          CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL,                 FN_PTR(lookupNameRefInPool)},
-  {CC"lookupNameAndTypeRefIndexInPool",              CC"("METASPACE_CONSTANT_POOL"I)I",                                FN_PTR(lookupNameAndTypeRefIndexInPool)},
-  {CC"lookupSignatureRefInPool",                     CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL,                 FN_PTR(lookupSignatureRefInPool)},
-  {CC"lookupKlassRefIndexInPool",                    CC"("METASPACE_CONSTANT_POOL"I)I",                                FN_PTR(lookupKlassRefIndexInPool)},
-  {CC"constantPoolKlassAt",                          CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS,                  FN_PTR(constantPoolKlassAt)},
-  {CC"lookupKlassInPool",                            CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS,                  FN_PTR(lookupKlassInPool)},
-  {CC"lookupAppendixInPool",                         CC"("METASPACE_CONSTANT_POOL"I)"OBJECT,                           FN_PTR(lookupAppendixInPool)},
-  {CC"lookupMethodInPool",                           CC"("METASPACE_CONSTANT_POOL"IB)"METASPACE_METHOD,                FN_PTR(lookupMethodInPool)},
-  {CC"constantPoolRemapInstructionOperandFromCache", CC"("METASPACE_CONSTANT_POOL"I)I",                                FN_PTR(constantPoolRemapInstructionOperandFromCache)},
-  {CC"resolveField",                                 CC"("METASPACE_CONSTANT_POOL"IB[J)"METASPACE_KLASS,               FN_PTR(resolveField)},
-  {CC"resolveInvokeDynamic",                         CC"("METASPACE_CONSTANT_POOL"I)V",                                FN_PTR(resolveInvokeDynamic)},
-  {CC"resolveMethod",                                CC"("METASPACE_KLASS STRING STRING")"METASPACE_METHOD,            FN_PTR(resolveMethod)},
-  {CC"getClassInitializer",                          CC"("METASPACE_KLASS")"METASPACE_METHOD,                          FN_PTR(getClassInitializer)},
-  {CC"hasFinalizableSubclass",                       CC"("METASPACE_KLASS")Z",                                         FN_PTR(hasFinalizableSubclass)},
-  {CC"getMaxCallTargetOffset",                       CC"(J)J",                                                         FN_PTR(getMaxCallTargetOffset)},
-  {CC"getMetaspaceMethod",                           CC"("CLASS"I)"METASPACE_METHOD,                                   FN_PTR(getMetaspaceMethod)},
-  {CC"initializeConfiguration",                      CC"("HS_CONFIG")V",                                               FN_PTR(initializeConfiguration)},
-  {CC"installCode0",                                 CC"("HS_COMPILED_CODE INSTALLED_CODE SPECULATION_LOG")I",         FN_PTR(installCode0)},
-  {CC"notifyCompilationStatistics",                  CC"(I"HS_RESOLVED_METHOD"ZIJJ"INSTALLED_CODE")V",                 FN_PTR(notifyCompilationStatistics)},
-  {CC"printCompilationStatistics",                   CC"(ZZ)V",                                                        FN_PTR(printCompilationStatistics)},
-  {CC"resetCompilationStatistics",                   CC"()V",                                                          FN_PTR(resetCompilationStatistics)},
-  {CC"disassembleCodeBlob",                          CC"(J)"STRING,                                                    FN_PTR(disassembleCodeBlob)},
-  {CC"executeCompiledMethodVarargs",                 CC"(["OBJECT INSTALLED_CODE")"OBJECT,                             FN_PTR(executeCompiledMethodVarargs)},
-  {CC"getLineNumberTable",                           CC"("METASPACE_METHOD")[J",                                       FN_PTR(getLineNumberTable)},
-  {CC"getLocalVariableTableStart",                   CC"("METASPACE_METHOD")J",                                        FN_PTR(getLocalVariableTableStart)},
-  {CC"getLocalVariableTableLength",                  CC"("METASPACE_METHOD")I",                                        FN_PTR(getLocalVariableTableLength)},
-  {CC"reprofile",                                    CC"("METASPACE_METHOD")V",                                        FN_PTR(reprofile)},
-  {CC"invalidateInstalledCode",                      CC"("INSTALLED_CODE")V",                                          FN_PTR(invalidateInstalledCode)},
-  {CC"getJavaMirror",                                CC"("METASPACE_KLASS")"CLASS,                                     FN_PTR(getJavaMirror)},
-  {CC"readUnsafeKlassPointer",                       CC"("OBJECT")J",                                                  FN_PTR(readUnsafeKlassPointer)},
-  {CC"collectCounters",                              CC"()[J",                                                         FN_PTR(collectCounters)},
-  {CC"getGPUs",                                      CC"()"STRING,                                                     FN_PTR(getGPUs)},
-  {CC"allocateCompileId",                            CC"("METASPACE_METHOD"I)I",                                       FN_PTR(allocateCompileId)},
-  {CC"isMature",                                     CC"("METASPACE_METHOD_DATA")Z",                                   FN_PTR(isMature)},
-  {CC"hasCompiledCodeForOSR",                        CC"("METASPACE_METHOD"II)Z",                                      FN_PTR(hasCompiledCodeForOSR)},
-  {CC"getTimeStamp",                                 CC"()J",                                                          FN_PTR(getTimeStamp)},
-  {CC"getNextStackFrame",                            CC"("HS_STACK_FRAME_REF "[JI)"HS_STACK_FRAME_REF,                 FN_PTR(getNextStackFrame)},
-  {CC"materializeVirtualObjects",                    CC"("HS_STACK_FRAME_REF"Z)V",                                     FN_PTR(materializeVirtualObjects)},
+  {CC"initializeBytecode",                           CC"("METASPACE_METHOD"[B)[B",                                             FN_PTR(initializeBytecode)},
+  {CC"exceptionTableStart",                          CC"("METASPACE_METHOD")J",                                                FN_PTR(exceptionTableStart)},
+  {CC"exceptionTableLength",                         CC"("METASPACE_METHOD")I",                                                FN_PTR(exceptionTableLength)},
+  {CC"hasBalancedMonitors",                          CC"("METASPACE_METHOD")Z",                                                FN_PTR(hasBalancedMonitors)},
+  {CC"findUniqueConcreteMethod",                     CC"("METASPACE_METHOD")"METASPACE_METHOD,                                 FN_PTR(findUniqueConcreteMethod)},
+  {CC"getKlassImplementor",                          CC"("METASPACE_KLASS")"METASPACE_KLASS,                                   FN_PTR(getKlassImplementor)},
+  {CC"getStackTraceElement",                         CC"("METASPACE_METHOD"I)"STACK_TRACE_ELEMENT,                             FN_PTR(getStackTraceElement)},
+  {CC"methodIsIgnoredBySecurityStackWalk",           CC"("METASPACE_METHOD")Z",                                                FN_PTR(methodIsIgnoredBySecurityStackWalk)},
+  {CC"doNotInlineOrCompile",                         CC"("METASPACE_METHOD")V",                                                FN_PTR(doNotInlineOrCompile)},
+  {CC"canInlineMethod",                              CC"("METASPACE_METHOD")Z",                                                FN_PTR(canInlineMethod)},
+  {CC"shouldInlineMethod",                           CC"("METASPACE_METHOD")Z",                                                FN_PTR(shouldInlineMethod)},
+  {CC"lookupType",                                   CC"("STRING CLASS"Z)"METASPACE_KLASS,                                     FN_PTR(lookupType)},
+  {CC"resolveConstantInPool",                        CC"("METASPACE_CONSTANT_POOL"I)"OBJECT,                                   FN_PTR(resolveConstantInPool)},
+  {CC"resolvePossiblyCachedConstantInPool",          CC"("METASPACE_CONSTANT_POOL"I)"OBJECT,                                   FN_PTR(resolvePossiblyCachedConstantInPool)},
+  {CC"lookupNameRefInPool",                          CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL,                         FN_PTR(lookupNameRefInPool)},
+  {CC"lookupNameAndTypeRefIndexInPool",              CC"("METASPACE_CONSTANT_POOL"I)I",                                        FN_PTR(lookupNameAndTypeRefIndexInPool)},
+  {CC"lookupSignatureRefInPool",                     CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL,                         FN_PTR(lookupSignatureRefInPool)},
+  {CC"lookupKlassRefIndexInPool",                    CC"("METASPACE_CONSTANT_POOL"I)I",                                        FN_PTR(lookupKlassRefIndexInPool)},
+  {CC"constantPoolKlassAt",                          CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS,                          FN_PTR(constantPoolKlassAt)},
+  {CC"lookupKlassInPool",                            CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS,                          FN_PTR(lookupKlassInPool)},
+  {CC"lookupAppendixInPool",                         CC"("METASPACE_CONSTANT_POOL"I)"OBJECT,                                   FN_PTR(lookupAppendixInPool)},
+  {CC"lookupMethodInPool",                           CC"("METASPACE_CONSTANT_POOL"IB)"METASPACE_METHOD,                        FN_PTR(lookupMethodInPool)},
+  {CC"constantPoolRemapInstructionOperandFromCache", CC"("METASPACE_CONSTANT_POOL"I)I",                                        FN_PTR(constantPoolRemapInstructionOperandFromCache)},
+  {CC"resolveField",                                 CC"("METASPACE_CONSTANT_POOL"IB[J)"METASPACE_KLASS,                       FN_PTR(resolveField)},
+  {CC"resolveInvokeDynamic",                         CC"("METASPACE_CONSTANT_POOL"I)V",                                        FN_PTR(resolveInvokeDynamic)},
+  {CC"resolveMethod",                                CC"("METASPACE_KLASS METASPACE_METHOD METASPACE_KLASS")"METASPACE_METHOD, FN_PTR(resolveMethod)},
+  {CC"getVtableIndexForInterface",                   CC"("METASPACE_KLASS METASPACE_METHOD")I",                                FN_PTR(getVtableIndexForInterface)},
+  {CC"getClassInitializer",                          CC"("METASPACE_KLASS")"METASPACE_METHOD,                                  FN_PTR(getClassInitializer)},
+  {CC"hasFinalizableSubclass",                       CC"("METASPACE_KLASS")Z",                                                 FN_PTR(hasFinalizableSubclass)},
+  {CC"getMaxCallTargetOffset",                       CC"(J)J",                                                                 FN_PTR(getMaxCallTargetOffset)},
+  {CC"getMetaspaceMethod",                           CC"("CLASS"I)"METASPACE_METHOD,                                           FN_PTR(getMetaspaceMethod)},
+  {CC"initializeConfiguration",                      CC"("HS_CONFIG")V",                                                       FN_PTR(initializeConfiguration)},
+  {CC"installCode0",                                 CC"("HS_COMPILED_CODE INSTALLED_CODE SPECULATION_LOG")I",                 FN_PTR(installCode0)},
+  {CC"notifyCompilationStatistics",                  CC"(I"HS_RESOLVED_METHOD"ZIJJ"INSTALLED_CODE")V",                         FN_PTR(notifyCompilationStatistics)},
+  {CC"printCompilationStatistics",                   CC"(ZZ)V",                                                                FN_PTR(printCompilationStatistics)},
+  {CC"resetCompilationStatistics",                   CC"()V",                                                                  FN_PTR(resetCompilationStatistics)},
+  {CC"disassembleCodeBlob",                          CC"(J)"STRING,                                                            FN_PTR(disassembleCodeBlob)},
+  {CC"executeCompiledMethodVarargs",                 CC"(["OBJECT INSTALLED_CODE")"OBJECT,                                     FN_PTR(executeCompiledMethodVarargs)},
+  {CC"getLineNumberTable",                           CC"("METASPACE_METHOD")[J",                                               FN_PTR(getLineNumberTable)},
+  {CC"getLocalVariableTableStart",                   CC"("METASPACE_METHOD")J",                                                FN_PTR(getLocalVariableTableStart)},
+  {CC"getLocalVariableTableLength",                  CC"("METASPACE_METHOD")I",                                                FN_PTR(getLocalVariableTableLength)},
+  {CC"reprofile",                                    CC"("METASPACE_METHOD")V",                                                FN_PTR(reprofile)},
+  {CC"invalidateInstalledCode",                      CC"("INSTALLED_CODE")V",                                                  FN_PTR(invalidateInstalledCode)},
+  {CC"getJavaMirror",                                CC"("METASPACE_KLASS")"CLASS,                                             FN_PTR(getJavaMirror)},
+  {CC"readUnsafeKlassPointer",                       CC"("OBJECT")J",                                                          FN_PTR(readUnsafeKlassPointer)},
+  {CC"collectCounters",                              CC"()[J",                                                                 FN_PTR(collectCounters)},
+  {CC"getGPUs",                                      CC"()"STRING,                                                             FN_PTR(getGPUs)},
+  {CC"allocateCompileId",                            CC"("METASPACE_METHOD"I)I",                                               FN_PTR(allocateCompileId)},
+  {CC"isMature",                                     CC"("METASPACE_METHOD_DATA")Z",                                           FN_PTR(isMature)},
+  {CC"hasCompiledCodeForOSR",                        CC"("METASPACE_METHOD"II)Z",                                              FN_PTR(hasCompiledCodeForOSR)},
+  {CC"getTimeStamp",                                 CC"()J",                                                                  FN_PTR(getTimeStamp)},
+  {CC"getNextStackFrame",                            CC"("HS_STACK_FRAME_REF "[JI)"HS_STACK_FRAME_REF,                         FN_PTR(getNextStackFrame)},
+  {CC"materializeVirtualObjects",                    CC"("HS_STACK_FRAME_REF"Z)V",                                             FN_PTR(materializeVirtualObjects)},
 };
 
 int CompilerToVM_methods_count() {
--- a/src/share/vm/graal/vmStructs_graal.hpp	Mon May 19 17:14:36 2014 -0700
+++ b/src/share/vm/graal/vmStructs_graal.hpp	Mon May 19 17:21:30 2014 -0700
@@ -66,5 +66,7 @@
   declare_constant(CodeInstaller::POLL_FAR)                                                       \
   declare_constant(CodeInstaller::POLL_RETURN_FAR)                                                \
   declare_constant(CodeInstaller::INVOKE_INVALID)                                                 \
+                                                                                                  \
+  declare_constant(Method::invalid_vtable_index)                                                  \
 
 #endif // SHARE_VM_GRAAL_VMSTRUCTS_GRAAL_HPP
--- a/src/share/vm/interpreter/linkResolver.hpp	Mon May 19 17:14:36 2014 -0700
+++ b/src/share/vm/interpreter/linkResolver.hpp	Mon May 19 17:21:30 2014 -0700
@@ -125,7 +125,13 @@
 
  private:
   static void lookup_method_in_klasses          (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, bool checkpolymorphism, bool in_imethod_resolve, TRAPS);
+#ifdef GRAAL
+ public:
+#endif
   static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
+#ifdef GRAAL
+ private:
+#endif
   static void lookup_method_in_interfaces       (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
   static void lookup_polymorphic_method         (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature,
                                                  KlassHandle current_klass, Handle *appendix_result_or_null, Handle *method_type_result, TRAPS);
@@ -139,8 +145,14 @@
 
   static void linktime_resolve_static_method    (methodHandle& resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS);
   static void linktime_resolve_special_method   (methodHandle& resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS);
+#ifdef GRAAL
+ public:
+#endif
   static void linktime_resolve_virtual_method   (methodHandle &resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature,KlassHandle current_klass, bool check_access, TRAPS);
   static void linktime_resolve_interface_method (methodHandle& resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS);
+#ifdef GRAAL
+ private:
+#endif
 
   static void runtime_resolve_special_method    (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, KlassHandle current_klass, bool check_access, TRAPS);
   static void runtime_resolve_virtual_method    (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS);
--- a/src/share/vm/runtime/deoptimization.cpp	Mon May 19 17:14:36 2014 -0700
+++ b/src/share/vm/runtime/deoptimization.cpp	Mon May 19 17:21:30 2014 -0700
@@ -1348,7 +1348,7 @@
     ScopeDesc*      trap_scope  = cvf->scope();
     
     if (TraceDeoptimization) {
-      tty->print_cr("  bci=%d pc=%d, relative_pc=%d, method=%s" GRAAL_ONLY(", debug_id=%d"), trap_scope->bci(), fr.pc(), fr.pc() - nm->code_begin(), trap_scope->method()->name_and_sig_as_C_string()
+      tty->print_cr("  bci=%d pc=" INTPTR_FORMAT ", relative_pc=%d, method=%s" GRAAL_ONLY(", debug_id=%d"), trap_scope->bci(), fr.pc(), fr.pc() - nm->code_begin(), trap_scope->method()->name_and_sig_as_C_string()
 #ifdef GRAAL
           , debug_id
 #endif
--- a/src/share/vm/runtime/globals.hpp	Mon May 19 17:14:36 2014 -0700
+++ b/src/share/vm/runtime/globals.hpp	Mon May 19 17:21:30 2014 -0700
@@ -3058,7 +3058,7 @@
           "If non-zero, maximum number of words that malloc/realloc can "   \
           "allocate (for testing only)")                                    \
                                                                             \
-  product(intx, TypeProfileWidth,     2,                                    \
+  product_pd(intx, TypeProfileWidth,                                        \
           "Number of receiver types to record in call/cast profile")        \
                                                                             \
   product_pd(intx, MethodProfileWidth,                                      \
--- a/src/share/vm/runtime/java.cpp	Mon May 19 17:14:36 2014 -0700
+++ b/src/share/vm/runtime/java.cpp	Mon May 19 17:21:30 2014 -0700
@@ -462,15 +462,6 @@
   #define BEFORE_EXIT_DONE    2
   static jint volatile _before_exit_status = BEFORE_EXIT_NOT_RUN;
 
-#ifdef GRAAL
-#ifdef COMPILERGRAAL
-  if (GraalCompiler::instance() != NULL) {
-    GraalCompiler::instance()->shutdown();
-  }
-#endif
-  VMToCompiler::shutdownRuntime();
-#endif
-
   // Note: don't use a Mutex to guard the entire before_exit(), as
   // JVMTI post_thread_end_event and post_vm_death_event will run native code.
   // A CAS or OSMutex would work just fine but then we need to manipulate
@@ -492,6 +483,15 @@
     }
   }
 
+#ifdef GRAAL
+#ifdef COMPILERGRAAL
+  if (GraalCompiler::instance() != NULL) {
+    GraalCompiler::instance()->shutdown();
+  }
+#endif
+  VMToCompiler::shutdownRuntime();
+#endif
+
   // The only difference between this and Win32's _onexit procs is that
   // this version is invoked before any threads get killed.
   ExitProc* current = exit_procs;