changeset 15881:a6eeb3750238

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Wed, 21 May 2014 11:45:50 +0200
parents a43ff5d18350 (current diff) b5a993ed67ea (diff)
children e751da27fd48
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConstant.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMField.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMFlag.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMType.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java 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/InliningIterator.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultSourceSection.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java
diffstat 212 files changed, 8031 insertions(+), 4491 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Tue May 13 19:19:27 2014 +0200
+++ b/CHANGELOG.md	Wed May 21 11:45:50 2014 +0200
@@ -3,6 +3,9 @@
 ## `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.
+* ResolvedJavaType.resolveMethod now takes a context type used to perform access checks. It now works correctly regarding default methods.
 
 ### 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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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/TestResolvedJavaField.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaField.java	Wed May 21 11:45:50 2014 +0200
@@ -164,6 +164,9 @@
     public void testCoverage() {
         Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
         for (Method m : ResolvedJavaField.class.getDeclaredMethods()) {
+            if (m.isSynthetic()) {
+                continue;
+            }
             if (findTestMethod(m) == null) {
                 assertTrue("test missing for " + m, known.contains(m.getName()));
             } else {
--- a/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java	Wed May 21 11:45:50 2014 +0200
@@ -211,10 +211,11 @@
      * {@link #findUniqueConcreteMethod(ResolvedJavaMethod)}.
      *
      * @param method the method to select the implementation of
+     * @param callerType the caller or context type used to perform access checks
      * @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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ObjectStamp.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Wed May 21 11:45:50 2014 +0200
@@ -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;
     }
 
     /**
--- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java	Wed May 21 11:45:50 2014 +0200
@@ -34,16 +34,19 @@
 import java.util.logging.*;
 
 import com.amd.okra.*;
+import com.oracle.graal.test.*;
 
 /**
  * Abstract class on which the HSAIL unit tests are built. Executes a method or lambda on both the
  * Java side and the Okra side and compares the results for fields that are annotated with
- * {@link KernelTester.Result}.
+ * {@link Result}.
  */
-public abstract class KernelTester {
+public abstract class KernelTester extends GraalTest {
 
     /**
-     * Denotes a field whose value is to be compared as part of computing the result of a test.
+     * Denotes a field whose value is to be
+     * {@linkplain KernelTester#assertResultFieldsEqual(KernelTester) compared} as part of computing
+     * the result of a test.
      */
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.FIELD)
@@ -129,171 +132,27 @@
 
     public abstract void runTest();
 
-    // Default comparison is to compare all things marked @Result.
-    public boolean compareResults(KernelTester base) {
+    /**
+     * Asserts that the value of all {@link Result} annotated fields in this object and
+     * {@code other} are {@linkplain #assertDeepEquals(Object, Object) equal}.
+     *
+     * @throws AssertionError if the value of a result field in this and {@code other} are not equal
+     */
+    public void assertResultFieldsEqual(KernelTester other) {
         Class<?> clazz = this.getClass();
         while (clazz != null && clazz != KernelTester.class) {
             for (Field f : clazz.getDeclaredFields()) {
                 if (!Modifier.isStatic(f.getModifiers())) {
                     Result annos = f.getAnnotation(Result.class);
                     if (annos != null) {
-                        logger.fine("@Result field = " + f);
-                        Object myResult = getFieldFromObject(f, this);
-                        Object otherResult = getFieldFromObject(f, base);
-                        boolean same = compareObjects(myResult, otherResult);
-                        logger.fine("comparing " + myResult + ", " + otherResult + ", match=" + same);
-                        if (!same) {
-                            logger.severe("mismatch comparing " + f + ", " + myResult + " vs. " + otherResult);
-                            logSevere("FAILED!!! " + this.getClass());
-                            return false;
-                        }
+                        Object actualResult = getFieldFromObject(f, this);
+                        Object expectedResult = getFieldFromObject(f, other);
+                        assertDeepEquals(f.toString(), expectedResult, actualResult);
                     }
                 }
             }
             clazz = clazz.getSuperclass();
         }
-        logInfo("PASSED: " + this.getClass());
-        return true;
-    }
-
-    private boolean compareObjects(Object first, Object second) {
-        if (first == null) {
-            return (second == null);
-        }
-        if (second == null) {
-            return (first == null);
-        }
-        Class<?> clazz = first.getClass();
-        if (clazz != second.getClass()) {
-            return false;
-        }
-        if (!clazz.isArray()) {
-            // Non arrays.
-            if (clazz.equals(float.class) || clazz.equals(double.class)) {
-                return isEqualsFP((double) first, (double) second);
-            } else {
-                return first.equals(second);
-            }
-        } else {
-            // Handle the case where Objects are arrays.
-            ArrayComparer comparer;
-            if (clazz.equals(float[].class) || clazz.equals(double[].class)) {
-                comparer = new FPArrayComparer();
-            } else if (clazz.equals(long[].class) || clazz.equals(int[].class) || clazz.equals(byte[].class)) {
-                comparer = new IntArrayComparer();
-            } else if (clazz.equals(boolean[].class)) {
-                comparer = new BooleanArrayComparer();
-            } else {
-                comparer = new ObjArrayComparer();
-            }
-            return comparer.compareArrays(first, second);
-        }
-    }
-
-    static final int MISMATCHLIMIT = 10;
-    static final int ELEMENTDISPLAYLIMIT = 20;
-
-    public int getMisMatchLimit() {
-        return MISMATCHLIMIT;
-    }
-
-    public int getElementDisplayLimit() {
-        return ELEMENTDISPLAYLIMIT;
-    }
-
-    abstract class ArrayComparer {
-
-        abstract Object getElement(Object ary, int index);
-
-        // Equality test, can be overridden
-        boolean isEquals(Object firstElement, Object secondElement) {
-            return firstElement.equals(secondElement);
-        }
-
-        boolean compareArrays(Object first, Object second) {
-            int len = Array.getLength(first);
-            if (len != Array.getLength(second)) {
-                return false;
-            }
-            // If info logLevel, build string of first few elements from first array.
-            if (logLevel.intValue() <= Level.INFO.intValue()) {
-                StringBuilder sb = new StringBuilder();
-                for (int i = 0; i < Math.min(len, getElementDisplayLimit()); i++) {
-                    sb.append(getElement(first, i));
-                    sb.append(", ");
-                }
-                logger.info(sb.toString());
-            }
-            boolean success = true;
-            int mismatches = 0;
-            for (int i = 0; i < len; i++) {
-                Object firstElement = getElement(first, i);
-                Object secondElement = getElement(second, i);
-                if (!isEquals(firstElement, secondElement)) {
-                    logSevere("mismatch at index " + i + ", expected " + secondElement + ", saw " + firstElement);
-                    success = false;
-                    mismatches++;
-                    if (mismatches >= getMisMatchLimit()) {
-                        logSevere("...Truncated");
-                        break;
-                    }
-                }
-            }
-            return success;
-        }
-    }
-
-    class FPArrayComparer extends ArrayComparer {
-
-        @Override
-        Object getElement(Object ary, int index) {
-            return Array.getDouble(ary, index);
-        }
-
-        @Override
-        boolean isEquals(Object firstElement, Object secondElement) {
-            return isEqualsFP((double) firstElement, (double) secondElement);
-        }
-    }
-
-    class IntArrayComparer extends ArrayComparer {
-
-        @Override
-        Object getElement(Object ary, int index) {
-            return Array.getLong(ary, index);
-        }
-    }
-
-    class BooleanArrayComparer extends ArrayComparer {
-
-        @Override
-        Object getElement(Object ary, int index) {
-            return Array.getBoolean(ary, index);
-        }
-    }
-
-    class ObjArrayComparer extends ArrayComparer {
-
-        @Override
-        Object getElement(Object ary, int index) {
-            return Array.get(ary, index);
-        }
-
-        @Override
-        boolean isEquals(Object firstElement, Object secondElement) {
-            return compareObjects(firstElement, secondElement);
-        }
-    }
-
-    /**
-     * Tests two floating point values for equality.
-     */
-    public boolean isEqualsFP(double first, double second) {
-        // Special case for checking whether expected and actual values are both NaNs.
-        if (Double.isNaN(first) && Double.isNaN(second)) {
-            return true;
-        }
-        return first == second;
     }
 
     public void setDispatchMode(DispatchMode dispatchMode) {
@@ -761,8 +620,8 @@
         }
     }
 
-    private void compareOkraToSeq(HsailMode hsailModeToUse) {
-        compareOkraToSeq(hsailModeToUse, false);
+    private void assertOkraEqualsSeq(HsailMode hsailModeToUse) {
+        assertOkraEqualsSeq(hsailModeToUse, false);
     }
 
     /**
@@ -770,7 +629,7 @@
      * runOkraFirst flag controls which order they are done in. Note the compiler must use eager
      * resolving if Okra is done first.
      */
-    private void compareOkraToSeq(HsailMode hsailModeToUse, boolean useLambda) {
+    private void assertOkraEqualsSeq(HsailMode hsailModeToUse, boolean useLambda) {
         KernelTester testerSeq;
         if (runOkraFirst) {
             runOkraInstance(hsailModeToUse, useLambda);
@@ -779,7 +638,7 @@
             testerSeq = runSeqInstance();
             runOkraInstance(hsailModeToUse, useLambda);
         }
-        assertTrue("failed comparison to SEQ", compareResults(testerSeq));
+        assertResultFieldsEqual(testerSeq);
     }
 
     private void runOkraInstance(HsailMode hsailModeToUse, boolean useLambda) {
@@ -800,19 +659,19 @@
     }
 
     public void testGeneratedHsail() {
-        compareOkraToSeq(HsailMode.COMPILED);
+        assertOkraEqualsSeq(HsailMode.COMPILED);
     }
 
     public void testGeneratedHsailUsingLambdaMethod() {
-        compareOkraToSeq(HsailMode.COMPILED, true);
+        assertOkraEqualsSeq(HsailMode.COMPILED, true);
     }
 
     public void testInjectedHsail() {
-        newInstance().compareOkraToSeq(HsailMode.INJECT_HSAIL);
+        newInstance().assertOkraEqualsSeq(HsailMode.INJECT_HSAIL);
     }
 
     public void testInjectedOpencl() {
-        newInstance().compareOkraToSeq(HsailMode.INJECT_OCL);
+        newInstance().assertOkraEqualsSeq(HsailMode.INJECT_OCL);
     }
 
     protected static Object getFieldFromObject(Field f, Object fromObj) {
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/BoundsCatchManyBase.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/BoundsCatchManyBase.java	Wed May 21 11:45:50 2014 +0200
@@ -33,11 +33,6 @@
         return (gid < 4096 && gid % 512 == 1);
     }
 
-    @Override
-    public int getMisMatchLimit() {
-        return 1000;
-    }
-
     public void run(int gid) {
         int outval = getOutval(gid);
         try {
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/EscapingNewStringConcatTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/EscapingNewStringConcatTest.java	Wed May 21 11:45:50 2014 +0200
@@ -24,7 +24,7 @@
 
 import static com.oracle.graal.debug.Debug.*;
 
-import org.junit.Test;
+import org.junit.*;
 
 import com.oracle.graal.debug.*;
 
@@ -51,7 +51,8 @@
     }
 
     // Node implementing Lowerable not handled in HSAIL Backend: 6274|MonitorEnter
-    @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class)
+    @Ignore
+    @Test
     public void test() {
         try (DebugConfigScope s = disableIntercept()) {
             testGeneratedHsail();
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java	Wed May 21 11:45:50 2014 +0200
@@ -49,8 +49,8 @@
     }
 
     @Override
-    public boolean isEqualsFP(double first, double second) {
-        return Math.abs(first - second) == 0;
+    protected double equalFloatsOrDoublesDelta() {
+        return 0.0D;
     }
 
     @Test
--- /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	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.graal.compiler.hsail.test.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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallTest.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Wed May 21 11:45:50 2014 +0200
@@ -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/BoxingEliminationTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java	Wed May 21 11:45:50 2014 +0200
@@ -300,7 +300,7 @@
 
     final ValueNode getResult(String snippet) {
         processMethod(snippet);
-        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        assertDeepEquals(1, graph.getNodes(ReturnNode.class).count());
         return graph.getNodes(ReturnNode.class).first().result();
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java	Wed May 21 11:45:50 2014 +0200
@@ -132,7 +132,7 @@
         result = getResult(getCanonicalizedGraph("integerTestCanonicalization2"));
         assertTrue(result.isConstant() && result.asConstant().asLong() == 1);
         StructuredGraph graph = getCanonicalizedGraph("integerTestCanonicalization3");
-        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        assertDeepEquals(1, graph.getNodes(ReturnNode.class).count());
         assertTrue(graph.getNodes(ReturnNode.class).first().result() instanceof ConditionalNode);
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ConditionalEliminationTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ConditionalEliminationTest.java	Wed May 21 11:45:50 2014 +0200
@@ -100,7 +100,7 @@
         new ConditionalEliminationPhase(getMetaAccess()).apply(graph, context);
         canonicalizer.apply(graph, context);
 
-        assertEquals(1, graph.getNodes().filter(GuardNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(GuardNode.class).count());
     }
 
     public static String testInstanceOfCheckCastSnippet(Object e) {
@@ -123,7 +123,7 @@
         new ConditionalEliminationPhase(getMetaAccess()).apply(graph, context);
         canonicalizer.apply(graph, context);
 
-        assertEquals(0, graph.getNodes().filter(GuardNode.class).count());
+        assertDeepEquals(0, graph.getNodes().filter(GuardNode.class).count());
     }
 
 }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSenReduTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSenReduTest.java	Wed May 21 11:45:50 2014 +0200
@@ -61,7 +61,7 @@
 
     @Test
     public void redundantCheckCastTest() {
-        assertEquals(i7, redundantCheckCastSnippet(i7));
+        assertDeepEquals(i7, redundantCheckCastSnippet(i7));
         StructuredGraph result = afterFlowSensitiveReduce("redundantCheckCastSnippet");
         nodeCountEquals(result, CheckCastNode.class, 0);
         nodeCountEquals(result, InstanceOfNode.class, 1);
@@ -79,7 +79,7 @@
     @Test
     public void redundantInstanceOfTest01() {
         String snippet = "redundantInstanceOfSnippet01";
-        assertEquals(true, redundantInstanceOfSnippet01(i7));
+        assertDeepEquals(true, redundantInstanceOfSnippet01(i7));
         nodeCountEquals(afterFlowSensitiveReduce(snippet), InstanceOfNode.class, 1);
     }
 
@@ -100,9 +100,9 @@
     @Test
     public void redundantInstanceOfTest02() {
         String snippet = "redundantInstanceOfSnippet02";
-        assertEquals(i7, redundantInstanceOfSnippet02(i7));
+        assertDeepEquals(i7, redundantInstanceOfSnippet02(i7));
         int ioAfter = getNodes(afterFlowSensitiveReduce(snippet), InstanceOfNode.class).size();
-        assertEquals(ioAfter, 1);
+        assertDeepEquals(ioAfter, 1);
     }
 
     /*
@@ -121,18 +121,18 @@
     @Test
     public void devirtualizationTest() {
         String snippet = "devirtualizationSnippet";
-        assertEquals(i7, devirtualizationSnippet(i7, i7));
+        assertDeepEquals(i7, devirtualizationSnippet(i7, i7));
         nodeCountEquals(afterFlowSensitiveReduce(snippet), CheckCastNode.class, 0);
 
         StructuredGraph graph = afterFlowSensitiveReduce(snippet);
-        assertEquals(0, graph.getNodes().filter(CheckCastNode.class).count());
+        assertDeepEquals(0, graph.getNodes().filter(CheckCastNode.class).count());
 
         List<InvokeNode> invokeNodes = getNodes(afterFlowSensitiveReduce(snippet), InvokeNode.class);
-        assertEquals(1, invokeNodes.size());
+        assertDeepEquals(1, invokeNodes.size());
 
         MethodCallTargetNode target = (MethodCallTargetNode) invokeNodes.get(0).callTarget();
-        assertEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind());
-        assertEquals("HotSpotMethod<Integer.intValue()>", target.targetMethod().toString());
+        assertDeepEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind());
+        assertDeepEquals("HotSpotMethod<Integer.intValue()>", target.targetMethod().toString());
     }
 
     /*
@@ -154,7 +154,7 @@
     @Test
     public void t5a() {
         String snippet = "t5Snippet";
-        assertEquals(false, t5Snippet(null, true));
+        assertDeepEquals(false, t5Snippet(null, true));
         StructuredGraph resultGraph = canonicalize(afterFlowSensitiveReduce(snippet));
         nodeCountEquals(resultGraph, ReturnNode.class, 2);
 
@@ -164,8 +164,8 @@
         ConstantNode c1 = (ConstantNode) iter.next().result();
         ConstantNode c2 = (ConstantNode) iter.next().result();
 
-        assertEquals(c1, c2);
-        assertEquals(0, c1.getValue().asInt());
+        assertDeepEquals(c1, c2);
+        assertDeepEquals(0, c1.getValue().asInt());
     }
 
     @Test
@@ -215,16 +215,16 @@
         StructuredGraph graph = afterFlowSensitiveReduce(snippet);
         graph = dce(canonicalize(graph));
         // TODO how to simplify IfNode(false)
-        assertEquals(1, getNodes(graph, InstanceOfNode.class).size());
+        assertDeepEquals(1, getNodes(graph, InstanceOfNode.class).size());
 
         List<ReturnNode> returnNodes = getNodes(graph, ReturnNode.class);
-        assertEquals(2, returnNodes.size());
+        assertDeepEquals(2, returnNodes.size());
         Iterator<ReturnNode> iter = returnNodes.iterator();
 
         ConstantNode c1 = (ConstantNode) iter.next().result();
         ConstantNode c2 = (ConstantNode) iter.next().result();
 
-        assertEquals(c1, c2);
+        assertDeepEquals(c1, c2);
         Assert.assertTrue(c1.getValue().isNull());
     }
 
@@ -253,14 +253,14 @@
         String snippet = "devirtualizationSnippet02";
         StructuredGraph graph = afterFlowSensitiveReduce(snippet);
 
-        assertEquals(1, getNodes(graph, InvokeNode.class).size());
+        assertDeepEquals(1, getNodes(graph, InvokeNode.class).size());
 
         List<InvokeNode> invokeNodes = getNodes(graph, InvokeNode.class);
-        assertEquals(1, invokeNodes.size());
+        assertDeepEquals(1, invokeNodes.size());
 
         MethodCallTargetNode target = (MethodCallTargetNode) invokeNodes.get(0).callTarget();
-        assertEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind());
-        assertEquals("HotSpotMethod<Integer.intValue()>", target.targetMethod().toString());
+        assertDeepEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind());
+        assertDeepEquals("HotSpotMethod<Integer.intValue()>", target.targetMethod().toString());
     }
 
     /*
@@ -312,7 +312,7 @@
         dce(graph);
 
         List<ReturnNode> returnNodes = getNodes(graph, ReturnNode.class);
-        assertEquals(2, returnNodes.size());
+        assertDeepEquals(2, returnNodes.size());
         Iterator<ReturnNode> iter = returnNodes.iterator();
 
         ValueNode c1 = GraphUtil.unproxify(iter.next().result());
@@ -339,7 +339,7 @@
         String snippet = "deduplicateInstanceOfSnippet";
         StructuredGraph graph = afterFlowSensitiveReduce(snippet);
         List<InstanceOfNode> ioNodes = getNodes(graph, InstanceOfNode.class);
-        assertEquals(1, ioNodes.size());
+        assertDeepEquals(1, ioNodes.size());
 
     }
 
@@ -371,7 +371,7 @@
     }
 
     public <N extends Node> void nodeCountEquals(StructuredGraph graph, Class<N> nodeClass, int expected) {
-        assertEquals(expected, getNodes(graph, nodeClass).size());
+        assertDeepEquals(expected, getNodes(graph, nodeClass).size());
     }
 
     public StructuredGraph afterFlowSensitiveReduce(String snippet) {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSensitiveReductionTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSensitiveReductionTest.java	Wed May 21 11:45:50 2014 +0200
@@ -209,7 +209,7 @@
         new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context);
 
         InvokeNode invoke = graph.getNodes().filter(InvokeNode.class).first();
-        assertEquals(InvokeKind.Special, ((MethodCallTargetNode) invoke.callTarget()).invokeKind());
+        assertDeepEquals(InvokeKind.Special, ((MethodCallTargetNode) invoke.callTarget()).invokeKind());
     }
 
     public static void testTypeMergingSnippet(Object o, boolean b) {
@@ -240,7 +240,7 @@
         new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context);
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null));
 
-        assertEquals(0, graph.getNodes().filter(StoreFieldNode.class).count());
+        assertDeepEquals(0, graph.getNodes().filter(StoreFieldNode.class).count());
     }
 
     public static String testInstanceOfCheckCastSnippet(Object e) {
@@ -258,7 +258,7 @@
         new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context);
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null));
 
-        assertEquals(0, graph.getNodes().filter(CheckCastNode.class).count());
+        assertDeepEquals(0, graph.getNodes().filter(CheckCastNode.class).count());
     }
 
     public static int testDuplicateNullChecksSnippet(Object a) {
@@ -287,7 +287,7 @@
         new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context);
         canonicalizer.apply(graph, context);
 
-        assertEquals(1, graph.getNodes().filter(GuardNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(GuardNode.class).count());
     }
 
 }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Wed May 21 11:45:50 2014 +0200
@@ -528,7 +528,7 @@
                 actual.exception.printStackTrace();
                 Assert.fail("expected " + expect.returnValue + " but got an exception");
             }
-            assertEquals(expect.returnValue, actual.returnValue);
+            assertDeepEquals(expect.returnValue, actual.returnValue);
         }
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java	Wed May 21 11:45:50 2014 +0200
@@ -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.test/src/com/oracle/graal/compiler/test/InfopointReasonTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InfopointReasonTest.java	Wed May 21 11:45:50 2014 +0200
@@ -66,7 +66,7 @@
         for (Infopoint sp : cr.getInfopoints()) {
             assertNotNull(sp.reason);
             if (sp instanceof Call) {
-                assertEquals(InfopointReason.CALL, sp.reason);
+                assertDeepEquals(InfopointReason.CALL, sp.reason);
             }
         }
     }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java	Wed May 21 11:45:50 2014 +0200
@@ -65,8 +65,8 @@
         StructuredGraph graph = getGraph("testSynchronizedSnippet");
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null));
         new LockEliminationPhase().apply(graph);
-        assertEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count());
-        assertEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
     }
 
     public static void testSynchronizedMethodSnippet(A x) {
@@ -83,8 +83,8 @@
         StructuredGraph graph = getGraph("testSynchronizedMethodSnippet");
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null));
         new LockEliminationPhase().apply(graph);
-        assertEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count());
-        assertEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
     }
 
     private StructuredGraph getGraph(String snippet) {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Wed May 21 11:45:50 2014 +0200
@@ -164,7 +164,7 @@
     @Test
     public void testLoop1() {
         SchedulePhase schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(6, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(6, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -189,7 +189,7 @@
     @Test
     public void testLoop2() {
         SchedulePhase schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(6, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(6, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, true);
     }
@@ -211,7 +211,7 @@
     @Test
     public void testLoop3() {
         SchedulePhase schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(6, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(6, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -247,7 +247,7 @@
     @Test
     public void testLoop5() {
         SchedulePhase schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(10, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(10, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -264,10 +264,10 @@
     public void testArrayCopy() {
         SchedulePhase schedule = getFinalSchedule("testArrayCopySnippet", TestMode.INLINED_WITHOUT_FRAMESTATES);
         StructuredGraph graph = schedule.getCFG().getStartBlock().getBeginNode().graph();
-        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        assertDeepEquals(1, graph.getNodes(ReturnNode.class).count());
         ReturnNode ret = graph.getNodes(ReturnNode.class).first();
         assertTrue(ret.result() + " should be a FloatingReadNode", ret.result() instanceof FloatingReadNode);
-        assertEquals(schedule.getCFG().blockFor(ret), schedule.getCFG().blockFor(ret.result()));
+        assertDeepEquals(schedule.getCFG().blockFor(ret), schedule.getCFG().blockFor(ret.result()));
         assertReadWithinAllReturnBlocks(schedule, true);
     }
 
@@ -285,7 +285,7 @@
     @Test
     public void testIfRead1() {
         SchedulePhase schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(3, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(3, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, true);
         assertReadAndWriteInSameBlock(schedule, false);
     }
@@ -306,8 +306,8 @@
     @Test
     public void testIfRead2() {
         SchedulePhase schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(3, schedule.getCFG().getBlocks().size());
-        assertEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count());
+        assertDeepEquals(3, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count());
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
         assertReadAndWriteInSameBlock(schedule, false);
@@ -328,7 +328,7 @@
     @Test
     public void testIfRead3() {
         SchedulePhase schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(4, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(4, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, true);
     }
@@ -349,7 +349,7 @@
     @Test
     public void testIfRead4() {
         SchedulePhase schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(3, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(3, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
         assertReadAndWriteInSameBlock(schedule, true);
@@ -368,12 +368,36 @@
     @Test
     public void testIfRead5() {
         SchedulePhase schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(4, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(4, schedule.getCFG().getBlocks().size());
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, true);
         assertReadAndWriteInSameBlock(schedule, false);
     }
 
+    public static int testAntiDependencySnippet(int a) {
+        /*
+         * This read must not be scheduled after the following write.
+         */
+        int res = container.a;
+        container.a = 10;
+
+        /*
+         * Add some more basic blocks.
+         */
+        if (a < 0) {
+            container.b = 20;
+        }
+        container.c = 30;
+        return res;
+    }
+
+    @Test
+    public void testAntiDependency() {
+        SchedulePhase schedule = getFinalSchedule("testAntiDependencySnippet", TestMode.WITHOUT_FRAMESTATES);
+        assertDeepEquals(4, schedule.getCFG().getBlocks().size());
+        assertReadBeforeAllWritesInStartBlock(schedule);
+    }
+
     /**
      * testing scheduling within a block.
      */
@@ -397,9 +421,9 @@
         StructuredGraph graph = schedule.getCFG().graph;
         NodeIterable<WriteNode> writeNodes = graph.getNodes().filter(WriteNode.class);
 
-        assertEquals(1, schedule.getCFG().getBlocks().size());
-        assertEquals(8, writeNodes.count());
-        assertEquals(1, graph.getNodes().filter(FloatingReadNode.class).count());
+        assertDeepEquals(1, schedule.getCFG().getBlocks().size());
+        assertDeepEquals(8, writeNodes.count());
+        assertDeepEquals(1, graph.getNodes().filter(FloatingReadNode.class).count());
 
         FloatingReadNode read = graph.getNodes().filter(FloatingReadNode.class).first();
 
@@ -554,7 +578,7 @@
             }
             returnBlocks++;
         }
-        assertEquals(withRead == returnBlocks, withinReturnBlock);
+        assertDeepEquals(withRead == returnBlocks, withinReturnBlock);
     }
 
     private void assertReadWithinStartBlock(SchedulePhase schedule, boolean withinStartBlock) {
@@ -564,7 +588,7 @@
                 readEncountered = true;
             }
         }
-        assertEquals(withinStartBlock, readEncountered);
+        assertDeepEquals(withinStartBlock, readEncountered);
     }
 
     private static void assertReadAndWriteInSameBlock(SchedulePhase schedule, boolean inSame) {
@@ -574,6 +598,20 @@
         assertTrue(!(inSame ^ schedule.getCFG().blockFor(read) == schedule.getCFG().blockFor(write)));
     }
 
+    private static void assertReadBeforeAllWritesInStartBlock(SchedulePhase schedule) {
+        boolean writeNodeFound = false;
+        boolean readNodeFound = false;
+        for (Node node : schedule.nodesFor(schedule.getCFG().getStartBlock())) {
+            if (node instanceof FloatingReadNode) {
+                assertTrue(!writeNodeFound);
+                readNodeFound = true;
+            } else if (node instanceof WriteNode) {
+                writeNodeFound = true;
+            }
+        }
+        assertTrue(readNodeFound);
+    }
+
     private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode) {
         return getFinalSchedule(snippet, mode, MemoryScheduling.OPTIMAL);
     }
@@ -617,7 +655,7 @@
 
                 SchedulePhase schedule = new SchedulePhase(schedulingStrategy, memsched);
                 schedule.apply(graph);
-                assertEquals(1, graph.getNodes().filter(StartNode.class).count());
+                assertDeepEquals(1, graph.getNodes().filter(StartNode.class).count());
                 return schedule;
             }
         } catch (Throwable e) {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MergeCanonicalizerTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MergeCanonicalizerTest.java	Wed May 21 11:45:50 2014 +0200
@@ -61,6 +61,6 @@
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), new Assumptions(false)));
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), new Assumptions(false)));
         Debug.dump(graph, "Graph");
-        assertEquals(returnCount, graph.getNodes(ReturnNode.class).count());
+        assertDeepEquals(returnCount, graph.getNodes(ReturnNode.class).count());
     }
 }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SimpleCFGTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SimpleCFGTest.java	Wed May 21 11:45:50 2014 +0200
@@ -65,24 +65,24 @@
 
         List<Block> blocks = cfg.getBlocks();
         // check number of blocks
-        assertEquals(4, blocks.size());
+        assertDeepEquals(4, blocks.size());
 
         // check block - node assignment
-        assertEquals(blocks.get(0), cfg.blockFor(graph.start()));
-        assertEquals(blocks.get(0), cfg.blockFor(ifNode));
-        assertEquals(blocks.get(1), cfg.blockFor(trueBegin));
-        assertEquals(blocks.get(1), cfg.blockFor(trueEnd));
-        assertEquals(blocks.get(2), cfg.blockFor(falseBegin));
-        assertEquals(blocks.get(2), cfg.blockFor(falseEnd));
-        assertEquals(blocks.get(3), cfg.blockFor(merge));
-        assertEquals(blocks.get(3), cfg.blockFor(returnNode));
+        assertDeepEquals(blocks.get(0), cfg.blockFor(graph.start()));
+        assertDeepEquals(blocks.get(0), cfg.blockFor(ifNode));
+        assertDeepEquals(blocks.get(1), cfg.blockFor(trueBegin));
+        assertDeepEquals(blocks.get(1), cfg.blockFor(trueEnd));
+        assertDeepEquals(blocks.get(2), cfg.blockFor(falseBegin));
+        assertDeepEquals(blocks.get(2), cfg.blockFor(falseEnd));
+        assertDeepEquals(blocks.get(3), cfg.blockFor(merge));
+        assertDeepEquals(blocks.get(3), cfg.blockFor(returnNode));
 
         // check postOrder
         Iterator<Block> it = cfg.postOrder().iterator();
         for (int i = blocks.size() - 1; i >= 0; i--) {
             assertTrue(it.hasNext());
             Block b = it.next();
-            assertEquals(blocks.get(i), b);
+            assertDeepEquals(blocks.get(i), b);
         }
 
         // check dominators
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java	Wed May 21 11:45:50 2014 +0200
@@ -33,7 +33,7 @@
     @Test
     public void testSimpleMerge() {
         testEscapeAnalysis("simpleMergeSnippet", null, false);
-        assertEquals(1, returnNodes.size());
+        assertDeepEquals(1, returnNodes.size());
         assertTrue(returnNodes.get(0).result() instanceof ValuePhiNode);
         PhiNode phi = (PhiNode) returnNodes.get(0).result();
         assertTrue(phi.valueAt(0) instanceof ParameterNode);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java	Wed May 21 11:45:50 2014 +0200
@@ -74,12 +74,12 @@
     public void testSimple() {
         ValueNode result = getReturn("testSimpleSnippet").result();
         assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty());
-        assertEquals(graph.getParameter(0), result);
+        assertDeepEquals(graph.getParameter(0), result);
     }
 
     final ReturnNode getReturn(String snippet) {
         processMethod(snippet);
-        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        assertDeepEquals(1, graph.getNodes(ReturnNode.class).count());
         return graph.getNodes(ReturnNode.class).first();
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java	Wed May 21 11:45:50 2014 +0200
@@ -87,7 +87,7 @@
         ValueNode result = getReturn("testSimpleSnippet").result();
         assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty());
         assertTrue(result.isConstant());
-        assertEquals(2, result.asConstant().asInt());
+        assertDeepEquals(2, result.asConstant().asInt());
     }
 
     @SuppressWarnings("all")
@@ -115,7 +115,7 @@
     public void testParam() {
         ValueNode result = getReturn("testParamSnippet").result();
         assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty());
-        assertEquals(graph.getParameter(1), result);
+        assertDeepEquals(graph.getParameter(1), result);
     }
 
     @SuppressWarnings("all")
@@ -129,7 +129,7 @@
     public void testMaterialized() {
         ValueNode result = getReturn("testMaterializedSnippet").result();
         assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty());
-        assertEquals(graph.getParameter(0), result);
+        assertDeepEquals(graph.getParameter(0), result);
     }
 
     @SuppressWarnings("all")
@@ -145,7 +145,7 @@
     public void testSimpleLoop() {
         ValueNode result = getReturn("testSimpleLoopSnippet").result();
         assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty());
-        assertEquals(graph.getParameter(1), result);
+        assertDeepEquals(graph.getParameter(1), result);
     }
 
     @SuppressWarnings("all")
@@ -162,7 +162,7 @@
     @Test
     public void testBadLoop() {
         ValueNode result = getReturn("testBadLoopSnippet").result();
-        assertEquals(0, graph.getNodes().filter(LoadFieldNode.class).count());
+        assertDeepEquals(0, graph.getNodes().filter(LoadFieldNode.class).count());
         assertTrue(result instanceof ProxyNode);
         assertTrue(((ProxyNode) result).value() instanceof ValuePhiNode);
     }
@@ -180,7 +180,7 @@
     @Test
     public void testBadLoop2() {
         ValueNode result = getReturn("testBadLoop2Snippet").result();
-        assertEquals(1, graph.getNodes().filter(LoadFieldNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(LoadFieldNode.class).count());
         assertTrue(result instanceof LoadFieldNode);
     }
 
@@ -199,7 +199,7 @@
         processMethod("testPhiSnippet");
         assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty());
         List<ReturnNode> returnNodes = graph.getNodes(ReturnNode.class).snapshot();
-        assertEquals(2, returnNodes.size());
+        assertDeepEquals(2, returnNodes.size());
         assertTrue(returnNodes.get(0).predecessor() instanceof StoreFieldNode);
         assertTrue(returnNodes.get(1).predecessor() instanceof StoreFieldNode);
         assertTrue(returnNodes.get(0).result().isConstant());
@@ -215,7 +215,7 @@
     @Test
     public void testSimpleStore() {
         processMethod("testSimpleStoreSnippet");
-        assertEquals(1, graph.getNodes().filter(StoreFieldNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(StoreFieldNode.class).count());
     }
 
     public static int testValueProxySnippet(boolean b, TestObject o) {
@@ -233,12 +233,12 @@
     @Test
     public void testValueProxy() {
         processMethod("testValueProxySnippet");
-        assertEquals(2, graph.getNodes().filter(LoadFieldNode.class).count());
+        assertDeepEquals(2, graph.getNodes().filter(LoadFieldNode.class).count());
     }
 
     final ReturnNode getReturn(String snippet) {
         processMethod(snippet);
-        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        assertDeepEquals(1, graph.getNodes(ReturnNode.class).count());
         return graph.getNodes(ReturnNode.class).first();
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Wed May 21 11:45:50 2014 +0200
@@ -171,7 +171,7 @@
     @Test
     public void testReference1() {
         prepareGraph("testReference1Snippet", false);
-        assertEquals(1, graph.getNodes().filter(NewInstanceNode.class).count());
+        assertDeepEquals(1, graph.getNodes().filter(NewInstanceNode.class).count());
     }
 
     @SafeVarargs
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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/AheadOfTimeCompilationTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/AheadOfTimeCompilationTest.java	Wed May 21 11:45:50 2014 +0200
@@ -66,19 +66,19 @@
     @Test
     public void testStaticFinalObjectAOT() {
         StructuredGraph result = compile("getStaticFinalObject", true);
-        assertEquals(1, getConstantNodes(result).count());
-        assertEquals(getCodeCache().getTarget().wordKind, getConstantNodes(result).first().getKind());
-        assertEquals(2, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes().filter(ReadNode.class).count());
+        assertDeepEquals(1, getConstantNodes(result).count());
+        assertDeepEquals(getCodeCache().getTarget().wordKind, getConstantNodes(result).first().getKind());
+        assertDeepEquals(2, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count());
     }
 
     @Test
     public void testStaticFinalObject() {
         StructuredGraph result = compile("getStaticFinalObject", false);
-        assertEquals(1, getConstantNodes(result).count());
-        assertEquals(Kind.Object, getConstantNodes(result).first().getKind());
-        assertEquals(0, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes().filter(ReadNode.class).count());
+        assertDeepEquals(1, getConstantNodes(result).count());
+        assertDeepEquals(Kind.Object, getConstantNodes(result).first().getKind());
+        assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count());
     }
 
     public static Class<AheadOfTimeCompilationTest> getClassObject() {
@@ -90,12 +90,12 @@
         StructuredGraph result = compile("getClassObject", true);
 
         NodeIterable<ConstantNode> filter = getConstantNodes(result);
-        assertEquals(1, filter.count());
+        assertDeepEquals(1, filter.count());
         HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(AheadOfTimeCompilationTest.class);
-        assertEquals(type.klass(), filter.first().asConstant());
+        assertDeepEquals(type.klass(), filter.first().asConstant());
 
-        assertEquals(1, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes().filter(ReadNode.class).count());
+        assertDeepEquals(1, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count());
     }
 
     @Test
@@ -103,13 +103,13 @@
         StructuredGraph result = compile("getClassObject", false);
 
         NodeIterable<ConstantNode> filter = getConstantNodes(result);
-        assertEquals(1, filter.count());
+        assertDeepEquals(1, filter.count());
         Object mirror = HotSpotObjectConstant.asObject(filter.first().asConstant());
-        assertEquals(Class.class, mirror.getClass());
-        assertEquals(AheadOfTimeCompilationTest.class, mirror);
+        assertDeepEquals(Class.class, mirror.getClass());
+        assertDeepEquals(AheadOfTimeCompilationTest.class, mirror);
 
-        assertEquals(0, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes().filter(ReadNode.class).count());
+        assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count());
     }
 
     public static Class<Integer> getPrimitiveClassObject() {
@@ -120,24 +120,24 @@
     public void testPrimitiveClassObjectAOT() {
         StructuredGraph result = compile("getPrimitiveClassObject", true);
         NodeIterable<ConstantNode> filter = getConstantNodes(result);
-        assertEquals(1, filter.count());
-        assertEquals(getCodeCache().getTarget().wordKind, filter.first().getKind());
+        assertDeepEquals(1, filter.count());
+        assertDeepEquals(getCodeCache().getTarget().wordKind, filter.first().getKind());
 
-        assertEquals(2, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes().filter(ReadNode.class).count());
+        assertDeepEquals(2, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count());
     }
 
     @Test
     public void testPrimitiveClassObject() {
         StructuredGraph result = compile("getPrimitiveClassObject", false);
         NodeIterable<ConstantNode> filter = getConstantNodes(result);
-        assertEquals(1, filter.count());
+        assertDeepEquals(1, filter.count());
         Object mirror = HotSpotObjectConstant.asObject(filter.first().asConstant());
-        assertEquals(Class.class, mirror.getClass());
-        assertEquals(Integer.TYPE, mirror);
+        assertDeepEquals(Class.class, mirror.getClass());
+        assertDeepEquals(Integer.TYPE, mirror);
 
-        assertEquals(0, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes().filter(ReadNode.class).count());
+        assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count());
     }
 
     public static String getStringObject() {
@@ -159,13 +159,13 @@
         StructuredGraph result = compile("getStringObject", compileAOT);
 
         NodeIterable<ConstantNode> filter = getConstantNodes(result);
-        assertEquals(1, filter.count());
+        assertDeepEquals(1, filter.count());
         Object mirror = HotSpotObjectConstant.asObject(filter.first().asConstant());
-        assertEquals(String.class, mirror.getClass());
-        assertEquals("test string", mirror);
+        assertDeepEquals(String.class, mirror.getClass());
+        assertDeepEquals("test string", mirror);
 
-        assertEquals(0, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes().filter(ReadNode.class).count());
+        assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count());
     }
 
     public static Boolean getBoxedBoolean() {
@@ -177,23 +177,23 @@
     public void testBoxedBooleanAOT() {
         StructuredGraph result = compile("getBoxedBoolean", true);
 
-        assertEquals(2, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(1, result.getNodes(PiNode.class).count());
-        assertEquals(1, getConstantNodes(result).count());
+        assertDeepEquals(2, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(1, result.getNodes(PiNode.class).count());
+        assertDeepEquals(1, getConstantNodes(result).count());
         ConstantNode constant = getConstantNodes(result).first();
-        assertEquals(Kind.Long, constant.getKind());
-        assertEquals(((HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(Boolean.class)).klass(), constant.asConstant());
+        assertDeepEquals(Kind.Long, constant.getKind());
+        assertDeepEquals(((HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(Boolean.class)).klass(), constant.asConstant());
     }
 
     @Test
     public void testBoxedBoolean() {
         StructuredGraph result = compile("getBoxedBoolean", false);
-        assertEquals(0, result.getNodes(FloatingReadNode.class).count());
-        assertEquals(0, result.getNodes(PiNode.class).count());
-        assertEquals(1, getConstantNodes(result).count());
+        assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count());
+        assertDeepEquals(0, result.getNodes(PiNode.class).count());
+        assertDeepEquals(1, getConstantNodes(result).count());
         ConstantNode constant = getConstantNodes(result).first();
-        assertEquals(Kind.Object, constant.getKind());
-        assertEquals(Boolean.TRUE, HotSpotObjectConstant.asObject(constant.asConstant()));
+        assertDeepEquals(Kind.Object, constant.getKind());
+        assertDeepEquals(Boolean.TRUE, HotSpotObjectConstant.asObject(constant.asConstant()));
     }
 
     private StructuredGraph compile(String test, boolean compileAOT) {
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ExplicitExceptionTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ExplicitExceptionTest.java	Wed May 21 11:45:50 2014 +0200
@@ -37,7 +37,7 @@
     @Override
     protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph) {
         InstalledCode installedCode = super.getCode(method, graph);
-        assertEquals(expectedForeignCallCount, graph.getNodes().filter(ForeignCallNode.class).count());
+        assertDeepEquals(expectedForeignCallCount, graph.getNodes().filter(ForeignCallNode.class).count());
         return installedCode;
     }
 
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMethodSubstitutionTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMethodSubstitutionTest.java	Wed May 21 11:45:50 2014 +0200
@@ -45,8 +45,8 @@
 
         Object obj = new Object();
 
-        assertEquals("a string".getClass(), ObjectSubstitutions.getClass("a string"));
-        assertEquals(obj.hashCode(), ObjectSubstitutions.hashCode(obj));
+        assertDeepEquals("a string".getClass(), ObjectSubstitutions.getClass("a string"));
+        assertDeepEquals(obj.hashCode(), ObjectSubstitutions.hashCode(obj));
     }
 
     @SuppressWarnings("all")
@@ -75,14 +75,14 @@
         test("getComponentType");
 
         for (Class<?> c : new Class[]{getClass(), Cloneable.class, int[].class, String[][].class}) {
-            assertEquals(c.getModifiers(), ClassSubstitutions.getModifiers(c));
-            assertEquals(c.isInterface(), ClassSubstitutions.isInterface(c));
-            assertEquals(c.isArray(), ClassSubstitutions.isArray(c));
-            assertEquals(c.isPrimitive(), ClassSubstitutions.isPrimitive(c));
-            assertEquals(c.getSuperclass(), ClassSubstitutions.getSuperclass(c));
-            assertEquals(c.getComponentType(), ClassSubstitutions.getComponentType(c));
+            assertDeepEquals(c.getModifiers(), ClassSubstitutions.getModifiers(c));
+            assertDeepEquals(c.isInterface(), ClassSubstitutions.isInterface(c));
+            assertDeepEquals(c.isArray(), ClassSubstitutions.isArray(c));
+            assertDeepEquals(c.isPrimitive(), ClassSubstitutions.isPrimitive(c));
+            assertDeepEquals(c.getSuperclass(), ClassSubstitutions.getSuperclass(c));
+            assertDeepEquals(c.getComponentType(), ClassSubstitutions.getComponentType(c));
             for (Object o : new Object[]{this, new int[5], new String[2][], new Object()}) {
-                assertEquals(c.isInstance(o), ClassSubstitutions.isInstance(c, o));
+                assertDeepEquals(c.isInstance(o), ClassSubstitutions.isInstance(c, o));
             }
         }
     }
@@ -134,8 +134,8 @@
         test("threadInterrupted");
 
         Thread currentThread = Thread.currentThread();
-        assertEquals(currentThread, ThreadSubstitutions.currentThread());
-        assertEquals(currentThread.isInterrupted(), ThreadSubstitutions.isInterrupted(currentThread, false));
+        assertDeepEquals(currentThread, ThreadSubstitutions.currentThread());
+        assertDeepEquals(currentThread.isInterrupted(), ThreadSubstitutions.isInterrupted(currentThread, false));
     }
 
     @SuppressWarnings("all")
@@ -161,7 +161,7 @@
         SystemSubstitutions.currentTimeMillis();
         SystemSubstitutions.nanoTime();
         for (Object o : new Object[]{this, new int[5], new String[2][], new Object()}) {
-            assertEquals(System.identityHashCode(o), SystemSubstitutions.identityHashCode(o));
+            assertDeepEquals(System.identityHashCode(o), SystemSubstitutions.identityHashCode(o));
         }
     }
 
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMonitorValueTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMonitorValueTest.java	Wed May 21 11:45:50 2014 +0200
@@ -51,8 +51,8 @@
                         BytecodeFrame caller = frame.caller();
                         assertNotNull(caller);
                         assertNull(caller.caller());
-                        assertEquals(2, frame.numLocks);
-                        assertEquals(2, caller.numLocks);
+                        assertDeepEquals(2, frame.numLocks);
+                        assertDeepEquals(2, caller.numLocks);
                         HotSpotMonitorValue lock1 = (HotSpotMonitorValue) frame.getLockValue(0);
                         HotSpotMonitorValue lock2 = (HotSpotMonitorValue) frame.getLockValue(1);
                         HotSpotMonitorValue lock3 = (HotSpotMonitorValue) caller.getLockValue(0);
@@ -67,7 +67,7 @@
                                 }
                             }
                         }
-                        assertEquals(lock3.getOwner(), lock4.getOwner());
+                        assertDeepEquals(lock3.getOwner(), lock4.getOwner());
                         assertThat(lock1.getOwner(), not(lock2.getOwner()));
                         return super.addMethod(method, compResult);
                     }
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotNmethodTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotNmethodTest.java	Wed May 21 11:45:50 2014 +0200
@@ -43,7 +43,7 @@
         Object result;
         try {
             result = nmethod.executeVarargs(null, "b", "c");
-            assertEquals(43, result);
+            assertDeepEquals(43, result);
         } catch (InvalidInstalledCodeException e) {
             Assert.fail("Code was invalidated");
         }
@@ -66,7 +66,7 @@
         Object result;
         try {
             result = nmethod.executeVarargs(nmethod, null, null);
-            assertEquals(43, result);
+            assertDeepEquals(43, result);
         } catch (InvalidInstalledCodeException e) {
             Assert.fail("Code was invalidated");
         }
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Wed May 21 11:45:50 2014 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.hotspotvmconfig.*;
 
 /**
  * Used to access native configuration details.
@@ -65,14 +66,21 @@
     public final int maxFrameSize = 16 * 1024;
 
     HotSpotVMConfig(CompilerToVM compilerToVm) {
+        compilerToVm.initializeConfiguration(this);
+        assert verifyInitialization();
+
+        oopEncoding = new CompressEncoding(narrowOopBase, narrowOopShift, logMinObjAlignment());
+        klassEncoding = new CompressEncoding(narrowKlassBase, narrowKlassShift, logKlassAlignment);
+
+        assert check();
+    }
+
+    /**
+     * Check that the initialization produces the same result as the values captured through
+     * vmStructs.
+     */
+    private boolean verifyInitialization() {
         /** These fields are set in {@link CompilerToVM#initializeConfiguration}. */
-        gHotSpotVMStructs = 0;
-        gHotSpotVMTypes = 0;
-        gHotSpotVMIntConstants = 0;
-        gHotSpotVMLongConstants = 0;
-
-        compilerToVm.initializeConfiguration(this);
-
         assert gHotSpotVMStructs != 0;
         assert gHotSpotVMTypes != 0;
         assert gHotSpotVMIntConstants != 0;
@@ -114,7 +122,7 @@
                 String type = annotation.type();
                 VMFields.Field entry = vmFields.get(name);
                 if (entry == null) {
-                    if (annotation.optional() || !isRequired(currentArch, annotation.archs())) {
+                    if (!isRequired(currentArch, annotation.archs())) {
                         continue;
                     }
                     throw new IllegalArgumentException("field not found: " + name);
@@ -129,13 +137,13 @@
 
                 switch (annotation.get()) {
                     case OFFSET:
-                        setField(f, entry.getOffset());
+                        checkField(f, entry.getOffset());
                         break;
                     case ADDRESS:
-                        setField(f, entry.getAddress());
+                        checkField(f, entry.getAddress());
                         break;
                     case VALUE:
-                        setField(f, entry.getValue());
+                        checkField(f, entry.getValue());
                         break;
                     default:
                         throw GraalInternalError.shouldNotReachHere("unknown kind " + annotation.get());
@@ -149,7 +157,7 @@
                 }
                 switch (annotation.get()) {
                     case SIZE:
-                        setField(f, entry.getSize());
+                        checkField(f, entry.getSize());
                         break;
                     default:
                         throw GraalInternalError.shouldNotReachHere("unknown kind " + annotation.get());
@@ -164,7 +172,7 @@
                     }
                     throw new IllegalArgumentException("constant not found: " + name);
                 }
-                setField(f, entry.getValue());
+                checkField(f, entry.getValue());
             } else if (f.isAnnotationPresent(HotSpotVMFlag.class)) {
                 HotSpotVMFlag annotation = f.getAnnotation(HotSpotVMFlag.class);
                 String name = annotation.name();
@@ -176,14 +184,10 @@
                     throw new IllegalArgumentException("flag not found: " + name);
 
                 }
-                setField(f, entry.getValue());
+                checkField(f, entry.getValue());
             }
         }
-
-        oopEncoding = new CompressEncoding(narrowOopBase, narrowOopShift, logMinObjAlignment());
-        klassEncoding = new CompressEncoding(narrowKlassBase, narrowKlassShift, logKlassAlignment);
-
-        assert check();
+        return true;
     }
 
     private final CompressEncoding oopEncoding;
@@ -197,29 +201,29 @@
         return klassEncoding;
     }
 
-    private void setField(Field field, Object value) {
+    private void checkField(Field field, Object value) {
         try {
             Class<?> fieldType = field.getType();
             if (fieldType == boolean.class) {
                 if (value instanceof String) {
-                    field.setBoolean(this, Boolean.valueOf((String) value));
+                    assert field.getBoolean(this) == Boolean.valueOf((String) value) : field + " " + value + " " + field.getBoolean(this);
                 } else if (value instanceof Boolean) {
-                    field.setBoolean(this, (boolean) value);
+                    assert field.getBoolean(this) == (boolean) value : field + " " + value + " " + field.getBoolean(this);
                 } else if (value instanceof Long) {
-                    field.setBoolean(this, ((long) value) != 0);
+                    assert field.getBoolean(this) == (((long) value) != 0) : field + " " + value + " " + field.getBoolean(this);
                 } else {
                     GraalInternalError.shouldNotReachHere(value.getClass().getSimpleName());
                 }
             } else if (fieldType == int.class) {
                 if (value instanceof Integer) {
-                    field.setInt(this, (int) value);
+                    assert field.getInt(this) == (int) value : field + " " + value + " " + field.getInt(this);
                 } else if (value instanceof Long) {
-                    field.setInt(this, (int) (long) value);
+                    assert field.getInt(this) == (int) (long) value : field + " " + value + " " + field.getInt(this);
                 } else {
                     GraalInternalError.shouldNotReachHere(value.getClass().getSimpleName());
                 }
             } else if (fieldType == long.class) {
-                field.setLong(this, (long) value);
+                assert field.getLong(this) == (long) value : field + " " + value + " " + field.getLong(this);
             } else {
                 GraalInternalError.shouldNotReachHere(field.toString());
             }
@@ -248,14 +252,14 @@
     /**
      * VMStructEntry (see vmStructs.hpp).
      */
-    private long gHotSpotVMStructs;
-    private long gHotSpotVMStructEntryTypeNameOffset;
-    private long gHotSpotVMStructEntryFieldNameOffset;
-    private long gHotSpotVMStructEntryTypeStringOffset;
-    private long gHotSpotVMStructEntryIsStaticOffset;
-    private long gHotSpotVMStructEntryOffsetOffset;
-    private long gHotSpotVMStructEntryAddressOffset;
-    private long gHotSpotVMStructEntryArrayStride;
+    @HotSpotVMValue(expression = "gHotSpotVMStructs", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMStructs;
+    @HotSpotVMValue(expression = "gHotSpotVMStructEntryTypeNameOffset") @Stable private long gHotSpotVMStructEntryTypeNameOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMStructEntryFieldNameOffset") @Stable private long gHotSpotVMStructEntryFieldNameOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMStructEntryTypeStringOffset") @Stable private long gHotSpotVMStructEntryTypeStringOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMStructEntryIsStaticOffset") @Stable private long gHotSpotVMStructEntryIsStaticOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMStructEntryOffsetOffset") @Stable private long gHotSpotVMStructEntryOffsetOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMStructEntryAddressOffset") @Stable private long gHotSpotVMStructEntryAddressOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMStructEntryArrayStride") @Stable private long gHotSpotVMStructEntryArrayStride;
 
     class VMFields implements Iterable<VMFields.Field> {
 
@@ -364,14 +368,14 @@
     /**
      * VMTypeEntry (see vmStructs.hpp).
      */
-    private long gHotSpotVMTypes;
-    private long gHotSpotVMTypeEntryTypeNameOffset;
-    private long gHotSpotVMTypeEntrySuperclassNameOffset;
-    private long gHotSpotVMTypeEntryIsOopTypeOffset;
-    private long gHotSpotVMTypeEntryIsIntegerTypeOffset;
-    private long gHotSpotVMTypeEntryIsUnsignedOffset;
-    private long gHotSpotVMTypeEntrySizeOffset;
-    private long gHotSpotVMTypeEntryArrayStride;
+    @HotSpotVMValue(expression = "gHotSpotVMTypes", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMTypes;
+    @HotSpotVMValue(expression = "gHotSpotVMTypeEntryTypeNameOffset") @Stable private long gHotSpotVMTypeEntryTypeNameOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMTypeEntrySuperclassNameOffset") @Stable private long gHotSpotVMTypeEntrySuperclassNameOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMTypeEntryIsOopTypeOffset") @Stable private long gHotSpotVMTypeEntryIsOopTypeOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMTypeEntryIsIntegerTypeOffset") @Stable private long gHotSpotVMTypeEntryIsIntegerTypeOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMTypeEntryIsUnsignedOffset") @Stable private long gHotSpotVMTypeEntryIsUnsignedOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMTypeEntrySizeOffset") @Stable private long gHotSpotVMTypeEntrySizeOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMTypeEntryArrayStride") @Stable private long gHotSpotVMTypeEntryArrayStride;
 
     class VMTypes implements Iterable<VMTypes.Type> {
 
@@ -476,10 +480,10 @@
     /**
      * VMIntConstantEntry (see vmStructs.hpp).
      */
-    private long gHotSpotVMIntConstants;
-    private long gHotSpotVMIntConstantEntryNameOffset;
-    private long gHotSpotVMIntConstantEntryValueOffset;
-    private long gHotSpotVMIntConstantEntryArrayStride;
+    @HotSpotVMValue(expression = "gHotSpotVMIntConstants", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMIntConstants;
+    @HotSpotVMValue(expression = "gHotSpotVMIntConstantEntryNameOffset") @Stable private long gHotSpotVMIntConstantEntryNameOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMIntConstantEntryValueOffset") @Stable private long gHotSpotVMIntConstantEntryValueOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMIntConstantEntryArrayStride") @Stable private long gHotSpotVMIntConstantEntryArrayStride;
 
     class VMIntConstants implements Iterable<VMIntConstants.Constant> {
 
@@ -540,10 +544,10 @@
     /**
      * VMLongConstantEntry (see vmStructs.hpp).
      */
-    private long gHotSpotVMLongConstants;
-    private long gHotSpotVMLongConstantEntryNameOffset;
-    private long gHotSpotVMLongConstantEntryValueOffset;
-    private long gHotSpotVMLongConstantEntryArrayStride;
+    @HotSpotVMValue(expression = "gHotSpotVMLongConstants", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMLongConstants;
+    @HotSpotVMValue(expression = "gHotSpotVMLongConstantEntryNameOffset") @Stable private long gHotSpotVMLongConstantEntryNameOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMLongConstantEntryValueOffset") @Stable private long gHotSpotVMLongConstantEntryValueOffset;
+    @HotSpotVMValue(expression = "gHotSpotVMLongConstantEntryArrayStride") @Stable private long gHotSpotVMLongConstantEntryArrayStride;
 
     class VMLongConstants implements Iterable<VMLongConstants.Constant> {
 
@@ -699,7 +703,7 @@
     }
 
     // os information, register layout, code generation, ...
-    @HotSpotVMConstant(name = "ASSERT") @Stable public boolean cAssertions;
+    @HotSpotVMValue(expression = "DEBUG_ONLY(1) NOT_DEBUG(0)") @Stable public boolean cAssertions;
     public final boolean windowsOs = System.getProperty("os.name", "").startsWith("Windows");
 
     @HotSpotVMFlag(name = "CodeEntryAlignment") @Stable public int codeEntryAlignment;
@@ -708,8 +712,8 @@
     @HotSpotVMFlag(name = "CITimeEach") @Stable public boolean ciTimeEach;
     @HotSpotVMFlag(name = "CompileThreshold") @Stable public long compileThreshold;
     @HotSpotVMFlag(name = "CompileTheWorld") @Stable public boolean compileTheWorld;
-    @HotSpotVMFlag(name = "CompileTheWorldStartAt") @Stable public int compileTheWorldStartAt;
-    @HotSpotVMFlag(name = "CompileTheWorldStopAt") @Stable public int compileTheWorldStopAt;
+    @HotSpotVMFlag(name = "CompileTheWorldStartAt", optional = true) @Stable public int compileTheWorldStartAt;
+    @HotSpotVMFlag(name = "CompileTheWorldStopAt", optional = true) @Stable public int compileTheWorldStopAt;
     @HotSpotVMFlag(name = "DontCompileHugeMethods") @Stable public boolean dontCompileHugeMethods;
     @HotSpotVMFlag(name = "HugeMethodLimit") @Stable public int hugeMethodLimit;
     @HotSpotVMFlag(name = "PrintCompilation") @Stable public boolean printCompilation;
@@ -737,6 +741,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;
 
@@ -846,12 +852,12 @@
 
     @HotSpotVMType(name = "vtableEntry", get = HotSpotVMType.Type.SIZE) @Stable public int vtableEntrySize;
     @HotSpotVMField(name = "vtableEntry::_method", type = "Method*", get = HotSpotVMField.Type.OFFSET) @Stable public int vtableEntryMethodOffset;
-    @Stable public int instanceKlassVtableStartOffset;
+    @HotSpotVMValue(expression = "InstanceKlass::vtable_start_offset() * HeapWordSize") @Stable public int instanceKlassVtableStartOffset;
 
     /**
      * The offset of the array length word in an array object's header.
      */
-    @Stable public int arrayLengthOffset;
+    @HotSpotVMValue(expression = "arrayOopDesc::length_offset_in_bytes()") @Stable public int arrayLengthOffset;
 
     @HotSpotVMField(name = "Array<int>::_length", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int arrayU1LengthOffset;
     @HotSpotVMField(name = "Array<u1>::_data", type = "", get = HotSpotVMField.Type.OFFSET) @Stable public int arrayU1DataOffset;
@@ -893,7 +899,7 @@
     @HotSpotVMField(name = "JavaThread::_is_method_handle_return", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int threadIsMethodHandleReturnOffset;
     @HotSpotVMField(name = "JavaThread::_satb_mark_queue", type = "ObjPtrQueue", get = HotSpotVMField.Type.OFFSET) @Stable public int javaThreadSatbMarkQueueOffset;
     @HotSpotVMField(name = "JavaThread::_vm_result", type = "oop", get = HotSpotVMField.Type.OFFSET) @Stable public int threadObjectResultOffset;
-    @HotSpotVMField(name = "JavaThread::_graal_counters[0]", type = "jlong", get = HotSpotVMField.Type.OFFSET, optional = true) @Stable public int graalCountersThreadOffset;
+    @HotSpotVMValue(expression = "in_bytes(JavaThread::graal_counters_offset())") @Stable public int graalCountersThreadOffset;
 
     /**
      * An invalid value for {@link #rtldDefault}.
@@ -907,7 +913,7 @@
      *     void* (const char *filename, char *ebuf, int ebuflen)
      * </pre>
      */
-    @Stable public long dllLoad;
+    @HotSpotVMValue(expression = "os::dll_load", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dllLoad;
 
     /**
      * Address of the library lookup routine. The C signature of this routine is:
@@ -916,7 +922,7 @@
      *     void* (void* handle, const char* name)
      * </pre>
      */
-    @Stable public long dllLookup;
+    @HotSpotVMValue(expression = "os::dll_lookup", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dllLookup;
 
     /**
      * A pseudo-handle which when used as the first argument to {@link #dllLookup} means lookup will
@@ -924,7 +930,7 @@
      * this field is {@value #INVALID_RTLD_DEFAULT_HANDLE}, then this capability is not supported on
      * the current platform.
      */
-    @Stable public long rtldDefault = INVALID_RTLD_DEFAULT_HANDLE;
+    @HotSpotVMValue(expression = "RTLD_DEFAULT", defines = {"TARGET_OS_FAMILY_bsd", "TARGET_OS_FAMILY_linux"}, get = HotSpotVMValue.Type.ADDRESS) @Stable public long rtldDefault = INVALID_RTLD_DEFAULT_HANDLE;
 
     /**
      * This field is used to pass exception objects into and out of the runtime system during
@@ -1033,9 +1039,9 @@
     @HotSpotVMField(name = "HSAILFrame::_pc_offset", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFramePcOffset;
     @HotSpotVMField(name = "HSAILFrame::_num_s_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumSRegOffset;
     @HotSpotVMField(name = "HSAILFrame::_num_d_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumDRegOffset;
-    @HotSpotVMConstant(name = "sizeof(HSAILFrame)") @Stable public int hsailFrameHeaderSize;
-    @HotSpotVMConstant(name = "sizeof(Hsail::HSAILKernelDeoptimization)") @Stable public int hsailKernelDeoptimizationHeaderSize;
-    @HotSpotVMConstant(name = "sizeof(Hsail::HSAILDeoptimizationInfo)") @Stable public int hsailDeoptimizationInfoHeaderSize;
+    @HotSpotVMType(name = "HSAILFrame", get = HotSpotVMType.Type.SIZE) @Stable public int hsailFrameHeaderSize;
+    @HotSpotVMType(name = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMType.Type.SIZE) @Stable public int hsailKernelDeoptimizationHeaderSize;
+    @HotSpotVMType(name = "Hsail::HSAILDeoptimizationInfo", get = HotSpotVMType.Type.SIZE) @Stable public int hsailDeoptimizationInfoHeaderSize;
 
     /**
      * Mark word right shift to get identity hash code.
@@ -1064,6 +1070,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;
@@ -1074,7 +1081,7 @@
     /**
      * Value of Method::extra_stack_entries().
      */
-    @Stable public int extraStackEntries;
+    @HotSpotVMValue(expression = "Method::extra_stack_entries()") @Stable public int extraStackEntries;
 
     @HotSpotVMField(name = "ConstMethod::_constants", type = "ConstantPool*", get = HotSpotVMField.Type.OFFSET) @Stable public int constMethodConstantsOffset;
     @HotSpotVMField(name = "ConstMethod::_flags", type = "u2", get = HotSpotVMField.Type.OFFSET) @Stable public int constMethodFlagsOffset;
@@ -1155,8 +1162,8 @@
     @HotSpotVMField(name = "Universe::_non_oop_bits", type = "intptr_t", get = HotSpotVMField.Type.VALUE) @Stable public long nonOopBits;
 
     @HotSpotVMField(name = "StubRoutines::_verify_oop_count", type = "jint", get = HotSpotVMField.Type.ADDRESS) @Stable public long verifyOopCounterAddress;
-    @Stable public long verifyOopMask;
-    @Stable public long verifyOopBits;
+    @HotSpotVMValue(expression = "Universe::verify_oop_mask()") @Stable public long verifyOopMask;
+    @HotSpotVMValue(expression = "Universe::verify_oop_bits()") @Stable public long verifyOopBits;
 
     @HotSpotVMField(name = "CollectedHeap::_barrier_set", type = "BarrierSet*", get = HotSpotVMField.Type.OFFSET) @Stable public int collectedHeapBarrierSetOffset;
 
@@ -1246,13 +1253,13 @@
     @HotSpotVMType(name = "BasicLock", get = HotSpotVMType.Type.SIZE) @Stable public int basicLockSize;
     @HotSpotVMField(name = "BasicLock::_displaced_header", type = "markOop", get = HotSpotVMField.Type.OFFSET) @Stable public int basicLockDisplacedHeaderOffset;
 
-    @Stable public long heapEndAddress;
-    @Stable public long heapTopAddress;
+    @HotSpotVMValue(expression = "Universe::heap()->end_addr()", get = HotSpotVMValue.Type.ADDRESS) @Stable public long heapEndAddress;
+    @HotSpotVMValue(expression = "Universe::heap()->top_addr()", get = HotSpotVMValue.Type.ADDRESS) @Stable public long heapTopAddress;
 
     @HotSpotVMField(name = "Thread::_allocated_bytes", type = "jlong", get = HotSpotVMField.Type.OFFSET) @Stable public int threadAllocatedBytesOffset;
 
     @HotSpotVMFlag(name = "TLABWasteIncrement") @Stable public int tlabRefillWasteIncrement;
-    @Stable public int tlabAlignmentReserve;
+    @HotSpotVMValue(expression = "ThreadLocalAllocBuffer::alignment_reserve()") @Stable public int tlabAlignmentReserve;
 
     @HotSpotVMField(name = "ThreadLocalAllocBuffer::_start", type = "HeapWord*", get = HotSpotVMField.Type.OFFSET) @Stable private int threadLocalAllocBufferStartOffset;
     @HotSpotVMField(name = "ThreadLocalAllocBuffer::_end", type = "HeapWord*", get = HotSpotVMField.Type.OFFSET) @Stable private int threadLocalAllocBufferEndOffset;
@@ -1301,7 +1308,7 @@
     }
 
     @HotSpotVMFlag(name = "TLABStats") @Stable public boolean tlabStats;
-    @Stable public boolean inlineContiguousAllocationSupported;
+    @HotSpotVMValue(expression = " !CMSIncrementalMode && Universe::heap()->supports_inline_contig_alloc()") @Stable public boolean inlineContiguousAllocationSupported;
 
     /**
      * The DataLayout header size is the same as the cell size.
@@ -1393,41 +1400,43 @@
     @HotSpotVMField(name = "StubRoutines::_unsafe_arraycopy", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long unsafeArraycopy;
     @HotSpotVMField(name = "StubRoutines::_generic_arraycopy", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long genericArraycopy;
 
-    @Stable public long newInstanceAddress;
-    @Stable public long newArrayAddress;
-    @Stable public long newMultiArrayAddress;
-    @Stable public long dynamicNewArrayAddress;
-    @Stable public long dynamicNewInstanceAddress;
-    @Stable public long registerFinalizerAddress;
-    @Stable public long threadIsInterruptedAddress;
-    @Stable public long vmMessageAddress;
-    @Stable public long identityHashCodeAddress;
-    @Stable public long exceptionHandlerForPcAddress;
-    @Stable public long exceptionHandlerForReturnAddressAddress;
-    @Stable public long osrMigrationEndAddress;
-    @Stable public long monitorenterAddress;
-    @Stable public long monitorexitAddress;
-    @Stable public long createNullPointerExceptionAddress;
-    @Stable public long createOutOfBoundsExceptionAddress;
-    @Stable public long logPrimitiveAddress;
-    @Stable public long logObjectAddress;
-    @Stable public long logPrintfAddress;
-    @Stable public long vmErrorAddress;
-    @Stable public long writeBarrierPreAddress;
-    @Stable public long writeBarrierPostAddress;
-    @Stable public long validateObject;
-    @Stable public long javaTimeMillisAddress;
-    @Stable public long javaTimeNanosAddress;
-    @Stable public long arithmeticSinAddress;
-    @Stable public long arithmeticCosAddress;
-    @Stable public long arithmeticTanAddress;
-    @Stable public long loadAndClearExceptionAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::new_instance", get = HotSpotVMValue.Type.ADDRESS) @Stable public long newInstanceAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::new_array", get = HotSpotVMValue.Type.ADDRESS) @Stable public long newArrayAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::new_multi_array", get = HotSpotVMValue.Type.ADDRESS) @Stable public long newMultiArrayAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::dynamic_new_array", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dynamicNewArrayAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::dynamic_new_instance", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dynamicNewInstanceAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::thread_is_interrupted", get = HotSpotVMValue.Type.ADDRESS) @Stable public long threadIsInterruptedAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::vm_message", get = HotSpotVMValue.Type.ADDRESS) @Stable public long vmMessageAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::identity_hash_code", get = HotSpotVMValue.Type.ADDRESS) @Stable public long identityHashCodeAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::exception_handler_for_pc", get = HotSpotVMValue.Type.ADDRESS) @Stable public long exceptionHandlerForPcAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::monitorenter", get = HotSpotVMValue.Type.ADDRESS) @Stable public long monitorenterAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::monitorexit", get = HotSpotVMValue.Type.ADDRESS) @Stable public long monitorexitAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::create_null_exception", get = HotSpotVMValue.Type.ADDRESS) @Stable public long createNullPointerExceptionAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::create_out_of_bounds_exception", get = HotSpotVMValue.Type.ADDRESS) @Stable public long createOutOfBoundsExceptionAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::log_primitive", get = HotSpotVMValue.Type.ADDRESS) @Stable public long logPrimitiveAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::log_object", get = HotSpotVMValue.Type.ADDRESS) @Stable public long logObjectAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::log_printf", get = HotSpotVMValue.Type.ADDRESS) @Stable public long logPrintfAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::vm_error", get = HotSpotVMValue.Type.ADDRESS) @Stable public long vmErrorAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::load_and_clear_exception", get = HotSpotVMValue.Type.ADDRESS) @Stable public long loadAndClearExceptionAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::write_barrier_pre", get = HotSpotVMValue.Type.ADDRESS) @Stable public long writeBarrierPreAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::write_barrier_post", get = HotSpotVMValue.Type.ADDRESS) @Stable public long writeBarrierPostAddress;
+    @HotSpotVMValue(expression = "GraalRuntime::validate_object", get = HotSpotVMValue.Type.ADDRESS) @Stable public long validateObject;
 
-    @Stable public int graalCountersSize;
+    @HotSpotVMValue(expression = "SharedRuntime::register_finalizer", get = HotSpotVMValue.Type.ADDRESS) @Stable public long registerFinalizerAddress;
+    @HotSpotVMValue(expression = "SharedRuntime::exception_handler_for_return_address", get = HotSpotVMValue.Type.ADDRESS) @Stable public long exceptionHandlerForReturnAddressAddress;
+    @HotSpotVMValue(expression = "SharedRuntime::OSR_migration_end", get = HotSpotVMValue.Type.ADDRESS) @Stable public long osrMigrationEndAddress;
 
-    @Stable public long deoptimizationFetchUnrollInfo;
-    @Stable public long deoptimizationUncommonTrap;
-    @Stable public long deoptimizationUnpackFrames;
+    @HotSpotVMValue(expression = "os::javaTimeMillis", get = HotSpotVMValue.Type.ADDRESS) @Stable public long javaTimeMillisAddress;
+    @HotSpotVMValue(expression = "os::javaTimeNanos", get = HotSpotVMValue.Type.ADDRESS) @Stable public long javaTimeNanosAddress;
+    @HotSpotVMValue(expression = "SharedRuntime::dsin", get = HotSpotVMValue.Type.ADDRESS) @Stable public long arithmeticSinAddress;
+    @HotSpotVMValue(expression = "SharedRuntime::dcos", get = HotSpotVMValue.Type.ADDRESS) @Stable public long arithmeticCosAddress;
+    @HotSpotVMValue(expression = "SharedRuntime::dtan", get = HotSpotVMValue.Type.ADDRESS) @Stable public long arithmeticTanAddress;
+
+    @HotSpotVMValue(expression = "(jint) GraalCounterSize") @Stable public int graalCountersSize;
+
+    @HotSpotVMValue(expression = "Deoptimization::fetch_unroll_info", get = HotSpotVMValue.Type.ADDRESS) @Stable public long deoptimizationFetchUnrollInfo;
+    @HotSpotVMValue(expression = "Deoptimization::uncommon_trap", get = HotSpotVMValue.Type.ADDRESS) @Stable public long deoptimizationUncommonTrap;
+    @HotSpotVMValue(expression = "Deoptimization::unpack_frames", get = HotSpotVMValue.Type.ADDRESS) @Stable public long deoptimizationUnpackFrames;
 
     @HotSpotVMConstant(name = "Deoptimization::Reason_none") @Stable public int deoptReasonNone;
     @HotSpotVMConstant(name = "Deoptimization::Reason_null_check") @Stable public int deoptReasonNullCheck;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConstant.java	Tue May 13 19:19:27 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot;
-
-import java.lang.annotation.*;
-
-@Target(ElementType.FIELD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface HotSpotVMConstant {
-
-    String name();
-
-    /**
-     * List of architectures where this constant is required. Names are derived from
-     * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is
-     * required on all architectures.
-     */
-    String[] archs() default {};
-
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMField.java	Tue May 13 19:19:27 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot;
-
-import java.lang.annotation.*;
-
-@Target(ElementType.FIELD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface HotSpotVMField {
-
-    enum Type {
-        OFFSET,
-        ADDRESS,
-        VALUE;
-    }
-
-    String name();
-
-    String type();
-
-    Type get();
-
-    /**
-     * List of architectures where this constant is required. Names are derived from
-     * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is
-     * required on all architectures.
-     */
-    String[] archs() default {};
-
-    boolean optional() default false;
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMFlag.java	Tue May 13 19:19:27 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot;
-
-import java.lang.annotation.*;
-
-@Target(ElementType.FIELD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface HotSpotVMFlag {
-
-    String name();
-
-    /**
-     * List of architectures where this constant is required. Names are derived from
-     * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is
-     * required on all architectures.
-     */
-    String[] archs() default {};
-
-    boolean optional() default false;
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMType.java	Tue May 13 19:19:27 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot;
-
-import java.lang.annotation.*;
-
-@Target(ElementType.FIELD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface HotSpotVMType {
-
-    enum Type {
-        SIZE;
-    }
-
-    String name();
-
-    Type get();
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Wed May 21 11:45:50 2014 +0200
@@ -35,13 +35,12 @@
 public interface CompilerToVM {
 
     /**
-     * Copies the original bytecode of a given method into a given byte array.
+     * Copies the original bytecode of a given method into a new byte array and returns it.
      *
      * @param metaspaceMethod the metaspace Method object
-     * @param code the array into which to copy the original bytecode
-     * @return the value of {@code code}
+     * @return a new byte array containing the original bytecode
      */
-    byte[] initializeBytecode(long metaspaceMethod, byte[] code);
+    byte[] initializeBytecode(long metaspaceMethod);
 
     int exceptionTableLength(long metaspaceMethod);
 
@@ -248,7 +247,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 +352,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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Wed May 21 11:45:50 2014 +0200
@@ -43,7 +43,7 @@
     public native long getMetaspaceMethod(Class<?> holder, int slot);
 
     @Override
-    public native byte[] initializeBytecode(long metaspaceMethod, byte[] code);
+    public native byte[] initializeBytecode(long metaspaceMethod);
 
     @Override
     public native int exceptionTableLength(long metaspaceMethod);
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Wed May 21 11:45:50 2014 +0200
@@ -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.*;
@@ -195,7 +195,7 @@
             return null;
         }
         if (code == null && holder.isLinked()) {
-            code = runtime().getCompilerToVM().initializeBytecode(metaspaceMethod, new byte[getCodeSize()]);
+            code = runtime().getCompilerToVM().initializeBytecode(metaspaceMethod);
             assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length;
         }
         return code;
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedPrimitiveType.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java	Wed May 21 11:45:50 2014 +0200
@@ -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);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspotvmconfig/src/META-INF/services/javax.annotation.processing.Processor	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,1 @@
+com.oracle.graal.hotspotvmconfig.HotSpotVMConfigProcessor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConfigProcessor.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,417 @@
+/*
+ * 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.hotspotvmconfig;
+
+import java.io.*;
+import java.lang.annotation.*;
+import java.util.*;
+import java.util.Map.Entry;
+
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.*;
+
+import com.oracle.graal.compiler.common.*;
+
+@SupportedAnnotationTypes({"com.oracle.graal.hotspotvmconfig.HotSpotVMConstant", "com.oracle.graal.hotspotvmconfig.HotSpotVMFlag", "com.oracle.graal.hotspotvmconfig.HotSpotVMField",
+                "com.oracle.graal.hotspotvmconfig.HotSpotVMType", "com.oracle.graal.hotspotvmconfig.HotSpotVMValue"})
+public class HotSpotVMConfigProcessor extends AbstractProcessor {
+
+    public HotSpotVMConfigProcessor() {
+    }
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    /**
+     * Set to true to enable logging to a local file during annotation processing. There's no normal
+     * channel for any debug messages and debugging annotation processors requires some special
+     * setup.
+     */
+    private static final boolean DEBUG = true;
+
+    private static final String LOGFILE = "/tmp/hotspotvmconfigprocessor.log";
+
+    private static PrintWriter log;
+
+    /**
+     * Logging facility for the debugging the annotation processor.
+     */
+
+    private static synchronized PrintWriter getLog() {
+        if (log == null) {
+            try {
+                log = new PrintWriter(new FileWriter(LOGFILE, true));
+            } catch (IOException e) {
+                // Do nothing
+            }
+        }
+        return log;
+    }
+
+    private static synchronized void logMessage(String format, Object... args) {
+        if (!DEBUG) {
+            return;
+        }
+        PrintWriter bw = getLog();
+        if (bw != null) {
+            bw.printf(format, args);
+            bw.flush();
+        }
+    }
+
+    private static synchronized void logException(Throwable t) {
+        if (!DEBUG) {
+            return;
+        }
+        PrintWriter bw = getLog();
+        if (bw != null) {
+            t.printStackTrace(bw);
+            bw.flush();
+        }
+    }
+
+    /**
+     * Bugs in an annotation processor can cause silent failure so try to report any exception
+     * throws as errors.
+     */
+    private void reportExceptionThrow(Element element, Throwable t) {
+        if (element != null) {
+            logMessage("throw for %s:\n", element);
+        }
+        logException(t);
+        processingEnv.getMessager().printMessage(Kind.ERROR, "Exception throw during processing: " + t.toString() + " " + Arrays.toString(Arrays.copyOf(t.getStackTrace(), 8)), element);
+    }
+
+    //@formatter:off
+    String[] prologue = new String[]{
+        "// The normal wrappers boolAt and intxAt skip constant flags",
+        "static bool boolAt(char* name, bool* value) {",
+        "  Flag* result = Flag::find_flag(name, strlen(name), true, true);",
+        "  if (result == NULL) return false;",
+        "  if (!result->is_bool()) return false;",
+        "  *value = result->get_bool();",
+        "  return true;",
+        "}",
+        "",
+        "static bool intxAt(char* name, intx* value) {",
+        "  Flag* result = Flag::find_flag(name, strlen(name), true, true);",
+        "  if (result == NULL) return false;",
+        "  if (!result->is_intx()) return false;",
+        "  *value = result->get_intx();",
+        "  return true;",
+        "}",
+        "",
+        "#define set_boolean(name, value) vmconfig_oop->bool_field_put(fs.offset(), value)",
+        "#define set_int(name, value) vmconfig_oop->int_field_put(fs.offset(), (int)value)",
+        "#define set_long(name, value) vmconfig_oop->long_field_put(fs.offset(), value)",
+        "#define set_address(name, value) do { set_long(name, (jlong) value); } while (0)",
+        "",
+        "#define set_optional_boolean_flag(varName, flagName) do { bool flagValue; if (boolAt((char*) flagName, &flagValue)) { set_boolean(varName, flagValue); } } while (0)",
+        "#define set_optional_int_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_int(varName, flagValue); } } while (0)",
+        "#define set_optional_long_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_long(varName, flagValue); } } while (0)",
+        "",
+        "void VMStructs::initHotSpotVMConfig(oop vmconfig_oop) {",
+        "  InstanceKlass* vmconfig_klass = InstanceKlass::cast(vmconfig_oop->klass());",
+        "",
+        "  for (JavaFieldStream fs(vmconfig_klass); !fs.done(); fs.next()) {",
+    };
+    //@formatter:on
+
+    String outputName = "HotSpotVMConfig.inline.hpp";
+    String outputDirectory = "hotspot";
+
+    private void createFiles(Map<String, VMConfigField> annotations, Element element) {
+
+        Filer filer = processingEnv.getFiler();
+        try (PrintWriter out = createSourceFile(outputDirectory, outputName, filer, element)) {
+
+            for (String line : prologue) {
+                out.println(line);
+            }
+
+            out.println();
+
+            Set<String> fieldTypes = new HashSet<>();
+            for (String key : annotations.keySet()) {
+                fieldTypes.add(annotations.get(key).getType());
+            }
+            // For each type of field, generate a switch on the length of the symbol and then do a
+            // direct compare. In general this reduces each operation to 2 tests plus a string
+            // compare. Being more prefect than that is probably not worth it.
+            for (String type : fieldTypes) {
+                String sigtype = type.equals("boolean") ? "bool" : type;
+                out.println("    if (fs.signature() == vmSymbols::" + sigtype + "_signature()) {");
+                Set<Integer> lengths = new HashSet<>();
+                for (Entry<String, VMConfigField> entry : annotations.entrySet()) {
+                    if (entry.getValue().getType().equals(type)) {
+                        lengths.add(entry.getKey().length());
+                    }
+                }
+                out.println("      switch (fs.name()->utf8_length()) {");
+                for (int len : lengths) {
+                    out.println("        case " + len + ":");
+                    for (Entry<String, VMConfigField> entry : annotations.entrySet()) {
+                        if (entry.getValue().getType().equals(type) && entry.getKey().length() == len) {
+                            out.println("          if (fs.name()->equals(\"" + entry.getKey() + "\")) {");
+                            entry.getValue().emit(out);
+                            out.println("            continue;");
+                            out.println("          }");
+                        }
+                    }
+                    out.println("          continue;");
+                }
+                out.println("      } // switch");
+                out.println("      continue;");
+                out.println("    } // if");
+            }
+            out.println("  } // for");
+            out.println("}");
+        }
+    }
+
+    protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
+        try {
+            // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle
+            FileObject sourceFile = filer.createResource(StandardLocation.SOURCE_OUTPUT, pkg, relativeName, originatingElements);
+            logMessage("%s\n", sourceFile);
+            return new PrintWriter(sourceFile.openWriter()) {
+
+                @Override
+                public void println() {
+                    print("\n");
+                }
+            };
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    class VMConfigField {
+        final VariableElement field;
+        final Annotation annotation;
+
+        public VMConfigField(VariableElement field, Annotation value) {
+            super();
+            this.field = field;
+            this.annotation = value;
+        }
+
+        public String getType() {
+            return field.asType().toString();
+        }
+
+        private String archDefine(String arch) {
+            switch (arch) {
+                case "amd64":
+                    return "defined(AMD64)";
+                case "sparcv9":
+                    return "(defined(SPARC) && defined(_LP64))";
+                case "sparc":
+                    return "defined(SPARC)";
+                default:
+                    throw new GraalInternalError("unexpected arch: " + arch);
+            }
+        }
+
+        private String archDefines(String[] archs) {
+            if (archs.length == 0) {
+                return null;
+            }
+            if (archs.length == 1) {
+                return archDefine(archs[0]);
+            }
+            String[] defs = new String[archs.length];
+            int i = 0;
+            for (String arch : archs) {
+                defs[i++] = archDefine(arch);
+            }
+            return String.join(" ||", defs);
+        }
+
+        public void emit(PrintWriter out) {
+            if (annotation instanceof HotSpotVMField) {
+                emitField(out, (HotSpotVMField) annotation);
+            } else if (annotation instanceof HotSpotVMType) {
+                emitType(out, (HotSpotVMType) annotation);
+            } else if (annotation instanceof HotSpotVMFlag) {
+                emitFlag(out, (HotSpotVMFlag) annotation);
+            } else if (annotation instanceof HotSpotVMConstant) {
+                emitConstant(out, (HotSpotVMConstant) annotation);
+            } else if (annotation instanceof HotSpotVMValue) {
+                emitValue(out, (HotSpotVMValue) annotation);
+            } else {
+                throw new InternalError(annotation.toString());
+            }
+
+        }
+
+        private void emitField(PrintWriter out, HotSpotVMField value) {
+            String type = field.asType().toString();
+            String define = archDefines(value.archs());
+            if (define != null) {
+                out.printf("#if %s\n", define);
+            }
+
+            String name = value.name();
+            int i = name.lastIndexOf("::");
+            switch (value.get()) {
+                case OFFSET:
+                    out.printf("            set_%s(\"%s\", offset_of(%s, %s));\n", type, field.getSimpleName(), name.substring(0, i), name.substring(i + 2));
+                    break;
+                case ADDRESS:
+                    out.printf("            set_address(\"%s\", &%s);\n", field.getSimpleName(), name);
+                    break;
+                case VALUE:
+                    out.printf("            set_%s(\"%s\", (%s) (intptr_t) %s);\n", type, field.getSimpleName(), type, name);
+                    break;
+            }
+            if (define != null) {
+                out.printf("#endif\n");
+            }
+        }
+
+        private void emitType(PrintWriter out, HotSpotVMType value) {
+            String type = field.asType().toString();
+            out.printf("            set_%s(\"%s\", sizeof(%s));\n", type, field.getSimpleName(), value.name());
+        }
+
+        private void emitValue(PrintWriter out, HotSpotVMValue value) {
+            String type = field.asType().toString();
+            int length = value.defines().length;
+            if (length != 0) {
+                out.printf("#if ");
+                for (int i = 0; i < length; i++) {
+                    out.printf("defined(%s)", value.defines()[i]);
+                    if (i + 1 < length) {
+                        out.printf(" || ");
+                    }
+                }
+                out.println();
+            }
+            if (value.get() == HotSpotVMValue.Type.ADDRESS) {
+                out.printf("            set_address(\"%s\", %s);\n", field.getSimpleName(), value.expression());
+            } else {
+                out.printf("            set_%s(\"%s\", %s);\n", type, field.getSimpleName(), value.expression());
+            }
+            if (length != 0) {
+                out.println("#endif");
+            }
+        }
+
+        private void emitConstant(PrintWriter out, HotSpotVMConstant value) {
+            String define = archDefines(value.archs());
+            if (define != null) {
+                out.printf("#if %s\n", define);
+            }
+            String type = field.asType().toString();
+            out.printf("            set_%s(\"%s\", %s);\n", type, field.getSimpleName(), value.name());
+            if (define != null) {
+                out.printf("#endif\n");
+            }
+        }
+
+        private void emitFlag(PrintWriter out, HotSpotVMFlag value) {
+            String type = field.asType().toString();
+
+            String define = archDefines(value.archs());
+            if (define != null) {
+                out.printf("#if %s\n", define);
+            }
+            if (value.optional()) {
+                out.printf("            set_optional_%s_flag(\"%s\",  \"%s\");\n", type, field.getSimpleName(), value.name());
+            } else {
+                out.printf("            set_%s(\"%s\", %s);\n", type, field.getSimpleName(), value.name());
+            }
+            if (define != null) {
+                out.printf("#endif\n");
+            }
+        }
+
+    }
+
+    private void collectAnnotations(RoundEnvironment roundEnv, Map<String, VMConfigField> annotationMap, Class<? extends Annotation> annotationClass) {
+        for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) {
+            Annotation constant = element.getAnnotation(annotationClass);
+            if (element.getKind() != ElementKind.FIELD) {
+                errorMessage(element, "%s annotations may only be on fields", annotationClass.getSimpleName());
+            }
+            if (annotationClass == HotSpotVMValue.class) {
+                HotSpotVMValue value = (HotSpotVMValue) constant;
+                if (value.get() == HotSpotVMValue.Type.ADDRESS && !element.asType().toString().equals("long")) {
+                    errorMessage(element, "HotSpotVMValue with get == ADDRESS must be of type long, but found %s", element.asType());
+                }
+            }
+            if (currentTypeElement == null) {
+                currentTypeElement = element.getEnclosingElement();
+            } else {
+                if (!currentTypeElement.equals(element.getEnclosingElement())) {
+                    errorMessage(element, "Multiple types encountered.  Only HotSpotVMConfig is supported");
+                }
+            }
+            annotationMap.put(element.getSimpleName().toString(), new VMConfigField((VariableElement) element, constant));
+        }
+    }
+
+    private void errorMessage(Element element, String format, Object... args) {
+        processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element);
+    }
+
+    Element currentTypeElement = null;
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (roundEnv.processingOver()) {
+            return true;
+        }
+        logMessage("Starting round %s %s\n", roundEnv, annotations);
+        try {
+
+            currentTypeElement = null;
+
+            // First collect all the annotations.
+            Map<String, VMConfigField> annotationMap = new HashMap<>();
+            collectAnnotations(roundEnv, annotationMap, HotSpotVMConstant.class);
+            collectAnnotations(roundEnv, annotationMap, HotSpotVMFlag.class);
+            collectAnnotations(roundEnv, annotationMap, HotSpotVMField.class);
+            collectAnnotations(roundEnv, annotationMap, HotSpotVMType.class);
+            collectAnnotations(roundEnv, annotationMap, HotSpotVMValue.class);
+
+            if (annotationMap.isEmpty()) {
+                return true;
+            }
+
+            logMessage("type element %s\n", currentTypeElement);
+            createFiles(annotationMap, currentTypeElement);
+
+        } catch (Throwable t) {
+            reportExceptionThrow(null, t);
+        }
+
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConstant.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,52 @@
+/*
+ * 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.hotspotvmconfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Refers to a C++ constant in the VM.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HotSpotVMConstant {
+
+    /**
+     * Returns the name of the constant.
+     *
+     * @return name of constant
+     */
+    String name();
+
+    /**
+     * List of architectures where this constant is required. Names are derived from
+     * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is
+     * required on all architectures.
+     */
+    @SuppressWarnings("javadoc")
+    String[] archs() default {};
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMField.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,85 @@
+/*
+ * 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.hotspotvmconfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Refers to a C++ field in the VM.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HotSpotVMField {
+
+    /**
+     * Types of information this annotation can return.
+     */
+    enum Type {
+        /**
+         * Returns the offset of this field within the type. Only valid for instance fields.
+         */
+        OFFSET,
+
+        /**
+         * Returns the absolute address of this field. Only valid for static fields.
+         */
+        ADDRESS,
+
+        /**
+         * Returns the value of this field. Only valid for static fields.
+         */
+        VALUE;
+    }
+
+    /**
+     * Specifies what type of information to return.
+     *
+     * @see Type
+     */
+    Type get();
+
+    /**
+     * Returns the type name containing this field.
+     *
+     * @return name of containing type
+     */
+    String type();
+
+    /**
+     * Returns the name of this field.
+     *
+     * @return name of field
+     */
+    String name();
+
+    /**
+     * List of architectures where this constant is required. Names are derived from
+     * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is
+     * required on all architectures.
+     */
+    @SuppressWarnings("javadoc")
+    String[] archs() default {};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMFlag.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,53 @@
+/*
+ * 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.hotspotvmconfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Refers to a C++ flag in the VM.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HotSpotVMFlag {
+
+    /**
+     * Returns the name of this flag.
+     *
+     * @return name of flag.
+     */
+    String name();
+
+    /**
+     * List of architectures where this constant is required. Names are derived from
+     * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is
+     * required on all architectures.
+     */
+    @SuppressWarnings("javadoc")
+    String[] archs() default {};
+
+    boolean optional() default false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMType.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,57 @@
+/*
+ * 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.hotspotvmconfig;
+
+import java.lang.annotation.*;
+
+/**
+ * Refers to a C++ type in the VM.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HotSpotVMType {
+
+    /**
+     * Types of information this annotation can return.
+     */
+    enum Type {
+        /**
+         * Returns the size of the type (C++ {@code sizeof()}).
+         */
+        SIZE;
+    }
+
+    /**
+     * Specifies what type of information to return.
+     *
+     * @see Type
+     */
+    Type get();
+
+    /**
+     * Returns the name of the type.
+     *
+     * @return name of type
+     */
+    String name();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMValue.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,55 @@
+/*
+ * 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.hotspotvmconfig;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HotSpotVMValue {
+
+    /**
+     * A C++ expression to be evaluated and assigned to the field.
+     */
+    String expression();
+
+    enum Type {
+        /**
+         * A C++ address which might require extra casts to be safely assigned to a Java field.
+         */
+        ADDRESS,
+
+        /**
+         * A simple value which can be assigned to a regular Java field.
+         */
+        VALUE
+    }
+
+    Type get() default Type.VALUE;
+
+    /**
+     * List of preprocessor symbols that should guard initialization of this value.
+     */
+    String[] defines() default {};
+
+}
--- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/JTTTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/JTTTest.java	Wed May 21 11:45:50 2014 +0200
@@ -80,11 +80,11 @@
     Double delta;
 
     @Override
-    protected void assertEquals(Object expected, Object actual) {
+    protected void assertDeepEquals(Object expected, Object actual) {
         if (delta != null) {
             Assert.assertEquals(((Number) expected).doubleValue(), ((Number) actual).doubleValue(), delta);
         } else {
-            super.assertEquals(expected, actual);
+            super.assertDeepEquals(expected, actual);
         }
     }
 
--- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Class_getInterfaces01.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Class_getInterfaces01.java	Wed May 21 11:45:50 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -30,36 +30,8 @@
  */
 public final class Class_getInterfaces01 extends JTTTest {
 
-    public static String test(int i) {
-        switch (i) {
-            case 0:
-                return toString(I1.class);
-            case 1:
-                return toString(I2.class);
-            case 2:
-                return toString(C1.class);
-            case 3:
-                return toString(C2.class);
-            case 4:
-                return toString(C12.class);
-            default:
-                return null;
-        }
-    }
-
-    private static String toString(Class<?> klass) {
-        final Class<?>[] classes = klass.getInterfaces();
-        final StringBuilder sb = new StringBuilder();
-        boolean first = true;
-        for (Class<?> c : classes) {
-            if (!first) {
-                sb.append(' ');
-            } else {
-                first = false;
-            }
-            sb.append(c.getName());
-        }
-        return sb.toString();
+    public static Class<?>[] test(Class<?> clazz) {
+        return clazz.getInterfaces();
     }
 
     interface I1 {
@@ -84,27 +56,26 @@
 
     @Test
     public void run0() throws Throwable {
-        runTest("test", 0);
+        runTest("test", I1.class);
     }
 
     @Test
     public void run1() throws Throwable {
-        runTest("test", 1);
+        runTest("test", I2.class);
     }
 
     @Test
     public void run2() throws Throwable {
-        runTest("test", 2);
+        runTest("test", C1.class);
     }
 
     @Test
     public void run3() throws Throwable {
-        runTest("test", 3);
+        runTest("test", C2.class);
     }
 
     @Test
     public void run4() throws Throwable {
-        runTest("test", 4);
+        runTest("test", C12.class);
     }
-
 }
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/ArithmeticLIRGenerator.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampJoinTest.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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.assertEquals(StampFactory.declared(getType(Object.class)), 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	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Wed May 21 11:45:50 2014 +0200
@@ -188,6 +188,10 @@
             } while (false);
         }
 
+        if (checkForUnsignedCompare(tool)) {
+            return;
+        }
+
         if (condition() instanceof LogicConstantNode) {
             LogicConstantNode c = (LogicConstantNode) condition();
             if (c.getValue()) {
@@ -220,7 +224,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);
@@ -244,6 +248,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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValuePhiNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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/calc/IntegerRemNode.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerRemNode.java	Wed May 21 11:45:50 2014 +0200
@@ -28,6 +28,7 @@
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
 
 @NodeInfo(shortName = "%")
 public class IntegerRemNode extends FixedBinaryNode implements Canonicalizable, Lowerable, LIRLowerable {
@@ -37,6 +38,11 @@
     }
 
     @Override
+    public boolean inferStamp() {
+        return updateStamp(StampTool.rem(x().stamp(), y().stamp()));
+    }
+
+    @Override
     public Node canonical(CanonicalizerTool tool) {
         if (x().isConstant() && y().isConstant()) {
             long y = y().asConstant().asLong();
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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.nodes/src/com/oracle/graal/nodes/type/StampTool.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java	Wed May 21 11:45:50 2014 +0200
@@ -105,6 +105,37 @@
         return stamp1.unrestricted();
     }
 
+    public static Stamp rem(Stamp stamp1, Stamp stamp2) {
+        if (stamp1 instanceof IntegerStamp && stamp2 instanceof IntegerStamp) {
+            return rem((IntegerStamp) stamp1, (IntegerStamp) stamp2);
+        }
+        return StampFactory.illegal();
+    }
+
+    public static Stamp rem(IntegerStamp stamp1, IntegerStamp stamp2) {
+        assert stamp1.getBits() == stamp2.getBits();
+        long magnitude; // the maximum absolute value of the result
+        if (stamp2.lowerBound() == IntegerStamp.defaultMinValue(stamp2.getBits())) {
+            // Math.abs(...) - 1 does not work in this case
+            magnitude = IntegerStamp.defaultMaxValue(stamp2.getBits());
+        } else {
+            magnitude = Math.max(Math.abs(stamp2.lowerBound()), Math.abs(stamp2.upperBound())) - 1;
+        }
+        long lowerBound = Math.max(stamp1.lowerBound(), -magnitude);
+        if (stamp1.upperBound() > magnitude) {
+            // if the result can wrap around at the upper bound, it can reach any value between 0
+            // and magnitude
+            lowerBound = Math.min(lowerBound, 0);
+        }
+        long upperBound = Math.min(stamp1.upperBound(), magnitude);
+        if (stamp1.lowerBound() < -magnitude) {
+            // if the result can wrap around at the lower bound, it can reach any value between
+            // -magnitude and 0
+            upperBound = Math.max(upperBound, 0);
+        }
+        return StampFactory.forInteger(stamp1.getBits(), lowerBound, upperBound);
+    }
+
     private static boolean addOverflowsPositively(long x, long y, int bits) {
         long result = x + y;
         if (bits == 64) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeoptimizationGroupingPhase.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java	Wed May 21 11:45:50 2014 +0200
@@ -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,111 @@
         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;
+    private static void tryUseTrappingNullCheck(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt) {
+        Node predecessor = deopt.predecessor();
+        if (predecessor instanceof MergeNode) {
+            MergeNode merge = (MergeNode) predecessor;
+
+            // Process each predecessor at the merge, unpacking the reasons and speculations as
+            // needed.
+            ValueNode reason = deopt.getActionAndReason();
+            ValuePhiNode reasonPhi = null;
+            List<ValueNode> reasons = null;
+            int expectedPhis = 0;
+
+            if (reason instanceof ValuePhiNode) {
+                reasonPhi = (ValuePhiNode) reason;
+                if (reasonPhi.merge() != merge) {
+                    return;
+                }
+                reasons = reasonPhi.values().snapshot();
+                expectedPhis++;
+            } else if (!reason.isConstant()) {
+                return;
+            }
+
+            ValueNode speculation = deopt.getSpeculation();
+            ValuePhiNode speculationPhi = null;
+            List<ValueNode> speculations = null;
+            if (speculation instanceof ValuePhiNode) {
+                speculationPhi = (ValuePhiNode) speculation;
+                if (speculationPhi.merge() != merge) {
+                    return;
+                }
+                speculations = speculationPhi.values().snapshot();
+                expectedPhis++;
+            }
+
+            if (merge.phis().count() != expectedPhis) {
+                return;
+            }
+
+            int index = 0;
+            for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
+                ValueNode thisReason = reasons != null ? reasons.get(index) : reason;
+                ValueNode thisSpeculation = speculations != null ? speculations.get(index++) : speculation;
+                if (!thisReason.isConstant() || !thisSpeculation.isConstant() || !thisSpeculation.asConstant().equals(Constant.NULL_OBJECT)) {
+                    continue;
+                }
+                DeoptimizationReason deoptimizationReason = metaAccessProvider.decodeDeoptReason(thisReason.asConstant());
+                tryUseTrappingNullCheck(deopt, end.predecessor(), deoptimizationReason, null);
+            }
         }
-        if (deopt.getSpeculation() != null && !deopt.getSpeculation().equals(Constant.NULL_OBJECT)) {
+    }
+
+    private static void tryUseTrappingNullCheck(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason, Constant speculation) {
+        if (deoptimizationReason != DeoptimizationReason.NullCheckException && deoptimizationReason != DeoptimizationReason.UnreachedCode) {
             return;
         }
-        Node predecessor = deopt.predecessor();
+        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 +161,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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ /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/InliningIterator.java	Tue May 13 19:19:27 2014 +0200
+++ /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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java	Wed May 21 11:45:50 2014 +0200
@@ -22,37 +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.compiler.common.type.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.debug.Debug.Scope;
-import com.oracle.graal.graph.Graph.Mark;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.common.*;
-import com.oracle.graal.phases.common.inlining.InliningUtil.InlineInfo;
-import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable;
-import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableGraph;
-import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableMacroNode;
-import com.oracle.graal.phases.common.inlining.InliningUtil.InliningPolicy;
-import com.oracle.graal.phases.graph.*;
+import com.oracle.graal.phases.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")
@@ -66,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);
     }
@@ -93,38 +71,79 @@
         return inliningCount;
     }
 
+    /**
+     * <p>
+     * The space of inlining decisions is explored depth-first with the help of a stack realized by
+     * {@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 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 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>
+     *
+     * <p>
+     * The bottom-most element in the stack consists of:
+     * <ul>
+     * <li>
+     * a single {@link CallsiteHolder} (the root one, for the method on which inlining was called)</li>
+     * <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>
+     *
+     * <p>
+     * The stack grows and shrinks as choices are made among the alternatives below:
+     * <ol>
+     * <li>
+     * not worth inlining: pop any remaining graphs not yet delved into, pop the current invocation.
+     * </li>
+     * <li>
+     * process next invoke: delve into one of the callsites hosted in the current candidate graph,
+     * determine whether any inlining should be performed in it</li>
+     * <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 com.oracle.graal.phases.common.inlining.walker.InliningData}, and then
+     * remove such callsite.</li>
+     * </ol>
+     * </p>
+     *
+     * <p>
+     * Some facts about the alternatives above:
+     * <ul>
+     * <li>
+     * the first step amounts to backtracking, the 2nd one to delving, and the 3rd one also involves
+     * 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 ones aren't picked</li>
+     * <li>
+     * as part of trying-to-inline, {@link InliningPolicy#isWorthInlining} again sees use, but
+     * that's another story.</li>
+     * </ul>
+     * </p>
+     *
+     */
     @Override
     protected void run(final StructuredGraph graph, final HighTierContext context) {
-        final InliningData data = new InliningData(graph, context.getAssumptions());
-        ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache();
+        final InliningData data = new InliningData(graph, context, maxMethodPerInlining, canonicalizer, inliningPolicy);
 
         while (data.hasUnprocessedGraphs()) {
-            final MethodInvocation currentInvocation = data.currentInvocation();
-            GraphInfo graphInfo = data.currentGraph();
-            if (!currentInvocation.isRoot() &&
-                            !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(),
-                                            currentInvocation.relevance(), false)) {
-                int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs();
-                assert remainingGraphs > 0;
-                data.popGraphs(remainingGraphs);
-                data.popInvocation();
-            } else if (graphInfo.hasRemainingInvokes() && inliningPolicy.continueInlining(graphInfo.graph())) {
-                processNextInvoke(data, graphInfo, context);
-            } else {
-                data.popGraph();
-                if (!currentInvocation.isRoot()) {
-                    assert currentInvocation.callee().invoke().asNode().isAlive();
-                    currentInvocation.incrementProcessedGraphs();
-                    if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) {
-                        data.popInvocation();
-                        final MethodInvocation parentInvoke = data.currentInvocation();
-                        try (Scope s = Debug.scope("Inlining", data.inliningContext())) {
-                            tryToInline(probabilities, data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1, context);
-                        } catch (Throwable e) {
-                            throw Debug.handle(e);
-                        }
-                    }
-                }
+            boolean wasInlined = data.moveForward();
+            if (wasInlined) {
+                inliningCount++;
             }
         }
 
@@ -132,645 +151,4 @@
         assert data.graphCount() == 0;
     }
 
-    /**
-     * Process the next invoke and enqueue all its graphs for processing.
-     */
-    private void processNextInvoke(InliningData data, GraphInfo graphInfo, HighTierContext context) {
-        Invoke invoke = graphInfo.popInvoke();
-        MethodInvocation callerInvocation = data.currentInvocation();
-        Assumptions parentAssumptions = callerInvocation.assumptions();
-        InlineInfo info = InliningUtil.getInlineInfo(data, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations());
-
-        if (info != null) {
-            double invokeProbability = graphInfo.invokeProbability(invoke);
-            double invokeRelevance = graphInfo.invokeRelevance(invoke);
-            MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance);
-
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                Inlineable elem = getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()));
-                info.setInlinableElement(i, elem);
-                if (elem instanceof InlineableGraph) {
-                    data.pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
-                } else {
-                    assert elem instanceof InlineableMacroNode;
-                    data.pushDummyGraph();
-                }
-            }
-        }
-    }
-
-    private void tryToInline(ToDoubleFunction<FixedNode> probabilities, GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth,
-                    HighTierContext context) {
-        InlineInfo callee = calleeInfo.callee();
-        Assumptions callerAssumptions = parentInvocation.assumptions();
-
-        if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) {
-            doInline(callerGraphInfo, calleeInfo, callerAssumptions, context);
-        } else if (context.getOptimisticOptimizations().devirtualizeInvokes()) {
-            callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions);
-        }
-        metricInliningConsidered.increment();
-    }
-
-    private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) {
-        StructuredGraph callerGraph = callerGraphInfo.graph();
-        Mark markBeforeInlining = callerGraph.getMark();
-        InlineInfo callee = calleeInfo.callee();
-        try {
-            try (Scope scope = Debug.scope("doInline", callerGraph)) {
-                List<Node> invokeUsages = callee.invoke().asNode().usages().snapshot();
-                callee.inline(new Providers(context), callerAssumptions);
-                callerAssumptions.record(calleeInfo.assumptions());
-                metricInliningRuns.increment();
-                Debug.dump(callerGraph, "after %s", callee);
-
-                if (OptCanonicalizer.getValue()) {
-                    Mark markBeforeCanonicalization = callerGraph.getMark();
-                    canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining);
-
-                    // process invokes that are possibly created during canonicalization
-                    for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) {
-                        if (newNode instanceof Invoke) {
-                            callerGraphInfo.pushInvoke((Invoke) newNode);
-                        }
-                    }
-                }
-
-                callerGraphInfo.computeProbabilities();
-
-                inliningCount++;
-                metricInliningPerformed.increment();
-            }
-        } catch (BailoutException bailout) {
-            throw bailout;
-        } catch (AssertionError | RuntimeException e) {
-            throw new GraalInternalError(e).addContext(callee.toString());
-        } catch (GraalInternalError e) {
-            throw e.addContext(callee.toString());
-        }
-    }
-
-    private Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context) {
-        Class<? extends FixedWithNextNode> macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method);
-        if (macroNodeClass != null) {
-            return new InlineableMacroNode(macroNodeClass);
-        } else {
-            return new InlineableGraph(buildGraph(method, invoke, context));
-        }
-    }
-
-    private StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context) {
-        final StructuredGraph newGraph;
-        final boolean parseBytecodes;
-
-        // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains
-        // any invokes
-        StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method);
-        if (intrinsicGraph != null) {
-            newGraph = intrinsicGraph.copy();
-            parseBytecodes = false;
-        } else {
-            StructuredGraph cachedGraph = getCachedGraph(method, context);
-            if (cachedGraph != null) {
-                newGraph = cachedGraph.copy();
-                parseBytecodes = false;
-            } else {
-                newGraph = new StructuredGraph(method);
-                parseBytecodes = true;
-            }
-        }
-
-        try (Scope s = Debug.scope("InlineGraph", newGraph)) {
-            if (parseBytecodes) {
-                parseBytecodes(newGraph, context);
-            }
-
-            boolean callerHasMoreInformationAboutArguments = false;
-            NodeInputList<ValueNode> args = invoke.callTarget().arguments();
-            for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) {
-                ValueNode arg = args.get(param.index());
-                if (arg.isConstant()) {
-                    Constant constant = arg.asConstant();
-                    newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph));
-                    callerHasMoreInformationAboutArguments = true;
-                } else {
-                    Stamp joinedStamp = param.stamp().join(arg.stamp());
-                    if (joinedStamp != null && !joinedStamp.equals(param.stamp())) {
-                        param.setStamp(joinedStamp);
-                        callerHasMoreInformationAboutArguments = true;
-                    }
-                }
-            }
-
-            if (!callerHasMoreInformationAboutArguments) {
-                // TODO (chaeubl): if args are not more concrete, inlining should be avoided
-                // in most cases or we could at least use the previous graph size + invoke
-                // probability to check the inlining
-            }
-
-            if (OptCanonicalizer.getValue()) {
-                canonicalizer.apply(newGraph, context);
-            }
-
-            return newGraph;
-        } catch (Throwable e) {
-            throw Debug.handle(e);
-        }
-    }
-
-    private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) {
-        if (context.getGraphCache() != null) {
-            StructuredGraph cachedGraph = context.getGraphCache().get(method);
-            if (cachedGraph != null) {
-                return cachedGraph;
-            }
-        }
-        return null;
-    }
-
-    private StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context) {
-        boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature();
-
-        if (context.getGraphBuilderSuite() != null) {
-            context.getGraphBuilderSuite().apply(newGraph, context);
-        }
-        assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING";
-
-        new DeadCodeEliminationPhase().apply(newGraph);
-
-        if (OptCanonicalizer.getValue()) {
-            canonicalizer.apply(newGraph, context);
-        }
-
-        if (hasMatureProfilingInfo && context.getGraphCache() != null) {
-            context.getGraphCache().put(newGraph.method(), newGraph.copy());
-        }
-        return newGraph;
-    }
-
-    private abstract static class AbstractInliningPolicy implements InliningPolicy {
-
-        protected final Map<Invoke, Double> hints;
-
-        public AbstractInliningPolicy(Map<Invoke, Double> hints) {
-            this.hints = hints;
-        }
-
-        protected double computeMaximumSize(double relevance, int configuredMaximum) {
-            double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance);
-            return configuredMaximum * inlineRatio;
-        }
-
-        protected double getInliningBonus(InlineInfo info) {
-            if (hints != null && hints.containsKey(info.invoke())) {
-                return hints.get(info.invoke());
-            }
-            return 1;
-        }
-
-        protected boolean isIntrinsic(Replacements replacements, InlineInfo info) {
-            if (AlwaysInlineIntrinsics.getValue()) {
-                return onlyIntrinsics(replacements, info);
-            } else {
-                return onlyForcedIntrinsics(replacements, info);
-            }
-        }
-
-        private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) {
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) {
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) {
-                    return false;
-                }
-                if (!replacements.isForcedSubstitution(info.methodAt(i))) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        protected static int previousLowLevelGraphSize(InlineInfo info) {
-            int size = 0;
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                ResolvedJavaMethod m = info.methodAt(i);
-                ProfilingInfo profile = m.getProfilingInfo();
-                int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class);
-                if (compiledGraphSize > 0) {
-                    size += compiledGraphSize;
-                }
-            }
-            return size;
-        }
-
-        protected static int determineNodeCount(InlineInfo info) {
-            int nodes = 0;
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                Inlineable elem = info.inlineableElementAt(i);
-                if (elem != null) {
-                    nodes += elem.getNodeCount();
-                }
-            }
-            return nodes;
-        }
-
-        protected static double determineInvokeProbability(ToDoubleFunction<FixedNode> probabilities, InlineInfo info) {
-            double invokeProbability = 0;
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                Inlineable callee = info.inlineableElementAt(i);
-                Iterable<Invoke> invokes = callee.getInvokes();
-                if (invokes.iterator().hasNext()) {
-                    for (Invoke invoke : invokes) {
-                        invokeProbability += probabilities.applyAsDouble(invoke.asNode());
-                    }
-                }
-            }
-            return invokeProbability;
-        }
-    }
-
-    public static class GreedyInliningPolicy extends AbstractInliningPolicy {
-
-        public GreedyInliningPolicy(Map<Invoke, Double> hints) {
-            super(hints);
-        }
-
-        public boolean continueInlining(StructuredGraph currentGraph) {
-            if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) {
-                InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize");
-                metricInliningStoppedByMaxDesiredSize.increment();
-                return false;
-            }
-            return true;
-        }
-
-        @Override
-        public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance,
-                        boolean fullyProcessed) {
-            if (InlineEverything.getValue()) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything");
-            }
-
-            if (isIntrinsic(replacements, info)) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic");
-            }
-
-            if (info.shouldInline()) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining");
-            }
-
-            double inliningBonus = getInliningBonus(info);
-            int nodes = determineNodeCount(info);
-            int lowLevelGraphSize = previousLowLevelGraphSize(info);
-
-            if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) {
-                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)",
-                                lowLevelGraphSize, relevance, probability, inliningBonus, nodes);
-            }
-
-            if (nodes < TrivialInliningSize.getValue() * inliningBonus) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes);
-            }
-
-            /*
-             * TODO (chaeubl): invoked methods that are on important paths but not yet compiled ->
-             * will be compiled anyways and it is likely that we are the only caller... might be
-             * useful to inline those methods but increases bootstrap time (maybe those methods are
-             * also getting queued in the compilation queue concurrently)
-             */
-            double invokes = determineInvokeProbability(probabilities, info);
-            if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) {
-                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes,
-                                relevance, probability, inliningBonus, nodes);
-            }
-
-            double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus));
-            if (nodes <= maximumNodes) {
-                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability,
-                                inliningBonus, nodes, maximumNodes);
-            }
-
-            return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes,
-                            maximumNodes);
-        }
-    }
-
-    public static final class InlineEverythingPolicy implements InliningPolicy {
-
-        public boolean continueInlining(StructuredGraph graph) {
-            if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) {
-                throw new BailoutException("Inline all calls failed. The resulting graph is too large.");
-            }
-            return true;
-        }
-
-        public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance,
-                        boolean fullyProcessed) {
-            return true;
-        }
-    }
-
-    /**
-     * 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 int maxGraphs;
-
-        public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions) {
-            this.graphQueue = new ArrayDeque<>();
-            this.invocationQueue = new ArrayDeque<>();
-            this.maxGraphs = 1;
-
-            invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0));
-            pushGraph(rootGraph, 1.0, 1.0);
-        }
-
-        public int graphCount() {
-            return graphQueue.size();
-        }
-
-        public void pushGraph(StructuredGraph graph, double probability, double relevance) {
-            assert !contains(graph);
-            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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Wed May 21 11:45:50 2014 +0200
@@ -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;
@@ -51,15 +46,10 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.util.*;
-import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.common.*;
-import com.oracle.graal.phases.common.inlining.InliningPhase.*;
-import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.phases.util.*;
+import com.oracle.graal.phases.common.inlining.info.*;
 
 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,66 +58,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();
-
-        Iterable<Invoke> getInvokes();
-    }
-
-    public static class InlineableGraph implements Inlineable {
-
-        private final StructuredGraph graph;
-
-        public InlineableGraph(StructuredGraph graph) {
-            this.graph = graph;
-        }
-
-        @Override
-        public int getNodeCount() {
-            return graph.getNodeCount();
-        }
-
-        @Override
-        public Iterable<Invoke> getInvokes() {
-            return graph.getInvokes();
-        }
-
-        public StructuredGraph getGraph() {
-            return graph;
-        }
-    }
-
-    public static class InlineableMacroNode implements Inlineable {
-
-        private final Class<? extends FixedWithNextNode> macroNodeClass;
-
-        public InlineableMacroNode(Class<? extends FixedWithNextNode> macroNodeClass) {
-            this.macroNodeClass = macroNodeClass;
-        }
-
-        @Override
-        public int getNodeCount() {
-            return 1;
-        }
-
-        @Override
-        public Iterable<Invoke> getInvokes() {
-            return Collections.emptyList();
-        }
-
-        public Class<? extends FixedWithNextNode> getMacroNodeClass() {
-            return macroNodeClass;
-        }
-    }
-
     /**
      * Print a HotSpot-style inlining message to the console.
      */
@@ -155,22 +85,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 +111,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 +173,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,45 +188,32 @@
         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) {
-            // 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 is dead code";
+        }
+        if (!(invoke.callTarget() instanceof MethodCallTargetNode)) {
+            return "the invoke has already been lowered, or has been created as a low-level node";
         }
-    }
-
-    private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) {
-        if (method == null) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved");
-        } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(replacements, method))) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is a non-intrinsic native method");
-        } else if (method.isAbstract()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method");
-        } else if (!method.getDeclaringClass().isInitialized()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized");
-        } else if (!method.canBeInlined()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable");
-        } else if (data.countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it exceeds the maximum recursive inlining depth");
-        } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(optimisticOpts)) {
-            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller");
-        } else {
-            return true;
+        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 "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;
     }
 
     /**
--- /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	Wed May 21 11:45:50 2014 +0200
@@ -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.info.elem.Inlineable;
+import com.oracle.graal.phases.common.inlining.info.elem.InlineableMacroNode;
+import com.oracle.graal.phases.common.inlining.info.elem.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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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.common.inlining.info.elem.Inlineable;
+import com.oracle.graal.phases.util.Providers;
+
+/**
+ * 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	Wed May 21 11:45:50 2014 +0200
@@ -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.info.elem.Inlineable;
+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);
+
+    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();
+}
--- /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	Wed May 21 11:45:50 2014 +0200
@@ -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.info.elem.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	Wed May 21 11:45:50 2014 +0200
@@ -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.common.inlining.info.elem.Inlineable;
+import com.oracle.graal.phases.util.Providers;
+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/info/elem/Inlineable.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.phases.common.inlining.info.elem;
+
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.nodes.FixedWithNextNode;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.phases.common.CanonicalizerPhase;
+import com.oracle.graal.phases.common.inlining.InliningUtil;
+import com.oracle.graal.phases.tiers.HighTierContext;
+
+public interface Inlineable {
+
+    static 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 InlineableMacroNode(macroNodeClass);
+        } else {
+            return new InlineableGraph(method, invoke, context, canonicalizer);
+        }
+    }
+
+    int getNodeCount();
+
+    Iterable<Invoke> getInvokes();
+}
--- /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/elem/InlineableGraph.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.phases.common.inlining.info.elem;
+
+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;
+
+public class InlineableGraph implements Inlineable {
+
+    private final StructuredGraph graph;
+
+    public InlineableGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) {
+        this.graph = 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;
+    }
+
+    @Override
+    public int getNodeCount() {
+        return graph.getNodeCount();
+    }
+
+    @Override
+    public Iterable<Invoke> getInvokes() {
+        return graph.getInvokes();
+    }
+
+    public StructuredGraph getGraph() {
+        return graph;
+    }
+}
--- /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/elem/InlineableMacroNode.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.phases.common.inlining.info.elem;
+
+import com.oracle.graal.nodes.FixedWithNextNode;
+import com.oracle.graal.nodes.Invoke;
+
+import java.util.Collections;
+
+public class InlineableMacroNode implements Inlineable {
+
+    private final Class<? extends FixedWithNextNode> macroNodeClass;
+
+    public InlineableMacroNode(Class<? extends FixedWithNextNode> macroNodeClass) {
+        this.macroNodeClass = macroNodeClass;
+    }
+
+    @Override
+    public int getNodeCount() {
+        return 1;
+    }
+
+    @Override
+    public Iterable<Invoke> getInvokes() {
+        return Collections.emptyList();
+    }
+
+    public Class<? extends FixedWithNextNode> getMacroNodeClass() {
+        return macroNodeClass;
+    }
+}
--- /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	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,127 @@
+/*
+ * 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 com.oracle.graal.phases.common.inlining.info.elem.Inlineable;
+
+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++) {
+            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;
+    }
+}
--- /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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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/InliningData.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,596 @@
+/*
+ * 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.info.elem.Inlineable;
+import com.oracle.graal.phases.common.inlining.info.elem.InlineableGraph;
+import com.oracle.graal.phases.common.inlining.info.elem.InlineableMacroNode;
+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.*;
+
+/**
+ * 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);
+    }
+
+    private String checkTargetConditionsHelper(ResolvedJavaMethod method) {
+        if (method == null) {
+            return "the method is not resolved";
+        } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(context.getReplacements(), method))) {
+            return "it is a non-intrinsic native method";
+        } else if (method.isAbstract()) {
+            return "it is an abstract method";
+        } else if (!method.getDeclaringClass().isInitialized()) {
+            return "the method's class is not initialized";
+        } else if (!method.canBeInlined()) {
+            return "it is marked non-inlinable";
+        } else if (countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) {
+            return "it exceeds the maximum recursive inlining depth";
+        } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(context.getOptimisticOptimizations())) {
+            return "the callee uses less optimistic optimizations than caller";
+        } else {
+            return null;
+        }
+    }
+
+    private boolean checkTargetConditions(Invoke invoke, ResolvedJavaMethod method) {
+        final String failureMessage = checkTargetConditionsHelper(method);
+        if (failureMessage == null) {
+            return true;
+        } else {
+            InliningUtil.logNotInlined(invoke, inliningDepth(), method, failureMessage);
+            return false;
+        }
+    }
+
+    /**
+     * 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 (!checkTargetConditions(invoke, concrete)) {
+                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 (!checkTargetConditions(invoke, concrete)) {
+                    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 (!checkTargetConditions(invoke, concrete)) {
+            return null;
+        }
+        return new AssumptionInlineInfo(invoke, concrete, takenAssumption);
+    }
+
+    private InlineInfo getExactInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) {
+        assert !targetMethod.isAbstract();
+        if (!checkTargetConditions(invoke, targetMethod)) {
+            return null;
+        }
+        return new ExactInlineInfo(invoke, targetMethod);
+    }
+
+    private void doInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInvocation, Assumptions callerAssumptions) {
+        StructuredGraph callerGraph = callerCallsiteHolder.graph();
+        Graph.Mark markBeforeInlining = callerGraph.getMark();
+        InlineInfo calleeInfo = calleeInvocation.callee();
+        try {
+            try (Debug.Scope scope = Debug.scope("doInline", callerGraph)) {
+                List<Node> invokeUsages = calleeInfo.invoke().asNode().usages().snapshot();
+                calleeInfo.inline(new Providers(context), callerAssumptions);
+                callerAssumptions.record(calleeInvocation.assumptions());
+                metricInliningRuns.increment();
+                Debug.dump(callerGraph, "after %s", calleeInfo);
+
+                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(calleeInfo.toString());
+        } catch (GraalInternalError e) {
+            throw e.addContext(calleeInfo.toString());
+        }
+    }
+
+    /**
+     * @return true iff inlining was actually performed
+     */
+    private boolean tryToInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInvocation, MethodInvocation parentInvocation, int inliningDepth) {
+        InlineInfo calleeInfo = calleeInvocation.callee();
+        Assumptions callerAssumptions = parentInvocation.assumptions();
+        metricInliningConsidered.increment();
+
+        if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), calleeInfo, inliningDepth, calleeInvocation.probability(), calleeInvocation.relevance(), true)) {
+            doInline(callerCallsiteHolder, calleeInvocation, callerAssumptions);
+            return true;
+        }
+
+        if (context.getOptimisticOptimizations().devirtualizeInvokes()) {
+            calleeInfo.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();
+        Assumptions calleeAssumptions = new Assumptions(parentAssumptions.useOptimisticAssumptions());
+        InlineInfo info = populateInlineInfo(invoke, parentAssumptions, calleeAssumptions);
+
+        if (info != null) {
+            double invokeProbability = callsiteHolder.invokeProbability(invoke);
+            double invokeRelevance = callsiteHolder.invokeRelevance(invoke);
+            MethodInvocation methodInvocation = new MethodInvocation(info, calleeAssumptions, invokeProbability, invokeRelevance);
+            pushInvocation(methodInvocation);
+
+            for (int i = 0; i < info.numberOfMethods(); i++) {
+                Inlineable elem = info.inlineableElementAt(i);
+                if (elem instanceof InlineableGraph) {
+                    pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
+                } else {
+                    assert elem instanceof InlineableMacroNode;
+                    pushDummyGraph();
+                }
+            }
+        }
+    }
+
+    private InlineInfo populateInlineInfo(Invoke invoke, Assumptions parentAssumptions, Assumptions calleeAssumptions) {
+        InlineInfo info = getInlineInfo(invoke, parentAssumptions);
+        if (info == null) {
+            return null;
+        }
+        for (int i = 0; i < info.numberOfMethods(); i++) {
+            Inlineable elem = Inlineable.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeAssumptions), canonicalizer);
+            info.setInlinableElement(i, elem);
+        }
+        return info;
+    }
+
+    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 void pushInvocation(MethodInvocation methodInvocation) {
+        invocationQueue.addFirst(methodInvocation);
+        maxGraphs += methodInvocation.callee().numberOfMethods();
+        assert graphQueue.size() <= maxGraphs;
+    }
+
+    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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/SinglePassNodeIterator.java	Wed May 21 11:45:50 2014 +0200
@@ -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/schedule/SchedulePhase.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Wed May 21 11:45:50 2014 +0200
@@ -235,18 +235,17 @@
             if (!foundExcludeNode && node == excludeNode) {
                 foundExcludeNode = true;
             }
-            if (node == startNode) {
-                continue;
+            if (node != startNode) {
+                if (node instanceof MemoryCheckpoint.Single) {
+                    LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity();
+                    accm.add(identity);
+                } else if (node instanceof MemoryCheckpoint.Multi) {
+                    for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
+                        accm.add(identity);
+                    }
+                }
+                assert MemoryCheckpoint.TypeAssertion.correctType(node);
             }
-            if (node instanceof MemoryCheckpoint.Single) {
-                LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity();
-                accm.add(identity);
-            } else if (node instanceof MemoryCheckpoint.Multi) {
-                for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
-                    accm.add(identity);
-                }
-            }
-            assert MemoryCheckpoint.TypeAssertion.correctType(node);
 
             if (foundExcludeNode) {
                 accm = set;
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Wed May 21 11:45:50 2014 +0200
@@ -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;
                             }
                         });
                     }
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java	Wed May 21 11:45:50 2014 +0200
@@ -79,9 +79,9 @@
             Object arg1 = args1[i];
             Object arg2 = args2[i];
             // Verify that the original method and the substitution produce the same value
-            assertEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2));
+            assertDeepEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2));
             // Verify that the generated code and the original produce the same value
-            assertEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2));
+            assertDeepEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2));
         }
     }
 
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewArrayTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewArrayTest.java	Wed May 21 11:45:50 2014 +0200
@@ -32,10 +32,10 @@
 public class NewArrayTest extends GraalCompilerTest {
 
     @Override
-    protected void assertEquals(Object expected, Object actual) {
+    protected void assertDeepEquals(Object expected, Object actual) {
         Assert.assertTrue(expected != null);
         Assert.assertTrue(actual != null);
-        super.assertEquals(expected.getClass(), actual.getClass());
+        super.assertDeepEquals(expected.getClass(), actual.getClass());
         if (expected instanceof int[]) {
             Assert.assertArrayEquals((int[]) expected, (int[]) actual);
         } else if (expected instanceof byte[]) {
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewInstanceTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewInstanceTest.java	Wed May 21 11:45:50 2014 +0200
@@ -34,10 +34,10 @@
 public class NewInstanceTest extends GraalCompilerTest {
 
     @Override
-    protected void assertEquals(Object expected, Object actual) {
+    protected void assertDeepEquals(Object expected, Object actual) {
         Assert.assertTrue(expected != null);
         Assert.assertTrue(actual != null);
-        super.assertEquals(expected.getClass(), actual.getClass());
+        super.assertDeepEquals(expected.getClass(), actual.getClass());
 
         if (expected instanceof Object[]) {
             Assert.assertTrue(actual instanceof Object[]);
@@ -45,12 +45,12 @@
             Object[] aArr = (Object[]) actual;
             Assert.assertTrue(eArr.length == aArr.length);
             for (int i = 0; i < eArr.length; i++) {
-                assertEquals(eArr[i], aArr[i]);
+                assertDeepEquals(eArr[i], aArr[i]);
             }
         } else if (expected.getClass() != Object.class) {
             try {
                 expected.getClass().getDeclaredMethod("equals", Object.class);
-                super.assertEquals(expected, actual);
+                super.assertDeepEquals(expected, actual);
             } catch (Exception e) {
             }
         }
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StandardMethodSubstitutionsTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StandardMethodSubstitutionsTest.java	Wed May 21 11:45:50 2014 +0200
@@ -44,12 +44,12 @@
         test("math");
 
         double value = 34567.891D;
-        assertEquals(Math.sqrt(value), MathSubstitutionsX86.sqrt(value));
-        assertEquals(Math.log(value), MathSubstitutionsX86.log(value));
-        assertEquals(Math.log10(value), MathSubstitutionsX86.log10(value));
-        assertEquals(Math.sin(value), MathSubstitutionsX86.sin(value));
-        assertEquals(Math.cos(value), MathSubstitutionsX86.cos(value));
-        assertEquals(Math.tan(value), MathSubstitutionsX86.tan(value));
+        assertDeepEquals(Math.sqrt(value), MathSubstitutionsX86.sqrt(value));
+        assertDeepEquals(Math.log(value), MathSubstitutionsX86.log(value));
+        assertDeepEquals(Math.log10(value), MathSubstitutionsX86.log10(value));
+        assertDeepEquals(Math.sin(value), MathSubstitutionsX86.sin(value));
+        assertDeepEquals(Math.cos(value), MathSubstitutionsX86.cos(value));
+        assertDeepEquals(Math.tan(value), MathSubstitutionsX86.tan(value));
     }
 
     @SuppressWarnings("all")
@@ -98,9 +98,9 @@
         assert optional || code != null;
         for (Object l : args) {
             // Verify that the original method and the substitution produce the same value
-            assertEquals(invokeSafe(testMethod, l), invokeSafe(realMethod, l));
+            assertDeepEquals(invokeSafe(testMethod, l), invokeSafe(realMethod, l));
             // Verify that the generated code and the original produce the same value
-            assertEquals(executeVarargsSafe(code, l), invokeSafe(realMethod, l));
+            assertDeepEquals(executeVarargsSafe(code, l), invokeSafe(realMethod, l));
         }
     }
 
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StringSubstitutionsTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StringSubstitutionsTest.java	Wed May 21 11:45:50 2014 +0200
@@ -73,9 +73,9 @@
             Object arg1 = args1[i];
             Object arg2 = args2[i];
             // Verify that the original method and the substitution produce the same value
-            assertEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, arg1, arg2));
+            assertDeepEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, arg1, arg2));
             // Verify that the generated code and the original produce the same value
-            assertEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, arg1, arg2));
+            assertDeepEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, arg1, arg2));
         }
     }
 
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java	Wed May 21 11:45:50 2014 +0200
@@ -69,14 +69,14 @@
         {
             Object expected = invokeSafe(originalMethod, receiver, args1);
             Object actual = invokeSafe(testMethod, null, args2);
-            assertEquals(expected, actual);
+            assertDeepEquals(expected, actual);
         }
 
         // Verify that the generated code and the original produce the same value
         {
             Object expected = invokeSafe(originalMethod, receiver, args1);
             Object actual = executeVarargsSafe(code, args2);
-            assertEquals(expected, actual);
+            assertDeepEquals(expected, actual);
         }
     }
 
@@ -201,18 +201,18 @@
 
         AtomicInteger a1 = new AtomicInteger(42);
         AtomicInteger a2 = new AtomicInteger(42);
-        assertEquals(unsafe.compareAndSwapInt(a1, off(a1, "value"), 42, 53), compareAndSwapInt(unsafe, a2, off(a2, "value"), 42, 53));
-        assertEquals(a1.get(), a2.get());
+        assertDeepEquals(unsafe.compareAndSwapInt(a1, off(a1, "value"), 42, 53), compareAndSwapInt(unsafe, a2, off(a2, "value"), 42, 53));
+        assertDeepEquals(a1.get(), a2.get());
 
         AtomicLong l1 = new AtomicLong(42);
         AtomicLong l2 = new AtomicLong(42);
-        assertEquals(unsafe.compareAndSwapLong(l1, off(l1, "value"), 42, 53), compareAndSwapLong(unsafe, l2, off(l2, "value"), 42, 53));
-        assertEquals(l1.get(), l2.get());
+        assertDeepEquals(unsafe.compareAndSwapLong(l1, off(l1, "value"), 42, 53), compareAndSwapLong(unsafe, l2, off(l2, "value"), 42, 53));
+        assertDeepEquals(l1.get(), l2.get());
 
         AtomicReference<String> o1 = new AtomicReference<>("42");
         AtomicReference<String> o2 = new AtomicReference<>("42");
-        assertEquals(unsafe.compareAndSwapObject(o1, off(o1, "value"), "42", "53"), compareAndSwapObject(unsafe, o2, off(o2, "value"), "42", "53"));
-        assertEquals(o1.get(), o2.get());
+        assertDeepEquals(unsafe.compareAndSwapObject(o1, off(o1, "value"), "42", "53"), compareAndSwapObject(unsafe, o2, off(o2, "value"), "42", "53"));
+        assertDeepEquals(o1.get(), o2.get());
 
         Foo f1 = new Foo();
         f1.test("z", "Boolean", Boolean.TRUE);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/AnsiTerminalDecorator.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java	Wed May 21 11:45:50 2014 +0200
@@ -76,37 +76,83 @@
      * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}.
      * Does a deep copy equality comparison if {@code expected} is an array.
      */
-    protected void assertEquals(Object expected, Object actual) {
-        if (expected != null && expected.getClass().isArray()) {
-            Assert.assertTrue(expected != null);
-            Assert.assertTrue(actual != null);
-            Assert.assertEquals(expected.getClass(), actual.getClass());
-            if (expected instanceof int[]) {
-                Assert.assertArrayEquals((int[]) expected, (int[]) actual);
-            } else if (expected instanceof byte[]) {
-                Assert.assertArrayEquals((byte[]) expected, (byte[]) actual);
-            } else if (expected instanceof char[]) {
-                Assert.assertArrayEquals((char[]) expected, (char[]) actual);
-            } else if (expected instanceof short[]) {
-                Assert.assertArrayEquals((short[]) expected, (short[]) actual);
-            } else if (expected instanceof float[]) {
-                Assert.assertArrayEquals((float[]) expected, (float[]) actual, 0.0f);
-            } else if (expected instanceof long[]) {
-                Assert.assertArrayEquals((long[]) expected, (long[]) actual);
-            } else if (expected instanceof double[]) {
-                Assert.assertArrayEquals((double[]) expected, (double[]) actual, 0.0d);
-            } else if (expected instanceof boolean[]) {
-                new ExactComparisonCriteria().arrayEquals(null, expected, actual);
-            } else if (expected instanceof Object[]) {
-                Assert.assertArrayEquals((Object[]) expected, (Object[]) actual);
+    protected void assertDeepEquals(Object expected, Object actual) {
+        assertDeepEquals(null, expected, actual);
+    }
+
+    /**
+     * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}.
+     * Does a deep copy equality comparison if {@code expected} is an array.
+     *
+     * @param message the identifying message for the {@link AssertionError}
+     */
+    protected void assertDeepEquals(String message, Object expected, Object actual) {
+        assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta());
+    }
+
+    /**
+     * Compares two given values for equality, doing a recursive test if both values are arrays of
+     * the same type.
+     *
+     * @param message the identifying message for the {@link AssertionError}
+     * @param delta the maximum delta between two doubles or floats for which both numbers are still
+     *            considered equal.
+     */
+    protected void assertDeepEquals(String message, Object expected, Object actual, double delta) {
+        if (expected != null && actual != null) {
+            Class<?> expectedClass = expected.getClass();
+            Class<?> actualClass = actual.getClass();
+            if (expectedClass.isArray()) {
+                Assert.assertTrue(message, expected != null);
+                Assert.assertTrue(message, actual != null);
+                Assert.assertEquals(message, expectedClass, actual.getClass());
+                if (expected instanceof int[]) {
+                    Assert.assertArrayEquals(message, (int[]) expected, (int[]) actual);
+                } else if (expected instanceof byte[]) {
+                    Assert.assertArrayEquals(message, (byte[]) expected, (byte[]) actual);
+                } else if (expected instanceof char[]) {
+                    Assert.assertArrayEquals(message, (char[]) expected, (char[]) actual);
+                } else if (expected instanceof short[]) {
+                    Assert.assertArrayEquals(message, (short[]) expected, (short[]) actual);
+                } else if (expected instanceof float[]) {
+                    Assert.assertArrayEquals(message, (float[]) expected, (float[]) actual, (float) delta);
+                } else if (expected instanceof long[]) {
+                    Assert.assertArrayEquals(message, (long[]) expected, (long[]) actual);
+                } else if (expected instanceof double[]) {
+                    Assert.assertArrayEquals(message, (double[]) expected, (double[]) actual, delta);
+                } else if (expected instanceof boolean[]) {
+                    new ExactComparisonCriteria().arrayEquals(message, expected, actual);
+                } else if (expected instanceof Object[]) {
+                    new ComparisonCriteria() {
+                        @Override
+                        protected void assertElementsEqual(Object e, Object a) {
+                            assertDeepEquals(message, e, a, delta);
+                        }
+                    }.arrayEquals(message, expected, actual);
+                } else {
+                    Assert.fail((message == null ? "" : message) + "non-array value encountered: " + expected);
+                }
+            } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) {
+                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.fail("non-array value encountered: " + expected);
+                Assert.assertEquals(message, expected, actual);
             }
         } else {
-            Assert.assertEquals(expected, actual);
+            Assert.assertEquals(message, expected, actual);
         }
     }
 
+    /**
+     * Gets the value used by {@link #assertDeepEquals(Object, Object)} and
+     * {@link #assertDeepEquals(String, Object, Object)} for the maximum delta between two doubles
+     * or floats for which both numbers are still considered equal.
+     */
+    protected double equalFloatsOrDoublesDelta() {
+        return 0.0D;
+    }
+
     @SuppressWarnings("serial")
     public static class MultiCauseAssertionError extends AssertionError {
 
--- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Wed May 21 11:45:50 2014 +0200
@@ -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/AssumptionPartialEvaluationTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/AssumptionPartialEvaluationTest.java	Wed May 21 11:45:50 2014 +0200
@@ -46,7 +46,7 @@
         InstalledCode installedCode = assertPartialEvalEquals("constant42", rootNode);
         Assert.assertTrue(installedCode.isValid());
         try {
-            assertEquals(42, installedCode.executeVarargs(null, null, null));
+            assertDeepEquals(42, installedCode.executeVarargs(null, null, null));
         } catch (InvalidInstalledCodeException e) {
             Assert.fail("Code must not have been invalidated.");
         }
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/ExactMathSubstitutions.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/VirtualObjectState.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExactMath.java	Wed May 21 11:45:50 2014 +0200
@@ -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/ExecutionContext.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java	Wed May 21 11:45:50 2014 +0200
@@ -27,31 +27,131 @@
 import java.util.*;
 
 import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.instrument.impl.*;
 import com.oracle.truffle.api.source.*;
 
 /**
  * Access to information and basic services in the runtime context for a Truffle-implemented guest
  * language.
  * <p>
- * <strong>Disclaimer:</strong> this interface is under development and will change.
+ * <strong>Disclaimer:</strong> this class is under development and will change.
  */
-public interface ExecutionContext {
+public abstract class ExecutionContext {
+
+    private final ProbeManager probeManager = new ProbeManager();
+    private final SourceManager sourceManager = new SourceManager();
+    private final List<SourceListener> sourceListeners = new ArrayList<>();
+    private Visualizer visualizer = new DefaultVisualizer();
+
+    protected ExecutionContext() {
+    }
+
+    public void initialize() {
+        setSourceCallback(new SourceCallback() {
+
+            public void startLoading(Source source) {
+                for (SourceListener listener : sourceListeners) {
+                    listener.loadStarting(source);
+                }
+            }
+
+            public void endLoading(Source source) {
+                for (SourceListener listener : sourceListeners) {
+                    listener.loadEnding(source);
+                }
+            }
+        });
+    }
+
+    /**
+     * Gets access to source management services.
+     */
+    public final SourceManager getSourceManager() {
+        return sourceManager;
+    }
+
+    /**
+     * Registers a tool interested in being notified about the loading of {@link Source}s.
+     */
+    public final void addSourceListener(SourceListener listener) {
+        assert listener != null;
+        sourceListeners.add(listener);
+    }
+
+    /**
+     * Registers a tool interested in being notified about the insertion of a newly created
+     * {@link Probe} into a Truffle AST.
+     */
+    public final void addProbeListener(ProbeListener listener) {
+        probeManager.addProbeListener(listener);
+    }
+
+    /**
+     * Return the (possibly newly created) {@link Probe} uniquely associated with a particular
+     * source code location. A newly created probe carries no tags.
+     *
+     * @return a probe uniquely associated with an extent of guest language source code.
+     */
+    public final Probe getProbe(SourceSection sourceSection) {
+        return probeManager.getProbe(sourceSection);
+    }
+
+    /**
+     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
+     * collection if no probes found.
+     */
+    public final Collection<Probe> findProbesTaggedAs(PhylumTag tag) {
+        return probeManager.findProbesTaggedAs(tag);
+    }
+
+    /**
+     * Returns all existing probes with first character on a specified line; empty collection if no
+     * probes found.
+     */
+    public final Collection<Probe> findProbesByLine(SourceLineLocation lineLocation) {
+        return probeManager.findProbesByLine(lineLocation);
+    }
+
+    /**
+     * Sets a trap that will make a callback at any AST location where a existing probe holds a
+     * specified tag; only one trap may be set at a time.
+     *
+     * @throws IllegalStateException if a trap is already set
+     */
+    public final void setPhylumTrap(PhylumTrap trap) throws IllegalStateException {
+        // TODO (mlvdv) consider allowing multiple traps (without inhibiting Truffle inlining)
+        probeManager.setPhylumTrap(trap);
+    }
+
+    /**
+     * Clears a trap that will halt execution; only one trap may be set at a time.
+     *
+     * @throws IllegalStateException if no trap is set.
+     */
+    public final void clearPhylumTrap() {
+        probeManager.clearPhylumTrap();
+    }
+
+    /**
+     * Access to information visualization services for the specific language.
+     */
+    public final Visualizer getVisualizer() {
+        return visualizer;
+    }
+
+    /**
+     * Assign guest language-specific visualization support for tools. This must be assigned outside
+     * the implementation context to avoid build circularities.
+     */
+    public final void setVisualizer(Visualizer visualizer) {
+        this.visualizer = visualizer;
+    }
 
     /**
      * Gets the name of the language, possibly with version number. in short enough form that it
      * might be used for an interactive prompt.
      */
-    String getLanguageShortName();
-
-    /**
-     * Gets access to source management services.
-     */
-    SourceManager getSourceManager();
-
-    /**
-     * Registers a tool interested in being notified of events related to the loading of sources.
-     */
-    void addSourceListener(SourceListener listener);
+    public abstract String getLanguageShortName();
 
     /**
      * Add instrumentation to subsequently constructed Truffle ASTs for the guest language; every
@@ -60,51 +160,19 @@
      * @throws IllegalStateException if AST instrumentation not enabled
      * @throws IllegalArgumentException if prober not usable for the guest language implementation.
      */
-    void addNodeProber(ASTNodeProber nodeProber) throws IllegalStateException, IllegalArgumentException;
-
-    /**
-     * Registers a tool interested in being notified about the insertion of a newly created
-     * {@link Probe} into a Truffle AST.
-     */
-    void addProbeListener(ProbeListener listener);
-
-    /**
-     * Return the (possibly newly created) {@link Probe} uniquely associated with a particular
-     * source code location. A newly created probe carries no tags.
-     *
-     * @return a probe uniquely associated with an extent of guest language source code.
-     */
-    Probe getProbe(SourceSection sourceSection);
-
-    /**
-     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
-     * collection if no probes found.
-     */
-    Collection<Probe> findProbesTaggedAs(PhylumTag tag);
+    public abstract void addNodeProber(ASTNodeProber nodeProber) throws IllegalStateException, IllegalArgumentException;
 
     /**
-     * Returns all existing probes with first character on a specified line; empty collection if no
-     * probes found.
+     * Assigns a guest language-specific manager for using {@link ASTNodeProber}s added by tools to
+     * instrument ASTs with {@link Probe}s at specified nodes. This must be assigned outside the
+     * implementation context to avoid build circularities. It must also be set before any
+     * instrumentation probe implementations are assigned.
      */
-    Collection<Probe> findProbesByLine(SourceLineLocation lineLocation);
+    public abstract void setASTProber(ASTProber astProber);
 
     /**
-     * Sets a trap that will make a callback at any AST location where a existing probe holds a
-     * specified tag; only one trap may be set at a time.
-     *
-     * @throws IllegalStateException if a trap is already set
+     * Establishes source event reporting
      */
-    void setTrap(PhylumTrap trap) throws IllegalStateException;
+    protected abstract void setSourceCallback(SourceCallback sourceCallback);
 
-    /**
-     * Clears a trap that will halt execution; only one trap may be set at a time.
-     *
-     * @throws IllegalStateException if no trap is set.
-     */
-    void clearTrap() throws IllegalStateException;
-
-    /**
-     * Access to information visualization services for the specific language.
-     */
-    Visualizer getVisualizer();
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java	Wed May 21 11:45:50 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -26,6 +26,8 @@
 
 import java.io.*;
 
+import com.oracle.truffle.api.source.*;
+
 /**
  * Represents a unit (typically a file) of guest language source code.
  */
@@ -97,4 +99,61 @@
      */
     int getLineLength(int lineNumber);
 
+    /**
+     * Creates a representation of a contiguous region of text in the source. Computes the
+     * {@code (startLine, startColumn)} values by building a {@linkplain TextMap map} of lines in
+     * the source.
+     * <p>
+     * Checks the position arguments for consistency with the source.
+     * <p>
+     * The resulting representation defines hash/equality around equivalent location, presuming that
+     * {@link Source} representations are cannonical.
+     *
+     *
+     * @param identifier terse description of the region
+     * @param charIndex 0-based position of the first character in the section
+     * @param length the number of characters in the section
+     * @return newly created object representing the specified region
+     * @throws IllegalArgumentException if either of the arguments are outside the text of the
+     *             source
+     * @throws IllegalStateException if the source is one of the "null" instances
+     */
+    SourceSection createSection(String identifier, int charIndex, int length) throws IllegalArgumentException, IllegalStateException;
+
+    /**
+     * Creates a representation of a contiguous region of text in the source. Computes the
+     * {@code charIndex} value by building a {@linkplain TextMap map} of lines in the source.
+     * <p>
+     * Checks the position arguments for consistency with the source.
+     * <p>
+     * The resulting representation defines hash/equality around equivalent location, presuming that
+     * {@link Source} representations are cannonical.
+     *
+     * @param identifier terse description of the region
+     * @param startLine 1-based line number of the first character in the section
+     * @param startColumn 1-based column number of the first character in the section
+     * @param length the number of characters in the section
+     * @return newly created object representing the specified region
+     * @throws IllegalArgumentException if arguments are outside the text of the source
+     * @throws IllegalStateException if the source is one of the "null" instances
+     */
+    SourceSection createSection(String identifier, int startLine, int startColumn, int length);
+
+    /**
+     * Creates a representation of a contiguous region of text in the source.
+     * <p>
+     * This method performs no checks on the validity of the arguments.
+     * <p>
+     * The resulting representation defines hash/equality around equivalent location, presuming that
+     * {@link Source} representations are cannonical.
+     *
+     * @param identifier terse description of the region
+     * @param startLine 1-based line number of the first character in the section
+     * @param startColumn 1-based column number of the first character in the section
+     * @param charIndex the 0-based index of the first character of the section
+     * @param length the number of characters in the section
+     * @return newly created object representing the specified region
+     */
+    SourceSection createSection(String identifier, int startLine, int startColumn, int charIndex, int length);
+
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java	Wed May 21 11:45:50 2014 +0200
@@ -25,46 +25,62 @@
 package com.oracle.truffle.api;
 
 /**
- * Description of contiguous text section within the source code of a guest language program.
+ * Description of contiguous section of text within a {@link Source} of program code.
+ *
+ * The starting location of the section can be described using two different coordinates:
+ * <ul>
+ * <li>{@code (startLine, startColumn)}: rows and columns are 1-based, so the first character in a
+ * source file is at position {@code (1,1)}. {@code Tab} characters are counted as occupying one
+ * column.</li>
+ * <li><b>character index</b>: 0-based offset of the character from the beginning of the source, so
+ * the first character in a file is at index {@code 0}.</li>
+ * </ul>
+ * The {@code Newline} that terminates each line counts as a single character for the purpose of a
+ * character index and when counting the length of text. The {@code (line,column)} coordinates of a
+ * {@code Newline} should never appear in a text section.
+ * <p>
+ * If the final character of source is not a {@code Newline}, the final characters of the text are
+ * still considered to be a line ("unterminated").
+ * <p>
+ *
+ * @see Source#createSection(String, int, int, int, int)
+ * @see Source#createSection(String, int, int, int)
+ * @see Source#createSection(String, int, int)
  */
 public interface SourceSection {
 
+    // TODO support alternate text representations/encodings
+
     /**
-     * Returns the object representing the source program that contains this section.
+     * Representation of the source program that contains this section.
      *
      * @return the source object
      */
     Source getSource();
 
     /**
-     * Returns 1-based line number of the first character in this source section (inclusive).
+     * Returns 1-based line number of the first character in this section (inclusive).
      *
      * @return the starting line number
      */
     int getStartLine();
 
     /**
-     * Returns the 1-based column number of the first character in this source section (inclusive).
+     * Returns the 1-based column number of the first character in this section (inclusive).
      *
      * @return the starting column number
      */
     int getStartColumn();
 
     /**
-     * Returns the 0-based index of the first character in this source section.
-     * <p>
-     * The complete text of the source that contains this section can be retrieved via
-     * {@link Source#getCode()}.
+     * Returns the 0-based index of the first character in this section.
      *
      * @return the starting character index
      */
     int getCharIndex();
 
     /**
-     * Returns the length of this source section in characters.
-     * <p>
-     * The complete text of the source that contains this section can be retrieved via
-     * {@link Source#getCode()}.
+     * Returns the length of this section in characters.
      *
      * @return the number of characters in the section
      */
@@ -79,14 +95,14 @@
     int getCharEndIndex();
 
     /**
-     * Returns the identifier of this source section that is used for printing the section.
+     * Returns terse text describing this source section, typically used for printing the section.
      *
      * @return the identifier of the section
      */
     String getIdentifier();
 
     /**
-     * Returns text of the code represented by this source section.
+     * Returns text described by this section.
      *
      * @return the code as a String object
      */
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java	Wed May 21 11:45:50 2014 +0200
@@ -106,7 +106,8 @@
 
     /**
      * Accesses the current stack, i.e., the contents of the {@link Frame}s and the associated
-     * {@link CallTarget}s.
+     * {@link CallTarget}s. Iteration starts at the caller frame, i.e., it does not include the
+     * current frame.
      *
      * @return a lazy collection of {@link FrameInstance}.
      */
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java	Tue May 13 19:19:27 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-/*
- * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.impl;
-
-import java.util.*;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.instrument.impl.*;
-import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeCallback;
-import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeImpl;
-import com.oracle.truffle.api.source.*;
-
-public abstract class AbstractExecutionContext implements ExecutionContext {
-
-    // TODO (mlvdv) use weak references.
-    /**
-     * Map: SourceSection ==> probe associated with that source section in an AST.
-     */
-    private final Map<SourceSection, ProbeImpl> srcToProbe = new HashMap<>();
-
-    // TODO (mlvdv) use weak references.
-    /**
-     * Map: Source line ==> probes associated with source sections starting on the line.
-     */
-    private final Map<SourceLineLocation, Collection<Probe>> lineToProbes = new HashMap<>();
-
-    private final SourceManager sourceManager = new SourceManager();
-    private final List<SourceListener> sourceListeners = new ArrayList<>();
-    private final List<ProbeListener> probeListeners = new ArrayList<>();
-
-    private final SourceCallback sourceCallback = new SourceCallback() {
-
-        public void startLoading(Source source) {
-            for (SourceListener listener : sourceListeners) {
-                listener.loadStarting(source);
-            }
-        }
-
-        public void endLoading(Source source) {
-            for (SourceListener listener : sourceListeners) {
-                listener.loadEnding(source);
-            }
-        }
-    };
-
-    private final ProbeCallback probeCallback = new ProbeCallback() {
-        /**
-         * Receives (from the {@link Probe} implementation) and distributes notification that a
-         * {@link Probe} has acquired a new {@linkplain PhylumTag tag}.
-         */
-        public void newTagAdded(ProbeImpl probe, PhylumTag tag) {
-            for (ProbeListener listener : probeListeners) {
-                listener.probeTaggedAs(probe, tag);
-            }
-            if (trap != null && tag == trap.getTag()) {
-                probe.setTrap(trap);
-            }
-        }
-    };
-
-    private Visualizer visualizer = new DefaultVisualizer();
-
-    /**
-     * When non-null, "enter" events with matching tags will trigger a callback.
-     */
-    private PhylumTrap trap = null;
-
-    protected AbstractExecutionContext() {
-    }
-
-    public void initialize() {
-        setSourceCallback(sourceCallback);
-    }
-
-    public final SourceManager getSourceManager() {
-        return sourceManager;
-    }
-
-    public void addSourceListener(SourceListener listener) {
-        assert listener != null;
-        sourceListeners.add(listener);
-    }
-
-    public void addProbeListener(ProbeListener listener) {
-        assert listener != null;
-        probeListeners.add(listener);
-    }
-
-    public Probe getProbe(SourceSection sourceSection) {
-        assert sourceSection != null;
-
-        ProbeImpl probe = srcToProbe.get(sourceSection);
-
-        if (probe != null) {
-            return probe;
-        }
-        probe = InstrumentationNode.createProbe(sourceSection, probeCallback);
-
-        // Register new probe by unique SourceSection
-        srcToProbe.put(sourceSection, probe);
-
-        // Register new probe by source line, there may be more than one
-        // Create line location for map key
-        final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine());
-
-        Collection<Probe> probes = lineToProbes.get(lineLocation);
-        if (probes == null) {
-            probes = new ArrayList<>(2);
-            lineToProbes.put(lineLocation, probes);
-        }
-        probes.add(probe);
-
-        for (ProbeListener listener : probeListeners) {
-            listener.newProbeInserted(sourceSection, probe);
-        }
-
-        return probe;
-    }
-
-    /**
-     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
-     * collection if no probes found.
-     */
-    public Collection<Probe> findProbesTaggedAs(PhylumTag tag) {
-        final List<Probe> probes = new ArrayList<>();
-        for (Probe probe : srcToProbe.values()) {
-            if (tag == null || probe.isTaggedAs(tag)) {
-                probes.add(probe);
-            }
-        }
-        return probes;
-    }
-
-    public Collection<Probe> findProbesByLine(SourceLineLocation lineLocation) {
-        final Collection<Probe> probes = lineToProbes.get(lineLocation);
-        if (probes == null) {
-            return Collections.emptyList();
-        }
-        return new ArrayList<>(probes);
-    }
-
-    // TODO (mlvdv) consider allowing multiple traps (without inhibiting Truffle inlining)
-    public void setTrap(PhylumTrap trap) {
-        assert trap != null;
-        if (this.trap != null) {
-            throw new IllegalStateException("trap already set");
-        }
-        this.trap = trap;
-
-        for (ProbeImpl probe : srcToProbe.values()) {
-            if (probe.isTaggedAs(trap.getTag())) {
-                probe.setTrap(trap);
-            }
-        }
-    }
-
-    public void clearTrap() {
-        if (this.trap == null) {
-            throw new IllegalStateException("no trap set");
-        }
-        for (ProbeImpl probe : srcToProbe.values()) {
-            if (probe.isTaggedAs(trap.getTag())) {
-                probe.setTrap(null);
-            }
-        }
-        trap = null;
-    }
-
-    public Visualizer getVisualizer() {
-        return visualizer;
-    }
-
-    /**
-     * Assign guest language-specific visualization support for tools. This must be assigned outside
-     * the implementation context to avoid build circularities.
-     */
-    public void setVisualizer(Visualizer visualizer) {
-        this.visualizer = visualizer;
-    }
-
-    /**
-     * Assigns a guest language-specific manager for using {@link ASTNodeProber}s added by tools to
-     * instrument ASTs with {@link Probe}s at specified nodes. This must be assigned outside the
-     * implementation context to avoid build circularities. It must also be set before any
-     * instrumentation probe implementations are assigned.
-     */
-    public abstract void setASTProber(ASTProber astProber);
-
-    /**
-     * Establishes source event reporting
-     */
-    protected abstract void setSourceCallback(SourceCallback sourceCallback);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultSourceSection.java	Tue May 13 19:19:27 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.impl;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Represents a contiguous text section within the source code of a guest language program.
- */
-public class DefaultSourceSection implements SourceSection {
-
-    private final Source source;
-    private final String identifier;
-    private final int startLine;
-    private final int startColumn;
-    private final int charIndex;
-    private final int charLength;
-
-    /**
-     * Creates a new object representing a contiguous text section within the source code of a guest
-     * language program's text.
-     * <p>
-     * The starting location of the section is specified using two different coordinate:
-     * <ul>
-     * <li><b>(row, column)</b>: rows and columns are 1-based, so the first character in a source
-     * file is at position {@code (1,1)}.</li>
-     * <li><b>character index</b>: 0-based offset of the character from the beginning of the source,
-     * so the first character in a file is at index {@code 0}.</li>
-     * </ul>
-     * The <b>newline</b> that terminates each line counts as a single character for the purpose of
-     * a character index. The (row,column) coordinates of a newline character should never appear in
-     * a text section.
-     * <p>
-     *
-     * @param source object representing the complete source program that contains this section
-     * @param identifier an identifier used when printing the section
-     * @param startLine the 1-based number of the start line of the section
-     * @param startColumn the 1-based number of the start column of the section
-     * @param charIndex the 0-based index of the first character of the section
-     * @param charLength the length of the section in number of characters
-     */
-    public DefaultSourceSection(Source source, String identifier, int startLine, int startColumn, int charIndex, int charLength) {
-        this.source = source;
-        this.identifier = identifier;
-        this.startLine = startLine;
-        this.startColumn = startColumn;
-        this.charIndex = charIndex;
-        this.charLength = charLength;
-    }
-
-    public final Source getSource() {
-        return source;
-    }
-
-    public final int getStartLine() {
-        return startLine;
-    }
-
-    public final int getStartColumn() {
-        return startColumn;
-    }
-
-    public final int getCharIndex() {
-        return charIndex;
-    }
-
-    public final int getCharLength() {
-        return charLength;
-    }
-
-    public final int getCharEndIndex() {
-        return charIndex + charLength;
-    }
-
-    public final String getIdentifier() {
-        return identifier;
-    }
-
-    public final String getCode() {
-        return getSource().getCode().substring(charIndex, charIndex + charLength);
-    }
-
-    public final String getShortDescription() {
-        return String.format("%s:%d", source.getShortName(), startLine);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%s:%d", source.getName(), startLine);
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + charIndex;
-        result = prime * result + charLength;
-        result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
-        result = prime * result + ((source == null) ? 0 : source.hashCode());
-        result = prime * result + startColumn;
-        result = prime * result + startLine;
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof DefaultSourceSection)) {
-            return false;
-        }
-        DefaultSourceSection other = (DefaultSourceSection) obj;
-        if (charIndex != other.charIndex) {
-            return false;
-        }
-        if (charLength != other.charLength) {
-            return false;
-        }
-        if (identifier == null) {
-            if (other.identifier != null) {
-                return false;
-            }
-        } else if (!identifier.equals(other.identifier)) {
-            return false;
-        }
-        if (source == null) {
-            if (other.source != null) {
-                return false;
-            }
-        } else if (!source.equals(other.source)) {
-            return false;
-        }
-        if (startColumn != other.startColumn) {
-            return false;
-        }
-        if (startLine != other.startLine) {
-            return false;
-        }
-        return true;
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Wed May 21 11:45:50 2014 +0200
@@ -31,29 +31,29 @@
 import com.oracle.truffle.api.nodes.*;
 
 /**
- * A receiver of Truffle AST {@link ExecutionEvents}, propagated from a {@link Probe} to which the
- * instrument is attached, for the benefit of associated <em>tools</em>.
+ * A receiver of Truffle AST runtime {@link ExecutionEvents}, propagated from the {@link Probe} to
+ * which the instrument is attached, for the benefit of associated <em>tools</em>.
  * <p>
- * Guidelines for implementing Instruments, with particular attention to avoiding undesired runtime
+ * Guidelines for implementing {@link Instrument}s, with particular attention to minimize runtime
  * performance overhead:
  * <ol>
- * <li>Extend {@link Instrument} and override only the event handling methods for which some action
- * is needed.</li>
+ * <li>Extend this abstract class and override only the {@linkplain ExecutionEvents event handling
+ * methods} for which intervention is needed.</li>
  * <li>Instruments are Truffle {@link Node}s and should be coded as much as possible in the desired
  * <em>Truffle style</em>, documented more thoroughly elsewhere.</li>
  * <li>Maintain as little state as possible.</li>
- * <li>If state is necessary, make it {@code final} if possible.</li>
- * <li>If non-final state is necessary, annotate it as {@link CompilationFinal} and call
- * {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.</li>
+ * <li>If state is necessary, make object fields {@code final} if at all possible.</li>
+ * <li>If non-final object-valued state is necessary, annotate it as {@link CompilationFinal} and
+ * call {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.</li>
  * <li>Never store a {@link Frame} value in a field.</li>
  * <li>Minimize computation in standard execution paths.</li>
- * <li>Callbacks to tools should be made via callbacks provided at construction and stored in
- * {@code final} fields.</li>
- * <li>Tool callback methods should usually be annotated as {@link SlowPath} to prevent them from
- * being inlined into fast execution paths.</li>
- * <li>If computation is needed, and if performance is important, then the computation is best
- * expressed as a guest language AST and evaluated using standard Truffle mechanisms so that
- * standard Truffle optimizations can be applied.</li>
+ * <li>If runtime calls must be made back to a tool, construct the instrument with a callback stored
+ * in a {@code final} field.</li>
+ * <li>Tool methods called by the instrument should be annotated as {@link SlowPath} to prevent them
+ * from being inlined into fast execution paths.</li>
+ * <li>If computation in the execution path is needed, and if performance is important, then the
+ * computation is best expressed as a guest language AST and evaluated using standard Truffle
+ * mechanisms so that standard Truffle optimizations can be applied.</li>
  * </ol>
  * <p>
  * Guidelines for attachment to a {@link Probe}:
@@ -65,6 +65,18 @@
  * every copy, and so the Instrument will receive events corresponding to the intended syntactic
  * unit of code, independent of which AST copy is being executed.</li>
  * </ol>
+ * <p>
+ * Guidelines for handling {@link ExecutionEvents}:
+ * <ol>
+ * <li>Separate event methods are defined for each kind of possible return: object-valued,
+ * primitive-valued, void-valued, and exceptional.</li>
+ * <li>Override "leave*" primitive methods if the language implementation returns primitives and the
+ * instrument should avoid boxing them.</li>
+ * <li>On the other hand, if boxing all primitives for instrumentation is desired, it is only
+ * necessary to override the object-valued return methods, since the default implementation of each
+ * primitive-valued return method is to box the value and forward it to the object-valued return
+ * method.</li>
+ * </ol>
  *
  * <p>
  * <strong>Disclaimer:</strong> experimental; under development.
@@ -72,7 +84,7 @@
  * @see Probe
  * @see ASTNodeProber
  */
-public class Instrument extends InstrumentationNode {
+public abstract class Instrument extends InstrumentationNode {
 
     protected Instrument() {
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Wed May 21 11:45:50 2014 +0200
@@ -25,8 +25,6 @@
 package com.oracle.truffle.api.instrument;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
 
 /**
  * A collector of {@link ExecutionEvents} at a specific site (node) in a Truffle AST (generated by a
@@ -54,7 +52,7 @@
  * @see Instrument
  * @see Wrapper
  */
-public interface Probe extends PhylumTagged {
+public interface Probe extends ExecutionEvents, PhylumTagged {
 
     /**
      * The source location with which this probe is (presumably uniquely) associated.
@@ -77,66 +75,4 @@
      */
     void removeInstrument(Instrument oldInstrument);
 
-    // TODO (mlvdv) migrate the remaining methods to another interface.
-
-    /**
-     * @see ExecutionEvents#enter(Node, VirtualFrame)
-     */
-    void notifyEnter(Node astNode, VirtualFrame frame);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, boolean)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, boolean result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, byte)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, byte result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, short)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, short result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, int)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, int result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, long)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, long result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, char)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, char result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, float)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, float result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, double)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, double result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, Object)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, Object result);
-
-    /**
-     * @see ExecutionEvents#leaveExceptional(Node, VirtualFrame, Exception)
-     */
-    void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e);
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceCallback.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,37 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.instrument;
+
+import com.oracle.truffle.api.*;
+
+/**
+ * Instrumentation callback for guest language source-related events.
+ */
+public interface SourceCallback {
+
+    public void startLoading(Source source);
+
+    public void endLoading(Source source);
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Wed May 21 11:45:50 2014 +0200
@@ -61,7 +61,7 @@
     /**
      * Converts a value in the guest language to a display string.
      */
-    String displayValue(Object value);
+    String displayValue(ExecutionContext context, Object value);
 
     /**
      * Converts a slot identifier in the guest language to a display string.
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Wed May 21 11:45:50 2014 +0200
@@ -77,13 +77,14 @@
         }
         if (node instanceof PhylumTagged) {
             final PhylumTagged taggedNode = (PhylumTagged) node;
+            p.print("[");
             String prefix = "";
             for (PhylumTag tag : taggedNode.getPhylumTags()) {
                 p.print(prefix);
                 prefix = ",";
                 p.print(tag.toString());
             }
-
+            p.print("]");
         }
 
         ArrayList<NodeField> childFields = new ArrayList<>();
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java	Wed May 21 11:45:50 2014 +0200
@@ -69,7 +69,7 @@
         return callTarget.toString();
     }
 
-    public String displayValue(Object value) {
+    public String displayValue(ExecutionContext context, Object value) {
         return value.toString();
     }
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java	Wed May 21 11:45:50 2014 +0200
@@ -39,7 +39,7 @@
  */
 public abstract class InstrumentationNode extends Node implements ExecutionEvents {
 
-    public interface ProbeCallback {
+    interface ProbeCallback {
         void newTagAdded(ProbeImpl probe, PhylumTag tag);
     }
 
@@ -49,7 +49,7 @@
      *
      * @return a new probe
      */
-    public static ProbeImpl createProbe(SourceSection sourceSection, ProbeCallback probeCallback) {
+    static ProbeImpl createProbe(SourceSection sourceSection, ProbeCallback probeCallback) {
         return new ProbeImpl(sourceSection, probeCallback);
     }
 
@@ -64,7 +64,7 @@
     /**
      * @return the instance of {@link Probe} to which this instrument is attached.
      */
-    public Probe getProbe() {
+    protected Probe getProbe() {
         final InstrumentationNode parent = (InstrumentationNode) getParent();
         return parent == null ? null : parent.getProbe();
     }
@@ -199,7 +199,7 @@
      * May be categorized by one or more {@linkplain PhylumTag tags}, signifying information useful
      * for instrumentation about its AST location(s).
      */
-    public static final class ProbeImpl extends InstrumentationNode implements Probe {
+    static final class ProbeImpl extends InstrumentationNode implements Probe {
 
         private final ProbeCallback probeCallback;
 
@@ -259,7 +259,7 @@
         }
 
         @Override
-        public Probe getProbe() {
+        protected Probe getProbe() {
             return this;
         }
 
@@ -271,14 +271,14 @@
         }
 
         @SlowPath
-        public void setTrap(PhylumTrap trap) {
+        void setTrap(PhylumTrap trap) {
             assert trap == null || isTaggedAs(trap.getTag());
             probeUnchanged.invalidate();
             this.trap = trap;
             probeUnchanged = Truffle.getRuntime().createAssumption();
         }
 
-        public void notifyEnter(Node astNode, VirtualFrame frame) {
+        public void enter(Node astNode, VirtualFrame frame) {
             if (trap != null || next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -292,7 +292,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame) {
+        public void leave(Node astNode, VirtualFrame frame) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -301,16 +301,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, boolean result) {
-            if (next != null) {
-                if (!probeUnchanged.isValid()) {
-                    CompilerDirectives.transferToInterpreter();
-                }
-                next.internalLeave(astNode, frame, result);
-            }
-        }
-
-        public void notifyLeave(Node astNode, VirtualFrame frame, byte result) {
+        public void leave(Node astNode, VirtualFrame frame, boolean result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -319,16 +310,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, short result) {
-            if (next != null) {
-                if (!probeUnchanged.isValid()) {
-                    CompilerDirectives.transferToInterpreter();
-                }
-                next.internalLeave(astNode, frame, result);
-            }
-        }
-
-        public void notifyLeave(Node astNode, VirtualFrame frame, int result) {
+        public void leave(Node astNode, VirtualFrame frame, byte result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -337,7 +319,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, long result) {
+        public void leave(Node astNode, VirtualFrame frame, short result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -346,16 +328,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, char result) {
-            if (next != null) {
-                if (!probeUnchanged.isValid()) {
-                    CompilerDirectives.transferToInterpreter();
-                }
-                next.internalLeave(astNode, frame, result);
-            }
-        }
-
-        public void notifyLeave(Node astNode, VirtualFrame frame, float result) {
+        public void leave(Node astNode, VirtualFrame frame, int result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -364,7 +337,16 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, double result) {
+        public void leave(Node astNode, VirtualFrame frame, long result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, char result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -373,7 +355,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, Object result) {
+        public void leave(Node astNode, VirtualFrame frame, float result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -382,7 +364,25 @@
             }
         }
 
-        public void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
+        public void leave(Node astNode, VirtualFrame frame, double result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, Object result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -391,50 +391,6 @@
             }
         }
 
-        public void enter(Node astNode, VirtualFrame frame) {
-        }
-
-        public void leave(Node astNode, VirtualFrame frame) {
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, boolean result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, byte result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, short result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, int result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, long result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, char result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, float result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, double result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, Object result) {
-        }
-
-        public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
-        }
-
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/ProbeManager.java	Wed May 21 11:45:50 2014 +0200
@@ -0,0 +1,159 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.instrument.impl;
+
+import java.util.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeCallback;
+import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeImpl;
+import com.oracle.truffle.api.source.*;
+
+/**
+ * Factory and services for AST {@link Probe}s
+ */
+public final class ProbeManager {
+
+    // TODO (mlvdv) use weak references.
+    /**
+     * Map: SourceSection ==> probe associated with that source section in an AST.
+     */
+    private final Map<SourceSection, ProbeImpl> srcToProbe = new HashMap<>();
+
+    // TODO (mlvdv) use weak references.
+    /**
+     * Map: Source line ==> probes associated with source sections starting on the line.
+     */
+    private final Map<SourceLineLocation, Collection<Probe>> lineToProbes = new HashMap<>();
+
+    private final List<ProbeListener> probeListeners = new ArrayList<>();
+
+    private final ProbeCallback probeCallback;
+
+    /**
+     * When non-null, "enter" events with matching tags will trigger a callback.
+     */
+    private PhylumTrap phylumTrap = null;
+
+    public ProbeManager() {
+        this.probeCallback = new ProbeCallback() {
+            /**
+             * Receives (from the {@link Probe} implementation) and distributes notification that a
+             * {@link Probe} has acquired a new {@linkplain PhylumTag tag}.
+             */
+            public void newTagAdded(ProbeImpl probe, PhylumTag tag) {
+                for (ProbeListener listener : probeListeners) {
+                    listener.probeTaggedAs(probe, tag);
+                }
+                if (phylumTrap != null && tag == phylumTrap.getTag()) {
+                    probe.setTrap(phylumTrap);
+                }
+            }
+        };
+    }
+
+    public void addProbeListener(ProbeListener listener) {
+        assert listener != null;
+        probeListeners.add(listener);
+    }
+
+    public Probe getProbe(SourceSection sourceSection) {
+        assert sourceSection != null;
+
+        ProbeImpl probe = srcToProbe.get(sourceSection);
+
+        if (probe != null) {
+            return probe;
+        }
+        probe = InstrumentationNode.createProbe(sourceSection, probeCallback);
+
+        // Register new probe by unique SourceSection
+        srcToProbe.put(sourceSection, probe);
+
+        // Register new probe by source line, there may be more than one
+        // Create line location for map key
+        final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine());
+
+        Collection<Probe> probes = lineToProbes.get(lineLocation);
+        if (probes == null) {
+            probes = new ArrayList<>(2);
+            lineToProbes.put(lineLocation, probes);
+        }
+        probes.add(probe);
+
+        for (ProbeListener listener : probeListeners) {
+            listener.newProbeInserted(sourceSection, probe);
+        }
+
+        return probe;
+    }
+
+    public Collection<Probe> findProbesTaggedAs(PhylumTag tag) {
+        final List<Probe> probes = new ArrayList<>();
+        for (Probe probe : srcToProbe.values()) {
+            if (tag == null || probe.isTaggedAs(tag)) {
+                probes.add(probe);
+            }
+        }
+        return probes;
+    }
+
+    public Collection<Probe> findProbesByLine(SourceLineLocation lineLocation) {
+        final Collection<Probe> probes = lineToProbes.get(lineLocation);
+        if (probes == null) {
+            return Collections.emptyList();
+        }
+        return new ArrayList<>(probes);
+    }
+
+    public void setPhylumTrap(PhylumTrap trap) {
+        assert trap != null;
+        if (this.phylumTrap != null) {
+            throw new IllegalStateException("trap already set");
+        }
+        this.phylumTrap = trap;
+
+        PhylumTag tag = trap.getTag();
+        for (ProbeImpl probe : srcToProbe.values()) {
+            if (probe.isTaggedAs(tag)) {
+                probe.setTrap(trap);
+            }
+        }
+    }
+
+    public void clearPhylumTrap() {
+        if (this.phylumTrap == null) {
+            throw new IllegalStateException("no trap set");
+        }
+        for (ProbeImpl probe : srcToProbe.values()) {
+            if (probe.isTaggedAs(phylumTrap.getTag())) {
+                probe.setTrap(null);
+            }
+        }
+        phylumTrap = null;
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java	Tue May 13 19:19:27 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.instrument.impl;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Instrumentation callback for guest language source-related events.
- */
-public interface SourceCallback {
-
-    public void startLoading(Source source);
-
-    public void endLoading(Source source);
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeInfo.java	Wed May 21 11:45:50 2014 +0200
@@ -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.api/src/com/oracle/truffle/api/source/SourceManager.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java	Wed May 21 11:45:50 2014 +0200
@@ -199,6 +199,32 @@
             return checkTextMap().lineLength(lineNumber);
         }
 
+        public final SourceSection createSection(String identifier, int startOffset, int sectionLength) throws IllegalArgumentException {
+            final int codeLength = getCode().length();
+            if (!(startOffset >= 0 && sectionLength >= 0 && startOffset + sectionLength <= codeLength)) {
+                throw new IllegalArgumentException("text positions out of range");
+            }
+            checkTextMap();
+            final int startLine = getLineNumber(startOffset);
+            final int startColumn = startOffset - getLineStartOffset(startLine) + 1;
+
+            return new SourceSectionImpl(this, identifier, startLine, startColumn, startOffset, sectionLength);
+        }
+
+        public SourceSection createSection(String identifier, int startLine, int startColumn, int sectionLength) {
+            checkTextMap();
+            final int lineStartOffset = textMap.lineStartOffset(startLine);
+            if (startColumn > textMap.lineLength(startLine)) {
+                throw new IllegalArgumentException("column out of range");
+            }
+            final int startOffset = lineStartOffset + startColumn - 1;
+            return new SourceSectionImpl(this, identifier, startLine, startColumn, startOffset, sectionLength);
+        }
+
+        public SourceSection createSection(String identifier, int startLine, int startColumn, int charIndex, int length) {
+            return new SourceSectionImpl(this, identifier, startLine, startColumn, charIndex, length);
+        }
+
         private TextMap checkTextMap() {
             if (textMap == null) {
                 final String code = getCode();
@@ -211,7 +237,7 @@
         }
     }
 
-    public static class LiteralSourceImpl extends SourceImpl {
+    private static class LiteralSourceImpl extends SourceImpl {
 
         private final String name; // Name used originally to describe the source
         private final String code;
@@ -337,4 +363,142 @@
 
     }
 
+    private static class SourceSectionImpl implements SourceSection {
+
+        private final Source source;
+        private final String identifier;
+        private final int startLine;
+        private final int startColumn;
+        private final int charIndex;
+        private final int charLength;
+
+        /**
+         * Creates a new object representing a contiguous text section within the source code of a
+         * guest language program's text.
+         * <p>
+         * The starting location of the section is specified using two different coordinate:
+         * <ul>
+         * <li><b>(row, column)</b>: rows and columns are 1-based, so the first character in a
+         * source file is at position {@code (1,1)}.</li>
+         * <li><b>character index</b>: 0-based offset of the character from the beginning of the
+         * source, so the first character in a file is at index {@code 0}.</li>
+         * </ul>
+         * The <b>newline</b> that terminates each line counts as a single character for the purpose
+         * of a character index. The (row,column) coordinates of a newline character should never
+         * appear in a text section.
+         * <p>
+         *
+         * @param source object representing the complete source program that contains this section
+         * @param identifier an identifier used when printing the section
+         * @param startLine the 1-based number of the start line of the section
+         * @param startColumn the 1-based number of the start column of the section
+         * @param charIndex the 0-based index of the first character of the section
+         * @param charLength the length of the section in number of characters
+         */
+        public SourceSectionImpl(Source source, String identifier, int startLine, int startColumn, int charIndex, int charLength) {
+            this.source = source;
+            this.identifier = identifier;
+            this.startLine = startLine;
+            this.startColumn = startColumn;
+            this.charIndex = charIndex;
+            this.charLength = charLength;
+        }
+
+        public final Source getSource() {
+            return source;
+        }
+
+        public final int getStartLine() {
+            return startLine;
+        }
+
+        public final int getStartColumn() {
+            return startColumn;
+        }
+
+        public final int getCharIndex() {
+            return charIndex;
+        }
+
+        public final int getCharLength() {
+            return charLength;
+        }
+
+        public final int getCharEndIndex() {
+            return charIndex + charLength;
+        }
+
+        public final String getIdentifier() {
+            return identifier;
+        }
+
+        public final String getCode() {
+            return getSource().getCode().substring(charIndex, charIndex + charLength);
+        }
+
+        public final String getShortDescription() {
+            return String.format("%s:%d", source.getShortName(), startLine);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s:%d", source.getName(), startLine);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + charIndex;
+            result = prime * result + charLength;
+            result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
+            result = prime * result + ((source == null) ? 0 : source.hashCode());
+            result = prime * result + startColumn;
+            result = prime * result + startLine;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (!(obj instanceof SourceSectionImpl)) {
+                return false;
+            }
+            SourceSectionImpl other = (SourceSectionImpl) obj;
+            if (charIndex != other.charIndex) {
+                return false;
+            }
+            if (charLength != other.charLength) {
+                return false;
+            }
+            if (identifier == null) {
+                if (other.identifier != null) {
+                    return false;
+                }
+            } else if (!identifier.equals(other.identifier)) {
+                return false;
+            }
+            if (source == null) {
+                if (other.source != null) {
+                    return false;
+                }
+            } else if (!source.equals(other.source)) {
+                return false;
+            }
+            if (startColumn != other.startColumn) {
+                return false;
+            }
+            if (startLine != other.startLine) {
+                return false;
+            }
+            return true;
+        }
+
+    }
+
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBlockNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBreakNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLContinueNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLIfNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLWhileNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java	Wed May 21 11:45:50 2014 +0200
@@ -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/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Tue May 13 19:19:27 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Wed May 21 11:45:50 2014 +0200
@@ -27,7 +27,6 @@
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.impl.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.sl.nodes.*;
 import com.oracle.truffle.sl.nodes.call.*;
@@ -243,7 +242,7 @@
         int startLine = t.line;
         int startColumn = t.col;
         int charLength = t.val.length();
-        SourceSection sourceSection = new DefaultSourceSection(source, functionName, startLine, startColumn, 0, charLength);
+        SourceSection sourceSection = source.createSection(functionName, startLine, startColumn, 0, charLength);
 
         node.assignSourceSection(sourceSection);
         return node;
--- a/make/bsd/makefiles/vm.make	Tue May 13 19:19:27 2014 +0200
+++ b/make/bsd/makefiles/vm.make	Wed May 21 11:45:50 2014 +0200
@@ -229,6 +229,7 @@
 else
   GRAAL_SPECIFIC_FILES   :=
   GRAAL_SPECIFIC_GPU_FILES  :=
+  Src_Dirs_I += $(HS_COMMON_SRC)/../graal/com.oracle.graal.hotspot/src_gen/hotspot
 endif
 
 # Always exclude these.
--- a/make/linux/makefiles/vm.make	Tue May 13 19:19:27 2014 +0200
+++ b/make/linux/makefiles/vm.make	Wed May 21 11:45:50 2014 +0200
@@ -211,6 +211,7 @@
 else
   GRAAL_SPECIFIC_FILES   :=
   GRAAL_SPECIFIC_GPU_FILES   :=
+  Src_Dirs_I += $(HS_COMMON_SRC)/../graal/com.oracle.graal.hotspot/src_gen/hotspot
 endif
 
 # Always exclude these.
--- a/make/solaris/makefiles/vm.make	Tue May 13 19:19:27 2014 +0200
+++ b/make/solaris/makefiles/vm.make	Wed May 21 11:45:50 2014 +0200
@@ -229,6 +229,7 @@
 else
   GRAAL_SPECIFIC_FILES   :=
   GRAAL_SPECIFIC_GPU_FILES   :=
+  Src_Dirs_I += $(HS_COMMON_SRC)/../graal/com.oracle.graal.hotspot/src_gen/hotspot
 endif
 
 # Always exclude these.
--- a/make/windows/makefiles/projectcreator.make	Tue May 13 19:19:27 2014 +0200
+++ b/make/windows/makefiles/projectcreator.make	Wed May 21 11:45:50 2014 +0200
@@ -57,6 +57,7 @@
         -relativeInclude src\os_cpu\windows_$(Platform_arch)\vm \
         -relativeInclude src\cpu\$(Platform_arch)\vm \
         -relativeInclude src\gpu \
+        -relativeInclude graal\com.oracle.graal.hotspot\src_gen\hotspot \
         -absoluteInclude $(HOTSPOTBUILDSPACE)/%f/generated \
         -relativeSrcInclude src \
         -absoluteSrcInclude $(HOTSPOTBUILDSPACE) \
@@ -151,7 +152,7 @@
 
 ProjectCreatorIDEOptionsIgnoreGraal=\
  -ignorePath_TARGET src/share/vm/graal \
- -ignorePath_TARGET graal/generated \
+ -ignorePath_TARGET graal\com.oracle.graal.hotspot\src_gen\hotspot \
  -ignorePath_TARGET vm/graal
 
 ProjectCreatorIDEOptionsIgnoreCompiler2=\
@@ -175,7 +176,6 @@
 ProjectCreatorIDEOptions=$(ProjectCreatorIDEOptions) \
  -define_compiler1 COMPILER1 \
  -define_compiler1 GRAAL \
- -ignorePath_compiler1 graal/generated \
  $(ProjectCreatorIDEOptionsIgnoreCompiler2:TARGET=compiler1)
 
 ##################################################
@@ -195,7 +195,6 @@
  -define_compiler2 COMPILER1 \
  -define_compiler2 COMPILER2 \
  -define_compiler2 GRAAL \
- -ignorePath_compiler2 graal/generated \
  -additionalFile_compiler2 $(Platform_arch_model).ad \
  -additionalFile_compiler2 ad_$(Platform_arch_model).cpp \
  -additionalFile_compiler2 ad_$(Platform_arch_model).hpp \
--- a/mx/mx_graal.py	Tue May 13 19:19:27 2014 +0200
+++ b/mx/mx_graal.py	Wed May 21 11:45:50 2014 +0200
@@ -690,7 +690,7 @@
             mustBuild = False
             timestamp = os.path.getmtime(timestampFile)
             sources = []
-            for d in ['src', 'make']:
+            for d in ['src', 'make', 'graal/com.oracle.graal.hotspot/src_gen/hotspot']:
                 for root, dirnames, files in os.walk(join(_graal_home, d)):
                     # ignore <graal>/src/share/tools
                     if root == join(_graal_home, 'src', 'share'):
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/mx/projects	Wed May 21 11:45:50 2014 +0200
@@ -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
@@ -33,12 +32,12 @@
 library@DACAPO@sha1=2626a9546df09009f6da0df854e6dc1113ef7dd4
 
 library@JACOCOAGENT@path=lib/jacocoagent.jar
-library@JACOCOAGENT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoagent.jar
-library@JACOCOAGENT@sha1=9e2c835289356d86afbf31840f05a0f9007c4e44
+library@JACOCOAGENT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoagent-0.7.1-1.jar
+library@JACOCOAGENT@sha1=2f73a645b02e39290e577ce555f00b02004650b0
 
 library@JACOCOREPORT@path=lib/jacocoreport.jar
-library@JACOCOREPORT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoreport.jar
-library@JACOCOREPORT@sha1=32fb5ba2f12d86c4feb74bcefc17a4e6fad8a323
+library@JACOCOREPORT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoreport-0.7.1-1.jar
+library@JACOCOREPORT@sha1=0c5db714804416dd1df4d8110762136ce3d5c7dc
 
 library@DACAPO_SCALA@path=lib/dacapo-scala-0.1.0-20120216.jar
 library@DACAPO_SCALA@urls=http://repo.scalabench.org/snapshots/org/scalabench/benchmarks/scala-benchmark-suite/0.1.0-SNAPSHOT/scala-benchmark-suite-0.1.0-20120216.103539-3.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
@@ -182,15 +182,35 @@
 project@com.oracle.graal.sparc@javaCompliance=1.8
 project@com.oracle.graal.sparc@workingSets=Graal,SPARC
 
+# graal.hotspotvmconfig
+project@com.oracle.graal.hotspotvmconfig@subDir=graal
+project@com.oracle.graal.hotspotvmconfig@sourceDirs=src
+project@com.oracle.graal.hotspotvmconfig@dependencies=com.oracle.graal.compiler.common
+project@com.oracle.graal.hotspotvmconfig@checkstyle=com.oracle.graal.graph
+project@com.oracle.graal.hotspotvmconfig@annotationProcessors=com.oracle.graal.service.processor
+project@com.oracle.graal.hotspotvmconfig@annotationProcessorForDependents=true
+project@com.oracle.graal.hotspotvmconfig@javaCompliance=1.8
+project@com.oracle.graal.hotspotvmconfig@workingSets=Graal,HotSpot
+
 # graal.hotspot
 project@com.oracle.graal.hotspot@subDir=graal
 project@com.oracle.graal.hotspot@sourceDirs=src
-project@com.oracle.graal.hotspot@dependencies=com.oracle.graal.replacements,com.oracle.graal.runtime,com.oracle.graal.printer,com.oracle.graal.baseline
+project@com.oracle.graal.hotspot@dependencies=com.oracle.graal.replacements,com.oracle.graal.runtime,com.oracle.graal.printer,com.oracle.graal.baseline,com.oracle.graal.hotspotvmconfig
 project@com.oracle.graal.hotspot@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.hotspot@annotationProcessors=com.oracle.graal.replacements.verifier,com.oracle.graal.service.processor
 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 +588,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
@@ -652,7 +672,7 @@
 # graal.compiler.hsail.test.infra - HSAIL compiler test infrastructure
 project@com.oracle.graal.compiler.hsail.test.infra@subDir=graal
 project@com.oracle.graal.compiler.hsail.test.infra@sourceDirs=src
-project@com.oracle.graal.compiler.hsail.test.infra@dependencies=com.oracle.graal.hotspot.hsail,JUNIT,OKRA_WITH_SIM
+project@com.oracle.graal.compiler.hsail.test.infra@dependencies=com.oracle.graal.test,com.oracle.graal.hotspot.hsail,OKRA_WITH_SIM
 project@com.oracle.graal.compiler.hsail.test.infra@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.compiler.hsail.test.infra@javaCompliance=1.8
 
--- a/mxtool/mx.py	Tue May 13 19:19:27 2014 +0200
+++ b/mxtool/mx.py	Wed May 21 11:45:50 2014 +0200
@@ -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')
@@ -1108,7 +1232,7 @@
                     entryPath = zi.filename
                     yield zf, entryPath
 
-def sorted_deps(projectNames=None, includeLibs=False, includeAnnotationProcessors=False):
+def sorted_deps(projectNames=None, includeLibs=False, includeJreLibs=False, includeAnnotationProcessors=False):
     """
     Gets projects and libraries sorted such that dependencies
     are before the projects that depend on them. Unless 'includeLibs' is
@@ -1116,12 +1240,12 @@
     """
     projects = projects_from_names(projectNames)
 
-    return sorted_project_deps(projects, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors)
-
-def sorted_project_deps(projects, includeLibs=False, includeAnnotationProcessors=False):
+    return sorted_project_deps(projects, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors)
+
+def sorted_project_deps(projects, includeLibs=False, includeJreLibs=False, includeAnnotationProcessors=False):
     deps = []
     for p in projects:
-        p.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors)
+        p.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors)
     return deps
 
 def _handle_missing_java_home():
@@ -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)
 
@@ -2123,6 +2248,7 @@
             failed = []
             for t in tasks:
                 t.proc.join()
+                _removeSubprocess(t.sub)
                 if t.proc.exitcode != 0:
                     failed.append(t)
             return failed
@@ -2133,7 +2259,6 @@
                 if t.proc.is_alive():
                     active.append(t)
                 else:
-                    _removeSubprocess(t.sub)
                     if t.proc.exitcode != 0:
                         return ([], joinTasks(tasks))
             return (active, [])
@@ -2164,6 +2289,7 @@
         cpus = multiprocessing.cpu_count()
         worklist = sortWorklist(tasks.values())
         active = []
+        failed = []
         while len(worklist) != 0:
             while True:
                 active, failed = checkTasks(active)
@@ -2176,7 +2302,12 @@
                 else:
                     break
 
+            if len(failed) != 0:
+                break
+
             def executeTask(task):
+                # Clear sub-process list cloned from parent process
+                del _currentSubprocesses[:]
                 task.execute()
 
             def depsDone(task):
@@ -2196,7 +2327,12 @@
                     break
 
             worklist = sortWorklist(worklist)
-        joinTasks(active)
+
+        failed += joinTasks(active)
+        if len(failed):
+            for t in failed:
+                log('Compiling {} failed'.format(t.proj.name))
+            abort('{} Java compilation tasks failed'.format(len(failed)))
 
     for dist in _dists.values():
         archive(['@' + dist.name])
@@ -2622,9 +2758,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 = []
@@ -2816,7 +2950,7 @@
         igv.close('properties')
         igv.open('graph', {'name' : 'dependencies'})
         igv.open('nodes')
-        for p in sorted_deps(includeLibs=True):
+        for p in sorted_deps(includeLibs=True, includeJreLibs=True):
             ident = len(ids)
             ids[p.name] = str(ident)
             igv.open('node', {'id' : str(ident)})
@@ -2868,10 +3002,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 +3169,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 +3210,7 @@
                     libraryDeps -= set(dep.all_deps([], True))
                 else:
                     libraryDeps.add(dep)
-            else:
+            elif dep.isProject():
                 projectDeps.add(dep)
 
         for dep in containerDeps:
@@ -3087,8 +3219,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 +3376,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 +3685,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 +3726,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 +3861,6 @@
                 continue
 
             if dep.isLibrary():
-                if not dep.mustExist:
-                    continue
                 path = dep.get_path(resolve=True)
                 if path:
                     if os.sep == '\\':
@@ -3747,7 +3869,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 +3936,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 +3983,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 +4057,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/gpu/hsail/vm/vmStructs_hsail.hpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/gpu/hsail/vm/vmStructs_hsail.hpp	Wed May 21 11:45:50 2014 +0200
@@ -53,9 +53,4 @@
   declare_toplevel_type(Hsail::HSAILKernelDeoptimization)            \
   declare_toplevel_type(Hsail::HSAILDeoptimizationInfo)
 
-#define VM_INT_CONSTANTS_GPU_HSAIL(declare_constant)                                                                                  \
-  declare_constant(sizeof(HSAILFrame))                                                                                                \
-  declare_constant(sizeof(Hsail::HSAILKernelDeoptimization))                                                                          \
-  declare_constant(sizeof(Hsail::HSAILDeoptimizationInfo))                                                                           \
-
 #endif // GPU_HSAIL_VM_VMSTRUCTS_HSAIL_HPP
--- a/src/share/vm/classfile/systemDictionary.hpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/classfile/systemDictionary.hpp	Wed May 21 11:45:50 2014 +0200
@@ -203,7 +203,6 @@
   do_klass(HotSpotCodeInfo_klass,                 com_oracle_graal_hotspot_meta_HotSpotCodeInfo,                Opt) \
   do_klass(HotSpotInstalledCode_klass,            com_oracle_graal_hotspot_meta_HotSpotInstalledCode,           Opt) \
   do_klass(HotSpotNmethod_klass,                  com_oracle_graal_hotspot_meta_HotSpotNmethod,                 Opt) \
-  do_klass(HotSpotJavaType_klass,                 com_oracle_graal_hotspot_meta_HotSpotJavaType,                Opt) \
   do_klass(HotSpotResolvedJavaMethod_klass,       com_oracle_graal_hotspot_meta_HotSpotResolvedJavaMethod,      Opt) \
   do_klass(HotSpotResolvedObjectType_klass,       com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType,      Opt) \
   do_klass(HotSpotMonitorValue_klass,             com_oracle_graal_hotspot_meta_HotSpotMonitorValue,            Opt) \
--- a/src/share/vm/classfile/vmSymbols.hpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/classfile/vmSymbols.hpp	Wed May 21 11:45:50 2014 +0200
@@ -312,7 +312,6 @@
   template(com_oracle_graal_hotspot_meta_HotSpotCodeInfo,            "com/oracle/graal/hotspot/meta/HotSpotCodeInfo")                 \
   template(com_oracle_graal_hotspot_meta_HotSpotInstalledCode,       "com/oracle/graal/hotspot/meta/HotSpotInstalledCode")            \
   template(com_oracle_graal_hotspot_meta_HotSpotNmethod,             "com/oracle/graal/hotspot/meta/HotSpotNmethod")                  \
-  template(com_oracle_graal_hotspot_meta_HotSpotJavaType,            "com/oracle/graal/hotspot/meta/HotSpotJavaType")                 \
   template(com_oracle_graal_hotspot_meta_HotSpotResolvedJavaMethod,  "com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod")       \
   template(com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType,  "com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType")       \
   template(com_oracle_graal_hotspot_meta_HotSpotMonitorValue,        "com/oracle/graal/hotspot/meta/HotSpotMonitorValue")             \
@@ -323,12 +322,10 @@
   template(com_oracle_graal_api_meta_Constant,                       "com/oracle/graal/api/meta/Constant")                            \
   template(com_oracle_graal_api_meta_PrimitiveConstant,              "com/oracle/graal/api/meta/PrimitiveConstant")                   \
   template(com_oracle_graal_api_meta_NullConstant,                   "com/oracle/graal/api/meta/NullConstant")                        \
-  template(com_oracle_graal_api_meta_ConstantPool,                   "com/oracle/graal/api/meta/ConstantPool")                        \
   template(com_oracle_graal_api_meta_ExceptionHandler,               "com/oracle/graal/api/meta/ExceptionHandler")                    \
   template(com_oracle_graal_api_meta_JavaMethod,                     "com/oracle/graal/api/meta/JavaMethod")                          \
   template(com_oracle_graal_api_meta_JavaType,                       "com/oracle/graal/api/meta/JavaType")                            \
   template(com_oracle_graal_api_meta_Kind,                           "com/oracle/graal/api/meta/Kind")                                \
-  template(com_oracle_graal_api_meta_ResolvedJavaField,              "com/oracle/graal/api/meta/ResolvedJavaField")                   \
   template(com_oracle_graal_api_meta_Value,                          "com/oracle/graal/api/meta/Value")                               \
   /* graal.api.code */                                                                                                                \
   template(com_oracle_graal_api_code_Assumptions,                    "com/oracle/graal/api/code/Assumptions")                         \
--- a/src/share/vm/graal/graalCodeInstaller.cpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/graal/graalCodeInstaller.cpp	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Wed May 21 11:45:50 2014 +0200
@@ -53,12 +53,6 @@
   TRACE_graal_3("CompilerToVM::" #name); \
   GRAAL_VM_ENTRY_MARK; \
 
-// Entry to native method implementation that calls a JNI function
-// and hence cannot transition current thread to '_thread_in_vm'.
-#define C2V_ENTRY(result_type, name, signature) \
-  JNIEXPORT result_type JNICALL c2v_ ## name signature { \
-  TRACE_graal_3("CompilerToVM::" #name); \
-
 #define C2V_END }
 
 extern "C" {
@@ -91,128 +85,11 @@
 extern uint64_t gHotSpotVMLongConstantEntryArrayStride;
 }
 
-// helpers used to set fields in the HotSpotVMConfig object
-static jfieldID getFieldID(JNIEnv* env, jobject obj, const char* name, const char* sig) {
-  jfieldID id = env->GetFieldID(env->GetObjectClass(obj), name, sig);
-  if (id == NULL) {
-    fatal(err_msg("field not found: %s (%s)", name, sig));
-  }
-  return id;
-}
-
-C2V_ENTRY(void, initializeConfiguration, (JNIEnv *env, jobject, jobject config))
-
-#define set_boolean(name, value) do { env->SetBooleanField(config, getFieldID(env, config, name, "Z"), value); } while (0)
-#define set_int(name, value) do { env->SetIntField(config, getFieldID(env, config, name, "I"), value); } while (0)
-#define set_long(name, value) do { env->SetLongField(config, getFieldID(env, config, name, "J"), value); } while (0)
-#define set_address(name, value) do { set_long(name, (jlong) value); } while (0)
-
-  guarantee(HeapWordSize == sizeof(char*), "Graal assumption that HeadWordSize == machine word size is wrong");
-
-  set_address("gHotSpotVMStructs", gHotSpotVMStructs);
-  set_long("gHotSpotVMStructEntryTypeNameOffset",   gHotSpotVMStructEntryTypeNameOffset);
-  set_long("gHotSpotVMStructEntryFieldNameOffset",  gHotSpotVMStructEntryFieldNameOffset);
-  set_long("gHotSpotVMStructEntryTypeStringOffset", gHotSpotVMStructEntryTypeStringOffset);
-  set_long("gHotSpotVMStructEntryIsStaticOffset",   gHotSpotVMStructEntryIsStaticOffset);
-  set_long("gHotSpotVMStructEntryOffsetOffset",     gHotSpotVMStructEntryOffsetOffset);
-  set_long("gHotSpotVMStructEntryAddressOffset",    gHotSpotVMStructEntryAddressOffset);
-  set_long("gHotSpotVMStructEntryArrayStride",      gHotSpotVMStructEntryArrayStride);
-
-  set_address("gHotSpotVMTypes", gHotSpotVMTypes);
-  set_long("gHotSpotVMTypeEntryTypeNameOffset",       gHotSpotVMTypeEntryTypeNameOffset);
-  set_long("gHotSpotVMTypeEntrySuperclassNameOffset", gHotSpotVMTypeEntrySuperclassNameOffset);
-  set_long("gHotSpotVMTypeEntryIsOopTypeOffset",      gHotSpotVMTypeEntryIsOopTypeOffset);
-  set_long("gHotSpotVMTypeEntryIsIntegerTypeOffset",  gHotSpotVMTypeEntryIsIntegerTypeOffset);
-  set_long("gHotSpotVMTypeEntryIsUnsignedOffset",     gHotSpotVMTypeEntryIsUnsignedOffset);
-  set_long("gHotSpotVMTypeEntrySizeOffset",           gHotSpotVMTypeEntrySizeOffset);
-  set_long("gHotSpotVMTypeEntryArrayStride",          gHotSpotVMTypeEntryArrayStride);
-
-  set_address("gHotSpotVMIntConstants", gHotSpotVMIntConstants);
-  set_long("gHotSpotVMIntConstantEntryNameOffset",  gHotSpotVMIntConstantEntryNameOffset);
-  set_long("gHotSpotVMIntConstantEntryValueOffset", gHotSpotVMIntConstantEntryValueOffset);
-  set_long("gHotSpotVMIntConstantEntryArrayStride", gHotSpotVMIntConstantEntryArrayStride);
-
-  set_address("gHotSpotVMLongConstants", gHotSpotVMLongConstants);
-  set_long("gHotSpotVMLongConstantEntryNameOffset",  gHotSpotVMLongConstantEntryNameOffset);
-  set_long("gHotSpotVMLongConstantEntryValueOffset", gHotSpotVMLongConstantEntryValueOffset);
-  set_long("gHotSpotVMLongConstantEntryArrayStride", gHotSpotVMLongConstantEntryArrayStride);
-
-  //------------------------------------------------------------------------------------------------
-
-  set_int("arrayLengthOffset", arrayOopDesc::length_offset_in_bytes());
-
-  set_int("extraStackEntries", Method::extra_stack_entries());
-
-  set_int("tlabAlignmentReserve", (int32_t)ThreadLocalAllocBuffer::alignment_reserve());
-  set_long("heapTopAddress", (jlong)(address) Universe::heap()->top_addr());
-  set_long("heapEndAddress", (jlong)(address) Universe::heap()->end_addr());
-
-  set_boolean("inlineContiguousAllocationSupported", !CMSIncrementalMode && Universe::heap()->supports_inline_contig_alloc());
-
-  set_long("verifyOopMask", Universe::verify_oop_mask());
-  set_long("verifyOopBits", Universe::verify_oop_bits());
-
-  set_int("instanceKlassVtableStartOffset", InstanceKlass::vtable_start_offset() * HeapWordSize);
-
-  //------------------------------------------------------------------------------------------------
-
-  set_address("registerFinalizerAddress", SharedRuntime::register_finalizer);
-  set_address("exceptionHandlerForReturnAddressAddress", SharedRuntime::exception_handler_for_return_address);
-  set_address("osrMigrationEndAddress", SharedRuntime::OSR_migration_end);
-
-  set_address("javaTimeMillisAddress", CAST_FROM_FN_PTR(address, os::javaTimeMillis));
-  set_address("javaTimeNanosAddress", CAST_FROM_FN_PTR(address, os::javaTimeNanos));
-  set_address("arithmeticSinAddress", CAST_FROM_FN_PTR(address, SharedRuntime::dsin));
-  set_address("arithmeticCosAddress", CAST_FROM_FN_PTR(address, SharedRuntime::dcos));
-  set_address("arithmeticTanAddress", CAST_FROM_FN_PTR(address, SharedRuntime::dtan));
-
-  set_address("newInstanceAddress", GraalRuntime::new_instance);
-  set_address("newArrayAddress", GraalRuntime::new_array);
-  set_address("newMultiArrayAddress", GraalRuntime::new_multi_array);
-  set_address("dynamicNewArrayAddress", GraalRuntime::dynamic_new_array);
-  set_address("dynamicNewInstanceAddress", GraalRuntime::dynamic_new_instance);
-  set_address("threadIsInterruptedAddress", GraalRuntime::thread_is_interrupted);
-  set_address("vmMessageAddress", GraalRuntime::vm_message);
-  set_address("identityHashCodeAddress", GraalRuntime::identity_hash_code);
-  set_address("exceptionHandlerForPcAddress", GraalRuntime::exception_handler_for_pc);
-  set_address("monitorenterAddress", GraalRuntime::monitorenter);
-  set_address("monitorexitAddress", GraalRuntime::monitorexit);
-  set_address("createNullPointerExceptionAddress", GraalRuntime::create_null_exception);
-  set_address("createOutOfBoundsExceptionAddress", GraalRuntime::create_out_of_bounds_exception);
-  set_address("logPrimitiveAddress", GraalRuntime::log_primitive);
-  set_address("logObjectAddress", GraalRuntime::log_object);
-  set_address("logPrintfAddress", GraalRuntime::log_printf);
-  set_address("vmErrorAddress", GraalRuntime::vm_error);
-  set_address("loadAndClearExceptionAddress", GraalRuntime::load_and_clear_exception);
-  set_address("writeBarrierPreAddress", GraalRuntime::write_barrier_pre);
-  set_address("writeBarrierPostAddress", GraalRuntime::write_barrier_post);
-  set_address("validateObject", GraalRuntime::validate_object);
-
-  set_address("deoptimizationFetchUnrollInfo", Deoptimization::fetch_unroll_info);
-  set_address("deoptimizationUncommonTrap", Deoptimization::uncommon_trap);
-  set_address("deoptimizationUnpackFrames", Deoptimization::unpack_frames);
-
-  //------------------------------------------------------------------------------------------------
-
-  set_int("graalCountersThreadOffset", in_bytes(JavaThread::graal_counters_offset()));
-  set_int("graalCountersSize", (jint) GraalCounterSize);
-
-  //------------------------------------------------------------------------------------------------
-
-  set_long("dllLoad", (jlong) os::dll_load);
-  set_long("dllLookup", (jlong) os::dll_lookup);
-  #if defined(TARGET_OS_FAMILY_bsd) || defined(TARGET_OS_FAMILY_linux)
-  set_long("rtldDefault", (jlong) RTLD_DEFAULT);
-  #endif
-
-#undef set_boolean
-#undef set_int
-#undef set_long
-#undef set_address
-
+C2V_VMENTRY(void, initializeConfiguration, (JNIEnv *, jobject, jobject config))
+  VMStructs::initHotSpotVMConfig(JNIHandles::resolve(config));
 C2V_END
 
-C2V_ENTRY(jbyteArray, initializeBytecode, (JNIEnv *env, jobject, jlong metaspace_method, jbyteArray result))
+C2V_VMENTRY(jbyteArray, initializeBytecode, (JNIEnv *, jobject, jlong metaspace_method))
   methodHandle method = asMethod(metaspace_method);
   ResourceMark rm;
 
@@ -279,9 +156,9 @@
     }
   }
 
-  env->SetByteArrayRegion(result, 0, code_size, reconstituted_code);
-
-  return result;
+  typeArrayOop result_array = oopFactory::new_byteArray(code_size, CHECK_NULL);
+  memcpy(result_array->byte_at_addr(0), reconstituted_code, code_size);
+  return (jbyteArray) JNIHandles::make_local(result_array);
 C2V_END
 
 C2V_VMENTRY(jint, exceptionTableLength, (JNIEnv *, jobject, jlong metaspace_method))
@@ -350,7 +227,7 @@
   return CompilerOracle::should_inline(method) || method->force_inline();
 C2V_END
 
-C2V_VMENTRY(jlong, lookupType, (JNIEnv *env, jobject, jstring jname, jclass accessing_class, jboolean resolve))
+C2V_VMENTRY(jlong, lookupType, (JNIEnv*, jobject, jstring jname, jclass accessing_class, jboolean resolve))
   ResourceMark rm;
   Handle name = JNIHandles::resolve(jname);
   Symbol* class_name = java_lang_String::as_symbol(name, THREAD);
@@ -375,44 +252,44 @@
   return (jlong) (address) resolved_klass;
 C2V_END
 
-C2V_VMENTRY(jobject, resolveConstantInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jobject, resolveConstantInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   ConstantPool* cp = (ConstantPool*) metaspace_constant_pool;
   oop result = cp->resolve_constant_at(index, CHECK_NULL);
   return JNIHandles::make_local(THREAD, result);
 C2V_END
 
-C2V_VMENTRY(jobject, resolvePossiblyCachedConstantInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jobject, resolvePossiblyCachedConstantInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   ConstantPool* cp = (ConstantPool*) metaspace_constant_pool;
   oop result = cp->resolve_possibly_cached_constant_at(index, CHECK_NULL);
   return JNIHandles::make_local(THREAD, result);
 C2V_END
 
-C2V_VMENTRY(jint, lookupNameAndTypeRefIndexInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jint, lookupNameAndTypeRefIndexInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   return cp->name_and_type_ref_index_at(index);
 C2V_END
 
-C2V_VMENTRY(jlong, lookupNameRefInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jlong, lookupNameRefInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   return (jlong) (address) cp->name_ref_at(index);
 C2V_END
 
-C2V_VMENTRY(jlong, lookupSignatureRefInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jlong, lookupSignatureRefInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   return (jlong) (address) cp->signature_ref_at(index);
 C2V_END
 
-C2V_VMENTRY(jint, lookupKlassRefIndexInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jint, lookupKlassRefIndexInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   return cp->klass_ref_index_at(index);
 C2V_END
 
-C2V_VMENTRY(jlong, constantPoolKlassAt, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jlong, constantPoolKlassAt, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   ConstantPool* cp = (ConstantPool*) metaspace_constant_pool;
   return (jlong) (address) cp->klass_at(index, THREAD);
 C2V_END
 
-C2V_VMENTRY(jlong, lookupKlassInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode))
+C2V_VMENTRY(jlong, lookupKlassInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode))
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   KlassHandle loading_klass(cp->pool_holder());
   bool is_accessible = false;
@@ -436,13 +313,13 @@
   return (jlong) CompilerToVM::tag_pointer(klass());
 C2V_END
 
-C2V_VMENTRY(jobject, lookupAppendixInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jobject, lookupAppendixInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   oop appendix_oop = ConstantPool::appendix_at_if_loaded(cp, index);
   return JNIHandles::make_local(THREAD, appendix_oop);
 C2V_END
 
-C2V_VMENTRY(jlong, lookupMethodInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode))
+C2V_VMENTRY(jlong, lookupMethodInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode))
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   instanceKlassHandle pool_holder(cp->pool_holder());
   Bytecodes::Code bc = (Bytecodes::Code) (((int) opcode) & 0xFF);
@@ -450,12 +327,12 @@
   return (jlong) (address) method();
 C2V_END
 
-C2V_VMENTRY(jint, constantPoolRemapInstructionOperandFromCache, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(jint, constantPoolRemapInstructionOperandFromCache, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   ConstantPool* cp = (ConstantPool*) metaspace_constant_pool;
   return cp->remap_instruction_operand_from_cache(index);
 C2V_END
 
-C2V_VMENTRY(jlong, resolveField, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode, jlongArray info_handle))
+C2V_VMENTRY(jlong, resolveField, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode, jlongArray info_handle))
   ResourceMark rm;
   constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool;
   Bytecodes::Code code = (Bytecodes::Code)(((int) opcode) & 0xFF);
@@ -468,11 +345,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;
+  assert(!klass->is_interface(), "");
+  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))
@@ -486,7 +428,7 @@
   return (jlong) (address) klass->class_initializer();
 C2V_END
 
-C2V_VMENTRY(jlong, getMaxCallTargetOffset, (JNIEnv *env, jobject, jlong addr))
+C2V_VMENTRY(jlong, getMaxCallTargetOffset, (JNIEnv*, jobject, jlong addr))
   address target_addr = (address) addr;
   if (target_addr != 0x0) {
     int64_t off_low = (int64_t)target_addr - ((int64_t)CodeCache::low_bound() + sizeof(int));
@@ -622,7 +564,7 @@
   return JNIHandles::make_local(result());
 C2V_END
 
-C2V_VMENTRY(jobject, getStackTraceElement, (JNIEnv *env, jobject, jlong metaspace_method, int bci))
+C2V_VMENTRY(jobject, getStackTraceElement, (JNIEnv*, jobject, jlong metaspace_method, int bci))
   ResourceMark rm;
   HandleMark hm;
 
@@ -631,7 +573,7 @@
   return JNIHandles::make_local(element);
 C2V_END
 
-C2V_VMENTRY(jobject, executeCompiledMethodVarargs, (JNIEnv *env, jobject, jobject args, jobject hotspotInstalledCode))
+C2V_VMENTRY(jobject, executeCompiledMethodVarargs, (JNIEnv*, jobject, jobject args, jobject hotspotInstalledCode))
   ResourceMark rm;
   HandleMark hm;
 
@@ -659,7 +601,7 @@
   }
 C2V_END
 
-C2V_ENTRY(jlongArray, getLineNumberTable, (JNIEnv *env, jobject, jlong metaspace_method))
+C2V_VMENTRY(jlongArray, getLineNumberTable, (JNIEnv *, jobject, jlong metaspace_method))
   Method* method = (Method*) metaspace_method;
   if (!method->has_linenumber_table()) {
     return NULL;
@@ -671,19 +613,19 @@
   }
 
   CompressedLineNumberReadStream stream(method->compressed_linenumber_table());
-  jlongArray result = env->NewLongArray(2 * num_entries);
+  typeArrayOop result = oopFactory::new_longArray(2 * num_entries, CHECK_NULL);
 
   int i = 0;
   jlong value;
   while (stream.read_pair()) {
     value = ((long) stream.bci());
-    env->SetLongArrayRegion(result,i,1,&value);
+    result->long_at_put(i, value);
     value = ((long) stream.line());
-    env->SetLongArrayRegion(result,i + 1,1,&value);
+    result->long_at_put(i + 1, value);
     i += 2;
   }
 
-  return result;
+  return (jlongArray) JNIHandles::make_local(result);
 C2V_END
 
 C2V_VMENTRY(jlong, getLocalVariableTableStart, (JNIEnv *, jobject, jlong metaspace_method))
@@ -701,7 +643,7 @@
   return method->localvariable_table_length();
 C2V_END
 
-C2V_VMENTRY(void, reprofile, (JNIEnv *env, jobject, jlong metaspace_method))
+C2V_VMENTRY(void, reprofile, (JNIEnv*, jobject, jlong metaspace_method))
   Method* method = asMethod(metaspace_method);
   MethodCounters* mcs = method->method_counters();
   if (mcs != NULL) {
@@ -725,7 +667,7 @@
 C2V_END
 
 
-C2V_VMENTRY(void, invalidateInstalledCode, (JNIEnv *env, jobject, jobject hotspotInstalledCode))
+C2V_VMENTRY(void, invalidateInstalledCode, (JNIEnv*, jobject, jobject hotspotInstalledCode))
   jlong nativeMethod = InstalledCode::address(hotspotInstalledCode);
   nmethod* m = (nmethod*)nativeMethod;
   if (m != NULL && !m->is_not_entrant()) {
@@ -736,32 +678,36 @@
   InstalledCode::set_address(hotspotInstalledCode, 0);
 C2V_END
 
-C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv *env, jobject, jlong metaspace_klass))
+C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv*, jobject, jlong metaspace_klass))
   Klass* klass = asKlass(metaspace_klass);
   return JNIHandles::make_local(klass->java_mirror());
 C2V_END
 
-C2V_VMENTRY(jlong, readUnsafeKlassPointer, (JNIEnv *env, jobject, jobject o))
+C2V_VMENTRY(jlong, readUnsafeKlassPointer, (JNIEnv*, jobject, jobject o))
   oop resolved_o = JNIHandles::resolve(o);
   jlong klass = (jlong)(address)resolved_o->klass();
   return klass;
 C2V_END
 
-C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv *env, jobject))
+C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv*, jobject))
   typeArrayOop arrayOop = oopFactory::new_longArray(GraalCounterSize, CHECK_NULL);
   JavaThread::collect_counters(arrayOop);
   return (jlongArray) JNIHandles::make_local(arrayOop);
 C2V_END
 
-C2V_ENTRY(jobject, getGPUs, (JNIEnv *env, jobject))
+// In general we should avoid using regular JNI methods to interact with the JVM but this
+// particular case is just about registering JNI methods so it should be a regular native
+// method.
+JNIEXPORT jobject JNICALL c2v_getGPUs (JNIEnv* env, jobject) {
+  TRACE_graal_3("CompilerToVM::getGPUs" );
 #if defined(TARGET_OS_FAMILY_bsd) || defined(TARGET_OS_FAMILY_linux) || defined(TARGET_OS_FAMILY_windows)
   return Gpu::probe_gpus(env);
 #else
   return env->NewStringUTF("");
 #endif
-C2V_END
+}
 
-C2V_VMENTRY(int, allocateCompileId, (JNIEnv *env, jobject, jlong metaspace_method, int entry_bci))
+C2V_VMENTRY(int, allocateCompileId, (JNIEnv*, jobject, jlong metaspace_method, int entry_bci))
   HandleMark hm;
   ResourceMark rm;
   Method* method = (Method*) metaspace_method;
@@ -769,17 +715,17 @@
 C2V_END
 
 
-C2V_VMENTRY(jboolean, isMature, (JNIEnv *env, jobject, jlong metaspace_method_data))
+C2V_VMENTRY(jboolean, isMature, (JNIEnv*, jobject, jlong metaspace_method_data))
   MethodData* mdo = asMethodData(metaspace_method_data);
   return mdo != NULL && mdo->is_mature();
 C2V_END
 
-C2V_VMENTRY(jboolean, hasCompiledCodeForOSR, (JNIEnv *env, jobject, jlong metaspace_method, int entry_bci, int comp_level))
+C2V_VMENTRY(jboolean, hasCompiledCodeForOSR, (JNIEnv*, jobject, jlong metaspace_method, int entry_bci, int comp_level))
   Method* method = asMethod(metaspace_method);
   return method->lookup_osr_nmethod_for(entry_bci, comp_level, true) != NULL;
 C2V_END
 
-C2V_VMENTRY(jlong, getTimeStamp, (JNIEnv *env, jobject))
+C2V_VMENTRY(jlong, getTimeStamp, (JNIEnv*, jobject))
   // tty->time_stamp is the time since VM start which should be used
   // for all HotSpot log output when a timestamp is required.
   return tty->time_stamp().milliseconds();
@@ -796,7 +742,7 @@
   return false;
 }
 
-C2V_VMENTRY(jobject, getNextStackFrame, (JNIEnv *env, jobject compilerToVM, jobject hs_frame, jlongArray methods, jint initialSkip))
+C2V_VMENTRY(jobject, getNextStackFrame, (JNIEnv*, jobject compilerToVM, jobject hs_frame, jlongArray methods, jint initialSkip))
   ResourceMark rm;
 
   if (!thread->has_last_Java_frame()) return NULL;
@@ -931,7 +877,7 @@
   return NULL;
 C2V_END
 
-C2V_VMENTRY(void, resolveInvokeDynamic, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index))
+C2V_VMENTRY(void, resolveInvokeDynamic, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index))
   ConstantPool* cp = (ConstantPool*)metaspace_constant_pool;
   CallInfo callInfo;
   LinkResolver::resolve_invokedynamic(callInfo, cp, index, CHECK);
@@ -940,7 +886,7 @@
 C2V_END
 
 // public native void materializeVirtualObjects(HotSpotStackFrameReference stackFrame, boolean invalidate);
-C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv *env, jobject, jobject hs_frame, bool invalidate))
+C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv*, jobject, jobject hs_frame, bool invalidate))
   ResourceMark rm;
 
   if (hs_frame == NULL) {
@@ -1058,58 +1004,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",                                               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/graalJavaAccess.hpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/graal/graalJavaAccess.hpp	Wed May 21 11:45:50 2014 +0200
@@ -52,13 +52,8 @@
     oop_field(HotSpotResolvedObjectType, javaClass, "Ljava/lang/Class;")                                                                                       \
   end_class                                                                                                                                                    \
   start_class(HotSpotResolvedJavaMethod)                                                                                                                       \
-    oop_field(HotSpotResolvedJavaMethod, name, "Ljava/lang/String;")                                                                                           \
-    oop_field(HotSpotResolvedJavaMethod, holder, "Lcom/oracle/graal/hotspot/meta/HotSpotResolvedObjectType;")                                                  \
     long_field(HotSpotResolvedJavaMethod, metaspaceMethod)                                                                                                     \
   end_class                                                                                                                                                    \
-  start_class(HotSpotJavaType)                                                                                                                                 \
-    oop_field(HotSpotJavaType, name, "Ljava/lang/String;")                                                                                                     \
-  end_class                                                                                                                                                    \
   start_class(InstalledCode)                                                                                                                                   \
     long_field(InstalledCode, address)                                                                                                                         \
     long_field(InstalledCode, version)                                                                                                                         \
--- a/src/share/vm/graal/vmStructs_graal.hpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/graal/vmStructs_graal.hpp	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/interpreter/linkResolver.hpp	Wed May 21 11:45:50 2014 +0200
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/runtime/deoptimization.cpp	Wed May 21 11:45:50 2014 +0200
@@ -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.cpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/runtime/globals.cpp	Wed May 21 11:45:50 2014 +0200
@@ -477,12 +477,12 @@
 }
 
 // Search the flag table for a named flag
-Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked) {
+Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked, bool allow_constant) {
   for (Flag* current = &flagTable[0]; current->_name != NULL; current++) {
     if (str_equal(current->_name, name, length)) {
       // Found a matching entry.
       // Don't report notproduct and develop flags in product builds.
-      if (current->is_constant_in_binary()) {
+      if (current->is_constant_in_binary() && !allow_constant) {
         return NULL;
       }
       // Report locked flags only if allowed.
--- a/src/share/vm/runtime/globals.hpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/runtime/globals.hpp	Wed May 21 11:45:50 2014 +0200
@@ -256,7 +256,7 @@
   // number of flags
   static size_t numFlags;
 
-  static Flag* find_flag(const char* name, size_t length, bool allow_locked = false);
+  static Flag* find_flag(const char* name, size_t length, bool allow_locked = false, bool allow_constant = false);
   static Flag* fuzzy_match(const char* name, size_t length, bool allow_locked = false);
 
   void check_writable();
@@ -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	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/runtime/java.cpp	Wed May 21 11:45:50 2014 +0200
@@ -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;
--- a/src/share/vm/runtime/vmStructs.cpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/runtime/vmStructs.cpp	Wed May 21 11:45:50 2014 +0200
@@ -70,6 +70,9 @@
 #include "oops/constMethod.hpp"
 #include "oops/constantPool.hpp"
 #include "oops/cpCache.hpp"
+#ifdef GRAAL
+#include "oops/fieldStreams.hpp"
+#endif
 #include "oops/instanceClassLoaderKlass.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/instanceMirrorKlass.hpp"
@@ -105,6 +108,7 @@
 #include "utilities/hashtable.hpp"
 #include "utilities/macros.hpp"
 #ifdef GRAAL
+# include "graal/graalRuntime.hpp"
 # include "graal/vmStructs_graal.hpp"
 # include "hsail/vm/vmStructs_hsail.hpp"
 #endif
@@ -3124,7 +3128,6 @@
   VM_INT_CONSTANTS_GRAAL(GENERATE_VM_INT_CONSTANT_ENTRY,
                          GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY)
 
-  VM_INT_CONSTANTS_GPU_HSAIL(GENERATE_VM_INT_CONSTANT_ENTRY)
 #endif
 
 #if INCLUDE_ALL_GCS
@@ -3480,3 +3483,11 @@
   }
 }
 #endif
+
+
+#ifdef GRAAL
+// Emit intialization code for HotSpotVMConfig.  It's placed here so
+// it can take advantage of the relaxed access checking enjoyed by
+// VMStructs.
+#include "HotSpotVMConfig.inline.hpp"
+#endif
--- a/src/share/vm/runtime/vmStructs.hpp	Tue May 13 19:19:27 2014 +0200
+++ b/src/share/vm/runtime/vmStructs.hpp	Wed May 21 11:45:50 2014 +0200
@@ -126,6 +126,10 @@
   static void test();
 #endif
 
+#ifdef GRAAL
+  static void initHotSpotVMConfig(oop config);
+#endif
+
 private:
   // Look up a type in localHotSpotVMTypes using strcmp() (debug build only).
   // Returns 1 if found, 0 if not.