changeset 9783:5402504894fe

Merge.
author Christian Humer <christian.humer@gmail.com>
date Tue, 21 May 2013 19:51:00 +0200
parents ba02d19dd3cc (current diff) 05b719a4ae09 (diff)
children 747b2517feae 06dc2d2324d6 d6d5e3dc2713
files graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RuntimeCallTarget.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CRuntimeCall.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorEnterStubCall.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ThreadIsInterruptedStubCall.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/VerifyOopStubCall.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/CRuntimeStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/LogObjectStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/LogPrimitiveStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/LogPrintfStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewMultiArrayStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/OSRMigrationEndStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/RuntimeCallStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ThreadIsInterruptedStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/VMErrorStub.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/RuntimeCallNode.java
diffstat 124 files changed, 2858 insertions(+), 2955 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Architecture.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Architecture.java	Tue May 21 19:51:00 2013 +0200
@@ -161,8 +161,25 @@
     /**
      * Determine the maximum vector length supported for vector operations on values of a given
      * {@link Kind}.
+     * 
+     * @param kind the kind of the individual vector elements
+     * @return the maximum supported vector size
      */
-    public int getMaxVectorLength(@SuppressWarnings("unused") Kind kind) {
+    public int getMaxVectorLength(Kind kind) {
+        return 1;
+    }
+
+    /**
+     * Get a natively supported vector length for breaking down some vector operation on a constant
+     * length vector.
+     * 
+     * @param kind the kind of the individual vector elements
+     * @param maxLength the maximum length that should be returned
+     * @param arithmetic whether the vector length needs to support arithmetic operations or just
+     *            load and store
+     * @return a supported vector size, but at most {@code maxLength}
+     */
+    public int getSupportedVectorLength(Kind kind, int maxLength, boolean arithmetic) {
         return 1;
     }
 
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CallingConvention.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CallingConvention.java	Tue May 21 19:51:00 2013 +0200
@@ -27,9 +27,8 @@
 import com.oracle.graal.api.meta.*;
 
 /**
- * A calling convention describes the locations in which the arguments for a call are placed, the
- * location in which the return value is placed if the call is not void and any
- * {@linkplain #getTemporaries() extra} locations used (and killed) by the call.
+ * A calling convention describes the locations in which the arguments for a call are placed and the
+ * location in which the return value is placed if the call is not void.
  */
 public class CallingConvention {
 
@@ -78,11 +77,6 @@
     private final AllocatableValue[] argumentLocations;
 
     /**
-     * The locations used (and killed) by the call in addition to the arguments.
-     */
-    private final AllocatableValue[] temporaryLocations;
-
-    /**
      * Creates a description of the registers and stack locations used by a call.
      * 
      * @param stackSize amount of stack space (in bytes) required for the stack-based arguments of
@@ -92,28 +86,11 @@
      * @param argumentLocations the ordered locations in which the arguments are placed
      */
     public CallingConvention(int stackSize, AllocatableValue returnLocation, AllocatableValue... argumentLocations) {
-        this(AllocatableValue.NONE, stackSize, returnLocation, argumentLocations);
-    }
-
-    /**
-     * Creates a description of the registers and stack locations used by a call.
-     * 
-     * @param temporaryLocations the locations used (and killed) by the call in addition to
-     *            {@code arguments}
-     * @param stackSize amount of stack space (in bytes) required for the stack-based arguments of
-     *            the call
-     * @param returnLocation the location for the return value or {@link Value#ILLEGAL} if a void
-     *            call
-     * @param argumentLocations the ordered locations in which the arguments are placed
-     */
-    public CallingConvention(AllocatableValue[] temporaryLocations, int stackSize, AllocatableValue returnLocation, AllocatableValue... argumentLocations) {
         assert argumentLocations != null;
-        assert temporaryLocations != null;
         assert returnLocation != null;
         this.argumentLocations = argumentLocations;
         this.stackSize = stackSize;
         this.returnLocation = returnLocation;
-        this.temporaryLocations = temporaryLocations;
         assert verify();
     }
 
@@ -155,17 +132,6 @@
         return argumentLocations.clone();
     }
 
-    /**
-     * Gets the locations used (and killed) by the call apart from the
-     * {@linkplain #getArgument(int) arguments}.
-     */
-    public AllocatableValue[] getTemporaries() {
-        if (temporaryLocations.length == 0) {
-            return temporaryLocations;
-        }
-        return temporaryLocations.clone();
-    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -178,14 +144,6 @@
         if (!returnLocation.equals(Value.ILLEGAL)) {
             sb.append(" -> ").append(returnLocation);
         }
-        if (temporaryLocations.length != 0) {
-            sb.append("; temps=");
-            sep = "";
-            for (Value op : temporaryLocations) {
-                sb.append(sep).append(op);
-                sep = ", ";
-            }
-        }
         sb.append("]");
         return sb.toString();
     }
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CodeCacheProvider.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CodeCacheProvider.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.api.code;
 
 import com.oracle.graal.api.code.CompilationResult.DataPatch;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 
 /**
@@ -68,9 +67,9 @@
     int getMinimumOutgoingSize();
 
     /**
-     * Gets the signature and linkage information for a runtime call.
+     * Gets the linkage for a foreign call.
      */
-    RuntimeCallTarget lookupRuntimeCall(Descriptor descriptor);
+    ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor);
 
     /**
      * Encodes a deoptimization action and a deoptimization reason in an integer value.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/ForeignCallLinkage.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,58 @@
+/*
+ * 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.api.code;
+
+import com.oracle.graal.api.meta.*;
+
+/**
+ * The runtime specific details of a {@linkplain ForeignCallDescriptor foreign} call.
+ */
+public interface ForeignCallLinkage extends InvokeTarget {
+
+    /**
+     * Gets the details of where parameters are passed and value(s) are returned.
+     */
+    CallingConvention getCallingConvention();
+
+    /**
+     * Returns the maximum absolute offset of PC relative call to this stub from any position in the
+     * code cache or -1 when not applicable. Intended for determining the required size of
+     * address/offset fields.
+     */
+    long getMaxCallTargetOffset();
+
+    ForeignCallDescriptor getDescriptor();
+
+    /**
+     * Gets the values used/killed by this foreign call.
+     */
+    Value[] getTemporaries();
+
+    /**
+     * Determines if the foreign call target destroys all registers.
+     * 
+     * @return {@code true} if the register allocator must save all live registers around a call to
+     *         this target
+     */
+    boolean destroysRegisters();
+}
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RuntimeCallTarget.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/*
- * 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.api.code;
-
-import java.util.*;
-
-import com.oracle.graal.api.meta.*;
-
-/**
- * The name, signature and calling convention of a call from compiled code to the runtime. The
- * target of such a call may be a leaf stub or a call into the runtime code proper.
- */
-public interface RuntimeCallTarget extends InvokeTarget {
-
-    /**
-     * The name and signature of a runtime call.
-     */
-    public static class Descriptor {
-
-        private final String name;
-        private final boolean hasSideEffect;
-        private final Class resultType;
-        private final Class[] argumentTypes;
-
-        public Descriptor(String name, boolean hasSideEffect, Class resultType, Class... argumentTypes) {
-            this.name = name;
-            this.hasSideEffect = hasSideEffect;
-            this.resultType = resultType;
-            this.argumentTypes = argumentTypes;
-        }
-
-        /**
-         * Gets the name of this runtime call.
-         */
-        public String getName() {
-            return name;
-        }
-
-        /**
-         * Determines if this call changes state visible to other threads. Such calls denote
-         * boundaries across which deoptimization points cannot be moved.
-         */
-        public boolean hasSideEffect() {
-            return hasSideEffect;
-        }
-
-        /**
-         * Gets the return kind of this runtime call.
-         */
-        public Class<?> getResultType() {
-            return resultType;
-        }
-
-        /**
-         * Gets the argument kinds of this runtime call.
-         */
-        public Class<?>[] getArgumentTypes() {
-            return argumentTypes.clone();
-        }
-
-        @Override
-        public int hashCode() {
-            return name.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj instanceof Descriptor) {
-                Descriptor nas = (Descriptor) obj;
-                return nas.name.equals(name) && nas.resultType.equals(resultType) && Arrays.equals(nas.argumentTypes, argumentTypes);
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(name).append('(');
-            String sep = "";
-            for (Class arg : argumentTypes) {
-                sb.append(sep).append(arg.getSimpleName());
-                sep = ",";
-            }
-            return sb.append(')').append(resultType.getSimpleName()).toString();
-        }
-    }
-
-    CallingConvention getCallingConvention();
-
-    /**
-     * Returns the maximum absolute offset of PC relative call to this stub from any position in the
-     * code cache or -1 when not applicable. Intended for determining the required size of
-     * address/offset fields.
-     */
-    long getMaxCallTargetOffset();
-
-    Descriptor getDescriptor();
-
-    /**
-     * Determines if the target routine destroys all registers.
-     * 
-     * @return {@code true} if the register allocator must save all live registers around a call to
-     *         this target
-     */
-    boolean destroysRegisters();
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/AbstractJavaProfile.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,151 @@
+/*
+ * 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.api.meta;
+
+import java.io.*;
+
+/**
+ * This profile object represents a certain set of profiling information at a specific BCI. The
+ * precision of the supplied values may vary, but a runtime that provides this information should be
+ * aware that it will be used to guide performance-critical decisions like speculative inlining,
+ * etc.
+ */
+public abstract class AbstractJavaProfile<T extends AbstractProfiledItem<U>, U> implements Serializable {
+
+    private static final long serialVersionUID = 5493379044459116749L;
+
+    private final double notRecordedProbability;
+    private final T[] pitems;
+
+    public AbstractJavaProfile(double notRecordedProbability, T[] pitems) {
+        this.pitems = pitems;
+        assert notRecordedProbability != Double.NaN;
+        this.notRecordedProbability = notRecordedProbability;
+        assert isSorted();
+    }
+
+    /**
+     * Determines if an array of profiled items are sorted in descending order of their
+     * probabilities.
+     */
+    private boolean isSorted() {
+        for (int i = 1; i < pitems.length; i++) {
+            if (pitems[i - 1].getProbability() < pitems[i].getProbability()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns the estimated probability of all types that could not be recorded due to profiling
+     * limitations.
+     * 
+     * @return double value >= 0.0 and <= 1.0
+     */
+    public double getNotRecordedProbability() {
+        return notRecordedProbability;
+    }
+
+    protected T[] getItems() {
+        return pitems;
+    }
+
+    /**
+     * Searches for an entry of a given resolved Java type.
+     * 
+     * @param type the type for which an entry should be searched
+     * @return the entry or null if no entry for this type can be found
+     */
+    public T findEntry(ResolvedJavaType type) {
+        if (pitems != null) {
+            for (T pt : pitems) {
+                if (pt.getItem() == type) {
+                    return pt;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(this.getClass().getName());
+        builder.append("[");
+        if (pitems != null) {
+            for (T pt : pitems) {
+                builder.append(pt.toString());
+                builder.append(", ");
+            }
+        }
+        builder.append(this.notRecordedProbability);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    public boolean isIncluded(U item) {
+        if (this.getNotRecordedProbability() > 0.0) {
+            return true;
+        } else {
+            for (int i = 0; i < getItems().length; i++) {
+                T pitem = getItems()[i];
+                U curType = pitem.getItem();
+                if (curType == item) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (getClass() == other.getClass()) {
+            AbstractJavaProfile javaTypeProfile = (AbstractJavaProfile) other;
+            if (javaTypeProfile.notRecordedProbability != notRecordedProbability) {
+                return false;
+            }
+            if (javaTypeProfile.pitems.length != pitems.length) {
+                return false;
+            }
+
+            for (int i = 0; i < pitems.length; ++i) {
+                if (!pitems[i].equals(javaTypeProfile.pitems[i])) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) Double.doubleToLongBits(notRecordedProbability) + pitems.length * 13;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/AbstractProfiledItem.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,99 @@
+/*
+ * 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.api.meta;
+
+import java.io.*;
+
+/**
+ * A profiled type that has a probability. Profiled types are naturally sorted in descending order
+ * of their probabilities.
+ */
+public abstract class AbstractProfiledItem<T> implements Comparable<AbstractProfiledItem>, Serializable {
+
+    private static final long serialVersionUID = 7838575753661305744L;
+
+    protected final T item;
+    protected final double probability;
+
+    public AbstractProfiledItem(T item, double probability) {
+        assert item != null;
+        assert probability >= 0.0D && probability <= 1.0D;
+        this.item = item;
+        this.probability = probability;
+    }
+
+    protected T getItem() {
+        return item;
+    }
+
+    /**
+     * Returns the estimated probability of {@link #getItem()}.
+     * 
+     * @return double value >= 0.0 and <= 1.0
+     */
+    public double getProbability() {
+        return probability;
+    }
+
+    @Override
+    public int compareTo(AbstractProfiledItem o) {
+        if (getProbability() > o.getProbability()) {
+            return -1;
+        } else if (getProbability() < o.getProbability()) {
+            return 1;
+        }
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        long temp;
+        temp = Double.doubleToLongBits(probability);
+        result = prime * result + (int) (temp ^ (temp >>> 32));
+        result = prime * result + item.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AbstractProfiledItem other = (AbstractProfiledItem) obj;
+        if (Double.doubleToLongBits(probability) != Double.doubleToLongBits(other.probability)) {
+            return false;
+        }
+        return item.equals(other.item);
+    }
+
+    @Override
+    public abstract String toString();
+}
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/DefaultProfilingInfo.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/DefaultProfilingInfo.java	Tue May 21 19:51:00 2013 +0200
@@ -48,6 +48,11 @@
     }
 
     @Override
+    public JavaMethodProfile getMethodProfile(int bci) {
+        return null;
+    }
+
+    @Override
     public double getBranchTakenProbability(int bci) {
         return -1;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ForeignCallDescriptor.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2009, 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.api.meta;
+
+import java.util.*;
+
+/**
+ * The name and signature of a foreign call. A foreign call differs from a normal compiled Java call
+ * in at least one of these aspects:
+ * <ul>
+ * <li>The call is to C/C++/assembler code.</li>
+ * <li>The call uses different conventions for passing parameters or returning values.</li>
+ * <li>The callee has different register saving semantics. For example, the callee may save all
+ * registers (apart from some specified temporaries) in which case the register allocator doesn't
+ * not need to spill all live registers around the call site.</li>
+ * <li>The call does not occur at an INVOKE* bytecode. Such a call could be transformed into a
+ * standard Java call if the foreign routine is a normal Java method and the runtime supports
+ * linking Java calls at arbitrary bytecodes.</li>
+ * </ul>
+ */
+public class ForeignCallDescriptor {
+
+    private final String name;
+    private final Class resultType;
+    private final Class[] argumentTypes;
+
+    public ForeignCallDescriptor(String name, Class resultType, Class... argumentTypes) {
+        this.name = name;
+        this.resultType = resultType;
+        this.argumentTypes = argumentTypes;
+    }
+
+    /**
+     * Gets the name of this foreign call.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the return type of this foreign call.
+     */
+    public Class<?> getResultType() {
+        return resultType;
+    }
+
+    /**
+     * Gets the argument types of this foreign call.
+     */
+    public Class<?>[] getArgumentTypes() {
+        return argumentTypes.clone();
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof ForeignCallDescriptor) {
+            ForeignCallDescriptor other = (ForeignCallDescriptor) obj;
+            return other.name.equals(name) && other.resultType.equals(resultType) && Arrays.equals(other.argumentTypes, argumentTypes);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(name).append('(');
+        String sep = "";
+        for (Class arg : argumentTypes) {
+            sb.append(sep).append(arg.getSimpleName());
+            sep = ",";
+        }
+        return sb.append(')').append(resultType.getSimpleName()).toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/JavaMethodProfile.java	Tue May 21 19:51:00 2013 +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.api.meta;
+
+import com.oracle.graal.api.meta.JavaMethodProfile.ProfiledMethod;
+
+/**
+ * This profile object represents the method profile at a specific BCI. The precision of the
+ * supplied values may vary, but a runtime that provides this information should be aware that it
+ * will be used to guide performance-critical decisions like speculative inlining, etc.
+ */
+public final class JavaMethodProfile extends AbstractJavaProfile<ProfiledMethod, ResolvedJavaMethod> {
+
+    private static final long serialVersionUID = -1440572119913692689L;
+
+    public JavaMethodProfile(double notRecordedProbability, ProfiledMethod[] pitems) {
+        super(notRecordedProbability, pitems);
+    }
+
+    public ProfiledMethod[] getMethods() {
+        return super.getItems();
+    }
+
+    public static class ProfiledMethod extends AbstractProfiledItem<ResolvedJavaMethod> {
+
+        private static final long serialVersionUID = 5418813647187024693L;
+
+        public ProfiledMethod(ResolvedJavaMethod item, double probability) {
+            super(item, probability);
+        }
+
+        /**
+         * Returns the type for this profile entry.
+         */
+        public ResolvedJavaMethod getMethod() {
+            return getItem();
+        }
+
+        @Override
+        public String toString() {
+            return "{" + item.getName() + ", " + probability + "}";
+        }
+    }
+}
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/JavaTypeProfile.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/JavaTypeProfile.java	Tue May 21 19:51:00 2013 +0200
@@ -22,134 +22,26 @@
  */
 package com.oracle.graal.api.meta;
 
-import java.io.*;
 import java.util.*;
 
-import com.oracle.graal.api.meta.ProfilingInfo.*;
+import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType;
+import com.oracle.graal.api.meta.ProfilingInfo.TriState;
 
 /**
  * This profile object represents the type profile at a specific BCI. The precision of the supplied
  * values may vary, but a runtime that provides this information should be aware that it will be
  * used to guide performance-critical decisions like speculative inlining, etc.
  */
-public final class JavaTypeProfile implements Serializable {
+public final class JavaTypeProfile extends AbstractJavaProfile<ProfiledType, ResolvedJavaType> {
 
     private static final long serialVersionUID = -6877016333706838441L;
-
-    /**
-     * A profiled type that has a probability. Profiled types are naturally sorted in descending
-     * order of their probabilities.
-     */
-    public static final class ProfiledType implements Comparable<ProfiledType>, Serializable {
-
-        private static final long serialVersionUID = 7838575753661305744L;
-
-        public static final ProfiledType[] EMPTY_ARRAY = new ProfiledType[0];
-
-        private final ResolvedJavaType type;
-        private final double probability;
-
-        public ProfiledType(ResolvedJavaType type, double probability) {
-            assert type != null;
-            assert probability >= 0.0D && probability <= 1.0D;
-            this.type = type;
-            this.probability = probability;
-        }
-
-        /**
-         * Returns the type for this profile entry.
-         */
-        public ResolvedJavaType getType() {
-            return type;
-        }
-
-        /**
-         * Returns the estimated probability of {@link #getType()}.
-         * 
-         * @return double value >= 0.0 and <= 1.0
-         */
-        public double getProbability() {
-            return probability;
-        }
-
-        @Override
-        public int compareTo(ProfiledType o) {
-            if (getProbability() > o.getProbability()) {
-                return -1;
-            } else if (getProbability() < o.getProbability()) {
-                return 1;
-            }
-            return 0;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            long temp;
-            temp = Double.doubleToLongBits(probability);
-            result = prime * result + (int) (temp ^ (temp >>> 32));
-            result = prime * result + type.hashCode();
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
-                return false;
-            }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            ProfiledType other = (ProfiledType) obj;
-            if (Double.doubleToLongBits(probability) != Double.doubleToLongBits(other.probability)) {
-                return false;
-            }
-            return type.equals(other.type);
-        }
-
-        @Override
-        public String toString() {
-            return "{" + type.getName() + ", " + probability + "}";
-        }
-    }
+    private static final ProfiledType[] EMPTY_ARRAY = new ProfiledType[0];
 
     private final TriState nullSeen;
-    private final double notRecordedProbability;
-    private final ProfiledType[] ptypes;
 
-    /**
-     * Determines if an array of profiled types are sorted in descending order of their
-     * probabilities.
-     */
-    private static boolean isSorted(ProfiledType[] ptypes) {
-        for (int i = 1; i < ptypes.length; i++) {
-            if (ptypes[i - 1].getProbability() < ptypes[i].getProbability()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public JavaTypeProfile(TriState nullSeen, double notRecordedProbability, ProfiledType... ptypes) {
+    public JavaTypeProfile(TriState nullSeen, double notRecordedProbability, ProfiledType[] pitems) {
+        super(notRecordedProbability, pitems);
         this.nullSeen = nullSeen;
-        this.ptypes = ptypes;
-        assert notRecordedProbability != Double.NaN;
-        this.notRecordedProbability = notRecordedProbability;
-        assert isSorted(ptypes);
-    }
-
-    /**
-     * Returns the estimated probability of all types that could not be recorded due to profiling
-     * limitations.
-     * 
-     * @return double value >= 0.0 and <= 1.0
-     */
-    public double getNotRecordedProbability() {
-        return notRecordedProbability;
     }
 
     /**
@@ -165,41 +57,7 @@
      * type and a negative type is not.
      */
     public ProfiledType[] getTypes() {
-        return ptypes;
-    }
-
-    /**
-     * Searches for an entry of a given resolved Java type.
-     * 
-     * @param type the type for which an entry should be searched
-     * @return the entry or null if no entry for this type can be found
-     */
-    public ProfiledType findEntry(ResolvedJavaType type) {
-        if (ptypes != null) {
-            for (ProfiledType pt : ptypes) {
-                if (pt.getType() == type) {
-                    return pt;
-                }
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("JavaTypeProfile[");
-        builder.append(this.nullSeen);
-        builder.append(", ");
-        if (ptypes != null) {
-            for (ProfiledType pt : ptypes) {
-                builder.append(pt.toString());
-                builder.append(", ");
-            }
-        }
-        builder.append(this.notRecordedProbability);
-        builder.append("]");
-        return builder.toString();
+        return getItems();
     }
 
     public JavaTypeProfile restrict(JavaTypeProfile otherProfile) {
@@ -214,57 +72,42 @@
         }
 
         ArrayList<ProfiledType> result = new ArrayList<>();
-        for (int i = 0; i < getTypes().length; i++) {
-            ProfiledType ptype = getTypes()[i];
-            ResolvedJavaType type = ptype.getType();
+        for (int i = 0; i < getItems().length; i++) {
+            ProfiledType ptype = getItems()[i];
+            ResolvedJavaType type = ptype.getItem();
             if (otherProfile.isIncluded(type)) {
                 result.add(ptype);
             }
         }
 
-        TriState newNullSeen = (otherProfile.getNullSeen() == TriState.FALSE) ? TriState.FALSE : this.nullSeen;
-        double newNotRecorded = this.notRecordedProbability;
+        TriState newNullSeen = (otherProfile.getNullSeen() == TriState.FALSE) ? TriState.FALSE : getNullSeen();
+        double newNotRecorded = getNotRecordedProbability();
         return createAdjustedProfile(result, newNullSeen, newNotRecorded);
     }
 
-    public boolean isIncluded(ResolvedJavaType type) {
-        if (this.getNotRecordedProbability() > 0.0) {
-            return true;
-        } else {
-            for (int i = 0; i < getTypes().length; i++) {
-                ProfiledType ptype = getTypes()[i];
-                ResolvedJavaType curType = ptype.getType();
-                if (curType == type) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     public JavaTypeProfile restrict(ResolvedJavaType declaredType, boolean nonNull) {
         ArrayList<ProfiledType> result = new ArrayList<>();
-        for (int i = 0; i < getTypes().length; i++) {
-            ProfiledType ptype = getTypes()[i];
-            ResolvedJavaType type = ptype.getType();
+        for (int i = 0; i < getItems().length; i++) {
+            ProfiledType ptype = getItems()[i];
+            ResolvedJavaType type = ptype.getItem();
             if (declaredType.isAssignableFrom(type)) {
                 result.add(ptype);
             }
         }
 
-        TriState newNullSeen = (nonNull) ? TriState.FALSE : this.nullSeen;
+        TriState newNullSeen = (nonNull) ? TriState.FALSE : getNullSeen();
         double newNotRecorded = this.getNotRecordedProbability();
         // Assume for the types not recorded, the incompatibility rate is the same.
-        if (getTypes().length != 0) {
-            newNotRecorded *= ((double) result.size() / (double) getTypes().length);
+        if (getItems().length != 0) {
+            newNotRecorded *= ((double) result.size() / (double) getItems().length);
         }
         return createAdjustedProfile(result, newNullSeen, newNotRecorded);
     }
 
     private JavaTypeProfile createAdjustedProfile(ArrayList<ProfiledType> result, TriState newNullSeen, double newNotRecorded) {
-        if (result.size() != this.getTypes().length || newNotRecorded != getNotRecordedProbability() || newNullSeen != this.nullSeen) {
+        if (result.size() != this.getItems().length || newNotRecorded != getNotRecordedProbability() || newNullSeen != getNullSeen()) {
             if (result.size() == 0) {
-                return new JavaTypeProfile(newNullSeen, 1.0, ProfiledType.EMPTY_ARRAY);
+                return new JavaTypeProfile(newNullSeen, 1.0, EMPTY_ARRAY);
             }
             double probabilitySum = 0.0;
             for (int i = 0; i < result.size(); i++) {
@@ -277,7 +120,7 @@
             ProfiledType[] newResult = new ProfiledType[result.size()];
             for (int i = 0; i < newResult.length; ++i) {
                 ProfiledType curType = result.get(i);
-                newResult[i] = new ProfiledType(curType.getType(), Math.min(1.0, curType.getProbability() * factor));
+                newResult[i] = new ProfiledType(curType.getItem(), Math.min(1.0, curType.getProbability() * factor));
             }
             double newNotRecordedTypeProbability = Math.min(1.0, newNotRecorded * factor);
             return new JavaTypeProfile(newNullSeen, newNotRecordedTypeProbability, newResult);
@@ -287,34 +130,32 @@
 
     @Override
     public boolean equals(Object other) {
-        if (other == this) {
-            return true;
-        }
-        if (other instanceof JavaTypeProfile) {
-            JavaTypeProfile javaTypeProfile = (JavaTypeProfile) other;
-            if (javaTypeProfile.nullSeen != nullSeen) {
-                return false;
-            }
-            if (javaTypeProfile.notRecordedProbability != notRecordedProbability) {
-                return false;
-            }
-            if (javaTypeProfile.ptypes.length != ptypes.length) {
-                return false;
-            }
-
-            for (int i = 0; i < ptypes.length; ++i) {
-                if (!ptypes[i].equals(javaTypeProfile.ptypes[i])) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-        return false;
+        return super.equals(other) && nullSeen.equals(((JavaTypeProfile) other).nullSeen);
     }
 
     @Override
     public int hashCode() {
-        return nullSeen.hashCode() + (int) Double.doubleToLongBits(notRecordedProbability) + ptypes.length * 13;
+        return nullSeen.hashCode() + super.hashCode();
+    }
+
+    public static class ProfiledType extends AbstractProfiledItem<ResolvedJavaType> {
+
+        private static final long serialVersionUID = 1481773321889860837L;
+
+        public ProfiledType(ResolvedJavaType item, double probability) {
+            super(item, probability);
+        }
+
+        /**
+         * Returns the type for this profile entry.
+         */
+        public ResolvedJavaType getType() {
+            return getItem();
+        }
+
+        @Override
+        public String toString() {
+            return "{" + item.getName() + ", " + probability + "}";
+        }
     }
 }
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java	Tue May 21 19:51:00 2013 +0200
@@ -94,4 +94,10 @@
      *         value cannot be read.
      */
     Constant readUnsafeConstant(Kind kind, Object base, long displacement);
+
+    /**
+     * Determines if a given foreign call has a side-effect. Deoptimization cannot return execution
+     * to a point before a foreign call that has a side effect.
+     */
+    boolean hasSideEffect(ForeignCallDescriptor descriptor);
 }
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java	Tue May 21 19:51:00 2013 +0200
@@ -28,7 +28,6 @@
 import java.lang.reflect.*;
 import java.util.*;
 
-import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType;
 import com.oracle.graal.api.meta.ProfilingInfo.TriState;
 
 /**
@@ -546,21 +545,10 @@
             }
 
             JavaTypeProfile typeProfile = info.getTypeProfile(i);
-            if (typeProfile != null) {
-                ProfiledType[] ptypes = typeProfile.getTypes();
-                if (ptypes != null) {
-                    buf.append(String.format("types@%d:", i));
-                    for (int j = 0; j < ptypes.length; j++) {
-                        ProfiledType ptype = ptypes[j];
-                        buf.append(String.format(" %.6f (%s)%s", ptype.getProbability(), ptype.getType(), sep));
-                    }
-                    if (typeProfile.getNotRecordedProbability() != 0) {
-                        buf.append(String.format(" %.6f <other types>%s", typeProfile.getNotRecordedProbability(), sep));
-                    } else {
-                        buf.append(String.format(" <no other types>%s", sep));
-                    }
-                }
-            }
+            appendProfile(buf, typeProfile, i, "types", sep);
+
+            JavaMethodProfile methodProfile = info.getMethodProfile(i);
+            appendProfile(buf, methodProfile, i, "methods", sep);
         }
 
         boolean firstDeoptReason = true;
@@ -582,6 +570,24 @@
         return s.substring(0, s.length() - sep.length());
     }
 
+    private static void appendProfile(StringBuilder buf, AbstractJavaProfile profile, int bci, String type, String sep) {
+        if (profile != null) {
+            AbstractProfiledItem[] pitems = profile.getItems();
+            if (pitems != null) {
+                buf.append(String.format("%s@%d:", type, bci));
+                for (int j = 0; j < pitems.length; j++) {
+                    AbstractProfiledItem pitem = pitems[j];
+                    buf.append(String.format(" %.6f (%s)%s", pitem.getProbability(), pitem.getItem(), sep));
+                }
+                if (profile.getNotRecordedProbability() != 0) {
+                    buf.append(String.format(" %.6f <other %s>%s", profile.getNotRecordedProbability(), type, sep));
+                } else {
+                    buf.append(String.format(" <no other %s>%s", type, sep));
+                }
+            }
+        }
+    }
+
     /**
      * Converts a Java source-language class name into the internal form.
      * 
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ProfilingInfo.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ProfilingInfo.java	Tue May 21 19:51:00 2013 +0200
@@ -66,25 +66,31 @@
     /**
      * Returns the TypeProfile for the given BCI.
      * 
-     * @return Returns an JavaTypeProfile object, or null if not available.
+     * @return Returns a JavaTypeProfile object, or null if not available.
      */
     JavaTypeProfile getTypeProfile(int bci);
 
     /**
+     * Returns the MethodProfile for the given BCI.
+     * 
+     * @return Returns a JavaMethodProfile object, or null if not available.
+     */
+    JavaMethodProfile getMethodProfile(int bci);
+
+    /**
      * Returns information if the given BCI did ever throw an exception.
      * 
      * @return {@link TriState#TRUE} if the instruction has thrown an exception at least once,
-     *         {@link TriState#FALSE} if it never threw an exception, and
-     *         {@link TriState#UNKNOWN} if this information was not recorded.
+     *         {@link TriState#FALSE} if it never threw an exception, and {@link TriState#UNKNOWN}
+     *         if this information was not recorded.
      */
     TriState getExceptionSeen(int bci);
 
     /**
      * Returns information if null was ever seen for the given BCI.
      * 
-     * @return {@link TriState#TRUE} if null was seen for the instruction,
-     *         {@link TriState#FALSE} if null was NOT seen, and
-     *         {@link TriState#UNKNOWN} if this information was not recorded.
+     * @return {@link TriState#TRUE} if null was seen for the instruction, {@link TriState#FALSE} if
+     *         null was NOT seen, and {@link TriState#UNKNOWN} if this information was not recorded.
      */
     TriState getNullSeen(int bci);
 
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64MacroAssembler.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64MacroAssembler.java	Tue May 21 19:51:00 2013 +0200
@@ -26,6 +26,7 @@
 
 import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
 
 /**
  * This class implements commonly used X86 code patterns.
@@ -255,7 +256,7 @@
     private AMD64Address trigPrologue(Register value) {
         assert value.getRegisterCategory() == AMD64.XMM;
         AMD64Address tmp = new AMD64Address(AMD64.rsp);
-        subq(AMD64.rsp, 8);
+        subq(AMD64.rsp, target.arch.getSizeInBytes(Kind.Double));
         movsd(tmp, value);
         fld_d(tmp);
         return tmp;
@@ -265,7 +266,7 @@
         assert dest.getRegisterCategory() == AMD64.XMM;
         fstp_d(tmp);
         movsd(dest, tmp);
-        addq(AMD64.rsp, 8);
+        addq(AMD64.rsp, target.arch.getSizeInBytes(Kind.Double));
     }
 
     /**
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Tue May 21 19:51:00 2013 +0200
@@ -135,7 +135,7 @@
 
     @Override
     public Variable emitMove(Value input) {
-        Variable result = newVariable(input.getKind());
+        Variable result = newVariable(input.getPlatformKind());
         emitMove(result, input);
         return result;
     }
@@ -784,13 +784,12 @@
     }
 
     @Override
-    protected void emitCall(RuntimeCallTarget callTarget, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
-
-        long maxOffset = callTarget.getMaxCallTargetOffset();
+    protected void emitForeignCall(ForeignCallLinkage linkage, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
+        long maxOffset = linkage.getMaxCallTargetOffset();
         if (maxOffset != (int) maxOffset) {
-            append(new AMD64Call.DirectFarRuntimeCallOp(this, callTarget, result, arguments, temps, info));
+            append(new AMD64Call.DirectFarForeignCallOp(this, linkage, result, arguments, temps, info));
         } else {
-            append(new AMD64Call.DirectNearRuntimeCallOp(callTarget, result, arguments, temps, info));
+            append(new AMD64Call.DirectNearForeignCallOp(linkage, result, arguments, temps, info));
         }
     }
 
--- a/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Tue May 21 19:51:00 2013 +0200
@@ -29,7 +29,6 @@
 import static com.oracle.graal.lir.ptx.PTXCompare.*;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.asm.NumUtil;
 import com.oracle.graal.compiler.gen.LIRGenerator;
@@ -70,8 +69,8 @@
  */
 public class PTXLIRGenerator extends LIRGenerator {
 
-    public static final Descriptor ARITHMETIC_FREM = new Descriptor("arithmeticFrem", false, float.class, float.class, float.class);
-    public static final Descriptor ARITHMETIC_DREM = new Descriptor("arithmeticDrem", false, double.class, double.class, double.class);
+    public static final ForeignCallDescriptor ARITHMETIC_FREM = new ForeignCallDescriptor("arithmeticFrem", float.class, float.class, float.class);
+    public static final ForeignCallDescriptor ARITHMETIC_DREM = new ForeignCallDescriptor("arithmeticDrem", double.class, double.class, double.class);
 
     public static class PTXSpillMoveFactory implements LIR.SpillMoveFactory {
 
@@ -643,7 +642,7 @@
     }
 
     @Override
-    protected void emitCall(RuntimeCallTarget callTarget, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
+    protected void emitForeignCall(ForeignCallLinkage callTarget, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
         throw new InternalError("NYI");
     }
 
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue May 21 19:51:00 2013 +0200
@@ -112,7 +112,7 @@
     }
 
     @Override
-    protected void emitCall(RuntimeCallTarget callTarget, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
+    protected void emitForeignCall(ForeignCallLinkage linkage, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
         // SPARC: Auto-generated method stub
 
     }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue May 21 19:51:00 2013 +0200
@@ -426,18 +426,29 @@
                 }
                 long start = System.currentTimeMillis();
                 PhasePlan phasePlan = new PhasePlan();
-                StructuredGraph graphCopy = graph.copy();
+                final StructuredGraph graphCopy = graph.copy();
                 GraphBuilderPhase graphBuilderPhase = new GraphBuilderPhase(runtime, GraphBuilderConfiguration.getDefault(), OptimisticOptimizations.ALL);
                 phasePlan.addPhase(PhasePosition.AFTER_PARSING, graphBuilderPhase);
                 phasePlan.addPhase(PhasePosition.LOW_LEVEL, new WriteBarrierAdditionPhase());
                 editPhasePlan(method, graph, phasePlan);
                 CallingConvention cc = getCallingConvention(runtime, Type.JavaCallee, graph.method(), false);
-                CompilationResult compResult = GraalCompiler.compileGraph(graph, cc, method, runtime, replacements, backend, runtime().getTarget(), null, phasePlan, OptimisticOptimizations.ALL,
+                final CompilationResult compResult = GraalCompiler.compileGraph(graph, cc, method, runtime, replacements, backend, runtime().getTarget(), null, phasePlan, OptimisticOptimizations.ALL,
                                 new SpeculationLog());
                 if (printCompilation) {
                     TTY.println(String.format("@%-6d Graal %-70s %-45s %-50s | %4dms %5dB", id, "", "", "", System.currentTimeMillis() - start, compResult.getTargetCodeSize()));
                 }
-                return addMethod(method, compResult, graphCopy);
+                return Debug.scope("CodeInstall", new Object[]{runtime, method}, new Callable<InstalledCode>() {
+
+                    @Override
+                    public InstalledCode call() throws Exception {
+                        InstalledCode code = addMethod(method, compResult, graphCopy);
+                        if (Debug.isDumpEnabled()) {
+                            Debug.dump(new Object[]{compResult, code}, "After code installation");
+                        }
+
+                        return code;
+                    }
+                });
             }
         });
 
@@ -448,18 +459,7 @@
     }
 
     protected InstalledCode addMethod(final ResolvedJavaMethod method, final CompilationResult compResult, final StructuredGraph graph) {
-        return Debug.scope("CodeInstall", new Object[]{runtime, method}, new Callable<InstalledCode>() {
-
-            @Override
-            public InstalledCode call() throws Exception {
-                InstalledCode installedCode = runtime.addMethod(method, compResult, graph);
-                if (Debug.isDumpEnabled()) {
-                    Debug.dump(new Object[]{compResult, installedCode}, "After code installation");
-                }
-
-                return installedCode;
-            }
-        });
+        return runtime.addMethod(method, compResult, graph);
     }
 
     /**
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ProfilingInfoTest.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ProfilingInfoTest.java	Tue May 21 19:51:00 2013 +0200
@@ -105,7 +105,7 @@
     }
 
     @Test
-    public void testTypeProfileInvokeVirtual() {
+    public void testProfileInvokeVirtual() {
         testTypeProfile("invokeVirtualSnippet", 1);
     }
 
@@ -140,18 +140,18 @@
         return obj instanceof Serializable;
     }
 
-    private void testTypeProfile(String methodName, int bci) {
+    private void testTypeProfile(String testSnippet, int bci) {
         ResolvedJavaType stringType = runtime.lookupJavaType(String.class);
         ResolvedJavaType stringBuilderType = runtime.lookupJavaType(StringBuilder.class);
 
-        ProfilingInfo info = profile(methodName, "ABC");
+        ProfilingInfo info = profile(testSnippet, "ABC");
         JavaTypeProfile typeProfile = info.getTypeProfile(bci);
         Assert.assertEquals(0.0, typeProfile.getNotRecordedProbability(), DELTA);
         Assert.assertEquals(1, typeProfile.getTypes().length);
         Assert.assertEquals(stringType, typeProfile.getTypes()[0].getType());
         Assert.assertEquals(1.0, typeProfile.getTypes()[0].getProbability(), DELTA);
 
-        continueProfiling(methodName, new StringBuilder());
+        continueProfiling(testSnippet, new StringBuilder());
         typeProfile = info.getTypeProfile(bci);
         Assert.assertEquals(0.0, typeProfile.getNotRecordedProbability(), DELTA);
         Assert.assertEquals(2, typeProfile.getTypes().length);
@@ -160,7 +160,7 @@
         Assert.assertEquals(0.5, typeProfile.getTypes()[0].getProbability(), DELTA);
         Assert.assertEquals(0.5, typeProfile.getTypes()[1].getProbability(), DELTA);
 
-        resetProfile(methodName);
+        resetProfile(testSnippet);
         typeProfile = info.getTypeProfile(bci);
         Assert.assertNull(typeProfile);
     }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Tue May 21 19:51:00 2013 +0200
@@ -608,9 +608,9 @@
 
         Value result = invokeCc.getReturn();
         if (callTarget instanceof DirectCallTargetNode) {
-            emitDirectCall((DirectCallTargetNode) callTarget, result, parameters, invokeCc.getTemporaries(), callState);
+            emitDirectCall((DirectCallTargetNode) callTarget, result, parameters, AllocatableValue.NONE, callState);
         } else if (callTarget instanceof IndirectCallTargetNode) {
-            emitIndirectCall((IndirectCallTargetNode) callTarget, result, parameters, invokeCc.getTemporaries(), callState);
+            emitIndirectCall((IndirectCallTargetNode) callTarget, result, parameters, AllocatableValue.NONE, callState);
         } else {
             throw GraalInternalError.shouldNotReachHere();
         }
@@ -624,7 +624,7 @@
 
     protected abstract void emitIndirectCall(IndirectCallTargetNode callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState callState);
 
-    protected abstract void emitCall(RuntimeCallTarget callTarget, Value result, Value[] arguments, Value[] temps, LIRFrameState info);
+    protected abstract void emitForeignCall(ForeignCallLinkage linkage, Value result, Value[] arguments, Value[] temps, LIRFrameState info);
 
     protected static AllocatableValue toStackKind(AllocatableValue value) {
         if (value.getKind().getStackKind() != value.getKind()) {
@@ -659,43 +659,29 @@
     }
 
     @Override
-    public Variable emitCall(RuntimeCallTarget callTarget, CallingConvention callCc, DeoptimizingNode info, Value... args) {
+    public Variable emitForeignCall(ForeignCallLinkage linkage, DeoptimizingNode info, Value... args) {
         LIRFrameState state = info != null ? state(info) : null;
 
         // move the arguments into the correct location
-        frameMap.callsMethod(callCc);
-        assert callCc.getArgumentCount() == args.length : "argument count mismatch";
+        CallingConvention linkageCc = linkage.getCallingConvention();
+        frameMap.callsMethod(linkageCc);
+        assert linkageCc.getArgumentCount() == args.length : "argument count mismatch";
         Value[] argLocations = new Value[args.length];
         for (int i = 0; i < args.length; i++) {
             Value arg = args[i];
-            AllocatableValue loc = callCc.getArgument(i);
+            AllocatableValue loc = linkageCc.getArgument(i);
             emitMove(loc, arg);
             argLocations[i] = loc;
         }
-        emitCall(callTarget, callCc.getReturn(), argLocations, callCc.getTemporaries(), state);
+        emitForeignCall(linkage, linkageCc.getReturn(), argLocations, linkage.getTemporaries(), state);
 
-        if (isLegal(callCc.getReturn())) {
-            return emitMove(callCc.getReturn());
+        if (isLegal(linkageCc.getReturn())) {
+            return emitMove(linkageCc.getReturn());
         } else {
             return null;
         }
     }
 
-    @Override
-    public void visitRuntimeCall(RuntimeCallNode x) {
-        RuntimeCallTarget call = runtime.lookupRuntimeCall(x.getDescriptor());
-        CallingConvention callCc = call.getCallingConvention();
-        frameMap.callsMethod(callCc);
-        Value resultOperand = callCc.getReturn();
-        Value[] args = visitInvokeArguments(callCc, x.arguments());
-
-        emitCall(call, resultOperand, args, callCc.getTemporaries(), state(x));
-
-        if (isLegal(resultOperand)) {
-            setResult(x, emitMove(resultOperand));
-        }
-    }
-
     /**
      * This method tries to create a switch implementation that is optimal for the given switch. It
      * will either generate a sequential if/then/else cascade, a set of range tests or a table
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64DeoptimizeOp.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64DeoptimizeOp.java	Tue May 21 19:51:00 2013 +0200
@@ -52,6 +52,6 @@
         HotSpotGraalRuntime runtime = graalRuntime();
         Register thread = runtime.getRuntime().threadRegister();
         masm.movl(new AMD64Address(thread, runtime.getConfig().pendingDeoptimizationOffset), tasm.runtime.encodeDeoptActionAndReason(action, reason));
-        AMD64Call.directCall(tasm, masm, tasm.runtime.lookupRuntimeCall(UNCOMMON_TRAP), null, false, info);
+        AMD64Call.directCall(tasm, masm, tasm.runtime.lookupForeignCall(UNCOMMON_TRAP), null, false, info);
     }
 }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Tue May 21 19:51:00 2013 +0200
@@ -250,9 +250,9 @@
         HotSpotFrameContext frameContext = (HotSpotFrameContext) tasm.frameContext;
         if (frameContext != null && !frameContext.isStub) {
             tasm.recordMark(Marks.MARK_EXCEPTION_HANDLER_ENTRY);
-            AMD64Call.directCall(tasm, asm, runtime().lookupRuntimeCall(EXCEPTION_HANDLER), null, false, null);
+            AMD64Call.directCall(tasm, asm, runtime().lookupForeignCall(EXCEPTION_HANDLER), null, false, null);
             tasm.recordMark(Marks.MARK_DEOPT_HANDLER_ENTRY);
-            AMD64Call.directCall(tasm, asm, runtime().lookupRuntimeCall(DEOPT_HANDLER), null, false, null);
+            AMD64Call.directCall(tasm, asm, runtime().lookupForeignCall(DEOPT_HANDLER), null, false, null);
         } else {
             // No need to emit the stubs for entries back into the method since
             // it has no calls that can cause such "return" entries
@@ -261,7 +261,7 @@
 
         if (unverifiedStub != null) {
             asm.bind(unverifiedStub);
-            AMD64Call.directJmp(tasm, asm, runtime().lookupRuntimeCall(IC_MISS_HANDLER));
+            AMD64Call.directJmp(tasm, asm, runtime().lookupForeignCall(IC_MISS_HANDLER));
         }
     }
 }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotDeoptimizeCallerOp.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotDeoptimizeCallerOp.java	Tue May 21 19:51:00 2013 +0200
@@ -54,6 +54,6 @@
         HotSpotGraalRuntime runtime = graalRuntime();
         Register thread = runtime.getRuntime().threadRegister();
         masm.movl(new AMD64Address(thread, runtime.getConfig().pendingDeoptimizationOffset), tasm.runtime.encodeDeoptActionAndReason(action, reason));
-        AMD64Call.directJmp(tasm, masm, tasm.runtime.lookupRuntimeCall(UNCOMMON_TRAP));
+        AMD64Call.directJmp(tasm, masm, tasm.runtime.lookupForeignCall(UNCOMMON_TRAP));
     }
 }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Tue May 21 19:51:00 2013 +0200
@@ -186,9 +186,9 @@
     private LIRFrameState currentRuntimeCallInfo;
 
     @Override
-    protected void emitCall(RuntimeCallTarget callTarget, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
+    protected void emitForeignCall(ForeignCallLinkage linkage, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
         currentRuntimeCallInfo = info;
-        super.emitCall(callTarget, result, arguments, temps, info);
+        super.emitForeignCall(linkage, result, arguments, temps, info);
     }
 
     protected AMD64SaveRegistersOp emitSaveRegisters(Register[] savedRegisters, StackSlot[] savedRegisterLocations) {
@@ -209,39 +209,50 @@
     }
 
     @Override
-    public Variable emitCall(RuntimeCallTarget callTarget, CallingConvention callCc, DeoptimizingNode info, Value... args) {
+    public Variable emitForeignCall(ForeignCallLinkage linkage, DeoptimizingNode info, Value... args) {
         Stub stub = getStub();
-        boolean destroysRegisters = ((HotSpotRuntimeCallTarget) callTarget).destroysRegisters();
-        assert !destroysRegisters || stub != null : "foreign call that destroys registers can only be made from compiled stub, not from " + graph;
+        HotSpotForeignCallLinkage hsLinkage = (HotSpotForeignCallLinkage) linkage;
+        boolean destroysRegisters = hsLinkage.destroysRegisters();
 
         AMD64SaveRegistersOp save = null;
         StackSlot[] savedRegisterLocations = null;
         if (destroysRegisters) {
-            if (stub.preservesRegisters()) {
-                Register[] savedRegisters = frameMap.registerConfig.getAllocatableRegisters();
-                savedRegisterLocations = new StackSlot[savedRegisters.length];
-                for (int i = 0; i < savedRegisters.length; i++) {
-                    PlatformKind kind = target.arch.getLargestStorableKind(savedRegisters[i].getRegisterCategory());
-                    assert kind != Kind.Illegal;
-                    StackSlot spillSlot = frameMap.allocateSpillSlot(kind);
-                    savedRegisterLocations[i] = spillSlot;
+            if (stub != null) {
+                if (stub.preservesRegisters()) {
+                    Register[] savedRegisters = frameMap.registerConfig.getAllocatableRegisters();
+                    savedRegisterLocations = new StackSlot[savedRegisters.length];
+                    for (int i = 0; i < savedRegisters.length; i++) {
+                        PlatformKind kind = target.arch.getLargestStorableKind(savedRegisters[i].getRegisterCategory());
+                        assert kind != Kind.Illegal;
+                        StackSlot spillSlot = frameMap.allocateSpillSlot(kind);
+                        savedRegisterLocations[i] = spillSlot;
+                    }
+                    save = emitSaveRegisters(savedRegisters, savedRegisterLocations);
                 }
-                save = emitSaveRegisters(savedRegisters, savedRegisterLocations);
             }
-            append(new AMD64HotSpotCRuntimeCallPrologueOp());
         }
 
-        Variable result = super.emitCall(callTarget, callCc, info, args);
+        Variable result;
+
+        if (!hsLinkage.isLeaf()) {
+            assert info != null;
+            append(new AMD64HotSpotCRuntimeCallPrologueOp());
+            result = super.emitForeignCall(linkage, info, args);
+            append(new AMD64HotSpotCRuntimeCallEpilogueOp());
+        } else {
+            result = super.emitForeignCall(linkage, null, args);
+        }
 
         if (destroysRegisters) {
-            append(new AMD64HotSpotCRuntimeCallEpilogueOp());
-            if (stub.preservesRegisters()) {
-                assert !calleeSaveInfo.containsKey(currentRuntimeCallInfo);
-                calleeSaveInfo.put(currentRuntimeCallInfo, save);
+            if (stub != null) {
+                if (stub.preservesRegisters()) {
+                    assert !calleeSaveInfo.containsKey(currentRuntimeCallInfo);
+                    calleeSaveInfo.put(currentRuntimeCallInfo, save);
 
-                emitRestoreRegisters(save);
-            } else {
-                assert zapRegisters();
+                    emitRestoreRegisters(save);
+                } else {
+                    assert zapRegisters();
+                }
             }
         }
 
@@ -331,10 +342,10 @@
 
     @Override
     public void emitUnwind(Value exception) {
-        RuntimeCallTarget stub = getRuntime().lookupRuntimeCall(HotSpotBackend.UNWIND_EXCEPTION_TO_CALLER);
-        CallingConvention stubCc = stub.getCallingConvention();
-        assert stubCc.getArgumentCount() == 2;
-        RegisterValue exceptionParameter = (RegisterValue) stubCc.getArgument(0);
+        ForeignCallLinkage linkage = getRuntime().lookupForeignCall(HotSpotBackend.UNWIND_EXCEPTION_TO_CALLER);
+        CallingConvention linkageCc = linkage.getCallingConvention();
+        assert linkageCc.getArgumentCount() == 2;
+        RegisterValue exceptionParameter = (RegisterValue) linkageCc.getArgument(0);
         emitMove(exceptionParameter, exception);
         append(new AMD64HotSpotUnwindOp(exceptionParameter));
     }
@@ -357,11 +368,11 @@
     @Override
     public void emitJumpToExceptionHandlerInCaller(ValueNode handlerInCallerPc, ValueNode exception, ValueNode exceptionPc) {
         Variable handler = load(operand(handlerInCallerPc));
-        RuntimeCallTarget stub = getRuntime().lookupRuntimeCall(EXCEPTION_HANDLER_IN_CALLER);
-        CallingConvention stubCc = stub.getCallingConvention();
-        assert stubCc.getArgumentCount() == 2;
-        RegisterValue exceptionFixed = (RegisterValue) stubCc.getArgument(0);
-        RegisterValue exceptionPcFixed = (RegisterValue) stubCc.getArgument(1);
+        ForeignCallLinkage linkage = getRuntime().lookupForeignCall(EXCEPTION_HANDLER_IN_CALLER);
+        CallingConvention linkageCc = linkage.getCallingConvention();
+        assert linkageCc.getArgumentCount() == 2;
+        RegisterValue exceptionFixed = (RegisterValue) linkageCc.getArgument(0);
+        RegisterValue exceptionPcFixed = (RegisterValue) linkageCc.getArgument(1);
         emitMove(exceptionFixed, operand(exception));
         emitMove(exceptionPcFixed, operand(exceptionPc));
         AMD64HotSpotJumpToExceptionHandlerInCallerOp op = new AMD64HotSpotJumpToExceptionHandlerInCallerOp(handler, exceptionFixed, exceptionPcFixed);
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Tue May 21 19:51:00 2013 +0200
@@ -24,13 +24,13 @@
 
 import static com.oracle.graal.amd64.AMD64.*;
 import static com.oracle.graal.api.code.CallingConvention.Type.*;
+import static com.oracle.graal.api.meta.Value.*;
 import static com.oracle.graal.hotspot.HotSpotBackend.*;
-import static com.oracle.graal.hotspot.HotSpotRuntimeCallTarget.*;
-import static com.oracle.graal.hotspot.HotSpotRuntimeCallTarget.RegisterEffect.*;
-import static com.oracle.graal.hotspot.replacements.AESCryptSubstitutions.DecryptBlockStubCall.*;
-import static com.oracle.graal.hotspot.replacements.AESCryptSubstitutions.EncryptBlockStubCall.*;
-import static com.oracle.graal.hotspot.replacements.CipherBlockChainingSubstitutions.DecryptAESCryptStubCall.*;
-import static com.oracle.graal.hotspot.replacements.CipherBlockChainingSubstitutions.EncryptAESCryptStubCall.*;
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.*;
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.*;
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.*;
+import static com.oracle.graal.hotspot.replacements.AESCryptSubstitutions.*;
+import static com.oracle.graal.hotspot.replacements.CipherBlockChainingSubstitutions.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
@@ -59,15 +59,15 @@
         // in templateInterpreter_x86_64.cpp around line 1923
         RegisterValue exception = rax.asValue(Kind.Object);
         RegisterValue exceptionPc = rdx.asValue(word);
-        CallingConvention exceptionCc = new CallingConvention(0, Value.ILLEGAL, exception, exceptionPc);
-        register(new HotSpotRuntimeCallTarget(EXCEPTION_HANDLER, 0L, PRESERVES_REGISTERS, exceptionCc, graalRuntime.getCompilerToVM()));
-        register(new HotSpotRuntimeCallTarget(EXCEPTION_HANDLER_IN_CALLER, JUMP_ADDRESS, PRESERVES_REGISTERS, exceptionCc, graalRuntime.getCompilerToVM()));
+        CallingConvention exceptionCc = new CallingConvention(0, ILLEGAL, exception, exceptionPc);
+        register(new HotSpotForeignCallLinkage(EXCEPTION_HANDLER, 0L, PRESERVES_REGISTERS, LEAF, exceptionCc));
+        register(new HotSpotForeignCallLinkage(EXCEPTION_HANDLER_IN_CALLER, JUMP_ADDRESS, PRESERVES_REGISTERS, LEAF, exceptionCc));
 
-        // The crypto stubs do callee saving
-        registerLeafCall(ENCRYPT_BLOCK, config.aescryptEncryptBlockStub, NativeCall, PRESERVES_REGISTERS);
-        registerLeafCall(DECRYPT_BLOCK, config.aescryptDecryptBlockStub, NativeCall, PRESERVES_REGISTERS);
-        registerLeafCall(ENCRYPT, config.cipherBlockChainingEncryptAESCryptStub, NativeCall, PRESERVES_REGISTERS);
-        registerLeafCall(DECRYPT, config.cipherBlockChainingDecryptAESCryptStub, NativeCall, PRESERVES_REGISTERS);
+        // The x86 crypto stubs do callee saving
+        registerForeignCall(ENCRYPT_BLOCK, config.aescryptEncryptBlockStub, NativeCall, PRESERVES_REGISTERS, LEAF);
+        registerForeignCall(DECRYPT_BLOCK, config.aescryptDecryptBlockStub, NativeCall, PRESERVES_REGISTERS, LEAF);
+        registerForeignCall(ENCRYPT, config.cipherBlockChainingEncryptAESCryptStub, NativeCall, PRESERVES_REGISTERS, LEAF);
+        registerForeignCall(DECRYPT, config.cipherBlockChainingDecryptAESCryptStub, NativeCall, PRESERVES_REGISTERS, LEAF);
 
         convertSnippets = new AMD64ConvertSnippets.Templates(this, replacements, graalRuntime.getTarget());
         super.registerReplacements(replacements);
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotUnwindOp.java	Tue May 21 19:51:00 2013 +0200
@@ -51,8 +51,8 @@
     public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
         leaveFrameAndRestoreRbp(tasm, masm);
 
-        RuntimeCallTarget stub = tasm.runtime.lookupRuntimeCall(UNWIND_EXCEPTION_TO_CALLER);
-        CallingConvention cc = stub.getCallingConvention();
+        ForeignCallLinkage linkage = tasm.runtime.lookupForeignCall(UNWIND_EXCEPTION_TO_CALLER);
+        CallingConvention cc = linkage.getCallingConvention();
         assert cc.getArgumentCount() == 2;
         assert exception.equals(cc.getArgument(0));
 
@@ -60,6 +60,6 @@
         Register returnAddress = asRegister(cc.getArgument(1));
         masm.movq(returnAddress, new AMD64Address(rsp, 0));
 
-        AMD64Call.directJmp(tasm, masm, tasm.runtime.lookupRuntimeCall(HotSpotBackend.UNWIND_EXCEPTION_TO_CALLER));
+        AMD64Call.directJmp(tasm, masm, tasm.runtime.lookupForeignCall(HotSpotBackend.UNWIND_EXCEPTION_TO_CALLER));
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2013, 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.test;
+
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.security.*;
+
+import javax.crypto.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.nodes.*;
+
+/**
+ * Tests the intrinsification of certain crypto methods.
+ */
+public class HotSpotCryptoSubstitutionTest extends GraalCompilerTest {
+
+    @Override
+    protected InstalledCode addMethod(ResolvedJavaMethod method, CompilationResult compResult, StructuredGraph graph) {
+        HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method;
+        HotSpotNmethod installedCode = new HotSpotNmethod(hsMethod, graph, true);
+        HotSpotCompiledNmethod compiledNmethod = new HotSpotCompiledNmethod(hsMethod, StructuredGraph.INVOCATION_ENTRY_BCI, compResult);
+        CodeInstallResult result = graalRuntime().getCompilerToVM().installCode(compiledNmethod, installedCode, null);
+        Assert.assertEquals("Error installing method " + method + ": " + result, result, CodeInstallResult.OK);
+
+        // HotSpotRuntime hsRuntime = (HotSpotRuntime) runtime;
+        // TTY.println(hsMethod.toString());
+        // TTY.println(hsRuntime.disassemble(installedCode));
+        return installedCode;
+    }
+
+    @Test
+    public void testAESEncryptSubstitution() throws Exception {
+        byte[] seed = {0x4, 0x7, 0x1, 0x1};
+        SecureRandom random = new SecureRandom(seed);
+        KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES");
+        aesKeyGen.init(128, random);
+        SecretKey aesKey = aesKeyGen.generateKey();
+        byte[] input = readClassfile16(getClass());
+
+        ByteArrayOutputStream expected = new ByteArrayOutputStream();
+        expected.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding", input));
+        expected.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding", input));
+
+        if (compiledAndInstall("com.sun.crypto.provider.AESCrypt", "encryptBlock", "decryptBlock")) {
+            ByteArrayOutputStream actual = new ByteArrayOutputStream();
+            actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding", input));
+            actual.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding", input));
+            Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray());
+        }
+
+        if (compiledAndInstall("com.sun.crypto.provider.CipherBlockChaining", "encrypt", "decrypt")) {
+            ByteArrayOutputStream actual = new ByteArrayOutputStream();
+            actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding", input));
+            actual.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding", input));
+            Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray());
+        }
+    }
+
+    /**
+     * Compiles and installs the substitution for some specified methods. Once installed, the next
+     * execution of the methods will use the newly installed code.
+     * 
+     * @param className the name of the class for which substitutions are available
+     * @param methodNames the names of the substituted methods
+     * @return true if at least one substitution was compiled and installed
+     */
+    private boolean compiledAndInstall(String className, String... methodNames) {
+        boolean atLeastOneCompiled = false;
+        for (String methodName : methodNames) {
+            Method method = lookup(className, methodName);
+            if (method != null) {
+                ResolvedJavaMethod installedCodeOwner = runtime.lookupJavaMethod(method);
+                StructuredGraph graph = replacements.getMethodSubstitution(installedCodeOwner);
+                if (graph != null) {
+                    Assert.assertNotNull(getCode(installedCodeOwner, graph, true));
+                    atLeastOneCompiled = true;
+                } else {
+                    Assert.assertFalse(graalRuntime().getConfig().useAESIntrinsics);
+                }
+            }
+        }
+        return atLeastOneCompiled;
+    }
+
+    private static Method lookup(String className, String methodName) {
+        Class c;
+        try {
+            c = Class.forName(className);
+            for (Method m : c.getDeclaredMethods()) {
+                if (m.getName().equals(methodName)) {
+                    return m;
+                }
+            }
+            // If the expected security provider exists, the specific method should also exist
+            throw new NoSuchMethodError(className + "." + methodName);
+        } catch (ClassNotFoundException e) {
+            // It's ok to not find the class - a different security provider
+            // may have been installed
+            return null;
+        }
+    }
+
+    AlgorithmParameters algorithmParameters;
+
+    private byte[] encrypt(byte[] indata, SecretKey key, String algorithm) throws Exception {
+
+        byte[] result = indata;
+
+        Cipher c = Cipher.getInstance(algorithm);
+        c.init(Cipher.ENCRYPT_MODE, key);
+        algorithmParameters = c.getParameters();
+
+        byte[] r1 = c.update(result);
+        byte[] r2 = c.doFinal();
+
+        result = new byte[r1.length + r2.length];
+        System.arraycopy(r1, 0, result, 0, r1.length);
+        System.arraycopy(r2, 0, result, r1.length, r2.length);
+
+        return result;
+    }
+
+    private byte[] decrypt(byte[] indata, SecretKey key, String algorithm) throws Exception {
+
+        byte[] result = indata;
+
+        Cipher c = Cipher.getInstance(algorithm);
+        c.init(Cipher.DECRYPT_MODE, key, algorithmParameters);
+
+        byte[] r1 = c.update(result);
+        byte[] r2 = c.doFinal();
+
+        result = new byte[r1.length + r2.length];
+        System.arraycopy(r1, 0, result, 0, r1.length);
+        System.arraycopy(r2, 0, result, r1.length, r2.length);
+        return result;
+    }
+
+    private static byte[] readClassfile16(Class c) throws IOException {
+        String classFilePath = "/" + c.getName().replace('.', '/') + ".class";
+        InputStream stream = c.getResourceAsStream(classFilePath);
+        int bytesToRead = stream.available();
+        bytesToRead -= bytesToRead % 16;
+        byte[] classFile = new byte[bytesToRead];
+        new DataInputStream(stream).readFully(classFile);
+        return classFile;
+    }
+
+    public byte[] runEncryptDecrypt(SecretKey key, String algorithm, byte[] input) throws Exception {
+        byte[] indata = input.clone();
+        byte[] cipher = encrypt(indata, key, algorithm);
+        byte[] plain = decrypt(cipher, key, algorithm);
+        Assert.assertArrayEquals(indata, plain);
+        return plain;
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java	Tue May 21 19:51:00 2013 +0200
@@ -22,8 +22,8 @@
  */
 package com.oracle.graal.hotspot;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.meta.*;
@@ -39,34 +39,34 @@
     /**
      * Descriptor for SharedRuntime::deopt_blob()->uncommon_trap().
      */
-    public static final Descriptor UNCOMMON_TRAP = new Descriptor("deoptimize", true, void.class);
+    public static final ForeignCallDescriptor UNCOMMON_TRAP = new ForeignCallDescriptor("deoptimize", void.class);
 
     /**
      * Descriptor for {@link ExceptionHandlerStub}. This stub is called by the
      * {@linkplain Marks#MARK_EXCEPTION_HANDLER_ENTRY exception handler} in a compiled method.
      */
-    public static final Descriptor EXCEPTION_HANDLER = new Descriptor("exceptionHandler", true, void.class, Object.class, Word.class);
+    public static final ForeignCallDescriptor EXCEPTION_HANDLER = new ForeignCallDescriptor("exceptionHandler", void.class, Object.class, Word.class);
 
     /**
      * Descriptor for SharedRuntime::deopt_blob()->unpack().
      */
-    public static final Descriptor DEOPT_HANDLER = new Descriptor("deoptHandler", true, void.class);
+    public static final ForeignCallDescriptor DEOPT_HANDLER = new ForeignCallDescriptor("deoptHandler", void.class);
 
     /**
      * Descriptor for SharedRuntime::get_ic_miss_stub().
      */
-    public static final Descriptor IC_MISS_HANDLER = new Descriptor("icMissHandler", true, void.class);
+    public static final ForeignCallDescriptor IC_MISS_HANDLER = new ForeignCallDescriptor("icMissHandler", void.class);
 
     /**
      * Descriptor for {@link UnwindExceptionToCallerStub}. This stub is called by code generated
      * from {@link UnwindNode}.
      */
-    public static final Descriptor UNWIND_EXCEPTION_TO_CALLER = new Descriptor("unwindExceptionToCaller", true, void.class, Object.class, Word.class);
+    public static final ForeignCallDescriptor UNWIND_EXCEPTION_TO_CALLER = new ForeignCallDescriptor("unwindExceptionToCaller", void.class, Object.class, Word.class);
 
     /**
      * Descriptor for the arguments when unwinding to an exception handler in a caller.
      */
-    public static final Descriptor EXCEPTION_HANDLER_IN_CALLER = new Descriptor("exceptionHandlerInCaller", false, void.class, Object.class, Word.class);
+    public static final ForeignCallDescriptor EXCEPTION_HANDLER_IN_CALLER = new ForeignCallDescriptor("exceptionHandlerInCaller", void.class, Object.class, Word.class);
 
     public HotSpotBackend(HotSpotRuntime runtime, TargetDescription target) {
         super(runtime, target);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotCompiledRuntimeStub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotCompiledRuntimeStub.java	Tue May 21 19:51:00 2013 +0200
@@ -57,9 +57,9 @@
         for (Infopoint infopoint : compResult.getInfopoints()) {
             assert infopoint instanceof Call : this + " cannot have non-call infopoint: " + infopoint;
             Call call = (Call) infopoint;
-            assert call.target instanceof HotSpotRuntimeCallTarget : this + " cannot have non runtime call: " + call.target;
-            HotSpotRuntimeCallTarget callTarget = (HotSpotRuntimeCallTarget) call.target;
-            assert !callTarget.isCompiledStub() : this + " cannot call compiled stub " + callTarget;
+            assert call.target instanceof HotSpotForeignCallLinkage : this + " cannot have non runtime call: " + call.target;
+            HotSpotForeignCallLinkage linkage = (HotSpotForeignCallLinkage) call.target;
+            assert !linkage.isCompiledStub() : this + " cannot call compiled stub " + linkage;
         }
         return true;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotForeignCallLinkage.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,222 @@
+/*
+ * 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.hotspot;
+
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.*;
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.code.CallingConvention.Type;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.target.*;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.hotspot.stubs.*;
+import com.oracle.graal.word.*;
+
+/**
+ * The details required to link a HotSpot runtime or stub call.
+ */
+public class HotSpotForeignCallLinkage implements ForeignCallLinkage, InvokeTarget {
+
+    /**
+     * Constants for specifying whether a foreign call destroys or preserves registers. A foreign
+     * call will always destroy {@link HotSpotForeignCallLinkage#getCallingConvention() its}
+     * {@linkplain ForeignCallLinkage#getTemporaries() temporary} registers.
+     */
+    public enum RegisterEffect {
+        DESTROYS_REGISTERS, PRESERVES_REGISTERS
+    }
+
+    /**
+     * Constants for specifying whether a call is a leaf or not. A leaf function does not lock, GC
+     * or throw exceptions. That is, the thread's execution state during the call is never inspected
+     * by another thread.
+     */
+    public enum Transition {
+        LEAF, NOT_LEAF;
+    }
+
+    /**
+     * Sentinel marker for a computed jump address.
+     */
+    public static final long JUMP_ADDRESS = 0xDEADDEADBEEFBEEFL;
+
+    /**
+     * The descriptor of the call.
+     */
+    private final ForeignCallDescriptor descriptor;
+
+    /**
+     * The entry point address of this call's target.
+     */
+    private long address;
+
+    /**
+     * Non-null (eventually) iff this is a call to a compiled {@linkplain Stub stub}.
+     */
+    private Stub stub;
+
+    /**
+     * The calling convention for this call.
+     */
+    private final CallingConvention cc;
+
+    private final RegisterEffect effect;
+
+    private final Transition transition;
+
+    /**
+     * The locations defined/killed by the call.
+     */
+    private Value[] temporaries = AllocatableValue.NONE;
+
+    /**
+     * Creates a {@link HotSpotForeignCallLinkage}.
+     * 
+     * @param descriptor the descriptor of the call
+     * @param address the address of the code to call
+     * @param effect specifies if the call destroys or preserves all registers (apart from
+     *            temporaries which are always destroyed)
+     * @param ccType calling convention type
+     * @param transition specifies if this is a {@linkplain #isLeaf() leaf} call
+     */
+    public static HotSpotForeignCallLinkage create(ForeignCallDescriptor descriptor, long address, RegisterEffect effect, Type ccType, Transition transition) {
+        CallingConvention targetCc = createCallingConvention(descriptor, ccType);
+        return new HotSpotForeignCallLinkage(descriptor, address, effect, transition, targetCc);
+    }
+
+    /**
+     * Gets a calling convention for a given descriptor and call type.
+     */
+    public static CallingConvention createCallingConvention(ForeignCallDescriptor descriptor, Type ccType) {
+        HotSpotRuntime runtime = graalRuntime().getRuntime();
+        Class<?>[] argumentTypes = descriptor.getArgumentTypes();
+        JavaType[] parameterTypes = new JavaType[argumentTypes.length];
+        for (int i = 0; i < parameterTypes.length; ++i) {
+            parameterTypes[i] = asJavaType(argumentTypes[i], runtime);
+        }
+        TargetDescription target = graalRuntime().getTarget();
+        JavaType returnType = asJavaType(descriptor.getResultType(), runtime);
+        return runtime.lookupRegisterConfig().getCallingConvention(ccType, returnType, parameterTypes, target, false);
+    }
+
+    private static JavaType asJavaType(Class type, HotSpotRuntime runtime) {
+        if (WordBase.class.isAssignableFrom(type)) {
+            return runtime.lookupJavaType(wordKind().toJavaClass());
+        } else {
+            return runtime.lookupJavaType(type);
+        }
+    }
+
+    public HotSpotForeignCallLinkage(ForeignCallDescriptor descriptor, long address, RegisterEffect effect, Transition transition, CallingConvention cc) {
+        this.address = address;
+        this.effect = effect;
+        this.transition = transition;
+        this.descriptor = descriptor;
+        this.cc = cc;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(stub == null ? descriptor.toString() : stub.toString());
+        sb.append("@0x").append(Long.toHexString(address)).append(':').append(cc);
+        if (temporaries != null && temporaries.length != 0) {
+            sb.append("; temps=");
+            String sep = "";
+            for (Value op : temporaries) {
+                sb.append(sep).append(op);
+                sep = ",";
+            }
+        }
+        return sb.toString();
+    }
+
+    public CallingConvention getCallingConvention() {
+        return cc;
+    }
+
+    public Value[] getTemporaries() {
+        if (temporaries.length == 0) {
+            return temporaries;
+        }
+        return temporaries.clone();
+    }
+
+    public long getMaxCallTargetOffset() {
+        return graalRuntime().getCompilerToVM().getMaxCallTargetOffset(address);
+    }
+
+    public ForeignCallDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    public void setCompiledStub(Stub stub) {
+        assert address == 0L : "cannot set stub for linkage that already has an address: " + this;
+        this.stub = stub;
+    }
+
+    /**
+     * Determines if this is a call to a compiled {@linkplain Stub stub}.
+     */
+    public boolean isCompiledStub() {
+        return address == 0L || stub != null;
+    }
+
+    public void finalizeAddress(Backend backend) {
+        if (address == 0) {
+            assert stub != null : "linkage without an address must be a stub - forgot to register a Stub associated with " + descriptor + "?";
+            InstalledCode code = stub.getCode(backend);
+
+            Set<Register> destroyedRegisters = stub.getDestroyedRegisters();
+            if (!destroyedRegisters.isEmpty()) {
+                AllocatableValue[] temporaryLocations = new AllocatableValue[destroyedRegisters.size()];
+                int i = 0;
+                for (Register reg : destroyedRegisters) {
+                    temporaryLocations[i++] = reg.asValue();
+                }
+                temporaries = temporaryLocations;
+            }
+            address = code.getStart();
+        }
+    }
+
+    public long getAddress() {
+        assert address != 0L : "address not yet finalized: " + this;
+        return address;
+    }
+
+    @Override
+    public boolean destroysRegisters() {
+        return effect == DESTROYS_REGISTERS;
+    }
+
+    /**
+     * Determines if this is call to a function that does not lock, GC or throw exceptions. That is,
+     * the thread's execution state during the call is never inspected by another thread.
+     */
+    public boolean isLeaf() {
+        return transition == Transition.LEAF;
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot;
-
-import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
-import static com.oracle.graal.hotspot.HotSpotRuntimeCallTarget.RegisterEffect.*;
-
-import java.util.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.CallingConvention.Type;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.target.*;
-import com.oracle.graal.hotspot.bridge.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.stubs.*;
-import com.oracle.graal.word.*;
-
-/**
- * The details required to link a HotSpot runtime or stub call.
- */
-public class HotSpotRuntimeCallTarget implements RuntimeCallTarget, InvokeTarget {
-
-    /**
-     * Constants for specifying whether a call destroys or preserves registers. A call will always
-     * destroy {@link HotSpotRuntimeCallTarget#getCallingConvention() its}
-     * {@linkplain CallingConvention#getTemporaries() temporary} registers.
-     */
-    public enum RegisterEffect {
-        DESTROYS_REGISTERS, PRESERVES_REGISTERS
-    }
-
-    /**
-     * Sentinel marker for a computed jump address.
-     */
-    public static final long JUMP_ADDRESS = 0xDEADDEADBEEFBEEFL;
-
-    /**
-     * The descriptor of the call.
-     */
-    private final Descriptor descriptor;
-
-    /**
-     * The entry point address of this call's target.
-     */
-    private long address;
-
-    /**
-     * Non-null (eventually) iff this is a call to a compiled {@linkplain Stub stub}.
-     */
-    private Stub stub;
-
-    /**
-     * The calling convention for this call.
-     */
-    private CallingConvention cc;
-
-    private final CompilerToVM vm;
-
-    private final RegisterEffect effect;
-
-    /**
-     * Creates a {@link HotSpotRuntimeCallTarget}.
-     * 
-     * @param descriptor the descriptor of the call
-     * @param address the address of the code to call
-     * @param effect specifies if the call destroys or preserves all registers (apart from
-     *            temporaries which are always destroyed)
-     * @param ccType calling convention type
-     * @param ccProvider calling convention provider
-     * @param vm the Java to HotSpot C/C++ runtime interface
-     */
-    public static HotSpotRuntimeCallTarget create(Descriptor descriptor, long address, RegisterEffect effect, Type ccType, RegisterConfig ccProvider, HotSpotRuntime runtime, CompilerToVM vm) {
-        CallingConvention targetCc = createCallingConvention(descriptor, ccType, ccProvider, runtime);
-        return new HotSpotRuntimeCallTarget(descriptor, address, effect, targetCc, vm);
-    }
-
-    /**
-     * Gets a calling convention for a given descriptor and call type.
-     */
-    public static CallingConvention createCallingConvention(Descriptor descriptor, Type ccType, RegisterConfig ccProvider, HotSpotRuntime runtime) {
-        Class<?>[] argumentTypes = descriptor.getArgumentTypes();
-        JavaType[] parameterTypes = new JavaType[argumentTypes.length];
-        for (int i = 0; i < parameterTypes.length; ++i) {
-            parameterTypes[i] = asJavaType(argumentTypes[i], runtime);
-        }
-        TargetDescription target = graalRuntime().getTarget();
-        JavaType returnType = asJavaType(descriptor.getResultType(), runtime);
-        return ccProvider.getCallingConvention(ccType, returnType, parameterTypes, target, false);
-    }
-
-    private static JavaType asJavaType(Class type, HotSpotRuntime runtime) {
-        if (WordBase.class.isAssignableFrom(type)) {
-            return runtime.lookupJavaType(wordKind().toJavaClass());
-        } else {
-            return runtime.lookupJavaType(type);
-        }
-    }
-
-    public HotSpotRuntimeCallTarget(Descriptor descriptor, long address, RegisterEffect effect, CallingConvention cc, CompilerToVM vm) {
-        this.address = address;
-        this.effect = effect;
-        this.descriptor = descriptor;
-        this.cc = cc;
-        this.vm = vm;
-    }
-
-    @Override
-    public String toString() {
-        return (stub == null ? descriptor.toString() : stub) + "@0x" + Long.toHexString(address) + ":" + cc;
-    }
-
-    public CallingConvention getCallingConvention() {
-        return cc;
-    }
-
-    public long getMaxCallTargetOffset() {
-        return vm.getMaxCallTargetOffset(address);
-    }
-
-    public Descriptor getDescriptor() {
-        return descriptor;
-    }
-
-    public void setCompiledStub(Stub stub) {
-        assert address == 0L : "cannot set stub for linkage that already has an address: " + this;
-        this.stub = stub;
-    }
-
-    /**
-     * Determines if this is a call to a compiled {@linkplain Stub stub}.
-     */
-    public boolean isCompiledStub() {
-        return address == 0L || stub != null;
-    }
-
-    public void finalizeAddress(Backend backend) {
-        if (address == 0) {
-            assert stub != null : "linkage without an address must be a stub - forgot to register a Stub associated with " + descriptor + "?";
-            InstalledCode code = stub.getCode(backend);
-
-            Set<Register> destroyedRegisters = stub.getDestroyedRegisters();
-            AllocatableValue[] temporaryLocations = new AllocatableValue[destroyedRegisters.size()];
-            int i = 0;
-            for (Register reg : destroyedRegisters) {
-                temporaryLocations[i++] = reg.asValue();
-            }
-            // Update calling convention with temporaries
-            cc = new CallingConvention(temporaryLocations, cc.getStackSize(), cc.getReturn(), cc.getArguments());
-            address = code.getStart();
-        }
-    }
-
-    public long getAddress() {
-        assert address != 0L : "address not yet finalized: " + this;
-        return address;
-    }
-
-    @Override
-    public boolean destroysRegisters() {
-        return effect == DESTROYS_REGISTERS;
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Tue May 21 19:51:00 2013 +0200
@@ -226,6 +226,16 @@
     public int constMethodMaxStackOffset;
 
     /**
+     * Offset of _constants in a metaspace ConstMethod object.
+     */
+    public int constMethodConstantsOffset;
+
+    /**
+     * Offset of _pool_holder in a metaspace ConstantPool object.
+     */
+    public int constantPoolHolderOffset;
+
+    /**
      * Value of extra_stack_entries() in method.hpp.
      */
     public int extraStackEntries;
@@ -365,6 +375,7 @@
     public int dataLayoutCellSize;
     public int bciProfileWidth;
     public int typeProfileWidth;
+    public int methodProfileWidth;
 
     public long inlineCacheMissStub;
     public long handleDeoptStub;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompiler.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompiler.java	Tue May 21 19:51:00 2013 +0200
@@ -53,7 +53,7 @@
 
     void shutdownCompiler() throws Throwable;
 
-    void startCompiler() throws Throwable;
+    void startCompiler(boolean bootstrapEnabled) throws Throwable;
 
     void bootstrap() throws Throwable;
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Tue May 21 19:51:00 2013 +0200
@@ -101,7 +101,9 @@
         assert unsafe.getObject(mirror, offset) == type;
     }
 
-    public void startCompiler() throws Throwable {
+    public void startCompiler(boolean bootstrapEnabled) throws Throwable {
+
+        bootstrapRunning = bootstrapEnabled;
 
         HotSpotVMConfig config = graalRuntime.getConfig();
         long offset = config.graalMirrorInClassOffset;
@@ -339,7 +341,6 @@
         TTY.flush();
         long startTime = System.currentTimeMillis();
 
-        bootstrapRunning = true;
         boolean firstRun = true;
         do {
             // Initialize compile queue with a selected set of methods.
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java	Tue May 21 19:51:00 2013 +0200
@@ -22,12 +22,13 @@
  */
 package com.oracle.graal.hotspot.meta;
 
-import static com.oracle.graal.graph.FieldIntrospection.*;
+import static com.oracle.graal.graph.UnsafeAccess.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
 
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.meta.JavaMethodProfile.ProfiledMethod;
 import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType;
 import com.oracle.graal.api.meta.ProfilingInfo.TriState;
 import com.oracle.graal.hotspot.*;
@@ -204,6 +205,11 @@
         }
 
         @Override
+        public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) {
+            return null;
+        }
+
+        @Override
         public double getBranchTakenProbability(HotSpotMethodData data, int position) {
             return -1;
         }
@@ -332,14 +338,14 @@
 
     private abstract static class AbstractTypeData extends CounterData {
 
-        private static final int RECEIVER_TYPE_DATA_ROW_SIZE = cellsToBytes(2);
-        private static final int RECEIVER_TYPE_DATA_SIZE = cellIndexToOffset(2) + RECEIVER_TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
-        protected static final int NONPROFILED_RECEIVER_COUNT_OFFSET = cellIndexToOffset(1);
-        private static final int RECEIVER_TYPE_DATA_FIRST_RECEIVER_OFFSET = cellIndexToOffset(2);
-        private static final int RECEIVER_TYPE_DATA_FIRST_COUNT_OFFSET = cellIndexToOffset(3);
+        protected static final int TYPE_DATA_ROW_SIZE = cellsToBytes(2);
 
-        protected AbstractTypeData(int tag) {
-            super(tag, RECEIVER_TYPE_DATA_SIZE);
+        protected static final int NONPROFILED_COUNT_OFFSET = cellIndexToOffset(1);
+        protected static final int TYPE_DATA_FIRST_TYPE_OFFSET = cellIndexToOffset(2);
+        protected static final int TYPE_DATA_FIRST_TYPE_COUNT_OFFSET = cellIndexToOffset(3);
+
+        protected AbstractTypeData(int tag, int staticSize) {
+            super(tag, staticSize);
         }
 
         @Override
@@ -352,10 +358,10 @@
             int entries = 0;
 
             for (int i = 0; i < typeProfileWidth; i++) {
-                long receiverKlass = data.readWord(position, getReceiverOffset(i));
+                long receiverKlass = data.readWord(position, getTypeOffset(i));
                 if (receiverKlass != 0) {
                     types[entries] = HotSpotResolvedObjectType.fromMetaspaceKlass(receiverKlass);
-                    long count = data.readUnsignedInt(position, getCountOffset(i));
+                    long count = data.readUnsignedInt(position, getTypeCountOffset(i));
                     totalCount += count;
                     counts[entries] = count;
 
@@ -390,21 +396,22 @@
             return new JavaTypeProfile(nullSeen, notRecordedTypeProbability, ptypes);
         }
 
-        private static int getReceiverOffset(int row) {
-            return RECEIVER_TYPE_DATA_FIRST_RECEIVER_OFFSET + row * RECEIVER_TYPE_DATA_ROW_SIZE;
+        private static int getTypeOffset(int row) {
+            return TYPE_DATA_FIRST_TYPE_OFFSET + row * TYPE_DATA_ROW_SIZE;
         }
 
-        protected static int getCountOffset(int row) {
-            return RECEIVER_TYPE_DATA_FIRST_COUNT_OFFSET + row * RECEIVER_TYPE_DATA_ROW_SIZE;
+        protected static int getTypeCountOffset(int row) {
+            return TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE;
         }
     }
 
     private static class TypeCheckData extends AbstractTypeData {
 
-        private static final int RECEIVER_TYPE_DATA_TAG = 4;
+        private static final int TYPE_CHECK_DATA_TAG = 4;
+        private static final int TYPE_CHECK_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
 
         public TypeCheckData() {
-            super(RECEIVER_TYPE_DATA_TAG);
+            super(TYPE_CHECK_DATA_TAG, TYPE_CHECK_DATA_SIZE);
         }
 
         @Override
@@ -414,16 +421,19 @@
 
         @Override
         protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
-            return data.readUnsignedIntAsSignedInt(position, NONPROFILED_RECEIVER_COUNT_OFFSET);
+            return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET);
         }
     }
 
     private static class VirtualCallData extends AbstractTypeData {
 
         private static final int VIRTUAL_CALL_DATA_TAG = 5;
+        private static final int VIRTUAL_CALL_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * (config.typeProfileWidth + config.methodProfileWidth);
+        private static final int VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET = TYPE_DATA_FIRST_TYPE_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
+        private static final int VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET = TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
 
         public VirtualCallData() {
-            super(VIRTUAL_CALL_DATA_TAG);
+            super(VIRTUAL_CALL_DATA_TAG, VIRTUAL_CALL_DATA_SIZE);
         }
 
         @Override
@@ -432,7 +442,7 @@
 
             long total = 0;
             for (int i = 0; i < typeProfileWidth; i++) {
-                total += data.readUnsignedInt(position, getCountOffset(i));
+                total += data.readUnsignedInt(position, getTypeCountOffset(i));
             }
 
             total += getCounterValue(data, position);
@@ -443,6 +453,64 @@
         protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
             return getCounterValue(data, position);
         }
+
+        private static long getMethodsNotRecordedExecutionCount(HotSpotMethodData data, int position) {
+            return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET);
+        }
+
+        @Override
+        public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) {
+            int profileWidth = config.methodProfileWidth;
+
+            ResolvedJavaMethod[] methods = new ResolvedJavaMethod[profileWidth];
+            long[] counts = new long[profileWidth];
+            long totalCount = 0;
+            int entries = 0;
+
+            for (int i = 0; i < profileWidth; i++) {
+                long method = data.readWord(position, getMethodOffset(i));
+                if (method != 0) {
+                    methods[entries] = HotSpotResolvedJavaMethod.fromMetaspace(method);
+                    long count = data.readUnsignedInt(position, getMethodCountOffset(i));
+                    totalCount += count;
+                    counts[entries] = count;
+
+                    entries++;
+                }
+            }
+
+            totalCount += getMethodsNotRecordedExecutionCount(data, position);
+            return createMethodProfile(methods, counts, totalCount, entries);
+        }
+
+        private static JavaMethodProfile createMethodProfile(ResolvedJavaMethod[] methods, long[] counts, long totalCount, int entries) {
+            if (entries <= 0 || totalCount < GraalOptions.MatureExecutionsTypeProfile) {
+                return null;
+            }
+
+            ProfiledMethod[] pmethods = new ProfiledMethod[entries];
+            double totalProbability = 0.0;
+            for (int i = 0; i < entries; i++) {
+                double p = counts[i];
+                p = p / totalCount;
+                totalProbability += p;
+                pmethods[i] = new ProfiledMethod(methods[i], p);
+            }
+
+            Arrays.sort(pmethods);
+
+            double notRecordedMethodProbability = entries < config.methodProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
+            assert notRecordedMethodProbability == 0 || entries == config.methodProfileWidth;
+            return new JavaMethodProfile(notRecordedMethodProbability, pmethods);
+        }
+
+        private static int getMethodOffset(int row) {
+            return VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET + row * TYPE_DATA_ROW_SIZE;
+        }
+
+        private static int getMethodCountOffset(int row) {
+            return VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE;
+        }
     }
 
     private static class RetData extends CounterData {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodDataAccessor.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodDataAccessor.java	Tue May 21 19:51:00 2013 +0200
@@ -55,6 +55,8 @@
 
     JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position);
 
+    JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position);
+
     double getBranchTakenProbability(HotSpotMethodData data, int position);
 
     double[] getSwitchProbabilities(HotSpotMethodData data, int position);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProfilingInfo.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProfilingInfo.java	Tue May 21 19:51:00 2013 +0200
@@ -57,6 +57,12 @@
     }
 
     @Override
+    public JavaMethodProfile getMethodProfile(int bci) {
+        findBCI(bci, false);
+        return dataAccessor.getMethodProfile(methodData, position);
+    }
+
+    @Override
     public double getBranchTakenProbability(int bci) {
         findBCI(bci, false);
         return dataAccessor.getBranchTakenProbability(methodData, position);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Tue May 21 19:51:00 2013 +0200
@@ -66,6 +66,35 @@
     private CompilationTask currentTask;
     private SpeculationLog speculationLog;
 
+    /**
+     * Gets the holder of a HotSpot metaspace method native object.
+     * 
+     * @param metaspaceMethod a metaspace Method object
+     * @return the {@link ResolvedJavaType} corresponding to the holder of the
+     *         {@code metaspaceMethod}
+     */
+    public static HotSpotResolvedObjectType getHolder(long metaspaceMethod) {
+        HotSpotVMConfig config = graalRuntime().getConfig();
+        long constMethod = unsafe.getLong(metaspaceMethod + config.methodConstMethodOffset);
+        assert constMethod != 0;
+        long constantPool = unsafe.getLong(constMethod + config.constMethodConstantsOffset);
+        assert constantPool != 0;
+        long holder = unsafe.getLong(constantPool + config.constantPoolHolderOffset);
+        assert holder != 0;
+        return (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromMetaspaceKlass(holder);
+    }
+
+    /**
+     * Gets the {@link ResolvedJavaMethod} for a HotSpot metaspace method native object.
+     * 
+     * @param metaspaceMethod a metaspace Method object
+     * @return the {@link ResolvedJavaMethod} corresponding to {@code metaspaceMethod}
+     */
+    public static HotSpotResolvedJavaMethod fromMetaspace(long metaspaceMethod) {
+        HotSpotResolvedObjectType holder = getHolder(metaspaceMethod);
+        return holder.createMethod(metaspaceMethod);
+    }
+
     HotSpotResolvedJavaMethod(HotSpotResolvedObjectType holder, long metaspaceMethod) {
         this.metaspaceMethod = metaspaceMethod;
         this.holder = holder;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Tue May 21 19:51:00 2013 +0200
@@ -28,32 +28,24 @@
 import static com.oracle.graal.api.meta.DeoptimizationReason.*;
 import static com.oracle.graal.graph.UnsafeAccess.*;
 import static com.oracle.graal.hotspot.HotSpotBackend.*;
-import static com.oracle.graal.hotspot.HotSpotGraalRuntime.wordKind;
-import static com.oracle.graal.hotspot.HotSpotRuntimeCallTarget.RegisterEffect.*;
-import static com.oracle.graal.hotspot.nodes.MonitorEnterStubCall.*;
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.*;
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.*;
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
 import static com.oracle.graal.hotspot.nodes.MonitorExitStubCall.*;
 import static com.oracle.graal.hotspot.nodes.NewArrayStubCall.*;
 import static com.oracle.graal.hotspot.nodes.NewInstanceStubCall.*;
 import static com.oracle.graal.hotspot.nodes.NewMultiArrayStubCall.*;
-import static com.oracle.graal.hotspot.nodes.ThreadIsInterruptedStubCall.*;
 import static com.oracle.graal.hotspot.nodes.VMErrorNode.*;
-import static com.oracle.graal.hotspot.nodes.VerifyOopStubCall.*;
 import static com.oracle.graal.hotspot.nodes.WriteBarrierPostStubCall.*;
 import static com.oracle.graal.hotspot.nodes.WriteBarrierPreStubCall.*;
-import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
+import static com.oracle.graal.hotspot.replacements.MonitorSnippets.*;
 import static com.oracle.graal.hotspot.replacements.SystemSubstitutions.*;
+import static com.oracle.graal.hotspot.replacements.ThreadSubstitutions.*;
 import static com.oracle.graal.hotspot.stubs.ExceptionHandlerStub.*;
-import static com.oracle.graal.hotspot.stubs.LogObjectStub.*;
-import static com.oracle.graal.hotspot.stubs.LogPrimitiveStub.*;
-import static com.oracle.graal.hotspot.stubs.LogPrintfStub.*;
 import static com.oracle.graal.hotspot.stubs.NewArrayStub.*;
 import static com.oracle.graal.hotspot.stubs.NewInstanceStub.*;
-import static com.oracle.graal.hotspot.stubs.NewMultiArrayStub.*;
-import static com.oracle.graal.hotspot.stubs.OSRMigrationEndStub.*;
 import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-import static com.oracle.graal.hotspot.stubs.ThreadIsInterruptedStub.*;
 import static com.oracle.graal.hotspot.stubs.UnwindExceptionToCallerStub.*;
-import static com.oracle.graal.hotspot.stubs.VMErrorStub.*;
 import static com.oracle.graal.java.GraphBuilderPhase.RuntimeCalls.*;
 import static com.oracle.graal.nodes.java.RegisterFinalizerNode.*;
 import static com.oracle.graal.replacements.Log.*;
@@ -70,11 +62,11 @@
 import com.oracle.graal.api.code.CompilationResult.DataPatch;
 import com.oracle.graal.api.code.CompilationResult.Infopoint;
 import com.oracle.graal.api.code.CompilationResult.Mark;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.HotSpotRuntimeCallTarget.*;
+import com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect;
+import com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult;
 import com.oracle.graal.hotspot.nodes.*;
@@ -100,7 +92,9 @@
  */
 public abstract class HotSpotRuntime implements GraalCodeCacheProvider, DisassemblerProvider, BytecodeDisassemblerProvider {
 
-    public static final Descriptor OSR_MIGRATION_END = new Descriptor("OSR_migration_end", true, void.class, long.class);
+    public static final ForeignCallDescriptor OSR_MIGRATION_END = new ForeignCallDescriptor("OSR_migration_end", void.class, long.class);
+    public static final ForeignCallDescriptor IDENTITY_HASHCODE = new ForeignCallDescriptor("identity_hashcode", int.class, Object.class);
+    public static final ForeignCallDescriptor VERIFY_OOP = new ForeignCallDescriptor("verify_oop", Object.class, Object.class);
 
     public final HotSpotVMConfig config;
 
@@ -115,7 +109,7 @@
     private BoxingSnippets.Templates boxingSnippets;
     private LoadExceptionObjectSnippets.Templates exceptionObjectSnippets;
 
-    private final Map<Descriptor, HotSpotRuntimeCallTarget> runtimeCalls = new HashMap<>();
+    private final Map<ForeignCallDescriptor, HotSpotForeignCallLinkage> foreignCalls = new HashMap<>();
 
     /**
      * The offset from the origin of an array to the first element.
@@ -183,82 +177,95 @@
         regConfig = createRegisterConfig();
     }
 
-    protected HotSpotRuntimeCallTarget register(HotSpotRuntimeCallTarget call) {
-        HotSpotRuntimeCallTarget oldValue = runtimeCalls.put(call.getDescriptor(), call);
-        assert oldValue == null;
-        return call;
-    }
+    protected abstract RegisterConfig createRegisterConfig();
 
     /**
-     * Registers the details for linking a call to a {@link Stub}.
+     * Registers the linkage for a foreign call.
      */
-    protected RuntimeCallTarget registerStubCall(Descriptor descriptor) {
-        return register(HotSpotRuntimeCallTarget.create(descriptor, 0L, PRESERVES_REGISTERS, JavaCallee, regConfig, this, graalRuntime.getCompilerToVM()));
+    protected HotSpotForeignCallLinkage register(HotSpotForeignCallLinkage linkage) {
+        assert !foreignCalls.containsKey(linkage.getDescriptor()) : "already registered linkage for " + linkage.getDescriptor();
+        foreignCalls.put(linkage.getDescriptor(), linkage);
+        return linkage;
     }
 
     /**
-     * Registers the details for a call to a runtime C/C++ function.
+     * Creates and registers the details for linking a foreign call to a {@link Stub}.
      */
-    protected RuntimeCallTarget registerCRuntimeCall(Descriptor descriptor, long address) {
-        Class<?> resultType = descriptor.getResultType();
-        assert resultType.isPrimitive() || Word.class.isAssignableFrom(resultType) : "C runtime call must return object thread local storage: " + descriptor;
-        return register(HotSpotRuntimeCallTarget.create(descriptor, address, DESTROYS_REGISTERS, NativeCall, regConfig, this, graalRuntime.getCompilerToVM()));
+    protected HotSpotForeignCallLinkage registerStubCall(ForeignCallDescriptor descriptor) {
+        return register(HotSpotForeignCallLinkage.create(descriptor, 0L, PRESERVES_REGISTERS, JavaCallee, NOT_LEAF));
     }
 
     /**
-     * Registers the details for a call to a stub that never returns.
+     * Creates and registers the linkage for a foreign call.
      */
-    protected RuntimeCallTarget registerNoReturnStub(Descriptor descriptor, long address, CallingConvention.Type ccType) {
-        return register(HotSpotRuntimeCallTarget.create(descriptor, address, PRESERVES_REGISTERS, ccType, regConfig, this, graalRuntime.getCompilerToVM()));
+    protected HotSpotForeignCallLinkage registerForeignCall(ForeignCallDescriptor descriptor, long address, CallingConvention.Type ccType, RegisterEffect effect, Transition transition) {
+        Class<?> resultType = descriptor.getResultType();
+        assert resultType.isPrimitive() || Word.class.isAssignableFrom(resultType) : "foreign calls must return objects in thread local storage: " + descriptor;
+        return register(HotSpotForeignCallLinkage.create(descriptor, address, effect, ccType, transition));
+    }
+
+    private static void link(Stub stub) {
+        stub.getLinkage().setCompiledStub(stub);
     }
 
     /**
-     * Registers the details for a call to a leaf function. A leaf function does not lock, GC or
-     * throw exceptions. That is, the thread's execution state during the call is never inspected by
-     * another thread.
+     * Creates a {@linkplain ForeignCallStub stub} for a non-leaf foreign call.
+     * 
+     * @param descriptor the signature of the call to this stub
+     * @param address the address of the code to call
+     * @param prependThread true if the JavaThread value for the current thread is to be prepended
+     *            to the arguments for the call to {@code address}
      */
-    protected RuntimeCallTarget registerLeafCall(Descriptor descriptor, long address, CallingConvention.Type ccType, RegisterEffect effect) {
-        return register(HotSpotRuntimeCallTarget.create(descriptor, address, effect, ccType, regConfig, this, graalRuntime.getCompilerToVM()));
+    private void linkForeignCall(ForeignCallDescriptor descriptor, long address, boolean prependThread, Replacements replacements) {
+        ForeignCallStub stub = new ForeignCallStub(address, descriptor, prependThread, this, replacements);
+        HotSpotForeignCallLinkage linkage = stub.getLinkage();
+        HotSpotForeignCallLinkage targetLinkage = stub.getTargetLinkage();
+        linkage.setCompiledStub(stub);
+        register(linkage);
+        register(targetLinkage);
     }
 
-    protected abstract RegisterConfig createRegisterConfig();
-
     public void registerReplacements(Replacements replacements) {
-        registerStubCall(VERIFY_OOP);
-        registerStubCall(OSR_MIGRATION_END);
-        registerStubCall(NEW_ARRAY);
-        registerStubCall(UNWIND_EXCEPTION_TO_CALLER);
-        registerStubCall(NEW_INSTANCE);
-        registerStubCall(NEW_MULTI_ARRAY);
-        registerStubCall(LOG_PRIMITIVE);
-        registerStubCall(LOG_PRINTF);
-        registerStubCall(LOG_OBJECT);
-        registerStubCall(THREAD_IS_INTERRUPTED);
-        registerStubCall(VM_ERROR);
+        HotSpotVMConfig c = config;
+        TargetDescription target = getTarget();
+
+        registerForeignCall(UNCOMMON_TRAP, c.uncommonTrapStub, NativeCall, PRESERVES_REGISTERS, LEAF);
+        registerForeignCall(DEOPT_HANDLER, c.handleDeoptStub, NativeCall, PRESERVES_REGISTERS, LEAF);
+        registerForeignCall(IC_MISS_HANDLER, c.inlineCacheMissStub, NativeCall, PRESERVES_REGISTERS, LEAF);
 
-        HotSpotVMConfig c = config;
-        registerNoReturnStub(UNCOMMON_TRAP, c.uncommonTrapStub, NativeCall);
-        registerNoReturnStub(DEOPT_HANDLER, c.handleDeoptStub, NativeCall);
-        registerNoReturnStub(IC_MISS_HANDLER, c.inlineCacheMissStub, NativeCall);
+        registerForeignCall(JAVA_TIME_MILLIS, c.javaTimeMillisAddress, NativeCall, DESTROYS_REGISTERS, LEAF);
+        registerForeignCall(JAVA_TIME_NANOS, c.javaTimeNanosAddress, NativeCall, DESTROYS_REGISTERS, LEAF);
+        registerForeignCall(ARITHMETIC_SIN, c.arithmeticSinAddress, NativeCall, DESTROYS_REGISTERS, LEAF);
+        registerForeignCall(ARITHMETIC_COS, c.arithmeticCosAddress, NativeCall, DESTROYS_REGISTERS, LEAF);
+        registerForeignCall(ARITHMETIC_TAN, c.arithmeticTanAddress, NativeCall, DESTROYS_REGISTERS, LEAF);
+
+        registerForeignCall(EXCEPTION_HANDLER_FOR_PC, c.exceptionHandlerForPcAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF);
+        registerForeignCall(EXCEPTION_HANDLER_FOR_RETURN_ADDRESS, c.exceptionHandlerForReturnAddressAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF);
+        registerForeignCall(NEW_ARRAY_C, c.newArrayAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF);
+        registerForeignCall(NEW_INSTANCE_C, c.newInstanceAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF);
+        registerForeignCall(VM_MESSAGE_C, c.vmMessageAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF);
 
-        registerLeafCall(JAVA_TIME_MILLIS, c.javaTimeMillisAddress, NativeCall, DESTROYS_REGISTERS);
-        registerLeafCall(JAVA_TIME_NANOS, c.javaTimeNanosAddress, NativeCall, DESTROYS_REGISTERS);
-        registerLeafCall(ARITHMETIC_SIN, c.arithmeticSinAddress, NativeCall, DESTROYS_REGISTERS);
-        registerLeafCall(ARITHMETIC_COS, c.arithmeticCosAddress, NativeCall, DESTROYS_REGISTERS);
-        registerLeafCall(ARITHMETIC_TAN, c.arithmeticTanAddress, NativeCall, DESTROYS_REGISTERS);
+        link(new NewInstanceStub(this, replacements, target, registerStubCall(NEW_INSTANCE)));
+        link(new NewArrayStub(this, replacements, target, registerStubCall(NEW_ARRAY)));
+        link(new ExceptionHandlerStub(this, replacements, target, foreignCalls.get(EXCEPTION_HANDLER)));
+        link(new UnwindExceptionToCallerStub(this, replacements, target, registerStubCall(UNWIND_EXCEPTION_TO_CALLER)));
+        link(new VerifyOopStub(this, replacements, target, registerStubCall(VERIFY_OOP)));
 
-        registerCRuntimeCall(OSR_MIGRATION_END_C, c.osrMigrationEndAddress);
-        registerCRuntimeCall(EXCEPTION_HANDLER_FOR_PC, c.exceptionHandlerForPcAddress);
-        registerCRuntimeCall(EXCEPTION_HANDLER_FOR_RETURN_ADDRESS, c.exceptionHandlerForReturnAddressAddress);
-        registerCRuntimeCall(NEW_ARRAY_C, c.newArrayAddress);
-        registerCRuntimeCall(NEW_INSTANCE_C, c.newInstanceAddress);
-        registerCRuntimeCall(NEW_MULTI_ARRAY_C, c.newMultiArrayAddress);
-        registerCRuntimeCall(LOG_PRIMITIVE_C, c.logPrimitiveAddress);
-        registerCRuntimeCall(LOG_PRINTF_C, c.logObjectAddress);
-        registerCRuntimeCall(VM_MESSAGE_C, c.vmMessageAddress);
-        registerCRuntimeCall(LOG_OBJECT_C, c.logObjectAddress);
-        registerCRuntimeCall(THREAD_IS_INTERRUPTED_C, c.threadIsInterruptedAddress);
-        registerCRuntimeCall(VM_ERROR_C, c.vmErrorAddress);
+        linkForeignCall(IDENTITY_HASHCODE, c.identityHashCodeAddress, true, replacements);
+        linkForeignCall(REGISTER_FINALIZER, c.registerFinalizerAddress, true, replacements);
+        linkForeignCall(CREATE_NULL_POINTER_EXCEPTION, c.createNullPointerExceptionAddress, true, replacements);
+        linkForeignCall(CREATE_OUT_OF_BOUNDS_EXCEPTION, c.createOutOfBoundsExceptionAddress, true, replacements);
+        linkForeignCall(MONITORENTER, c.monitorenterAddress, true, replacements);
+        linkForeignCall(MONITOREXIT, c.monitorexitAddress, true, replacements);
+        linkForeignCall(WRITE_BARRIER_PRE, c.writeBarrierPreAddress, true, replacements);
+        linkForeignCall(WRITE_BARRIER_POST, c.writeBarrierPostAddress, true, replacements);
+        linkForeignCall(NEW_MULTI_ARRAY, c.newMultiArrayAddress, true, replacements);
+        linkForeignCall(LOG_PRINTF, c.logPrintfAddress, true, replacements);
+        linkForeignCall(LOG_OBJECT, c.logObjectAddress, true, replacements);
+        linkForeignCall(LOG_PRIMITIVE, c.logPrimitiveAddress, true, replacements);
+        linkForeignCall(THREAD_IS_INTERRUPTED, c.threadIsInterruptedAddress, true, replacements);
+        linkForeignCall(VM_ERROR, c.vmErrorAddress, true, replacements);
+        linkForeignCall(OSR_MIGRATION_END, c.osrMigrationEndAddress, false, replacements);
 
         if (GraalOptions.IntrinsifyObjectMethods) {
             replacements.registerSubstitutions(ObjectSubstitutions.class);
@@ -286,46 +293,10 @@
         checkcastSnippets = new CheckCastSnippets.Templates(this, replacements, graalRuntime.getTarget());
         instanceofSnippets = new InstanceOfSnippets.Templates(this, replacements, graalRuntime.getTarget());
         newObjectSnippets = new NewObjectSnippets.Templates(this, replacements, graalRuntime.getTarget());
-        monitorSnippets = new MonitorSnippets.Templates(this, replacements, graalRuntime.getTarget(), config.useFastLocking);
+        monitorSnippets = new MonitorSnippets.Templates(this, replacements, graalRuntime.getTarget(), c.useFastLocking);
         writeBarrierSnippets = new WriteBarrierSnippets.Templates(this, replacements, graalRuntime.getTarget());
         boxingSnippets = new BoxingSnippets.Templates(this, replacements, graalRuntime.getTarget());
         exceptionObjectSnippets = new LoadExceptionObjectSnippets.Templates(this, replacements, graalRuntime.getTarget());
-
-        TargetDescription target = getTarget();
-        link(new NewInstanceStub(this, replacements, target, runtimeCalls.get(NEW_INSTANCE)));
-        link(new NewArrayStub(this, replacements, target, runtimeCalls.get(NEW_ARRAY)));
-        link(new NewMultiArrayStub(this, replacements, target, runtimeCalls.get(NEW_MULTI_ARRAY)));
-        link(new ThreadIsInterruptedStub(this, replacements, target, runtimeCalls.get(THREAD_IS_INTERRUPTED)));
-        link(new ExceptionHandlerStub(this, replacements, target, runtimeCalls.get(EXCEPTION_HANDLER)));
-        link(new UnwindExceptionToCallerStub(this, replacements, target, runtimeCalls.get(UNWIND_EXCEPTION_TO_CALLER)));
-        link(new VerifyOopStub(this, replacements, target, runtimeCalls.get(VERIFY_OOP)));
-        link(new OSRMigrationEndStub(this, replacements, target, runtimeCalls.get(OSR_MIGRATION_END)));
-        link(new LogPrimitiveStub(this, replacements, target, runtimeCalls.get(LOG_PRIMITIVE)));
-        link(new LogObjectStub(this, replacements, target, runtimeCalls.get(LOG_OBJECT)));
-        link(new LogPrintfStub(this, replacements, target, runtimeCalls.get(LOG_PRINTF)));
-        link(new VMErrorStub(this, replacements, target, runtimeCalls.get(VM_ERROR)));
-
-        linkRuntimeCall(IDENTITY_HASHCODE, config.identityHashCodeAddress, replacements);
-        linkRuntimeCall(REGISTER_FINALIZER, config.registerFinalizerAddress, replacements);
-        linkRuntimeCall(CREATE_NULL_POINTER_EXCEPTION, config.createNullPointerExceptionAddress, replacements);
-        linkRuntimeCall(CREATE_OUT_OF_BOUNDS_EXCEPTION, config.createOutOfBoundsExceptionAddress, replacements);
-        linkRuntimeCall(MONITORENTER, config.monitorenterAddress, replacements);
-        linkRuntimeCall(MONITOREXIT, config.monitorexitAddress, replacements);
-        linkRuntimeCall(WRITE_BARRIER_PRE, config.writeBarrierPreAddress, replacements);
-        linkRuntimeCall(WRITE_BARRIER_POST, config.writeBarrierPostAddress, replacements);
-    }
-
-    private static void link(Stub stub) {
-        stub.getLinkage().setCompiledStub(stub);
-    }
-
-    private void linkRuntimeCall(Descriptor descriptor, long address, Replacements replacements) {
-        RuntimeCallStub stub = new RuntimeCallStub(address, descriptor, true, this, replacements, regConfig, graalRuntime.getCompilerToVM());
-        HotSpotRuntimeCallTarget linkage = stub.getLinkage();
-        HotSpotRuntimeCallTarget targetLinkage = stub.getTargetLinkage();
-        linkage.setCompiledStub(stub);
-        runtimeCalls.put(linkage.getDescriptor(), linkage);
-        runtimeCalls.put(targetLinkage.getDescriptor(), targetLinkage);
     }
 
     public HotSpotGraalRuntime getGraalRuntime() {
@@ -712,8 +683,8 @@
             OSRStartNode osrStart = (OSRStartNode) n;
             StartNode newStart = graph.add(new StartNode());
             LocalNode buffer = graph.unique(new LocalNode(0, StampFactory.forKind(wordKind())));
-            RuntimeCallNode migrationEnd = graph.add(new RuntimeCallNode(OSR_MIGRATION_END, buffer));
-            migrationEnd.setStateAfter(osrStart.stateAfter());
+            ForeignCallNode migrationEnd = graph.add(new ForeignCallNode(OSR_MIGRATION_END, buffer));
+            newStart.setStateAfter(osrStart.stateAfter());
 
             newStart.setNext(migrationEnd);
             FixedNode next = osrStart.next();
@@ -854,9 +825,9 @@
         return HotSpotResolvedObjectType.fromClass(clazz);
     }
 
-    public HotSpotRuntimeCallTarget lookupRuntimeCall(Descriptor descriptor) {
-        HotSpotRuntimeCallTarget callTarget = runtimeCalls.get(descriptor);
-        assert runtimeCalls != null : descriptor;
+    public HotSpotForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor) {
+        HotSpotForeignCallLinkage callTarget = foreignCalls.get(descriptor);
+        assert foreignCalls != null : descriptor;
         callTarget.finalizeAddress(graalRuntime.getBackend());
         return callTarget;
     }
@@ -996,6 +967,15 @@
     }
 
     @Override
+    public boolean hasSideEffect(ForeignCallDescriptor descriptor) {
+        // Only these two foreign calls are expected to be made with
+        // a node that implements StateSplit. They need to be a state
+        // split so that the stack trace they produce is accurate.
+        assert descriptor == CREATE_NULL_POINTER_EXCEPTION || descriptor == CREATE_OUT_OF_BOUNDS_EXCEPTION : descriptor;
+        return false;
+    }
+
+    @Override
     public TargetDescription getTarget() {
         return graalRuntime.getTarget();
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CRuntimeCall.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.nodes;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.lir.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * Implements a direct call to a C/C++ HotSpot function.
- */
-public class CRuntimeCall extends DeoptimizingFixedWithNextNode implements LIRGenLowerable {
-
-    @Input private final NodeInputList<ValueNode> arguments;
-
-    private final Descriptor descriptor;
-
-    public CRuntimeCall(Descriptor descriptor, ValueNode... arguments) {
-        super(StampFactory.forKind(Kind.fromJavaClass(descriptor.getResultType())));
-        this.arguments = new NodeInputList<>(this, arguments);
-        this.descriptor = descriptor;
-    }
-
-    @Override
-    public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(descriptor);
-        Value[] args = new Value[arguments.size()];
-        for (int i = 0; i < args.length; i++) {
-            args[i] = gen.operand(arguments.get(i));
-        }
-        Variable result = gen.emitCall(stub, stub.getCallingConvention(), this, args);
-        if (result != null) {
-            gen.setResult(this, result);
-        }
-    }
-
-    @Override
-    public boolean canDeoptimize() {
-        return true;
-    }
-
-    @Override
-    public DeoptimizationReason getDeoptimizationReason() {
-        return null;
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorEnterStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.nodes;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.word.*;
-
-/**
- * Node implementing a call to {@code GraalRuntime::monitorenter}.
- */
-public class MonitorEnterStubCall extends DeoptimizingStubCall implements LIRGenLowerable {
-
-    @Input private ValueNode object;
-    @Input private ValueNode lock;
-    public static final Descriptor MONITORENTER = new Descriptor("monitorenter", true, void.class, Object.class, Word.class);
-
-    public MonitorEnterStubCall(ValueNode object, ValueNode lock) {
-        super(StampFactory.forVoid());
-        this.object = object;
-        this.lock = lock;
-    }
-
-    @Override
-    public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(MONITORENTER);
-        gen.emitCall(stub, stub.getCallingConvention(), this, gen.operand(object), gen.operand(lock));
-    }
-
-    @NodeIntrinsic
-    public static native void call(Object object, Word lock);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorExitStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorExitStubCall.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,7 @@
 package com.oracle.graal.hotspot.nodes;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.hotspot.*;
@@ -38,7 +38,7 @@
 
     @Input private ValueNode object;
     private int lockDepth;
-    public static final Descriptor MONITOREXIT = new Descriptor("monitorexit", true, void.class, Object.class, Word.class);
+    public static final ForeignCallDescriptor MONITOREXIT = new ForeignCallDescriptor("monitorexit", void.class, Object.class, Word.class);
 
     public MonitorExitStubCall(ValueNode object, int lockDepth) {
         super(StampFactory.forVoid());
@@ -51,8 +51,8 @@
         assert lockDepth != -1;
         HotSpotLIRGenerator hsGen = (HotSpotLIRGenerator) gen;
         StackSlot slot = hsGen.getLockSlot(lockDepth);
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(MonitorExitStubCall.MONITOREXIT);
-        gen.emitCall(stub, stub.getCallingConvention(), this, gen.operand(object), gen.emitAddress(slot));
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(MonitorExitStubCall.MONITOREXIT);
+        gen.emitForeignCall(linkage, this, gen.operand(object), gen.emitAddress(slot));
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/NewArrayStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/NewArrayStubCall.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,7 @@
 package com.oracle.graal.hotspot.nodes;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.hotspot.meta.*;
@@ -43,7 +43,7 @@
     @Input private ValueNode hub;
     @Input private ValueNode length;
 
-    public static final Descriptor NEW_ARRAY = new Descriptor("new_array", false, Object.class, Word.class, int.class);
+    public static final ForeignCallDescriptor NEW_ARRAY = new ForeignCallDescriptor("new_array", Object.class, Word.class, int.class);
 
     public NewArrayStubCall(ValueNode hub, ValueNode length) {
         super(defaultStamp);
@@ -62,8 +62,8 @@
 
     @Override
     public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(NEW_ARRAY);
-        Variable result = gen.emitCall(stub, stub.getCallingConvention(), this, gen.operand(hub), gen.operand(length));
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(NEW_ARRAY);
+        Variable result = gen.emitForeignCall(linkage, this, gen.operand(hub), gen.operand(length));
         gen.setResult(this, result);
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/NewInstanceStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/NewInstanceStubCall.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,7 @@
 package com.oracle.graal.hotspot.nodes;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.hotspot.meta.*;
@@ -42,7 +42,7 @@
 
     @Input private ValueNode hub;
 
-    public static final Descriptor NEW_INSTANCE = new Descriptor("new_instance", false, Object.class, Word.class);
+    public static final ForeignCallDescriptor NEW_INSTANCE = new ForeignCallDescriptor("new_instance", Object.class, Word.class);
 
     public NewInstanceStubCall(ValueNode hub) {
         super(defaultStamp);
@@ -60,8 +60,8 @@
 
     @Override
     public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(NEW_INSTANCE);
-        Variable result = gen.emitCall(stub, stub.getCallingConvention(), this, gen.operand(hub));
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(NEW_INSTANCE);
+        Variable result = gen.emitForeignCall(linkage, this, gen.operand(hub));
         gen.setResult(this, result);
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/NewMultiArrayStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/NewMultiArrayStubCall.java	Tue May 21 19:51:00 2013 +0200
@@ -22,22 +22,18 @@
  */
 package com.oracle.graal.hotspot.nodes;
 
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.stubs.*;
-import com.oracle.graal.lir.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.word.*;
 
 /**
- * Node implementing a call to {@link NewMultiArrayStub}.
+ * Node implementing a call to {@code GraalRuntime::new_multi_array}.
  */
-public class NewMultiArrayStubCall extends DeoptimizingStubCall implements LIRGenLowerable {
+public class NewMultiArrayStubCall extends ForeignCallNode {
 
     private static final Stamp defaultStamp = StampFactory.objectNonNull();
 
@@ -45,10 +41,10 @@
     @Input private ValueNode dims;
     private final int rank;
 
-    public static final Descriptor NEW_MULTI_ARRAY = new Descriptor("new_multi_array", false, Object.class, Word.class, int.class, Word.class);
+    public static final ForeignCallDescriptor NEW_MULTI_ARRAY = new ForeignCallDescriptor("new_multi_array", Object.class, Word.class, int.class, Word.class);
 
     public NewMultiArrayStubCall(ValueNode hub, int rank, ValueNode dims) {
-        super(defaultStamp);
+        super(NEW_MULTI_ARRAY, defaultStamp);
         this.hub = hub;
         this.rank = rank;
         this.dims = dims;
@@ -64,10 +60,8 @@
     }
 
     @Override
-    public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(NewMultiArrayStubCall.NEW_MULTI_ARRAY);
-        Variable result = gen.emitCall(stub, stub.getCallingConvention(), this, gen.operand(hub), Constant.forInt(rank), gen.operand(dims));
-        gen.setResult(this, result);
+    protected Value[] operands(LIRGeneratorTool gen) {
+        return new Value[]{gen.operand(hub), Constant.forInt(rank), gen.operand(dims)};
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ThreadIsInterruptedStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.nodes;
-
-import java.lang.reflect.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.hotspot.stubs.*;
-import com.oracle.graal.lir.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * Node implementing a call to {@link ThreadIsInterruptedStub}.
- */
-public class ThreadIsInterruptedStubCall extends DeoptimizingStubCall implements LIRGenLowerable {
-
-    @Input private ValueNode thread;
-    @Input private ValueNode clearIsInterrupted;
-    public static final Descriptor THREAD_IS_INTERRUPTED = new Descriptor("thread_is_interrupted", false, boolean.class, Object.class, boolean.class);
-
-    public ThreadIsInterruptedStubCall(ValueNode thread, ValueNode clearIsInterrupted) {
-        super(StampFactory.forInteger(Kind.Int, 0, 1));
-        this.thread = thread;
-        this.clearIsInterrupted = clearIsInterrupted;
-    }
-
-    @Override
-    public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(ThreadIsInterruptedStubCall.THREAD_IS_INTERRUPTED);
-        Variable result = gen.emitCall(stub, stub.getCallingConvention(), this, gen.operand(thread), gen.operand(clearIsInterrupted));
-        gen.setResult(this, result);
-    }
-
-    @NodeIntrinsic
-    public static boolean call(Thread thread, boolean clearIsInterrupted) {
-        try {
-            Method isInterrupted = Thread.class.getDeclaredMethod("isInterrupted", boolean.class);
-            isInterrupted.setAccessible(true);
-            return (Boolean) isInterrupted.invoke(thread, clearIsInterrupted);
-        } catch (Exception e) {
-            throw new GraalInternalError(e);
-        }
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/VMErrorNode.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/VMErrorNode.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.hotspot.nodes;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.target.*;
@@ -39,7 +38,7 @@
 
     private final String format;
     @Input private ValueNode value;
-    public static final Descriptor VM_ERROR = new Descriptor("vm_error", false, void.class, Object.class, Object.class, long.class);
+    public static final ForeignCallDescriptor VM_ERROR = new ForeignCallDescriptor("vm_error", void.class, Object.class, Object.class, long.class);
 
     private VMErrorNode(String format, ValueNode value) {
         super(StampFactory.forVoid());
@@ -58,8 +57,8 @@
         Constant whereArg = Constant.forObject(whereString.intern());
         Constant formatArg = Constant.forObject(format.intern());
 
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(VMErrorNode.VM_ERROR);
-        gen.emitCall(stub, stub.getCallingConvention(), null, whereArg, formatArg, gen.operand(value));
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(VMErrorNode.VM_ERROR);
+        gen.emitForeignCall(linkage, null, whereArg, formatArg, gen.operand(value));
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/VerifyOopStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.nodes;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
-import com.oracle.graal.hotspot.stubs.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * Call to {@link VerifyOopStub}.
- */
-public class VerifyOopStubCall extends DeoptimizingStubCall implements LIRGenLowerable {
-
-    @Input private ValueNode object;
-    public static final Descriptor VERIFY_OOP = new Descriptor("verify_oop", false, Object.class, Object.class);
-
-    public VerifyOopStubCall(ValueNode object) {
-        super(StampFactory.objectNonNull());
-        this.object = object;
-    }
-
-    @Override
-    public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(VerifyOopStubCall.VERIFY_OOP);
-        gen.emitCall(stub, stub.getCallingConvention(), this, gen.operand(object));
-    }
-
-    @NodeIntrinsic
-    public static native Object call(Object object);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrierPostStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrierPostStubCall.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,7 @@
 package com.oracle.graal.hotspot.nodes;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.nodes.*;
@@ -37,7 +37,7 @@
 
     @Input private ValueNode object;
     @Input private ValueNode card;
-    public static final Descriptor WRITE_BARRIER_POST = new Descriptor("writeBarrierPost", true, void.class, Object.class, Word.class);
+    public static final ForeignCallDescriptor WRITE_BARRIER_POST = new ForeignCallDescriptor("writeBarrierPost", void.class, Object.class, Word.class);
 
     public WriteBarrierPostStubCall(ValueNode object, ValueNode card) {
         super(StampFactory.forVoid());
@@ -47,8 +47,8 @@
 
     @Override
     public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(WriteBarrierPostStubCall.WRITE_BARRIER_POST);
-        gen.emitCall(stub, stub.getCallingConvention(), null, gen.operand(object), gen.operand(card));
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(WriteBarrierPostStubCall.WRITE_BARRIER_POST);
+        gen.emitForeignCall(linkage, null, gen.operand(object), gen.operand(card));
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrierPreStubCall.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrierPreStubCall.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,7 @@
 package com.oracle.graal.hotspot.nodes;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.nodes.*;
@@ -35,7 +35,7 @@
 public class WriteBarrierPreStubCall extends FixedWithNextNode implements LIRGenLowerable {
 
     @Input private ValueNode object;
-    public static final Descriptor WRITE_BARRIER_PRE = new Descriptor("writeBarrierPre", true, void.class, Object.class);
+    public static final ForeignCallDescriptor WRITE_BARRIER_PRE = new ForeignCallDescriptor("writeBarrierPre", void.class, Object.class);
 
     public WriteBarrierPreStubCall(ValueNode object) {
         super(StampFactory.forVoid());
@@ -44,8 +44,8 @@
 
     @Override
     public void generate(LIRGenerator gen) {
-        RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(WriteBarrierPreStubCall.WRITE_BARRIER_PRE);
-        gen.emitCall(stub, stub.getCallingConvention(), null, gen.operand(object));
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(WriteBarrierPreStubCall.WRITE_BARRIER_PRE);
+        gen.emitForeignCall(linkage, null, gen.operand(object));
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AESCryptSubstitutions.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AESCryptSubstitutions.java	Tue May 21 19:51:00 2013 +0200
@@ -25,16 +25,13 @@
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 import sun.misc.*;
 
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node.ConstantNodeParameter;
+import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.word.*;
 
 /**
@@ -73,56 +70,18 @@
         Word inAddr = Word.unsigned(GetObjectAddressNode.get(in) + arrayBaseOffset(Kind.Byte) + inOffset);
         Word outAddr = Word.unsigned(GetObjectAddressNode.get(out) + arrayBaseOffset(Kind.Byte) + outOffset);
         if (encrypt) {
-            EncryptBlockStubCall.call(inAddr, outAddr, kAddr);
+            encryptBlockStub(ENCRYPT_BLOCK, inAddr, outAddr, kAddr);
         } else {
-            DecryptBlockStubCall.call(inAddr, outAddr, kAddr);
+            decryptBlockStub(DECRYPT_BLOCK, inAddr, outAddr, kAddr);
         }
     }
 
-    abstract static class CryptBlockStubCall extends DeoptimizingStubCall implements LIRGenLowerable {
-
-        @Input private ValueNode in;
-        @Input private ValueNode out;
-        @Input private ValueNode key;
-
-        private final Descriptor descriptor;
-
-        public CryptBlockStubCall(ValueNode in, ValueNode out, ValueNode key, Descriptor descriptor) {
-            super(StampFactory.forVoid());
-            this.in = in;
-            this.out = out;
-            this.key = key;
-            this.descriptor = descriptor;
-        }
-
-        @Override
-        public void generate(LIRGenerator gen) {
-            RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(descriptor);
-            gen.emitCall(stub, stub.getCallingConvention(), null, gen.operand(in), gen.operand(out), gen.operand(key));
-        }
-    }
+    public static final ForeignCallDescriptor ENCRYPT_BLOCK = new ForeignCallDescriptor("encrypt_block", void.class, Word.class, Word.class, Word.class);
+    public static final ForeignCallDescriptor DECRYPT_BLOCK = new ForeignCallDescriptor("decrypt_block", void.class, Word.class, Word.class, Word.class);
 
-    public static class EncryptBlockStubCall extends CryptBlockStubCall {
-
-        public static final Descriptor ENCRYPT_BLOCK = new Descriptor("encrypt_block", false, void.class, Word.class, Word.class, Word.class);
-
-        public EncryptBlockStubCall(ValueNode in, ValueNode out, ValueNode key) {
-            super(in, out, key, ENCRYPT_BLOCK);
-        }
-
-        @NodeIntrinsic
-        public static native void call(Word in, Word out, Word key);
-    }
+    @NodeIntrinsic(ForeignCallNode.class)
+    public static native void encryptBlockStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Word in, Word out, Word key);
 
-    public static class DecryptBlockStubCall extends CryptBlockStubCall {
-
-        public static final Descriptor DECRYPT_BLOCK = new Descriptor("decrypt_block", false, void.class, Word.class, Word.class, Word.class);
-
-        public DecryptBlockStubCall(ValueNode in, ValueNode out, ValueNode key) {
-            super(in, out, key, DECRYPT_BLOCK);
-        }
-
-        @NodeIntrinsic
-        public static native void call(Word in, Word out, Word key);
-    }
+    @NodeIntrinsic(ForeignCallNode.class)
+    public static native void decryptBlockStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Word in, Word out, Word key);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CipherBlockChainingSubstitutions.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CipherBlockChainingSubstitutions.java	Tue May 21 19:51:00 2013 +0200
@@ -25,16 +25,13 @@
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 import sun.misc.*;
 
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node.ConstantNodeParameter;
+import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.replacements.Snippet.Fold;
 import com.oracle.graal.word.*;
 
@@ -73,19 +70,6 @@
         }
     }
 
-    private static void crypt(Object rcvr, byte[] in, int inOffset, int inLength, byte[] out, int outOffset, Object embeddedCipher, boolean encrypt) {
-        Word kAddr = Word.fromObject(embeddedCipher).readWord(Word.unsigned(AESCryptSubstitutions.kOffset), ANY_LOCATION).add(arrayBaseOffset(Kind.Byte));
-        Word rAddr = Word.unsigned(GetObjectAddressNode.get(rcvr)).readWord(Word.unsigned(rOffset), ANY_LOCATION).add(arrayBaseOffset(Kind.Byte));
-        Word inAddr = Word.unsigned(GetObjectAddressNode.get(in) + arrayBaseOffset(Kind.Byte) + inOffset);
-        Word outAddr = Word.unsigned(GetObjectAddressNode.get(out) + arrayBaseOffset(Kind.Byte) + outOffset);
-        if (encrypt) {
-            EncryptAESCryptStubCall.call(inAddr, outAddr, kAddr, rAddr, inLength);
-        } else {
-            DecryptAESCryptStubCall.call(inAddr, outAddr, kAddr, rAddr, inLength);
-        }
-
-    }
-
     @MethodSubstitution(isStatic = false)
     static void decrypt(Object rcvr, byte[] in, int inOffset, int inLength, byte[] out, int outOffset) {
         Object embeddedCipher = Word.fromObject(rcvr).readObject(Word.unsigned(embeddedCipherOffset), ANY_LOCATION);
@@ -96,54 +80,24 @@
         }
     }
 
-    abstract static class AESCryptStubCall extends DeoptimizingStubCall implements LIRGenLowerable {
-
-        @Input private ValueNode in;
-        @Input private ValueNode out;
-        @Input private ValueNode key;
-        @Input private ValueNode r;
-        @Input private ValueNode inLength;
-
-        private final Descriptor descriptor;
-
-        public AESCryptStubCall(ValueNode in, ValueNode out, ValueNode key, ValueNode r, ValueNode inLength, Descriptor descriptor) {
-            super(StampFactory.forVoid());
-            this.in = in;
-            this.out = out;
-            this.key = key;
-            this.r = r;
-            this.inLength = inLength;
-            this.descriptor = descriptor;
-        }
-
-        @Override
-        public void generate(LIRGenerator gen) {
-            RuntimeCallTarget stub = gen.getRuntime().lookupRuntimeCall(descriptor);
-            gen.emitCall(stub, stub.getCallingConvention(), null, gen.operand(in), gen.operand(out), gen.operand(key), gen.operand(r), gen.operand(inLength));
+    private static void crypt(Object rcvr, byte[] in, int inOffset, int inLength, byte[] out, int outOffset, Object embeddedCipher, boolean encrypt) {
+        Word kAddr = Word.fromObject(embeddedCipher).readWord(Word.unsigned(AESCryptSubstitutions.kOffset), ANY_LOCATION).add(arrayBaseOffset(Kind.Byte));
+        Word rAddr = Word.unsigned(GetObjectAddressNode.get(rcvr)).readWord(Word.unsigned(rOffset), ANY_LOCATION).add(arrayBaseOffset(Kind.Byte));
+        Word inAddr = Word.unsigned(GetObjectAddressNode.get(in) + arrayBaseOffset(Kind.Byte) + inOffset);
+        Word outAddr = Word.unsigned(GetObjectAddressNode.get(out) + arrayBaseOffset(Kind.Byte) + outOffset);
+        if (encrypt) {
+            encryptAESCryptStub(ENCRYPT, inAddr, outAddr, kAddr, rAddr, inLength);
+        } else {
+            decryptAESCryptStub(DECRYPT, inAddr, outAddr, kAddr, rAddr, inLength);
         }
     }
 
-    public static class EncryptAESCryptStubCall extends AESCryptStubCall {
-
-        public static final Descriptor ENCRYPT = new Descriptor("encrypt", false, void.class, Word.class, Word.class, Word.class, Word.class, int.class);
-
-        public EncryptAESCryptStubCall(ValueNode in, ValueNode out, ValueNode key, ValueNode r, ValueNode inLength) {
-            super(in, out, key, r, inLength, ENCRYPT);
-        }
-
-        @NodeIntrinsic
-        public static native void call(Word in, Word out, Word key, Word r, int inLength);
-    }
+    public static final ForeignCallDescriptor ENCRYPT = new ForeignCallDescriptor("encrypt", void.class, Word.class, Word.class, Word.class, Word.class, int.class);
+    public static final ForeignCallDescriptor DECRYPT = new ForeignCallDescriptor("decrypt", void.class, Word.class, Word.class, Word.class, Word.class, int.class);
 
-    public static class DecryptAESCryptStubCall extends AESCryptStubCall {
-
-        public static final Descriptor DECRYPT = new Descriptor("decrypt", false, void.class, Word.class, Word.class, Word.class, Word.class, int.class);
+    @NodeIntrinsic(ForeignCallNode.class)
+    public static native void encryptAESCryptStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Word in, Word out, Word key, Word r, int inLength);
 
-        public DecryptAESCryptStubCall(ValueNode in, ValueNode out, ValueNode key, ValueNode r, ValueNode inLength) {
-            super(in, out, key, r, inLength, DECRYPT);
-        }
-
-        @NodeIntrinsic
-        public static native void call(Word in, Word out, Word key, Word r, int inLength);
-    }
+    @NodeIntrinsic(ForeignCallNode.class)
+    public static native void decryptAESCryptStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Word in, Word out, Word key, Word r, int inLength);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Tue May 21 19:51:00 2013 +0200
@@ -23,17 +23,16 @@
 package com.oracle.graal.hotspot.replacements;
 
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+import static com.oracle.graal.hotspot.meta.HotSpotRuntime.*;
 import static com.oracle.graal.replacements.nodes.BranchProbabilityNode.*;
 import sun.misc.*;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.extended.LocationNode.LocationIdentity;
 import com.oracle.graal.replacements.Snippet.Fold;
@@ -450,11 +449,14 @@
 
     public static Object verifyOop(Object object) {
         if (verifyOops()) {
-            VerifyOopStubCall.call(object);
+            verifyOopStub(VERIFY_OOP, object);
         }
         return object;
     }
 
+    @NodeIntrinsic(ForeignCallNode.class)
+    private static native Object verifyOopStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object);
+
     /**
      * Gets the value of the stack pointer register as a Word.
      */
@@ -672,11 +674,9 @@
         return identityHashCode(IDENTITY_HASHCODE, x);
     }
 
-    public static final Descriptor IDENTITY_HASHCODE = new Descriptor("identity_hashcode", false, int.class, Object.class);
-
     @SuppressWarnings("unused")
-    @NodeIntrinsic(RuntimeCallNode.class)
-    public static int identityHashCode(@ConstantNodeParameter Descriptor descriptor, Object object) {
+    @NodeIntrinsic(ForeignCallNode.class)
+    public static int identityHashCode(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object) {
         return System.identityHashCode(object);
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Tue May 21 19:51:00 2013 +0200
@@ -35,6 +35,7 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.hotspot.nodes.*;
@@ -159,7 +160,7 @@
                         // owns the bias and we need to revoke that bias. The revocation will occur
                         // in the interpreter runtime.
                         traceObject(trace, "+lock{stub:revoke}", object);
-                        MonitorEnterStubCall.call(object, lock);
+                        monitorenterStub(MONITORENTER, object, lock);
                         return;
                     } else {
                         // At this point we know the epoch has expired, meaning that the
@@ -179,7 +180,7 @@
                         // succeeded in biasing it toward itself and we need to revoke that
                         // bias. The revocation will occur in the runtime in the slow case.
                         traceObject(trace, "+lock{stub:epoch-expired}", object);
-                        MonitorEnterStubCall.call(object, lock);
+                        monitorenterStub(MONITORENTER, object, lock);
                         return;
                     }
                 } else {
@@ -236,7 +237,7 @@
             if (probability(VERY_SLOW_PATH_PROBABILITY, currentMark.subtract(stackPointer).and(alignedMask.subtract(pageSize())).notEqual(0))) {
                 // Most likely not a recursive lock, go into a slow runtime call
                 traceObject(trace, "+lock{stub:failed-cas}", object);
-                MonitorEnterStubCall.call(object, lock);
+                monitorenterStub(MONITORENTER, object, lock);
                 return;
             } else {
                 // Recursively locked => write 0 to the lock slot
@@ -262,7 +263,7 @@
         // cannot float about the null check above
         final Word lock = beginLockScope(lockDepth);
         traceObject(trace, "+lock{stub}", object);
-        MonitorEnterStubCall.call(object, lock);
+        monitorenterStub(MONITORENTER, object, lock);
     }
 
     @Snippet
@@ -517,4 +518,10 @@
             }
         }
     }
+
+    public static final ForeignCallDescriptor MONITORENTER = new ForeignCallDescriptor("monitorenter", void.class, Object.class, Word.class);
+
+    @NodeIntrinsic(ForeignCallNode.class)
+    private static native void monitorenterStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object, Word lock);
+
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/SystemSubstitutions.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/SystemSubstitutions.java	Tue May 21 19:51:00 2013 +0200
@@ -25,7 +25,7 @@
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 import static com.oracle.graal.replacements.nodes.BranchProbabilityNode.*;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
@@ -38,8 +38,8 @@
 @ClassSubstitution(java.lang.System.class)
 public class SystemSubstitutions {
 
-    public static final Descriptor JAVA_TIME_MILLIS = new Descriptor("javaTimeMillis", false, long.class);
-    public static final Descriptor JAVA_TIME_NANOS = new Descriptor("javaTimeNanos", false, long.class);
+    public static final ForeignCallDescriptor JAVA_TIME_MILLIS = new ForeignCallDescriptor("javaTimeMillis", long.class);
+    public static final ForeignCallDescriptor JAVA_TIME_NANOS = new ForeignCallDescriptor("javaTimeNanos", long.class);
 
     @MacroSubstitution(macro = ArrayCopyNode.class)
     public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
@@ -63,8 +63,8 @@
         return computeHashCode(x);
     }
 
-    @NodeIntrinsic(value = RuntimeCallNode.class, setStampFromReturnType = true)
-    public static long callLong(@ConstantNodeParameter Descriptor descriptor) {
+    @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true)
+    public static long callLong(@ConstantNodeParameter ForeignCallDescriptor descriptor) {
         if (descriptor == JAVA_TIME_MILLIS) {
             return System.currentTimeMillis();
         }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ThreadSubstitutions.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ThreadSubstitutions.java	Tue May 21 19:51:00 2013 +0200
@@ -24,8 +24,15 @@
 
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 
+import java.lang.reflect.*;
+
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node.ConstantNodeParameter;
+import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.nodes.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.word.*;
 
 /**
@@ -51,6 +58,22 @@
             }
         }
 
-        return ThreadIsInterruptedStubCall.call(thisObject, clearInterrupted);
+        return threadIsInterruptedStub(THREAD_IS_INTERRUPTED, thisObject, clearInterrupted);
+    }
+
+    public static final ForeignCallDescriptor THREAD_IS_INTERRUPTED = new ForeignCallDescriptor("thread_is_interrupted", boolean.class, Object.class, boolean.class);
+
+    /**
+     * @param descriptor
+     */
+    @NodeIntrinsic(ForeignCallNode.class)
+    private static boolean threadIsInterruptedStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Thread thread, boolean clearIsInterrupted) {
+        try {
+            Method isInterrupted = Thread.class.getDeclaredMethod("isInterrupted", boolean.class);
+            isInterrupted.setAccessible(true);
+            return (Boolean) isInterrupted.invoke(thread, clearIsInterrupted);
+        } catch (Exception e) {
+            throw new GraalInternalError(e);
+        }
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/CRuntimeStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.SnippetTemplate.Arguments;
-import com.oracle.graal.replacements.SnippetTemplate.SnippetInfo;
-
-/**
- * Base class for a stub that saves registers around a C runtime call.
- */
-public abstract class CRuntimeStub extends SnippetStub {
-
-    public CRuntimeStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Override
-    protected Arguments makeArguments(SnippetInfo stub) {
-        Arguments args = new Arguments(stub);
-        for (int i = 0; i < stub.getParameterCount(); i++) {
-            args.add(stub.getParameterName(i), null);
-        }
-        return args;
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java	Tue May 21 19:51:00 2013 +0200
@@ -27,14 +27,14 @@
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
 import static com.oracle.graal.hotspot.stubs.StubUtil.*;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.Snippet.Fold;
@@ -48,9 +48,9 @@
  * <p>
  * The descriptor for a call to this stub is {@link HotSpotBackend#EXCEPTION_HANDLER}.
  */
-public class ExceptionHandlerStub extends CRuntimeStub {
+public class ExceptionHandlerStub extends SnippetStub {
 
-    public ExceptionHandlerStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
+    public ExceptionHandlerStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotForeignCallLinkage linkage) {
         super(runtime, replacements, target, linkage);
     }
 
@@ -125,8 +125,8 @@
         return enabled || graalRuntime().getConfig().cAssertions;
     }
 
-    public static final Descriptor EXCEPTION_HANDLER_FOR_PC = descriptorFor(ExceptionHandlerStub.class, "exceptionHandlerForPc", false);
+    public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_PC = descriptorFor(ExceptionHandlerStub.class, "exceptionHandlerForPc");
 
-    @NodeIntrinsic(value = CRuntimeCall.class, setStampFromReturnType = true)
-    public static native Word exceptionHandlerForPc(@ConstantNodeParameter Descriptor exceptionHandlerForPc, Word thread);
+    @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true)
+    public static native Word exceptionHandlerForPc(@ConstantNodeParameter ForeignCallDescriptor exceptionHandlerForPc, Word thread);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ForeignCallStub.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,302 @@
+/*
+ * 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.hotspot.stubs;
+
+import static com.oracle.graal.api.code.CallingConvention.Type.*;
+import static com.oracle.graal.api.meta.MetaUtil.*;
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.*;
+import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.*;
+
+import java.lang.reflect.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.hotspot.nodes.*;
+import com.oracle.graal.hotspot.replacements.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.replacements.*;
+import com.oracle.graal.replacements.nodes.*;
+import com.oracle.graal.word.*;
+import com.oracle.graal.word.phases.*;
+
+/**
+ * A {@linkplain #getGraph() generated} stub for a {@link Transition non-leaf} foreign call from
+ * compiled code. A stub is required for such calls as the caller may be scheduled for
+ * deoptimization while the call is in progress. And since these are foreign/runtime calls on slow
+ * paths, we don't want to force the register allocator to spill around the call. As such, this stub
+ * saves and restores all allocatable registers. It also
+ * {@linkplain StubUtil#handlePendingException(boolean) handles} any exceptions raised during the
+ * foreign call.
+ */
+public class ForeignCallStub extends Stub {
+
+    /**
+     * The target of the call.
+     */
+    private final HotSpotForeignCallLinkage target;
+
+    /**
+     * Specifies if the JavaThread value for the current thread is to be prepended to the arguments
+     * for the call to {@link #target}.
+     */
+    protected final boolean prependThread;
+
+    /**
+     * Creates a stub for a call to code at a given address.
+     * 
+     * @param address the address of the code to call
+     * @param descriptor the signature of the call to this stub
+     * @param prependThread true if the JavaThread value for the current thread is to be prepended
+     *            to the arguments for the call to {@code address}
+     */
+    public ForeignCallStub(long address, ForeignCallDescriptor descriptor, boolean prependThread, HotSpotRuntime runtime, Replacements replacements) {
+        super(runtime, replacements, HotSpotForeignCallLinkage.create(descriptor, 0L, PRESERVES_REGISTERS, JavaCallee, NOT_LEAF));
+        this.prependThread = prependThread;
+        Class[] targetParameterTypes = createTargetParameters(descriptor);
+        ForeignCallDescriptor targetSig = new ForeignCallDescriptor(descriptor.getName() + ":C", descriptor.getResultType(), targetParameterTypes);
+        target = HotSpotForeignCallLinkage.create(targetSig, address, DESTROYS_REGISTERS, NativeCall, NOT_LEAF);
+    }
+
+    /**
+     * Gets the linkage information for the call from this stub.
+     */
+    public HotSpotForeignCallLinkage getTargetLinkage() {
+        return target;
+    }
+
+    private Class[] createTargetParameters(ForeignCallDescriptor descriptor) {
+        Class[] parameters = descriptor.getArgumentTypes();
+        if (prependThread) {
+            Class[] newParameters = new Class[parameters.length + 1];
+            System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+            newParameters[0] = Word.class;
+            return newParameters;
+        }
+        return parameters;
+    }
+
+    @Override
+    protected ResolvedJavaMethod getInstalledCodeOwner() {
+        return null;
+    }
+
+    @Override
+    protected Object debugScopeContext() {
+        return new JavaMethod() {
+
+            public Signature getSignature() {
+                ForeignCallDescriptor d = linkage.getDescriptor();
+                Class<?>[] arguments = d.getArgumentTypes();
+                JavaType[] parameters = new JavaType[arguments.length];
+                for (int i = 0; i < arguments.length; i++) {
+                    parameters[i] = runtime.lookupJavaType(arguments[i]);
+                }
+                return new HotSpotSignature(runtime.lookupJavaType(d.getResultType()), parameters);
+            }
+
+            public String getName() {
+                return linkage.getDescriptor().getName();
+            }
+
+            public JavaType getDeclaringClass() {
+                return runtime.lookupJavaType(ForeignCallStub.class);
+            }
+
+            @Override
+            public String toString() {
+                return format("ForeignCallStub<%n(%p)>", this);
+            }
+        };
+    }
+
+    static class GraphBuilder {
+
+        public GraphBuilder(Stub stub) {
+            this.graph = new StructuredGraph(stub.toString(), null);
+            graph.replaceFixed(graph.start(), graph.add(new StubStartNode(stub)));
+            this.lastFixedNode = graph.start();
+        }
+
+        final StructuredGraph graph;
+        private FixedWithNextNode lastFixedNode;
+
+        <T extends FloatingNode> T add(T node) {
+            return graph.unique(node);
+        }
+
+        <T extends FixedNode> T append(T node) {
+            T result = graph.add(node);
+            assert lastFixedNode != null;
+            assert result.predecessor() == null;
+            graph.addAfterFixed(lastFixedNode, result);
+            if (result instanceof FixedWithNextNode) {
+                lastFixedNode = (FixedWithNextNode) result;
+            } else {
+                lastFixedNode = null;
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Creates a graph for this stub.
+     * <p>
+     * If the stub returns an object, the graph created corresponds to this pseudo code:
+     * 
+     * <pre>
+     *     Object foreignFunctionStub(args...) {
+     *         foreignFunction(currentThread,  args);
+     *         if (clearPendingException(thread())) {
+     *             getAndClearObjectResult(thread());
+     *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
+     *         }
+     *         return verifyObject(getAndClearObjectResult(thread()));
+     *     }
+     * </pre>
+     * 
+     * If the stub returns a primitive or word, the graph created corresponds to this pseudo code
+     * (using {@code int} as the primitive return type):
+     * 
+     * <pre>
+     *     int foreignFunctionStub(args...) {
+     *         int result = foreignFunction(currentThread,  args);
+     *         if (clearPendingException(thread())) {
+     *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
+     *         }
+     *         return result;
+     *     }
+     * </pre>
+     * 
+     * If the stub is void, the graph created corresponds to this pseudo code:
+     * 
+     * <pre>
+     *     void foreignFunctionStub(args...) {
+     *         foreignFunction(currentThread,  args);
+     *         if (clearPendingException(thread())) {
+     *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
+     *         }
+     *     }
+     * </pre>
+     * 
+     * In each example above, the {@code currentThread} argument is the C++ JavaThread value (i.e.,
+     * %r15 on AMD64) and is only prepended if {@link #prependThread} is true.
+     */
+    @Override
+    protected StructuredGraph getGraph() {
+        Class<?>[] args = linkage.getDescriptor().getArgumentTypes();
+        boolean isObjectResult = linkage.getCallingConvention().getReturn().getKind() == Kind.Object;
+        GraphBuilder builder = new GraphBuilder(this);
+        LocalNode[] locals = createLocals(builder, args);
+
+        ReadRegisterNode thread = prependThread || isObjectResult ? builder.append(new ReadRegisterNode(runtime.threadRegister(), true, false)) : null;
+        ValueNode result = createTargetCall(builder, locals, thread);
+        createInvoke(builder, StubUtil.class, "handlePendingException", ConstantNode.forBoolean(isObjectResult, builder.graph));
+        if (isObjectResult) {
+            InvokeNode object = createInvoke(builder, HotSpotReplacementsUtil.class, "getAndClearObjectResult", thread);
+            result = createInvoke(builder, StubUtil.class, "verifyObject", object);
+        }
+        builder.append(new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result));
+
+        if (Debug.isDumpEnabled()) {
+            Debug.dump(builder.graph, "Initial stub graph");
+        }
+
+        for (InvokeNode invoke : builder.graph.getNodes(InvokeNode.class).snapshot()) {
+            inline(invoke);
+        }
+        assert builder.graph.getNodes(InvokeNode.class).isEmpty();
+
+        if (Debug.isDumpEnabled()) {
+            Debug.dump(builder.graph, "Stub graph before compilation");
+        }
+
+        return builder.graph;
+    }
+
+    private LocalNode[] createLocals(GraphBuilder builder, Class<?>[] args) {
+        LocalNode[] locals = new LocalNode[args.length];
+        ResolvedJavaType accessingClass = runtime.lookupJavaType(getClass());
+        for (int i = 0; i < args.length; i++) {
+            ResolvedJavaType type = runtime.lookupJavaType(args[i]).resolve(accessingClass);
+            Kind kind = type.getKind().getStackKind();
+            Stamp stamp;
+            if (kind == Kind.Object) {
+                stamp = StampFactory.declared(type);
+            } else {
+                stamp = StampFactory.forKind(kind);
+            }
+            LocalNode local = builder.add(new LocalNode(i, stamp));
+            locals[i] = local;
+        }
+        return locals;
+    }
+
+    private InvokeNode createInvoke(GraphBuilder builder, Class<?> declaringClass, String name, ValueNode... hpeArgs) {
+        ResolvedJavaMethod method = null;
+        for (Method m : declaringClass.getDeclaredMethods()) {
+            if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(name)) {
+                assert method == null : "found more than one method in " + declaringClass + " named " + name;
+                method = runtime.lookupJavaMethod(m);
+            }
+        }
+        assert method != null : "did not find method in " + declaringClass + " named " + name;
+        JavaType returnType = method.getSignature().getReturnType(null);
+        MethodCallTargetNode callTarget = builder.graph.add(new MethodCallTargetNode(InvokeKind.Static, method, hpeArgs, returnType));
+        InvokeNode invoke = builder.append(new InvokeNode(callTarget, FrameState.UNKNOWN_BCI));
+        return invoke;
+    }
+
+    private ForeignCallNode createTargetCall(GraphBuilder builder, LocalNode[] locals, ReadRegisterNode thread) {
+        if (prependThread) {
+            ValueNode[] targetArguments = new ValueNode[1 + locals.length];
+            targetArguments[0] = thread;
+            System.arraycopy(locals, 0, targetArguments, 1, locals.length);
+            return builder.append(new ForeignCallNode(target.getDescriptor(), targetArguments));
+        } else {
+            return builder.append(new ForeignCallNode(target.getDescriptor(), locals));
+        }
+    }
+
+    private void inline(InvokeNode invoke) {
+        StructuredGraph graph = invoke.graph();
+        ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod();
+        ReplacementsImpl repl = new ReplacementsImpl(runtime, new Assumptions(false), runtime.getTarget());
+        StructuredGraph calleeGraph = repl.makeGraph(method, null, null);
+        InliningUtil.inline(invoke, calleeGraph, false);
+        new NodeIntrinsificationPhase(runtime).apply(graph);
+        new WordTypeRewriterPhase(runtime, wordKind()).apply(graph);
+        new DeadCodeEliminationPhase().apply(graph);
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/LogObjectStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
-import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.Node.ConstantNodeParameter;
-import com.oracle.graal.graph.Node.NodeIntrinsic;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.word.*;
-
-/**
- * Stub called from {@link Log}.
- */
-public class LogObjectStub extends CRuntimeStub {
-
-    public LogObjectStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Snippet
-    private static void logObject(Object object, int flags) {
-        logObjectC(LOG_OBJECT_C, thread(), object, flags);
-    }
-
-    public static final Descriptor LOG_OBJECT_C = descriptorFor(LogObjectStub.class, "logObjectC", false);
-
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void logObjectC(@ConstantNodeParameter Descriptor logObjectC, Word thread, Object object, int flags);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/LogPrimitiveStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
-import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.Node.ConstantNodeParameter;
-import com.oracle.graal.graph.Node.NodeIntrinsic;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.word.*;
-
-/**
- * Stub called from {@link Log}.
- */
-public class LogPrimitiveStub extends CRuntimeStub {
-
-    public LogPrimitiveStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Snippet
-    private static void logPrimitive(char typeChar, long value, boolean newline) {
-        logPrimitivefC(LOG_PRIMITIVE_C, thread(), typeChar, value, newline);
-    }
-
-    public static final Descriptor LOG_PRIMITIVE_C = descriptorFor(LogPrimitiveStub.class, "logPrimitivefC", false);
-
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void logPrimitivefC(@ConstantNodeParameter Descriptor logPrimitivefC, Word thread, char typeChar, long value, boolean newline);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/LogPrintfStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
-import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.Node.ConstantNodeParameter;
-import com.oracle.graal.graph.Node.NodeIntrinsic;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.word.*;
-
-/**
- * Stub called from {@link Log}.
- */
-public class LogPrintfStub extends CRuntimeStub {
-
-    public LogPrintfStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Snippet
-    private static void logPrintf(String format, long v1, long v2, long v3) {
-        logPrintfC(LOG_PRINTF_C, thread(), format, v1, v2, v3);
-    }
-
-    public static final Descriptor LOG_PRINTF_C = descriptorFor(LogPrintfStub.class, "logPrintfC", false);
-
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void logPrintfC(@ConstantNodeParameter Descriptor logPrintfC, Word thread, String format, long v1, long v2, long v3);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java	Tue May 21 19:51:00 2013 +0200
@@ -28,7 +28,6 @@
 import static com.oracle.graal.hotspot.stubs.NewInstanceStub.*;
 import static com.oracle.graal.hotspot.stubs.StubUtil.*;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
@@ -37,6 +36,7 @@
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.replacements.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.Snippet.ConstantParameter;
@@ -53,7 +53,7 @@
  */
 public class NewArrayStub extends SnippetStub {
 
-    public NewArrayStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
+    public NewArrayStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotForeignCallLinkage linkage) {
         super(runtime, replacements, target, linkage);
     }
 
@@ -120,8 +120,8 @@
         return verifyObject(getAndClearObjectResult(thread()));
     }
 
-    public static final Descriptor NEW_ARRAY_C = descriptorFor(NewArrayStub.class, "newArrayC", false);
+    public static final ForeignCallDescriptor NEW_ARRAY_C = descriptorFor(NewArrayStub.class, "newArrayC");
 
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void newArrayC(@ConstantNodeParameter Descriptor newArrayC, Word thread, Word hub, int length);
+    @NodeIntrinsic(ForeignCallNode.class)
+    public static native void newArrayC(@ConstantNodeParameter ForeignCallDescriptor newArrayC, Word thread, Word hub, int length);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java	Tue May 21 19:51:00 2013 +0200
@@ -28,7 +28,6 @@
 import static com.oracle.graal.hotspot.replacements.NewObjectSnippets.*;
 import static com.oracle.graal.hotspot.stubs.StubUtil.*;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
@@ -37,6 +36,7 @@
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.replacements.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.Snippet.ConstantParameter;
@@ -53,7 +53,7 @@
  */
 public class NewInstanceStub extends SnippetStub {
 
-    public NewInstanceStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
+    public NewInstanceStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotForeignCallLinkage linkage) {
         super(runtime, replacements, target, linkage);
     }
 
@@ -239,8 +239,8 @@
         return Boolean.getBoolean("graal.newInstanceStub.forceSlowPath");
     }
 
-    public static final Descriptor NEW_INSTANCE_C = descriptorFor(NewInstanceStub.class, "newInstanceC", false);
+    public static final ForeignCallDescriptor NEW_INSTANCE_C = descriptorFor(NewInstanceStub.class, "newInstanceC");
 
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void newInstanceC(@ConstantNodeParameter Descriptor newInstanceC, Word thread, Word hub);
+    @NodeIntrinsic(ForeignCallNode.class)
+    public static native void newInstanceC(@ConstantNodeParameter ForeignCallDescriptor newInstanceC, Word thread, Word hub);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewMultiArrayStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
-import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.Node.ConstantNodeParameter;
-import com.oracle.graal.graph.Node.NodeIntrinsic;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.word.*;
-
-/**
- * Stub called from {@link NewMultiArrayStubCall}.
- */
-public class NewMultiArrayStub extends CRuntimeStub {
-
-    public NewMultiArrayStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Snippet
-    private static Object newMultiArray(Word hub, int rank, Word dims) {
-        newMultiArrayC(NEW_MULTI_ARRAY_C, thread(), hub, rank, dims);
-        handlePendingException(true);
-        return verifyObject(getAndClearObjectResult(thread()));
-    }
-
-    public static final Descriptor NEW_MULTI_ARRAY_C = descriptorFor(NewMultiArrayStub.class, "newMultiArrayC", false);
-
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void newMultiArrayC(@ConstantNodeParameter Descriptor newArrayC, Word thread, Word hub, int rank, Word dims);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/OSRMigrationEndStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.Node.ConstantNodeParameter;
-import com.oracle.graal.graph.Node.NodeIntrinsic;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.word.*;
-
-/**
- * Stub called from {@link OSRStartNode}.
- */
-public class OSRMigrationEndStub extends CRuntimeStub {
-
-    public OSRMigrationEndStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Snippet
-    private static void osrMigrationEnd(Word buffer) {
-        osrMigrationEndC(OSR_MIGRATION_END_C, buffer);
-    }
-
-    public static final Descriptor OSR_MIGRATION_END_C = descriptorFor(OSRMigrationEndStub.class, "osrMigrationEndC", false);
-
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void osrMigrationEndC(@ConstantNodeParameter Descriptor osrMigrationEndC, Word buffer);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/RuntimeCallStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,257 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.api.code.CallingConvention.Type.*;
-import static com.oracle.graal.api.meta.MetaUtil.*;
-import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
-import static com.oracle.graal.hotspot.HotSpotRuntimeCallTarget.RegisterEffect.*;
-
-import java.lang.reflect.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.bridge.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.hotspot.replacements.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.phases.common.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.replacements.nodes.*;
-import com.oracle.graal.word.*;
-import com.oracle.graal.word.phases.*;
-
-/**
- * A stub that calls into a HotSpot C/C++ runtime function using the native
- * {@link CallingConvention}.
- */
-public class RuntimeCallStub extends Stub {
-
-    /**
-     * The target of the call.
-     */
-    private final HotSpotRuntimeCallTarget target;
-
-    /**
-     * Specifies if the JavaThread value for the current thread is to be prepended to the arguments
-     * for the call to {@link #target}.
-     */
-    protected final boolean prependThread;
-
-    /**
-     * Creates a stub for a call to code at a given address.
-     * 
-     * @param address the address of the code to call
-     * @param sig the signature of the call to this stub
-     * @param prependThread true if the JavaThread value for the current thread is to be prepended
-     *            to the arguments for the call to {@code address}
-     * @param regConfig used to get the calling convention for the call to this stub from Graal
-     *            compiled Java code as well as the calling convention for the call to
-     *            {@code address}
-     * @param vm the Java to HotSpot C/C++ runtime interface
-     */
-    public RuntimeCallStub(long address, Descriptor sig, boolean prependThread, HotSpotRuntime runtime, Replacements replacements, RegisterConfig regConfig, CompilerToVM vm) {
-        super(runtime, replacements, HotSpotRuntimeCallTarget.create(sig, 0L, PRESERVES_REGISTERS, JavaCallee, regConfig, runtime, vm));
-        this.prependThread = prependThread;
-        Class[] targetParameterTypes = createTargetParameters(sig);
-        Descriptor targetSig = new Descriptor(sig.getName() + ":C", sig.hasSideEffect(), sig.getResultType(), targetParameterTypes);
-        target = HotSpotRuntimeCallTarget.create(targetSig, address, DESTROYS_REGISTERS, NativeCall, regConfig, runtime, vm);
-    }
-
-    /**
-     * Gets the linkage information for the runtime call.
-     */
-    public HotSpotRuntimeCallTarget getTargetLinkage() {
-        return target;
-    }
-
-    private Class[] createTargetParameters(Descriptor sig) {
-        Class[] parameters = sig.getArgumentTypes();
-        if (prependThread) {
-            Class[] newParameters = new Class[parameters.length + 1];
-            System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
-            newParameters[0] = Word.class;
-            return newParameters;
-        }
-        return parameters;
-    }
-
-    @Override
-    protected ResolvedJavaMethod getInstalledCodeOwner() {
-        return null;
-    }
-
-    @Override
-    protected Object debugScopeContext() {
-        return new JavaMethod() {
-
-            public Signature getSignature() {
-                Descriptor d = linkage.getDescriptor();
-                Class<?>[] arguments = d.getArgumentTypes();
-                JavaType[] parameters = new JavaType[arguments.length];
-                for (int i = 0; i < arguments.length; i++) {
-                    parameters[i] = runtime.lookupJavaType(arguments[i]);
-                }
-                return new HotSpotSignature(runtime.lookupJavaType(d.getResultType()), parameters);
-            }
-
-            public String getName() {
-                return linkage.getDescriptor().getName();
-            }
-
-            public JavaType getDeclaringClass() {
-                return runtime.lookupJavaType(RuntimeCallStub.class);
-            }
-
-            @Override
-            public String toString() {
-                return format("HotSpotStub<%n(%p)>", this);
-            }
-        };
-    }
-
-    static class GraphBuilder {
-
-        public GraphBuilder(Stub stub) {
-            this.graph = new StructuredGraph(stub.toString(), null);
-            graph.replaceFixed(graph.start(), graph.add(new StubStartNode(stub)));
-            this.lastFixedNode = graph.start();
-        }
-
-        final StructuredGraph graph;
-        private FixedWithNextNode lastFixedNode;
-
-        <T extends FloatingNode> T add(T node) {
-            return graph.unique(node);
-        }
-
-        <T extends FixedNode> T append(T node) {
-            T result = graph.add(node);
-            assert lastFixedNode != null;
-            assert result.predecessor() == null;
-            graph.addAfterFixed(lastFixedNode, result);
-            if (result instanceof FixedWithNextNode) {
-                lastFixedNode = (FixedWithNextNode) result;
-            } else {
-                lastFixedNode = null;
-            }
-            return result;
-        }
-    }
-
-    @Override
-    protected StructuredGraph getGraph() {
-        Class<?>[] args = linkage.getDescriptor().getArgumentTypes();
-        boolean isObjectResult = linkage.getCallingConvention().getReturn().getKind() == Kind.Object;
-        GraphBuilder builder = new GraphBuilder(this);
-        LocalNode[] locals = createLocals(builder, args);
-
-        ReadRegisterNode thread = prependThread || isObjectResult ? builder.append(new ReadRegisterNode(runtime.threadRegister(), true, false)) : null;
-        ValueNode result = createTargetCall(builder, locals, thread);
-        createInvoke(builder, StubUtil.class, "handlePendingException", ConstantNode.forBoolean(isObjectResult, builder.graph));
-        if (isObjectResult) {
-            InvokeNode object = createInvoke(builder, HotSpotReplacementsUtil.class, "getAndClearObjectResult", thread);
-            result = createInvoke(builder, StubUtil.class, "verifyObject", object);
-        }
-        builder.append(new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result));
-
-        if (Debug.isDumpEnabled()) {
-            Debug.dump(builder.graph, "Initial stub graph");
-        }
-
-        for (InvokeNode invoke : builder.graph.getNodes(InvokeNode.class).snapshot()) {
-            inline(invoke);
-        }
-        assert builder.graph.getNodes(InvokeNode.class).isEmpty();
-
-        if (Debug.isDumpEnabled()) {
-            Debug.dump(builder.graph, "Stub graph before compilation");
-        }
-
-        return builder.graph;
-    }
-
-    private LocalNode[] createLocals(GraphBuilder builder, Class<?>[] args) {
-        LocalNode[] locals = new LocalNode[args.length];
-        ResolvedJavaType accessingClass = runtime.lookupJavaType(getClass());
-        for (int i = 0; i < args.length; i++) {
-            ResolvedJavaType type = runtime.lookupJavaType(args[i]).resolve(accessingClass);
-            Kind kind = type.getKind().getStackKind();
-            Stamp stamp;
-            if (kind == Kind.Object) {
-                stamp = StampFactory.declared(type);
-            } else {
-                stamp = StampFactory.forKind(kind);
-            }
-            LocalNode local = builder.add(new LocalNode(i, stamp));
-            locals[i] = local;
-        }
-        return locals;
-    }
-
-    private InvokeNode createInvoke(GraphBuilder builder, Class<?> declaringClass, String name, ValueNode... hpeArgs) {
-        ResolvedJavaMethod method = null;
-        for (Method m : declaringClass.getDeclaredMethods()) {
-            if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(name)) {
-                assert method == null : "found more than one method in " + declaringClass + " named " + name;
-                method = runtime.lookupJavaMethod(m);
-            }
-        }
-        assert method != null : "did not find method in " + declaringClass + " named " + name;
-        JavaType returnType = method.getSignature().getReturnType(null);
-        MethodCallTargetNode callTarget = builder.graph.add(new MethodCallTargetNode(InvokeKind.Static, method, hpeArgs, returnType));
-        InvokeNode invoke = builder.append(new InvokeNode(callTarget, FrameState.UNKNOWN_BCI));
-        return invoke;
-    }
-
-    private CRuntimeCall createTargetCall(GraphBuilder builder, LocalNode[] locals, ReadRegisterNode thread) {
-        if (prependThread) {
-            ValueNode[] targetArguments = new ValueNode[1 + locals.length];
-            targetArguments[0] = thread;
-            System.arraycopy(locals, 0, targetArguments, 1, locals.length);
-            return builder.append(new CRuntimeCall(target.getDescriptor(), targetArguments));
-        } else {
-            return builder.append(new CRuntimeCall(target.getDescriptor(), locals));
-        }
-    }
-
-    private void inline(InvokeNode invoke) {
-        StructuredGraph graph = invoke.graph();
-        ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod();
-        ReplacementsImpl repl = new ReplacementsImpl(runtime, new Assumptions(false), runtime.getTarget());
-        StructuredGraph calleeGraph = repl.makeGraph(method, null, null);
-        InliningUtil.inline(invoke, calleeGraph, false);
-        new NodeIntrinsificationPhase(runtime).apply(graph);
-        new WordTypeRewriterPhase(runtime, wordKind()).apply(graph);
-        new DeadCodeEliminationPhase().apply(graph);
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Tue May 21 19:51:00 2013 +0200
@@ -30,7 +30,6 @@
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.Snippet.ConstantParameter;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.SnippetTemplate.AbstractTemplates;
 import com.oracle.graal.replacements.SnippetTemplate.Arguments;
@@ -66,7 +65,7 @@
      * 
      * @param linkage linkage details for a call to the stub
      */
-    public SnippetStub(HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
+    public SnippetStub(HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotForeignCallLinkage linkage) {
         super(runtime, replacements, linkage);
         this.snippet = new Template(runtime, replacements, target, getClass());
     }
@@ -77,9 +76,15 @@
     }
 
     /**
-     * Adds the {@linkplain ConstantParameter constant} arguments of this stub.
+     * Adds the arguments to this snippet stub.
      */
-    protected abstract Arguments makeArguments(SnippetInfo stub);
+    protected Arguments makeArguments(SnippetInfo stub) {
+        Arguments args = new Arguments(stub);
+        for (int i = 0; i < stub.getParameterCount(); i++) {
+            args.add(stub.getParameterName(i), null);
+        }
+        return args;
+    }
 
     @Override
     protected Object debugScopeContext() {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Tue May 21 19:51:00 2013 +0200
@@ -56,7 +56,7 @@
     /**
      * The linkage information for a call to this stub from compiled code.
      */
-    protected final HotSpotRuntimeCallTarget linkage;
+    protected final HotSpotForeignCallLinkage linkage;
 
     /**
      * The code installed for the stub.
@@ -100,7 +100,7 @@
      * 
      * @param linkage linkage details for a call to the stub
      */
-    public Stub(HotSpotRuntime runtime, Replacements replacements, HotSpotRuntimeCallTarget linkage) {
+    public Stub(HotSpotRuntime runtime, Replacements replacements, HotSpotForeignCallLinkage linkage) {
         this.linkage = linkage;
         this.runtime = runtime;
         this.replacements = replacements;
@@ -109,7 +109,7 @@
     /**
      * Gets the linkage for a call to this stub from compiled code.
      */
-    public HotSpotRuntimeCallTarget getLinkage() {
+    public HotSpotForeignCallLinkage getLinkage() {
         return linkage;
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java	Tue May 21 19:51:00 2013 +0200
@@ -31,10 +31,11 @@
 import java.lang.reflect.*;
 import java.util.*;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.nodes.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.Snippet.Fold;
 import com.oracle.graal.word.*;
@@ -46,28 +47,29 @@
  */
 public class StubUtil {
 
-    public static final Descriptor VM_MESSAGE_C = descriptorFor(StubUtil.class, "vmMessageC", false);
+    public static final ForeignCallDescriptor VM_MESSAGE_C = descriptorFor(StubUtil.class, "vmMessageC");
 
     /**
-     * Looks for a {@link CRuntimeCall} node intrinsic named {@code name} in {@code stubClass} and
-     * returns a {@link Descriptor} based on its signature and the value of {@code hasSideEffect}.
+     * Looks for a {@link ForeignCallNode} node intrinsic named {@code name} in {@code stubClass}
+     * and returns a {@link ForeignCallDescriptor} based on its signature and the value of
+     * {@code hasSideEffect}.
      */
-    public static Descriptor descriptorFor(Class<?> stubClass, String name, boolean hasSideEffect) {
+    public static ForeignCallDescriptor descriptorFor(Class<?> stubClass, String name) {
         Method found = null;
         for (Method method : stubClass.getDeclaredMethods()) {
             if (Modifier.isStatic(method.getModifiers()) && method.getAnnotation(NodeIntrinsic.class) != null && method.getName().equals(name)) {
-                if (method.getAnnotation(NodeIntrinsic.class).value() == CRuntimeCall.class) {
-                    assert found == null : "found more than one C runtime call named " + name + " in " + stubClass;
-                    assert method.getParameterTypes().length != 0 && method.getParameterTypes()[0] == Descriptor.class : "first parameter of C runtime call '" + name + "' in " + stubClass +
-                                    " must be of type " + Descriptor.class.getSimpleName();
+                if (method.getAnnotation(NodeIntrinsic.class).value() == ForeignCallNode.class) {
+                    assert found == null : "found more than one foreign call named " + name + " in " + stubClass;
+                    assert method.getParameterTypes().length != 0 && method.getParameterTypes()[0] == ForeignCallDescriptor.class : "first parameter of foreign call '" + name + "' in " + stubClass +
+                                    " must be of type " + ForeignCallDescriptor.class.getSimpleName();
                     found = method;
                 }
             }
         }
-        assert found != null : "could not find C runtime call named " + name + " in " + stubClass;
+        assert found != null : "could not find foreign call named " + name + " in " + stubClass;
         List<Class<?>> paramList = Arrays.asList(found.getParameterTypes());
         Class[] cCallTypes = paramList.subList(1, paramList.size()).toArray(new Class[paramList.size() - 1]);
-        return new Descriptor(name, hasSideEffect, found.getReturnType(), cCallTypes);
+        return new ForeignCallDescriptor(name, found.getReturnType(), cCallTypes);
     }
 
     public static void handlePendingException(boolean isObjectResult) {
@@ -79,8 +81,8 @@
         }
     }
 
-    @NodeIntrinsic(CRuntimeCall.class)
-    private static native void vmMessageC(@ConstantNodeParameter Descriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3);
+    @NodeIntrinsic(ForeignCallNode.class)
+    private static native void vmMessageC(@ConstantNodeParameter ForeignCallDescriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3);
 
     /**
      * Prints a message to the log stream.
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ThreadIsInterruptedStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
-import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.Node.ConstantNodeParameter;
-import com.oracle.graal.graph.Node.NodeIntrinsic;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.word.*;
-
-/**
- * Stub called from {@link ThreadIsInterruptedStubCall}.
- */
-public class ThreadIsInterruptedStub extends CRuntimeStub {
-
-    public ThreadIsInterruptedStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Snippet
-    private static boolean threadIsInterrupted(Thread receiverThread, boolean clearIsInterrupted) {
-        boolean result = threadIsInterruptedC(THREAD_IS_INTERRUPTED_C, thread(), receiverThread, clearIsInterrupted);
-        handlePendingException(false);
-        return result;
-    }
-
-    public static final Descriptor THREAD_IS_INTERRUPTED_C = descriptorFor(ThreadIsInterruptedStub.class, "threadIsInterruptedC", false);
-
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native boolean threadIsInterruptedC(@ConstantNodeParameter Descriptor threadIsInterruptedC, Word thread, Thread receiverThread, boolean clearIsInterrupted);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UnwindExceptionToCallerStub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UnwindExceptionToCallerStub.java	Tue May 21 19:51:00 2013 +0200
@@ -28,14 +28,14 @@
 import static com.oracle.graal.hotspot.stubs.ExceptionHandlerStub.*;
 import static com.oracle.graal.hotspot.stubs.StubUtil.*;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.Snippet.Fold;
@@ -45,9 +45,9 @@
  * Stub called by an {@link UnwindNode}. This stub executes in the frame of the method throwing an
  * exception and completes by jumping to the exception handler in the calling frame.
  */
-public class UnwindExceptionToCallerStub extends CRuntimeStub {
+public class UnwindExceptionToCallerStub extends SnippetStub {
 
-    public UnwindExceptionToCallerStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
+    public UnwindExceptionToCallerStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotForeignCallLinkage linkage) {
         super(runtime, replacements, target, linkage);
     }
 
@@ -97,8 +97,8 @@
         return enabled || graalRuntime().getConfig().cAssertions;
     }
 
-    public static final Descriptor EXCEPTION_HANDLER_FOR_RETURN_ADDRESS = descriptorFor(UnwindExceptionToCallerStub.class, "exceptionHandlerForReturnAddress", false);
+    public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_RETURN_ADDRESS = descriptorFor(UnwindExceptionToCallerStub.class, "exceptionHandlerForReturnAddress");
 
-    @NodeIntrinsic(value = CRuntimeCall.class, setStampFromReturnType = true)
-    public static native Word exceptionHandlerForReturnAddress(@ConstantNodeParameter Descriptor exceptionHandlerForReturnAddress, Word thread, Word returnAddress);
+    @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true)
+    public static native Word exceptionHandlerForReturnAddress(@ConstantNodeParameter ForeignCallDescriptor exceptionHandlerForReturnAddress, Word thread, Word returnAddress);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/VMErrorStub.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.hotspot.stubs;
-
-import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
-import static com.oracle.graal.hotspot.stubs.StubUtil.*;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.Node.ConstantNodeParameter;
-import com.oracle.graal.graph.Node.NodeIntrinsic;
-import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.replacements.*;
-import com.oracle.graal.word.*;
-
-/**
- * Stub called from {@link VMErrorNode}.
- */
-public class VMErrorStub extends CRuntimeStub {
-
-    public VMErrorStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
-        super(runtime, replacements, target, linkage);
-    }
-
-    @Snippet
-    private static void vmError(String where, String format, long value) {
-        vmErrorC(VM_ERROR_C, thread(), where, format, value);
-    }
-
-    public static final Descriptor VM_ERROR_C = descriptorFor(VMErrorStub.class, "vmErrorC", false);
-
-    @NodeIntrinsic(CRuntimeCall.class)
-    public static native void vmErrorC(@ConstantNodeParameter Descriptor vmErrorC, Word thread, String where, String format, long value);
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/VerifyOopStub.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/VerifyOopStub.java	Tue May 21 19:51:00 2013 +0200
@@ -27,16 +27,15 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.replacements.*;
 
 /**
- * Stub called from {@link VerifyOopStubCall}.
+ * Stub called via {@link HotSpotRuntime#VERIFY_OOP}.
  */
-public class VerifyOopStub extends CRuntimeStub {
+public class VerifyOopStub extends SnippetStub {
 
-    public VerifyOopStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotRuntimeCallTarget linkage) {
+    public VerifyOopStub(final HotSpotRuntime runtime, Replacements replacements, TargetDescription target, HotSpotForeignCallLinkage linkage) {
         super(runtime, replacements, target, linkage);
     }
 
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Tue May 21 19:51:00 2013 +0200
@@ -358,6 +358,7 @@
      * @param object the object whose monitor will be locked.
      */
     public void pushLock(ValueNode object) {
+        assert object.isAlive() && object.kind() == Kind.Object : "unexpected value: " + object;
         locks = Arrays.copyOf(locks, locks.length + 1);
         locks[locks.length - 1] = object;
     }
@@ -406,7 +407,7 @@
      * @param x the instruction which produces the value for the local
      */
     public void storeLocal(int i, ValueNode x) {
-        assert x == null || x.kind() != Kind.Void && x.kind() != Kind.Illegal : "unexpected value: " + x;
+        assert x == null || x.isAlive() && x.kind() != Kind.Void && x.kind() != Kind.Illegal : "unexpected value: " + x;
         locals[i] = x;
         if (x != null && isTwoSlot(x.kind())) {
             // if this is a double word, then kill i+1
@@ -422,7 +423,7 @@
     }
 
     private void storeStack(int i, ValueNode x) {
-        assert x == null || stack[i] == null || x.kind() == stack[i].kind() : "Method does not handle changes from one-slot to two-slot values";
+        assert x == null || x.isAlive() && (stack[i] == null || x.kind() == stack[i].kind()) : "Method does not handle changes from one-slot to two-slot values or non-alive values";
         stack[i] = x;
     }
 
@@ -433,7 +434,7 @@
      * @param x the instruction to push onto the stack
      */
     public void push(Kind kind, ValueNode x) {
-        assert !x.isDeleted() && x.kind() != Kind.Void && x.kind() != Kind.Illegal;
+        assert x.isAlive() && x.kind() != Kind.Void && x.kind() != Kind.Illegal;
         xpush(assertKind(kind, x));
         if (isTwoSlot(kind)) {
             xpush(null);
@@ -446,7 +447,7 @@
      * @param x the instruction to push onto the stack
      */
     public void xpush(ValueNode x) {
-        assert x == null || (!x.isDeleted() && x.kind() != Kind.Void && x.kind() != Kind.Illegal);
+        assert x == null || (x.isAlive() && x.kind() != Kind.Void && x.kind() != Kind.Illegal);
         stack[stackSize++] = x;
     }
 
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue May 21 19:51:00 2013 +0200
@@ -33,7 +33,6 @@
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.meta.ProfilingInfo.TriState;
 import com.oracle.graal.api.meta.ResolvedJavaType.Representation;
@@ -59,8 +58,8 @@
 
     public static final class RuntimeCalls {
 
-        public static final Descriptor CREATE_NULL_POINTER_EXCEPTION = new Descriptor("createNullPointerException", true, Object.class);
-        public static final Descriptor CREATE_OUT_OF_BOUNDS_EXCEPTION = new Descriptor("createOutOfBoundsException", true, Object.class, int.class);
+        public static final ForeignCallDescriptor CREATE_NULL_POINTER_EXCEPTION = new ForeignCallDescriptor("createNullPointerException", Object.class);
+        public static final ForeignCallDescriptor CREATE_OUT_OF_BOUNDS_EXCEPTION = new ForeignCallDescriptor("createOutOfBoundsException", Object.class, int.class);
     }
 
     /**
@@ -305,8 +304,8 @@
      * @param type the unresolved type of the constant
      */
     protected void handleUnresolvedLoadConstant(JavaType type) {
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
-        frameState.push(Kind.Object, append(ConstantNode.forObject(null, runtime, currentGraph)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+        frameState.push(Kind.Object, appendConstant(Constant.NULL_OBJECT));
     }
 
     /**
@@ -314,7 +313,7 @@
      * @param object the object value whose type is being checked against {@code type}
      */
     protected void handleUnresolvedCheckCast(JavaType type, ValueNode object) {
-        append(currentGraph.add(new FixedGuardNode(currentGraph.unique(new IsNullNode(object)), Unresolved, InvalidateRecompile)));
+        append(new FixedGuardNode(currentGraph.unique(new IsNullNode(object)), Unresolved, InvalidateRecompile));
         frameState.apush(appendConstant(Constant.NULL_OBJECT));
     }
 
@@ -325,8 +324,7 @@
     protected void handleUnresolvedInstanceOf(JavaType type, ValueNode object) {
         BlockPlaceholderNode successor = currentGraph.add(new BlockPlaceholderNode());
         DeoptimizeNode deopt = currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved));
-        IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new IsNullNode(object)), successor, deopt, 1));
-        append(ifNode);
+        append(new IfNode(currentGraph.unique(new IsNullNode(object)), successor, deopt, 1));
         lastInstr = successor;
         frameState.ipush(appendConstant(Constant.INT_0));
     }
@@ -335,7 +333,7 @@
      * @param type the type being instantiated
      */
     protected void handleUnresolvedNewInstance(JavaType type) {
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
         frameState.apush(appendConstant(Constant.NULL_OBJECT));
     }
 
@@ -344,7 +342,7 @@
      * @param length the length of the array
      */
     protected void handleUnresolvedNewObjectArray(JavaType type, ValueNode length) {
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
         frameState.apush(appendConstant(Constant.NULL_OBJECT));
     }
 
@@ -353,7 +351,7 @@
      * @param dims the dimensions for the multi-array
      */
     protected void handleUnresolvedNewMultiArray(JavaType type, ValueNode[] dims) {
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
         frameState.apush(appendConstant(Constant.NULL_OBJECT));
     }
 
@@ -363,8 +361,8 @@
      */
     protected void handleUnresolvedLoadField(JavaField field, ValueNode receiver) {
         Kind kind = field.getKind();
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
-        frameState.push(kind.getStackKind(), append(ConstantNode.defaultForKind(kind, currentGraph)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
+        frameState.push(kind.getStackKind(), appendConstant(Constant.defaultForKind(kind)));
     }
 
     /**
@@ -373,7 +371,7 @@
      * @param receiver the object containing the field or {@code null} if {@code field} is static
      */
     protected void handleUnresolvedStoreField(JavaField field, ValueNode value, ValueNode receiver) {
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
     }
 
     /**
@@ -381,16 +379,16 @@
      * @param type
      */
     protected void handleUnresolvedExceptionType(Representation representation, JavaType type) {
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
     }
 
     protected void handleUnresolvedInvoke(JavaMethod javaMethod, InvokeKind invokeKind) {
         boolean withReceiver = invokeKind != InvokeKind.Static;
-        append(currentGraph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)));
+        append(new DeoptimizeNode(InvalidateRecompile, Unresolved));
         frameState.popArguments(javaMethod.getSignature().getParameterSlots(withReceiver), javaMethod.getSignature().getParameterCount(withReceiver));
         Kind kind = javaMethod.getSignature().getReturnKind();
         if (kind != Kind.Void) {
-            frameState.push(kind.getStackKind(), append(ConstantNode.defaultForKind(kind, currentGraph)));
+            frameState.push(kind.getStackKind(), appendConstant(Constant.defaultForKind(kind)));
         }
     }
 
@@ -433,7 +431,7 @@
             // this is a load of class constant which might be unresolved
             JavaType type = (JavaType) con;
             if (type instanceof ResolvedJavaType) {
-                frameState.push(Kind.Object, append(ConstantNode.forConstant(((ResolvedJavaType) type).getEncoding(Representation.JavaClass), runtime, currentGraph)));
+                frameState.push(Kind.Object, appendConstant(((ResolvedJavaType) type).getEncoding(Representation.JavaClass)));
             } else {
                 handleUnresolvedLoadConstant(type);
             }
@@ -450,8 +448,7 @@
 
         ValueNode index = frameState.ipop();
         ValueNode array = frameState.apop();
-        ValueNode v = append(currentGraph.add(new LoadIndexedNode(array, index, kind)));
-        frameState.push(kind.getStackKind(), v);
+        frameState.push(kind.getStackKind(), append(new LoadIndexedNode(array, index, kind)));
     }
 
     private void genStoreIndexed(Kind kind) {
@@ -460,8 +457,7 @@
         ValueNode value = frameState.pop(kind.getStackKind());
         ValueNode index = frameState.ipop();
         ValueNode array = frameState.apop();
-        StoreIndexedNode result = currentGraph.add(new StoreIndexedNode(array, index, kind, value));
-        append(result);
+        append(new StoreIndexedNode(array, index, kind, value));
     }
 
     private void stackOp(int opcode) {
@@ -586,8 +582,7 @@
             default:
                 throw new GraalInternalError("should not reach");
         }
-        ValueNode result1 = append(currentGraph.unique(v));
-        frameState.push(result, result1);
+        frameState.push(result, append(v));
     }
 
     private void genIntegerDivOp(Kind result, int opcode) {
@@ -606,12 +601,11 @@
             default:
                 throw new GraalInternalError("should not reach");
         }
-        ValueNode result1 = append(currentGraph.add(v));
-        frameState.push(result, result1);
+        frameState.push(result, append(v));
     }
 
     private void genNegateOp(Kind kind) {
-        frameState.push(kind, append(currentGraph.unique(new NegateNode(frameState.pop(kind)))));
+        frameState.push(kind, append(new NegateNode(frameState.pop(kind))));
     }
 
     private void genShiftOp(Kind kind, int opcode) {
@@ -634,7 +628,7 @@
             default:
                 throw new GraalInternalError("should not reach");
         }
-        frameState.push(kind, append(currentGraph.unique(v)));
+        frameState.push(kind, append(v));
     }
 
     private void genLogicOp(Kind kind, int opcode) {
@@ -657,26 +651,26 @@
             default:
                 throw new GraalInternalError("should not reach");
         }
-        frameState.push(kind, append(currentGraph.unique(v)));
+        frameState.push(kind, append(v));
     }
 
     private void genCompareOp(Kind kind, boolean isUnorderedLess) {
         ValueNode y = frameState.pop(kind);
         ValueNode x = frameState.pop(kind);
-        frameState.ipush(append(currentGraph.unique(new NormalizeCompareNode(x, y, isUnorderedLess))));
+        frameState.ipush(append(new NormalizeCompareNode(x, y, isUnorderedLess)));
     }
 
     private void genConvert(ConvertNode.Op opcode) {
         ValueNode input = frameState.pop(opcode.from.getStackKind());
-        frameState.push(opcode.to.getStackKind(), append(currentGraph.unique(new ConvertNode(opcode, input))));
+        frameState.push(opcode.to.getStackKind(), append(new ConvertNode(opcode, input)));
     }
 
     private void genIncrement() {
         int index = stream().readLocalIndex();
         int delta = stream().readIncrement();
         ValueNode x = frameState.loadLocal(index);
-        ValueNode y = append(ConstantNode.forInt(delta, currentGraph));
-        frameState.storeLocal(index, append(currentGraph.unique(new IntegerAddNode(Kind.Int, x, y))));
+        ValueNode y = appendConstant(Constant.forInt(delta));
+        frameState.storeLocal(index, append(new IntegerAddNode(Kind.Int, x, y)));
     }
 
     private void genGoto() {
@@ -726,7 +720,7 @@
         AbstractBeginNode falseSuccessor = createBlockTarget(1 - probability, falseBlock, frameState);
 
         IfNode ifNode = negate ? new IfNode(condition, falseSuccessor, trueSuccessor, 1 - probability) : new IfNode(condition, trueSuccessor, falseSuccessor, probability);
-        append(currentGraph.add(ifNode));
+        append(ifNode);
     }
 
     private void genIfZero(Condition cond) {
@@ -750,9 +744,8 @@
 
     private void genThrow() {
         ValueNode exception = frameState.apop();
-        FixedGuardNode node = currentGraph.add(new FixedGuardNode(currentGraph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
-        append(node);
-        append(handleException(exception, bci()));
+        append(new FixedGuardNode(currentGraph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
+        lastInstr.setNext(handleException(exception, bci()));
     }
 
     private JavaType lookupType(int cpi, int bytecode) {
@@ -804,9 +797,8 @@
         ValueNode object = frameState.apop();
         if (type instanceof ResolvedJavaType) {
             JavaTypeProfile profileForTypeCheck = getProfileForTypeCheck((ResolvedJavaType) type);
-            CheckCastNode checkCast = currentGraph.add(new CheckCastNode((ResolvedJavaType) type, object, profileForTypeCheck, false));
-            append(checkCast);
-            frameState.apush(checkCast);
+            CheckCastNode checkCastNode = append(new CheckCastNode((ResolvedJavaType) type, object, profileForTypeCheck, false));
+            frameState.apush(checkCastNode);
         } else {
             handleUnresolvedCheckCast(type, object);
         }
@@ -819,8 +811,7 @@
         if (type instanceof ResolvedJavaType) {
             ResolvedJavaType resolvedType = (ResolvedJavaType) type;
             InstanceOfNode instanceOfNode = new InstanceOfNode((ResolvedJavaType) type, object, getProfileForTypeCheck(resolvedType));
-            ConditionalNode conditional = currentGraph.unique(new ConditionalNode(currentGraph.unique(instanceOfNode), ConstantNode.forInt(1, currentGraph), ConstantNode.forInt(0, currentGraph)));
-            frameState.ipush(append(conditional));
+            frameState.ipush(append(new ConditionalNode(currentGraph.unique(instanceOfNode), ConstantNode.forInt(1, currentGraph), ConstantNode.forInt(0, currentGraph))));
         } else {
             handleUnresolvedInstanceOf(type, object);
         }
@@ -829,8 +820,7 @@
     void genNewInstance(int cpi) {
         JavaType type = lookupType(cpi, NEW);
         if (type instanceof ResolvedJavaType && ((ResolvedJavaType) type).isInitialized()) {
-            NewInstanceNode n = currentGraph.add(new NewInstanceNode((ResolvedJavaType) type, true));
-            frameState.apush(append(n));
+            frameState.apush(append(new NewInstanceNode((ResolvedJavaType) type, true)));
         } else {
             handleUnresolvedNewInstance(type);
         }
@@ -871,16 +861,14 @@
     private void genNewPrimitiveArray(int typeCode) {
         Class<?> clazz = arrayTypeCodeToClass(typeCode);
         ResolvedJavaType elementType = runtime.lookupJavaType(clazz);
-        NewArrayNode nta = currentGraph.add(new NewArrayNode(elementType, frameState.ipop(), true));
-        frameState.apush(append(nta));
+        frameState.apush(append(new NewArrayNode(elementType, frameState.ipop(), true)));
     }
 
     private void genNewObjectArray(int cpi) {
         JavaType type = lookupType(cpi, ANEWARRAY);
         ValueNode length = frameState.ipop();
         if (type instanceof ResolvedJavaType) {
-            NewArrayNode n = currentGraph.add(new NewArrayNode((ResolvedJavaType) type, length, true));
-            frameState.apush(append(n));
+            frameState.apush(append(new NewArrayNode((ResolvedJavaType) type, length, true)));
         } else {
             handleUnresolvedNewObjectArray(type, length);
         }
@@ -895,8 +883,7 @@
             dims[i] = frameState.ipop();
         }
         if (type instanceof ResolvedJavaType) {
-            FixedWithNextNode n = currentGraph.add(new NewMultiArrayNode((ResolvedJavaType) type, dims));
-            frameState.apush(append(n));
+            frameState.apush(append(new NewMultiArrayNode((ResolvedJavaType) type, dims)));
         } else {
             handleUnresolvedNewMultiArray(type, dims);
         }
@@ -908,8 +895,7 @@
         Kind kind = field.getKind();
         ValueNode receiver = frameState.apop();
         if ((field instanceof ResolvedJavaField) && ((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
-            LoadFieldNode load = currentGraph.add(new LoadFieldNode(receiver, (ResolvedJavaField) field));
-            appendOptimizedLoadField(kind, load);
+            appendOptimizedLoadField(kind, new LoadFieldNode(receiver, (ResolvedJavaField) field));
         } else {
             handleUnresolvedLoadField(field, receiver);
         }
@@ -932,16 +918,14 @@
         }
         BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode());
         BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode());
-        IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new IsNullNode(receiver)), trueSucc, falseSucc, 0.1));
-
-        append(ifNode);
+        append(new IfNode(currentGraph.unique(new IsNullNode(receiver)), trueSucc, falseSucc, 0.1));
         lastInstr = falseSucc;
 
         if (GraalOptions.OmitHotExceptionStacktrace) {
             ValueNode exception = ConstantNode.forObject(cachedNullPointerException, runtime, currentGraph);
             trueSucc.setNext(handleException(exception, bci()));
         } else {
-            RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CREATE_NULL_POINTER_EXCEPTION));
+            ForeignCallStateSplitNode call = currentGraph.add(new ForeignCallStateSplitNode(runtime, CREATE_NULL_POINTER_EXCEPTION));
             call.setStateAfter(frameState.create(bci()));
             trueSucc.setNext(call);
             call.setNext(handleException(call, bci()));
@@ -958,16 +942,14 @@
     private void emitBoundsCheck(ValueNode index, ValueNode length) {
         BlockPlaceholderNode trueSucc = currentGraph.add(new BlockPlaceholderNode());
         BlockPlaceholderNode falseSucc = currentGraph.add(new BlockPlaceholderNode());
-        IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new IntegerBelowThanNode(index, length)), trueSucc, falseSucc, 0.9));
-
-        append(ifNode);
+        append(new IfNode(currentGraph.unique(new IntegerBelowThanNode(index, length)), trueSucc, falseSucc, 0.9));
         lastInstr = trueSucc;
 
         if (GraalOptions.OmitHotExceptionStacktrace) {
             ValueNode exception = ConstantNode.forObject(cachedArrayIndexOutOfBoundsException, runtime, currentGraph);
             falseSucc.setNext(handleException(exception, bci()));
         } else {
-            RuntimeCallNode call = currentGraph.add(new RuntimeCallNode(CREATE_OUT_OF_BOUNDS_EXCEPTION, index));
+            ForeignCallStateSplitNode call = currentGraph.add(new ForeignCallStateSplitNode(runtime, CREATE_OUT_OF_BOUNDS_EXCEPTION, index));
             call.setStateAfter(frameState.create(bci()));
             falseSucc.setNext(call);
             call.setNext(handleException(call, bci()));
@@ -982,7 +964,7 @@
 
         emitNullCheck(receiver);
         if (outOfBoundsIndex != null) {
-            ValueNode length = append(currentGraph.add(new ArrayLengthNode(receiver)));
+            ValueNode length = append(new ArrayLengthNode(receiver));
             emitBoundsCheck(outOfBoundsIndex, length);
         }
         Debug.metric("ExplicitExceptions").increment();
@@ -994,8 +976,7 @@
         ValueNode value = frameState.pop(field.getKind().getStackKind());
         ValueNode receiver = frameState.apop();
         if (field instanceof ResolvedJavaField && ((ResolvedJavaField) field).getDeclaringClass().isInitialized()) {
-            StoreFieldNode store = currentGraph.add(new StoreFieldNode(receiver, (ResolvedJavaField) field, value));
-            appendOptimizedStoreField(store);
+            appendOptimizedStoreField(new StoreFieldNode(receiver, (ResolvedJavaField) field, value));
         } else {
             handleUnresolvedStoreField(field, value, receiver);
         }
@@ -1004,8 +985,7 @@
     private void genGetStatic(JavaField field) {
         Kind kind = field.getKind();
         if (field instanceof ResolvedJavaField && ((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
-            LoadFieldNode load = currentGraph.add(new LoadFieldNode(null, (ResolvedJavaField) field));
-            appendOptimizedLoadField(kind, load);
+            appendOptimizedLoadField(kind, new LoadFieldNode(null, (ResolvedJavaField) field));
         } else {
             handleUnresolvedLoadField(field, null);
         }
@@ -1014,8 +994,7 @@
     private void genPutStatic(JavaField field) {
         ValueNode value = frameState.pop(field.getKind().getStackKind());
         if (field instanceof ResolvedJavaField && ((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) {
-            StoreFieldNode store = currentGraph.add(new StoreFieldNode(null, (ResolvedJavaField) field, value));
-            appendOptimizedStoreField(store);
+            appendOptimizedStoreField(new StoreFieldNode(null, (ResolvedJavaField) field, value));
         } else {
             handleUnresolvedStoreField(field, value, null);
         }
@@ -1142,8 +1121,7 @@
     private void appendInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args) {
         Kind resultType = targetMethod.getSignature().getReturnKind();
         if (GraalOptions.DeoptALot) {
-            DeoptimizeNode deoptimize = currentGraph.add(new DeoptimizeNode(DeoptimizationAction.None, RuntimeConstraint));
-            append(deoptimize);
+            append(new DeoptimizeNode(DeoptimizationAction.None, RuntimeConstraint));
             frameState.pushReturn(resultType, ConstantNode.defaultForKind(resultType, currentGraph));
             return;
         }
@@ -1164,15 +1142,12 @@
         // be conservative if information was not recorded (could result in endless recompiles
         // otherwise)
         if (graphBuilderConfig.omitAllExceptionEdges() || (optimisticOpts.useExceptionProbability() && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE)) {
-            InvokeNode invoke = new InvokeNode(callTarget, bci());
-            ValueNode result = appendWithBCI(currentGraph.add(invoke));
-            frameState.pushReturn(resultType, result);
-            return invoke;
+            frameState.pushReturn(resultType, append(new InvokeNode(callTarget, bci())));
+            return new InvokeNode(callTarget, bci());
         } else {
             DispatchBeginNode exceptionEdge = handleException(null, bci());
-            InvokeWithExceptionNode invoke = currentGraph.add(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci()));
-            ValueNode result = append(invoke);
-            frameState.pushReturn(resultType, result);
+            InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci()));
+            frameState.pushReturn(resultType, invoke);
             Block nextBlock = currentBlock.successors.get(0);
 
             assert bci() == currentBlock.endBci;
@@ -1193,19 +1168,17 @@
     }
 
     private MonitorEnterNode genMonitorEnter(ValueNode x) {
-        MonitorEnterNode monitorEnter = currentGraph.add(new MonitorEnterNode(x, frameState.lockDepth()));
+        MonitorEnterNode monitorEnter = append(new MonitorEnterNode(x, frameState.lockDepth()));
         frameState.pushLock(x);
-        appendWithBCI(monitorEnter);
         return monitorEnter;
     }
 
     private MonitorExitNode genMonitorExit(ValueNode x) {
         ValueNode lockedObject = frameState.popLock();
-        MonitorExitNode monitorExit = currentGraph.add(new MonitorExitNode(x, frameState.lockDepth()));
         if (GraphUtil.originalValue(lockedObject) != GraphUtil.originalValue(x)) {
             throw new BailoutException("unbalanced monitors: mismatch at monitorexit, %s != %s", GraphUtil.originalValue(x), GraphUtil.originalValue(lockedObject));
         }
-        appendWithBCI(monitorExit);
+        MonitorExitNode monitorExit = append(new MonitorExitNode(x, frameState.lockDepth()));
         return monitorExit;
     }
 
@@ -1228,7 +1201,7 @@
         ValueNode local = frameState.loadLocal(localIndex);
         JsrScope scope = currentBlock.jsrScope;
         int retAddress = scope.nextReturnAddress();
-        append(currentGraph.add(new FixedGuardNode(currentGraph.unique(new IntegerEqualsNode(local, ConstantNode.forInt(retAddress, currentGraph))), JavaSubroutineMismatch, InvalidateReprofile)));
+        append(new FixedGuardNode(currentGraph.unique(new IntegerEqualsNode(local, ConstantNode.forInt(retAddress, currentGraph))), JavaSubroutineMismatch, InvalidateReprofile));
         if (!successor.jsrScope.equals(scope.pop())) {
             throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)");
         }
@@ -1315,12 +1288,11 @@
         }
 
         double[] successorProbabilities = successorProbabilites(actualSuccessors.size(), keySuccessors, keyProbabilities);
-        IntegerSwitchNode switchNode = currentGraph.add(new IntegerSwitchNode(value, actualSuccessors.size(), keys, keyProbabilities, keySuccessors));
+        IntegerSwitchNode switchNode = append(new IntegerSwitchNode(value, actualSuccessors.size(), keys, keyProbabilities, keySuccessors));
         for (int i = 0; i < actualSuccessors.size(); i++) {
             switchNode.setBlockSuccessor(i, createBlockTarget(successorProbabilities[i], actualSuccessors.get(i), frameState));
         }
 
-        append(switchNode);
     }
 
     private static class SuccessorInfo {
@@ -1339,26 +1311,37 @@
         return ConstantNode.forConstant(constant, runtime, currentGraph);
     }
 
-    private ValueNode append(FixedNode fixed) {
-        lastInstr.setNext(fixed);
+    private <T extends ControlSinkNode> T append(T fixed) {
+        assert !fixed.isAlive() && !fixed.isDeleted() : "instruction should not have been appended yet";
+        assert lastInstr.next() == null : "cannot append instruction to instruction which isn't end (" + lastInstr + "->" + lastInstr.next() + ")";
+        T added = currentGraph.add(fixed);
+        lastInstr.setNext(added);
         lastInstr = null;
-        return fixed;
-    }
-
-    protected ValueNode append(FixedWithNextNode x) {
-        return appendWithBCI(x);
+        return added;
     }
 
-    private static ValueNode append(ValueNode v) {
-        return v;
+    private <T extends ControlSplitNode> T append(T fixed) {
+        assert !fixed.isAlive() && !fixed.isDeleted() : "instruction should not have been appended yet";
+        assert lastInstr.next() == null : "cannot append instruction to instruction which isn't end (" + lastInstr + "->" + lastInstr.next() + ")";
+        T added = currentGraph.add(fixed);
+        lastInstr.setNext(added);
+        lastInstr = null;
+        return added;
     }
 
-    protected ValueNode appendWithBCI(FixedWithNextNode x) {
-        assert x.predecessor() == null : "instruction should not have been appended yet";
+    protected <T extends FixedWithNextNode> T append(T fixed) {
+        assert !fixed.isAlive() && !fixed.isDeleted() : "instruction should not have been appended yet";
         assert lastInstr.next() == null : "cannot append instruction to instruction which isn't end (" + lastInstr + "->" + lastInstr.next() + ")";
-        lastInstr.setNext(x);
-        lastInstr = x;
-        return x;
+        T added = currentGraph.add(fixed);
+        lastInstr.setNext(added);
+        lastInstr = added;
+        return added;
+    }
+
+    private <T extends FloatingNode> T append(T v) {
+        assert !(v instanceof ConstantNode);
+        T added = currentGraph.unique(v);
+        return added;
     }
 
     private static class Target {
@@ -1525,7 +1508,7 @@
 
     private ValueNode synchronizedObject(FrameStateBuilder state, ResolvedJavaMethod target) {
         if (isStatic(target.getModifiers())) {
-            return append(ConstantNode.forConstant(target.getDeclaringClass().getEncoding(Representation.JavaClass), runtime, currentGraph));
+            return appendConstant(target.getDeclaringClass().getEncoding(Representation.JavaClass));
         } else {
             return state.loadLocal(0);
         }
@@ -1590,11 +1573,9 @@
     private void createUnwind() {
         assert frameState.stackSize() == 1 : frameState;
         ValueNode exception = frameState.apop();
-        FixedGuardNode guard = currentGraph.add(new FixedGuardNode(currentGraph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
-        append(guard);
+        append(new FixedGuardNode(currentGraph.unique(new IsNullNode(exception)), NullCheckException, InvalidateReprofile, true));
         synchronizedEpilogue(FrameState.AFTER_EXCEPTION_BCI);
-        UnwindNode unwindNode = currentGraph.add(new UnwindNode(exception));
-        append(unwindNode);
+        append(new UnwindNode(exception));
     }
 
     private void createReturn() {
@@ -1603,7 +1584,7 @@
         assert frameState.stackSize() == 0;
 
         if (Modifier.isSynchronized(method.getModifiers())) {
-            append(currentGraph.add(new ValueAnchorNode(true, x)));
+            append(new ValueAnchorNode(true, x));
             assert !frameState.rethrowException();
         }
 
@@ -1611,15 +1592,13 @@
         if (frameState.lockDepth() != 0) {
             throw new BailoutException("unbalanced monitors");
         }
-        ReturnNode returnNode = currentGraph.add(new ReturnNode(x));
 
         if (graphBuilderConfig.eagerInfopointMode()) {
-            InfopointNode ipn = currentGraph.add(new InfopointNode(InfopointReason.METHOD_END));
+            InfopointNode ipn = append(new InfopointNode(InfopointReason.METHOD_END));
             ipn.setStateAfter(frameState.create(FrameState.AFTER_BCI));
-            append(ipn);
         }
 
-        append(returnNode);
+        append(new ReturnNode(x));
     }
 
     private void synchronizedEpilogue(int bci) {
@@ -1665,8 +1644,7 @@
             frameState.push(Kind.Object, exception);
             FixedNode nextDispatch = createTarget(nextBlock, frameState);
             checkCast.setNext(catchSuccessor);
-            IfNode ifNode = currentGraph.add(new IfNode(currentGraph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), checkCast, nextDispatch, 0.5));
-            append(ifNode);
+            append(new IfNode(currentGraph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), checkCast, nextDispatch, 0.5));
         }
     }
 
@@ -1732,9 +1710,8 @@
             if (graphBuilderConfig.eagerInfopointMode() && lnt != null) {
                 currentLineNumber = lnt.getLineNumber(bci);
                 if (currentLineNumber != previousLineNumber) {
-                    InfopointNode ipn = currentGraph.add(new InfopointNode(InfopointReason.LINE_NUMBER));
+                    InfopointNode ipn = append(new InfopointNode(InfopointReason.LINE_NUMBER));
                     ipn.setStateAfter(frameState.create(bci));
-                    append(ipn);
                     previousLineNumber = currentLineNumber;
                 }
             }
@@ -1747,8 +1724,7 @@
                 if (block.jsrScope != JsrScope.EMPTY_SCOPE) {
                     throw new BailoutException("OSR into a JSR scope is not supported");
                 }
-                EntryMarkerNode x = currentGraph.add(new EntryMarkerNode());
-                append(x);
+                EntryMarkerNode x = append(new EntryMarkerNode());
                 frameState.insertProxies(x);
                 x.setStateAfter(frameState.create(bci));
             }
@@ -2037,6 +2013,6 @@
     }
 
     private void genArrayLength() {
-        frameState.ipush(append(currentGraph.add(new ArrayLengthNode(frameState.apop()))));
+        frameState.ipush(append(new ArrayLengthNode(frameState.apop())));
     }
 }
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Call.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Call.java	Tue May 21 19:51:00 2013 +0200
@@ -102,11 +102,11 @@
         }
     }
 
-    public abstract static class RuntimeCallOp extends CallOp {
+    public abstract static class ForeignCallOp extends CallOp {
 
-        protected final RuntimeCallTarget callTarget;
+        protected final ForeignCallLinkage callTarget;
 
-        public RuntimeCallOp(RuntimeCallTarget callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
+        public ForeignCallOp(ForeignCallLinkage callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
             super(result, parameters, temps, state);
             this.callTarget = callTarget;
         }
@@ -117,11 +117,11 @@
         }
     }
 
-    @Opcode("CALL_NEAR_RUNTIME")
-    public static class DirectNearRuntimeCallOp extends RuntimeCallOp {
+    @Opcode("NEAR_FOREIGN_CALL")
+    public static class DirectNearForeignCallOp extends ForeignCallOp {
 
-        public DirectNearRuntimeCallOp(RuntimeCallTarget callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
-            super(callTarget, result, parameters, temps, state);
+        public DirectNearForeignCallOp(ForeignCallLinkage linkage, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
+            super(linkage, result, parameters, temps, state);
         }
 
         @Override
@@ -130,12 +130,12 @@
         }
     }
 
-    @Opcode("CALL_FAR_RUNTIME")
-    public static class DirectFarRuntimeCallOp extends RuntimeCallOp {
+    @Opcode("FAR_FOREIGN_CALL")
+    public static class DirectFarForeignCallOp extends ForeignCallOp {
 
         @Temp({REG}) protected AllocatableValue callTemp;
 
-        public DirectFarRuntimeCallOp(LIRGeneratorTool gen, RuntimeCallTarget callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
+        public DirectFarForeignCallOp(LIRGeneratorTool gen, ForeignCallLinkage callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
             super(callTarget, result, parameters, temps, state);
             callTemp = gen.newVariable(Kind.Long);
         }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRVerifier.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRVerifier.java	Tue May 21 19:51:00 2013 +0200
@@ -31,7 +31,9 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
-import com.oracle.graal.lir.LIRInstruction.*;
+import com.oracle.graal.lir.LIRInstruction.OperandFlag;
+import com.oracle.graal.lir.LIRInstruction.OperandMode;
+import com.oracle.graal.lir.LIRInstruction.ValueProcedure;
 import com.oracle.graal.nodes.cfg.*;
 
 public final class LIRVerifier {
@@ -230,14 +232,17 @@
         return value;
     }
 
+    // @formatter:off
     private static Value allowed(Object op, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
-        if ((isVariable(value) && flags.contains(OperandFlag.REG)) || (isRegister(value) && flags.contains(OperandFlag.REG)) || (isStackSlot(value) && flags.contains(OperandFlag.STACK)) ||
-                        (isConstant(value) && flags.contains(OperandFlag.CONST) && mode != OperandMode.DEF) || (isIllegal(value) && flags.contains(OperandFlag.ILLEGAL))) {
+        if ((isVariable(value) && flags.contains(OperandFlag.REG)) ||
+            (isRegister(value) && flags.contains(OperandFlag.REG)) ||
+            (isStackSlot(value) && flags.contains(OperandFlag.STACK)) ||
+            (isConstant(value) && flags.contains(OperandFlag.CONST) && mode != OperandMode.DEF) ||
+            (isIllegal(value) && flags.contains(OperandFlag.ILLEGAL))) {
             return value;
         }
-        TTY.println("instruction %s", op);
-        TTY.println("mode: %s  flags: %s", mode, flags);
-        TTY.println("Unexpected value: %s %s", value.getClass().getSimpleName(), value);
-        throw GraalInternalError.shouldNotReachHere();
+        throw new GraalInternalError("Invalid LIR%n  Instruction: %s%n  Mode: %s%n  Flags: %s%n  Unexpected value: %s %s",
+                        op, mode, flags, value.getClass().getSimpleName(), value);
     }
+    // @formatter:on
 }
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopSafepointEliminationPhase.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopSafepointEliminationPhase.java	Tue May 21 19:51:00 2013 +0200
@@ -52,7 +52,7 @@
                 blocks: while (b != loop.lirLoop().header) {
                     assert b != null;
                     for (FixedNode node : b.getNodes()) {
-                        if (node instanceof Invoke || node instanceof RuntimeCallNode) {
+                        if (node instanceof Invoke || node instanceof ForeignCallNode) {
                             loopEnd.disableSafepoint();
                             break blocks;
                         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Tue May 21 19:51:00 2013 +0200
@@ -218,7 +218,7 @@
     }
 
     /**
-     * Unlinks a node from all its control flow neighbours and then removes it from its graph. The
+     * Unlinks a node from all its control flow neighbors and then removes it from its graph. The
      * node must have no {@linkplain Node#usages() usages}.
      * 
      * @param node the node to be unlinked and removed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,99 @@
+/*
+ * 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.nodes.extended;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.LocationNode.LocationIdentity;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * Node for a {@linkplain ForeignCallDescriptor foreign} call.
+ */
+@NodeInfo(nameTemplate = "ForeignCall#{p#descriptor/s}")
+public class ForeignCallNode extends DeoptimizingFixedWithNextNode implements LIRLowerable, DeoptimizingNode, MemoryCheckpoint {
+
+    @Input private final NodeInputList<ValueNode> arguments;
+
+    private final ForeignCallDescriptor descriptor;
+
+    public ForeignCallNode(ForeignCallDescriptor descriptor, ValueNode... arguments) {
+        super(StampFactory.forKind(Kind.fromJavaClass(descriptor.getResultType())));
+        this.arguments = new NodeInputList<>(this, arguments);
+        this.descriptor = descriptor;
+    }
+
+    protected ForeignCallNode(ForeignCallDescriptor descriptor, Stamp stamp) {
+        super(stamp);
+        this.arguments = new NodeInputList<>(this);
+        this.descriptor = descriptor;
+    }
+
+    public ForeignCallDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    @Override
+    public LocationIdentity[] getLocationIdentities() {
+        return new LocationIdentity[]{LocationNode.ANY_LOCATION};
+    }
+
+    protected Value[] operands(LIRGeneratorTool gen) {
+        Value[] operands = new Value[arguments.size()];
+        for (int i = 0; i < operands.length; i++) {
+            operands[i] = gen.operand(arguments.get(i));
+        }
+        return operands;
+    }
+
+    @Override
+    public void generate(LIRGeneratorTool gen) {
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(descriptor);
+        Value[] operands = operands(gen);
+        Value result = gen.emitForeignCall(linkage, this, operands);
+        if (result != null) {
+            gen.setResult(this, result);
+        }
+    }
+
+    @Override
+    public String toString(Verbosity verbosity) {
+        if (verbosity == Verbosity.Name) {
+            return super.toString(verbosity) + "#" + descriptor;
+        }
+        return super.toString(verbosity);
+    }
+
+    @Override
+    public boolean canDeoptimize() {
+        return true;
+    }
+
+    @Override
+    public DeoptimizationReason getDeoptimizationReason() {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallStateSplitNode.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,80 @@
+/*
+ * 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.nodes.extended;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+
+/**
+ * A foreign call that is also a state split.
+ */
+@NodeInfo(nameTemplate = "ForeignCallStateSplit#{p#descriptor/s}")
+public class ForeignCallStateSplitNode extends ForeignCallNode implements LIRLowerable, StateSplit, DeoptimizingNode {
+
+    @Input(notDataflow = true) private FrameState stateAfter;
+    private MetaAccessProvider runtime;
+
+    public ForeignCallStateSplitNode(MetaAccessProvider runtime, ForeignCallDescriptor descriptor, ValueNode... arguments) {
+        super(descriptor, arguments);
+        this.runtime = runtime;
+    }
+
+    public FrameState stateAfter() {
+        return stateAfter;
+    }
+
+    public void setStateAfter(FrameState x) {
+        assert x == null || x.isAlive() : "frame state must be in a graph";
+        updateUsages(stateAfter, x);
+        stateAfter = x;
+    }
+
+    public boolean hasSideEffect() {
+        return runtime.hasSideEffect(getDescriptor());
+    }
+
+    @Override
+    public FrameState getDeoptimizationState() {
+        if (super.getDeoptimizationState() != null) {
+            return super.getDeoptimizationState();
+        } else if (stateAfter() != null) {
+            FrameState stateDuring = stateAfter();
+            if ((stateDuring.stackSize() > 0 && stateDuring.stackAt(stateDuring.stackSize() - 1) == this) || (stateDuring.stackSize() > 1 && stateDuring.stackAt(stateDuring.stackSize() - 2) == this)) {
+                stateDuring = stateDuring.duplicateModified(stateDuring.bci, stateDuring.rethrowException(), this.kind());
+            }
+            setDeoptimizationState(stateDuring);
+            return stateDuring;
+        }
+        return null;
+    }
+
+    @Override
+    public void setDeoptimizationState(FrameState f) {
+        if (super.getDeoptimizationState() != null) {
+            throw new IllegalStateException();
+        }
+        super.setDeoptimizationState(f);
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/RuntimeCallNode.java	Tue May 21 19:43:53 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +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.nodes.extended;
-
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.extended.LocationNode.LocationIdentity;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-
-@NodeInfo(nameTemplate = "RuntimeCall#{p#descriptor/s}")
-public final class RuntimeCallNode extends AbstractCallNode implements LIRLowerable, DeoptimizingNode {
-
-    private final Descriptor descriptor;
-    @Input private FrameState deoptState;
-
-    public RuntimeCallNode(Descriptor descriptor, ValueNode... arguments) {
-        super(StampFactory.forKind(Kind.fromJavaClass(descriptor.getResultType())), arguments);
-        this.descriptor = descriptor;
-    }
-
-    public Descriptor getDescriptor() {
-        return descriptor;
-    }
-
-    @Override
-    public boolean hasSideEffect() {
-        return descriptor.hasSideEffect();
-    }
-
-    @Override
-    public LocationIdentity[] getLocationIdentities() {
-        return new LocationIdentity[]{LocationNode.ANY_LOCATION};
-    }
-
-    @Override
-    public void generate(LIRGeneratorTool gen) {
-        gen.visitRuntimeCall(this);
-    }
-
-    @Override
-    public String toString(Verbosity verbosity) {
-        if (verbosity == Verbosity.Name) {
-            return super.toString(verbosity) + "#" + descriptor;
-        }
-        return super.toString(verbosity);
-    }
-
-    @Override
-    public boolean canDeoptimize() {
-        return true;
-    }
-
-    @Override
-    public FrameState getDeoptimizationState() {
-        if (deoptState != null) {
-            return deoptState;
-        } else if (stateAfter() != null) {
-            FrameState stateDuring = stateAfter();
-            if ((stateDuring.stackSize() > 0 && stateDuring.stackAt(stateDuring.stackSize() - 1) == this) || (stateDuring.stackSize() > 1 && stateDuring.stackAt(stateDuring.stackSize() - 2) == this)) {
-                stateDuring = stateDuring.duplicateModified(stateDuring.bci, stateDuring.rethrowException(), this.kind());
-            }
-            updateUsages(deoptState, stateDuring);
-            return deoptState = stateDuring;
-        }
-        return null;
-    }
-
-    @Override
-    public void setDeoptimizationState(FrameState f) {
-        if (deoptState != null) {
-            throw new IllegalStateException();
-        }
-        updateUsages(deoptState, f);
-        deoptState = f;
-    }
-
-    @Override
-    public DeoptimizationReason getDeoptimizationReason() {
-        return null;
-    }
-
-    @Override
-    public boolean isCallSiteDeoptimization() {
-        return stateAfter() != null;
-    }
-}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Tue May 21 19:51:00 2013 +0200
@@ -33,7 +33,7 @@
  * The {@code LoadFieldNode} represents a read of a static or instance field.
  */
 @NodeInfo(nameTemplate = "LoadField#{p#field/s}")
-public final class LoadFieldNode extends AccessFieldNode implements Canonicalizable, Node.IterableNodeType, Virtualizable {
+public final class LoadFieldNode extends AccessFieldNode implements Canonicalizable, Node.IterableNodeType, VirtualizableRoot {
 
     /**
      * Creates a new LoadFieldNode instance.
@@ -79,6 +79,13 @@
             if (fieldIndex != -1) {
                 tool.replaceWith(state.getEntry(fieldIndex));
             }
+        } else {
+            ValueNode cachedValue = tool.getReadCache(object(), field());
+            if (cachedValue != null) {
+                tool.replaceWithValue(cachedValue);
+            } else {
+                tool.addReadCache(object(), field(), this);
+            }
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/RegisterFinalizerNode.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/RegisterFinalizerNode.java	Tue May 21 19:51:00 2013 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.nodes.java;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
@@ -35,7 +34,7 @@
  */
 public final class RegisterFinalizerNode extends AbstractStateSplit implements StateSplit, Canonicalizable, LIRLowerable, Virtualizable, DeoptimizingNode {
 
-    public static final Descriptor REGISTER_FINALIZER = new Descriptor("registerFinalizer", true, void.class, Object.class);
+    public static final ForeignCallDescriptor REGISTER_FINALIZER = new ForeignCallDescriptor("registerFinalizer", void.class, Object.class);
 
     @Input private FrameState deoptState;
     @Input private ValueNode object;
@@ -51,8 +50,8 @@
 
     @Override
     public void generate(LIRGeneratorTool gen) {
-        RuntimeCallTarget call = gen.getRuntime().lookupRuntimeCall(REGISTER_FINALIZER);
-        gen.emitCall(call, call.getCallingConvention(), this, gen.operand(object()));
+        ForeignCallLinkage linkage = gen.getRuntime().lookupForeignCall(REGISTER_FINALIZER);
+        gen.emitForeignCall(linkage, this, gen.operand(object()));
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Tue May 21 19:51:00 2013 +0200
@@ -33,7 +33,7 @@
  * The {@code StoreFieldNode} represents a write to a static or instance field.
  */
 @NodeInfo(nameTemplate = "StoreField#{p#field/s}")
-public final class StoreFieldNode extends AccessFieldNode implements StateSplit, Virtualizable {
+public final class StoreFieldNode extends AccessFieldNode implements StateSplit, VirtualizableRoot {
 
     @Input private ValueNode value;
     @Input(notDataflow = true) private FrameState stateAfter;
@@ -77,6 +77,12 @@
                 tool.setVirtualEntry(state, fieldIndex, value());
                 tool.delete();
             }
+        } else {
+            if (value() == tool.getReadCache(object(), field())) {
+                tool.delete();
+            }
+            tool.killReadCache(field());
+            tool.addReadCache(object(), field(), value());
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java	Tue May 21 19:51:00 2013 +0200
@@ -101,7 +101,7 @@
 
     void emitNullCheck(ValueNode v, DeoptimizingNode deopting);
 
-    Value emitCall(RuntimeCallTarget callTarget, CallingConvention cc, DeoptimizingNode info, Value... args);
+    Value emitForeignCall(ForeignCallLinkage linkage, DeoptimizingNode info, Value... args);
 
     void emitIf(IfNode i);
 
@@ -111,8 +111,6 @@
 
     void emitInvoke(Invoke i);
 
-    void visitRuntimeCall(RuntimeCallNode i);
-
     // Handling of block-end nodes still needs to be unified in the LIRGenerator.
     void visitMerge(MergeNode i);
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizableAllocation.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizableAllocation.java	Tue May 21 19:51:00 2013 +0200
@@ -26,12 +26,9 @@
  * This interface allows a node to convey information about what its effect would be if some of its
  * inputs were virtualized.
  * 
- * The difference to {@link Virtualizable} is that the {@link #virtualize(VirtualizerTool)} method
- * will be called regardless of whether this node had any interaction with virtualized nodes. This
- * interface can therefore be used for object allocations, for which virtualization introduces new
- * virtualized objects.
- * 
+ * The difference to {@link VirtualizableRoot} is that removing {@link VirtualizableAllocation}
+ * nodes is not considered progress during the escape analysis iterations.
  */
-public interface VirtualizableAllocation extends Virtualizable {
+public interface VirtualizableAllocation extends VirtualizableRoot {
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizableRoot.java	Tue May 21 19:51:00 2013 +0200
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.spi;
+
+/**
+ * This interface allows a node to convey information about what its effect would be if some of its
+ * inputs were virtualized.
+ * 
+ * The difference to {@link Virtualizable} is that the {@link #virtualize(VirtualizerTool)} method
+ * will be called regardless of whether this node had any interaction with virtualized nodes. This
+ * interface can therefore be used for object allocations, for which virtualization introduces new
+ * virtualized objects.
+ * 
+ */
+public interface VirtualizableRoot extends Virtualizable {
+
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Tue May 21 19:51:00 2013 +0200
@@ -152,4 +152,11 @@
      * @param value the replacement value
      */
     void replaceWith(ValueNode value);
+
+    void addReadCache(ValueNode object, ResolvedJavaField identity, ValueNode value);
+
+    ValueNode getReadCache(ValueNode object, ResolvedJavaField identity);
+
+    void killReadCache(ResolvedJavaField identity);
+
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java	Tue May 21 19:51:00 2013 +0200
@@ -34,7 +34,7 @@
 
 public class FrameStateAssignmentPhase extends Phase {
 
-    private static class FrameStateAssignementClosure extends NodeIteratorClosure<FrameState> {
+    private static class FrameStateAssignmentClosure extends NodeIteratorClosure<FrameState> {
 
         @Override
         protected FrameState processNode(FixedNode node, FrameState currentState) {
@@ -79,7 +79,7 @@
     @Override
     protected void run(StructuredGraph graph) {
         assert checkFixedDeopts(graph);
-        ReentrantNodeIterator.apply(new FrameStateAssignementClosure(), graph.start(), null, null);
+        ReentrantNodeIterator.apply(new FrameStateAssignmentClosure(), graph.start(), null, null);
     }
 
     private static boolean checkFixedDeopts(StructuredGraph graph) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Tue May 21 19:51:00 2013 +0200
@@ -99,27 +99,34 @@
 
     @Override
     protected void run(final StructuredGraph graph) {
-        InliningData data = new InliningData();
-        data.pushGraph(graph, 1.0, 1.0);
+        InliningData data = new InliningData(graph, compilationAssumptions);
 
         while (data.hasUnprocessedGraphs()) {
+            MethodInvocation currentInvocation = data.currentInvocation();
             GraphInfo graphInfo = data.currentGraph();
-            if (graphInfo.hasRemainingInvokes() && inliningPolicy.continueInlining(data)) {
+            if (!currentInvocation.isRoot() && !inliningPolicy.isWorthInlining(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);
             } else {
                 data.popGraph();
-                MethodInvocation currentInvocation = data.currentInvocation();
-                if (currentInvocation != null) {
+                if (!currentInvocation.isRoot()) {
                     assert currentInvocation.callee().invoke().asNode().isAlive();
                     currentInvocation.incrementProcessedGraphs();
-                    if (currentInvocation.processedAllGraphs()) {
+                    if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) {
                         data.popInvocation();
                         MethodInvocation parentInvoke = data.currentInvocation();
-                        tryToInline(data.currentGraph(), currentInvocation, parentInvoke);
+                        tryToInline(data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1);
                     }
                 }
             }
         }
+
+        assert data.inliningDepth() == 0;
+        assert data.graphCount() == 0;
     }
 
     /**
@@ -128,32 +135,35 @@
     private void processNextInvoke(InliningData data, GraphInfo graphInfo) {
         Invoke invoke = graphInfo.popInvoke();
         MethodInvocation callerInvocation = data.currentInvocation();
-        Assumptions parentAssumptions = callerInvocation == null ? compilationAssumptions : callerInvocation.assumptions();
+        Assumptions parentAssumptions = callerInvocation.assumptions();
         InlineInfo info = InliningUtil.getInlineInfo(data, invoke, maxMethodPerInlining, replacements, parentAssumptions, optimisticOpts);
 
-        double invokeProbability = graphInfo.invokeProbability(invoke);
-        double invokeRelevance = graphInfo.invokeRelevance(invoke);
-        if (info != null && inliningPolicy.isWorthInlining(info, invokeProbability, invokeRelevance, false)) {
-            MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance);
+        if (info != null) {
+            double invokeProbability = graphInfo.invokeProbability(invoke);
+            double invokeRelevance = graphInfo.invokeRelevance(invoke);
+
+            if (inliningPolicy.isWorthInlining(info, data.inliningDepth(), invokeProbability, invokeRelevance, false)) {
+                MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance);
 
-            for (int i = 0; i < info.numberOfMethods(); i++) {
-                InlineableElement elem = getInlineableElement(info.methodAt(i), info.invoke(), calleeInvocation.assumptions());
-                info.setInlinableElement(i, elem);
-                if (elem instanceof StructuredGraph) {
-                    data.pushGraph((StructuredGraph) elem, invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
-                } else {
-                    assert elem instanceof InlineableMacroNode;
-                    data.pushDummyGraph();
+                for (int i = 0; i < info.numberOfMethods(); i++) {
+                    InlineableElement elem = getInlineableElement(info.methodAt(i), info.invoke(), calleeInvocation.assumptions());
+                    info.setInlinableElement(i, elem);
+                    if (elem instanceof StructuredGraph) {
+                        data.pushGraph((StructuredGraph) elem, invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i));
+                    } else {
+                        assert elem instanceof InlineableMacroNode;
+                        data.pushDummyGraph();
+                    }
                 }
             }
         }
     }
 
-    private void tryToInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation) {
+    private void tryToInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth) {
         InlineInfo callee = calleeInfo.callee();
-        Assumptions callerAssumptions = parentInvocation == null ? compilationAssumptions : parentInvocation.assumptions();
+        Assumptions callerAssumptions = parentInvocation.assumptions();
 
-        if (inliningPolicy.isWorthInlining(callee, calleeInfo.probability(), calleeInfo.relevance(), true)) {
+        if (inliningPolicy.isWorthInlining(callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) {
             doInline(callerGraphInfo, calleeInfo, callerAssumptions);
         } else if (optimisticOpts.devirtualizeInvokes()) {
             callee.tryToDevirtualizeInvoke(runtime, callerAssumptions);
@@ -399,32 +409,26 @@
             super(replacements, hints);
         }
 
-        public boolean continueInlining(InliningData data) {
-            if (data.currentGraph().graph().getNodeCount() >= GraalOptions.MaximumDesiredSize) {
+        public boolean continueInlining(StructuredGraph currentGraph) {
+            if (currentGraph.getNodeCount() >= GraalOptions.MaximumDesiredSize) {
                 InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize");
                 metricInliningStoppedByMaxDesiredSize.increment();
                 return false;
             }
-
-            MethodInvocation currentInvocation = data.currentInvocation();
-            if (currentInvocation == null) {
-                return true;
-            }
-
-            return isWorthInlining(currentInvocation.callee(), currentInvocation.probability(), currentInvocation.relevance(), false);
+            return true;
         }
 
         @Override
-        public boolean isWorthInlining(InlineInfo info, double probability, double relevance, boolean fullyProcessed) {
+        public boolean isWorthInlining(InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed) {
             if (isIntrinsic(info)) {
-                return InliningUtil.logInlinedMethod(info, fullyProcessed, "intrinsic");
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic");
             }
 
             double inliningBonus = getInliningBonus(info);
 
             int lowLevelGraphSize = previousLowLevelGraphSize(info);
             if (GraalOptions.SmallCompiledLowLevelGraphSize > 0 && lowLevelGraphSize > GraalOptions.SmallCompiledLowLevelGraphSize * inliningBonus) {
-                return InliningUtil.logNotInlinedMethod(info, "too large previous low-level graph: %d", lowLevelGraphSize);
+                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph: %d", lowLevelGraphSize);
             }
 
             /*
@@ -436,20 +440,20 @@
 
             int nodes = determineNodeCount(info);
             if (nodes < GraalOptions.TrivialInliningSize * inliningBonus) {
-                return InliningUtil.logInlinedMethod(info, fullyProcessed, "trivial (nodes=%d)", nodes);
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (nodes=%d)", nodes);
             }
 
             double invokes = determineInvokeProbability(info);
             if (GraalOptions.LimitInlinedInvokes > 0 && fullyProcessed && invokes > GraalOptions.LimitInlinedInvokes * inliningBonus) {
-                return InliningUtil.logNotInlinedMethod(info, "invoke probability is too high (%f)", invokes);
+                return InliningUtil.logNotInlinedMethod(info, inliningDepth, "invoke probability is too high (%f)", invokes);
             }
 
             double maximumNodes = computeMaximumSize(relevance, (int) (GraalOptions.MaximumInliningSize * inliningBonus));
             if (nodes < maximumNodes) {
-                return InliningUtil.logInlinedMethod(info, fullyProcessed, "relevance-based (relevance=%f, nodes=%d)", relevance, nodes);
+                return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, nodes=%d)", relevance, nodes);
             }
 
-            return InliningUtil.logNotInlinedMethod(info, "(relevance=%f, probability=%f, bonus=%f)", relevance, probability, inliningBonus);
+            return InliningUtil.logNotInlinedMethod(info, inliningDepth, "(relevance=%f, probability=%f, bonus=%f)", relevance, probability, inliningBonus);
         }
     }
 
@@ -466,8 +470,8 @@
             assert start.isAlive();
         }
 
-        public Stack<Invoke> apply() {
-            Stack<Invoke> invokes = new Stack<>();
+        public LinkedList<Invoke> apply() {
+            LinkedList<Invoke> invokes = new LinkedList<>();
             FixedNode current;
             forcedQueue(start);
 
@@ -476,7 +480,7 @@
 
                 if (current instanceof Invoke) {
                     if (current != start) {
-                        invokes.push((Invoke) current);
+                        invokes.addLast((Invoke) current);
                     }
                     queueSuccessors(current);
                 } else if (current instanceof LoopBeginNode) {
@@ -552,22 +556,30 @@
      */
     static class InliningData {
 
-        private static final GraphInfo DummyGraphInfo = new GraphInfo(null, new Stack<Invoke>(), 1.0, 1.0);
+        private static final GraphInfo DummyGraphInfo = new GraphInfo(null, new LinkedList<Invoke>(), 1.0, 1.0);
 
         private final ArrayDeque<GraphInfo> graphQueue;
         private final ArrayDeque<MethodInvocation> invocationQueue;
 
-        private int maxGraphs = 1;
+        private int maxGraphs;
 
-        public InliningData() {
+        public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions) {
             this.graphQueue = new ArrayDeque<>();
             this.invocationQueue = new ArrayDeque<>();
+            this.maxGraphs = 1;
+
+            invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0));
+            pushGraph(rootGraph, 1.0, 1.0);
+        }
+
+        public int graphCount() {
+            return graphQueue.size();
         }
 
         public void pushGraph(StructuredGraph graph, double probability, double relevance) {
             assert !contains(graph);
             NodeBitMap visitedFixedNodes = graph.createNodeBitMap();
-            Stack<Invoke> invokes = new InliningIterator(graph.start(), visitedFixedNodes).apply();
+            LinkedList<Invoke> invokes = new InliningIterator(graph.start(), visitedFixedNodes).apply();
             assert invokes.size() == count(graph.getInvokes());
             graphQueue.push(new GraphInfo(graph, invokes, probability, relevance));
             assert graphQueue.size() <= maxGraphs;
@@ -590,6 +602,13 @@
             assert graphQueue.size() <= maxGraphs;
         }
 
+        public void popGraphs(int count) {
+            assert count >= 0;
+            for (int i = 0; i < count; i++) {
+                graphQueue.pop();
+            }
+        }
+
         public MethodInvocation currentInvocation() {
             return invocationQueue.peek();
         }
@@ -619,7 +638,8 @@
         }
 
         public int inliningDepth() {
-            return invocationQueue.size();
+            assert invocationQueue.size() > 0;
+            return invocationQueue.size() - 1;
         }
 
         @Override
@@ -683,9 +703,13 @@
             assert processedGraphs <= callee.numberOfMethods();
         }
 
-        public boolean processedAllGraphs() {
+        public int processedGraphs() {
             assert processedGraphs <= callee.numberOfMethods();
-            return processedGraphs == callee.numberOfMethods();
+            return processedGraphs;
+        }
+
+        public int totalGraphs() {
+            return callee.numberOfMethods();
         }
 
         public InlineInfo callee() {
@@ -703,19 +727,23 @@
         public double relevance() {
             return relevance;
         }
+
+        public boolean isRoot() {
+            return callee == null;
+        }
     }
 
     private static class GraphInfo {
 
         private final StructuredGraph graph;
-        private final Stack<Invoke> remainingInvokes;
+        private final LinkedList<Invoke> remainingInvokes;
         private final double probability;
         private final double relevance;
 
         private NodesToDoubles nodeProbabilities;
         private NodesToDoubles nodeRelevance;
 
-        public GraphInfo(StructuredGraph graph, Stack<Invoke> invokes, double probability, double relevance) {
+        public GraphInfo(StructuredGraph graph, LinkedList<Invoke> invokes, double probability, double relevance) {
             this.graph = graph;
             this.remainingInvokes = invokes;
             this.probability = probability;
@@ -739,7 +767,7 @@
         }
 
         public Invoke popInvoke() {
-            return remainingInvokes.pop();
+            return remainingInvokes.removeFirst();
         }
 
         public void pushInvoke(Invoke invoke) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Tue May 21 19:51:00 2013 +0200
@@ -59,9 +59,9 @@
 
     public interface InliningPolicy {
 
-        boolean continueInlining(InliningData data);
+        boolean continueInlining(StructuredGraph graph);
 
-        boolean isWorthInlining(InlineInfo info, double probability, double relevance, boolean fullyProcessed);
+        boolean isWorthInlining(InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed);
     }
 
     public static class InlineableMacroNode implements InlineableElement {
@@ -90,14 +90,14 @@
     /**
      * Print a HotSpot-style inlining message to the console.
      */
-    private static void printInlining(final InlineInfo info, final boolean success, final String msg, final Object... args) {
-        printInlining(info.methodAt(0), info.invoke(), success, msg, args);
+    private static void printInlining(final InlineInfo info, final int inliningDepth, final boolean success, final String msg, final Object... args) {
+        printInlining(info.methodAt(0), info.invoke(), inliningDepth, success, msg, args);
     }
 
     /**
      * Print a HotSpot-style inlining message to the console.
      */
-    private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final boolean success, final String msg, final Object... args) {
+    private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final int inliningDepth, final boolean success, final String msg, final Object... args) {
         if (GraalOptions.HotSpotPrintInlining) {
             final int mod = method.getModifiers();
             // 1234567
@@ -108,25 +108,24 @@
             TTY.print("%c%c%c%c%c ", ' ', Modifier.isSynchronized(mod) ? 's' : ' ', ' ', ' ', Modifier.isNative(mod) ? 'n' : ' ');
             TTY.print("     ");        // more indent
             TTY.print("    ");         // initial inlining indent
-            final int level = computeInliningLevel(invoke);
-            for (int i = 0; i < level; i++) {
+            for (int i = 0; i < inliningDepth; i++) {
                 TTY.print("  ");
             }
             TTY.println(String.format("@ %d  %s   %s%s", invoke.bci(), methodName(method, null), success ? "" : "not inlining ", String.format(msg, args)));
         }
     }
 
-    public static boolean logInlinedMethod(InlineInfo info, boolean allowLogging, String msg, Object... args) {
-        return logInliningDecision(info, allowLogging, true, msg, args);
+    public static boolean logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) {
+        return logInliningDecision(info, inliningDepth, allowLogging, true, msg, args);
     }
 
-    public static boolean logNotInlinedMethod(InlineInfo info, String msg, Object... args) {
-        return logInliningDecision(info, true, false, msg, args);
+    public static boolean logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) {
+        return logInliningDecision(info, inliningDepth, true, false, msg, args);
     }
 
-    public static boolean logInliningDecision(InlineInfo info, boolean allowLogging, boolean success, String msg, final Object... args) {
+    public static boolean logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) {
         if (allowLogging) {
-            printInlining(info, success, msg, args);
+            printInlining(info, inliningDepth, success, msg, args);
             if (shouldLogInliningDecision()) {
                 logInliningDecision(methodName(info), success, msg, args);
             }
@@ -151,12 +150,12 @@
         return false;
     }
 
-    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, ResolvedJavaMethod method, String msg) {
-        return logNotInlinedMethodAndReturnNull(invoke, method, msg, new Object[0]);
+    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) {
+        return logNotInlinedMethodAndReturnNull(invoke, inliningDepth, method, msg, new Object[0]);
     }
 
-    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, ResolvedJavaMethod method, String msg, Object... args) {
-        printInlining(method, invoke, false, msg, args);
+    private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) {
+        printInlining(method, invoke, inliningDepth, false, msg, args);
         if (shouldLogInliningDecision()) {
             String methodString = methodName(method, invoke);
             logInliningDecision(methodString, false, msg, args);
@@ -164,8 +163,8 @@
         return null;
     }
 
-    private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, ResolvedJavaMethod method, String msg) {
-        printInlining(method, invoke, false, msg, new Object[0]);
+    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]);
@@ -1085,18 +1084,18 @@
             TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver;
             typeProfile = typeProfileProxyNode.getProfile();
         } else {
-            return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no type profile exists");
+            return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no type profile exists");
         }
 
         ProfiledType[] ptypes = typeProfile.getTypes();
         if (ptypes == null || ptypes.length <= 0) {
-            return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types in profile");
+            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, targetMethod, "inlining monomorphic calls is disabled");
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining monomorphic calls is disabled");
             }
 
             ResolvedJavaType type = ptypes[0].getType();
@@ -1109,12 +1108,12 @@
             invoke.setPolymorphic(true);
 
             if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) {
-                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length);
+                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, targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length,
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length,
                                 notRecordedTypeProbability * 100);
             }
 
@@ -1135,7 +1134,7 @@
             }
 
             if (concreteMethods.size() > maxNumberOfMethods) {
-                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "polymorphic call with more than %d target methods", maxNumberOfMethods);
+                return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxNumberOfMethods);
             }
 
             // Clear methods that fall below the threshold.
@@ -1151,7 +1150,8 @@
 
                 if (newConcreteMethods.size() == 0) {
                     // No method left that is worth inlining.
-                    return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)", concreteMethods.size());
+                    return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)",
+                                    concreteMethods.size());
                 }
 
                 concreteMethods = newConcreteMethods;
@@ -1174,12 +1174,12 @@
 
             if (usedTypes.size() == 0) {
                 // No type left that is worth checking for.
-                return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length);
+                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, targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined");
+                    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);
@@ -1214,34 +1214,24 @@
 
     private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) {
         if (method == null) {
-            return logNotInlinedMethodAndReturnFalse(invoke, method, "the method is not resolved");
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved");
         } else if (Modifier.isNative(method.getModifiers()) && (!GraalOptions.Intrinsify || !InliningUtil.canIntrinsify(replacements, method))) {
-            return logNotInlinedMethodAndReturnFalse(invoke, method, "it is a non-intrinsic native method");
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is a non-intrinsic native method");
         } else if (Modifier.isAbstract(method.getModifiers())) {
-            return logNotInlinedMethodAndReturnFalse(invoke, method, "it is an abstract method");
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method");
         } else if (!method.getDeclaringClass().isInitialized()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, method, "the method's class is not initialized");
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized");
         } else if (!method.canBeInlined()) {
-            return logNotInlinedMethodAndReturnFalse(invoke, method, "it is marked non-inlinable");
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable");
         } else if (data.countRecursiveInlining(method) > GraalOptions.MaximumRecursiveInlining) {
-            return logNotInlinedMethodAndReturnFalse(invoke, method, "it exceeds the maximum recursive inlining depth");
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it exceeds the maximum recursive inlining depth");
         } else if (new OptimisticOptimizations(method).lessOptimisticThan(optimisticOpts)) {
-            return logNotInlinedMethodAndReturnFalse(invoke, method, "the callee uses less optimistic optimizations than caller");
+            return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller");
         } else {
             return true;
         }
     }
 
-    private static int computeInliningLevel(Invoke invoke) {
-        int count = -1;
-        FrameState curState = invoke.stateAfter();
-        while (curState != null) {
-            count++;
-            curState = curState.outerFrameState();
-        }
-        return count;
-    }
-
     static MonitorExitNode findPrecedingMonitorExit(UnwindNode unwind) {
         Node pred = unwind.predecessor();
         while (pred != null) {
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Tue May 21 19:51:00 2013 +0200
@@ -60,7 +60,7 @@
     public static int     TrivialInliningSize                = 10;
     public static int     MaximumInliningSize                = 300;
     public static int     SmallCompiledLowLevelGraphSize     = 300;
-    public static double  LimitInlinedInvokes                = 10.0;
+    public static double  LimitInlinedInvokes                = 5.0;
 
     // escape analysis settings
     public static boolean PartialEscapeAnalysis              = true;
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Tue May 21 19:51:00 2013 +0200
@@ -95,6 +95,7 @@
                             FixedNode fixed = (FixedNode) node;
                             if (identity == LocationNode.ANY_LOCATION || read.location().getLocationIdentity() == identity) {
                                 addPhantomReference(read, fixed);
+                                iter.remove();
                             }
                         }
                     }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/Log.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/Log.java	Tue May 21 19:51:00 2013 +0200
@@ -24,7 +24,6 @@
 
 import java.io.*;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
@@ -33,28 +32,27 @@
 //JaCoCo Exclude
 
 /**
- * Provides {@link PrintStream}-like logging facility. This should only be used in
- * {@linkplain Snippet snippets}.
+ * Provides {@link PrintStream}-like logging facility.
  */
 public final class Log {
 
-    public static final Descriptor LOG_PRIMITIVE = new Descriptor("logPrimitive", false, void.class, int.class, long.class, boolean.class);
-    public static final Descriptor LOG_OBJECT = new Descriptor("logObject", false, void.class, Object.class, int.class);
-    public static final Descriptor LOG_PRINTF = new Descriptor("logPrintf", false, void.class, Object.class, long.class, long.class, long.class);
+    public static final ForeignCallDescriptor LOG_PRIMITIVE = new ForeignCallDescriptor("logPrimitive", void.class, int.class, long.class, boolean.class);
+    public static final ForeignCallDescriptor LOG_OBJECT = new ForeignCallDescriptor("logObject", void.class, Object.class, int.class);
+    public static final ForeignCallDescriptor LOG_PRINTF = new ForeignCallDescriptor("logPrintf", void.class, Object.class, long.class, long.class, long.class);
 
-    // Note: Must be kept in sync with constants in c1_Runtime1.hpp
+    // Note: Must be kept in sync with constants in graalRuntime.hpp
     private static final int LOG_OBJECT_NEWLINE = 0x01;
     private static final int LOG_OBJECT_STRING = 0x02;
     private static final int LOG_OBJECT_ADDRESS = 0x04;
 
-    @NodeIntrinsic(RuntimeCallNode.class)
-    private static native void log(@ConstantNodeParameter Descriptor logObject, Object object, int flags);
+    @NodeIntrinsic(ForeignCallNode.class)
+    private static native void log(@ConstantNodeParameter ForeignCallDescriptor logObject, Object object, int flags);
 
-    @NodeIntrinsic(RuntimeCallNode.class)
-    private static native void log(@ConstantNodeParameter Descriptor logPrimitive, int typeChar, long value, boolean newline);
+    @NodeIntrinsic(ForeignCallNode.class)
+    private static native void log(@ConstantNodeParameter ForeignCallDescriptor logPrimitive, int typeChar, long value, boolean newline);
 
-    @NodeIntrinsic(RuntimeCallNode.class)
-    private static native void printf(@ConstantNodeParameter Descriptor logPrintf, String format, long v1, long v2, long v3);
+    @NodeIntrinsic(ForeignCallNode.class)
+    private static native void printf(@ConstantNodeParameter ForeignCallDescriptor logPrintf, String format, long v1, long v2, long v3);
 
     public static void print(boolean value) {
         log(LOG_PRIMITIVE, Kind.Boolean.getTypeChar(), value ? 1L : 0L, false);
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/MathSubstitutionsX86.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/MathSubstitutionsX86.java	Tue May 21 19:51:00 2013 +0200
@@ -22,7 +22,7 @@
  */
 package com.oracle.graal.replacements;
 
-import com.oracle.graal.api.code.RuntimeCallTarget.Descriptor;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
@@ -91,12 +91,12 @@
         }
     }
 
-    public static final Descriptor ARITHMETIC_SIN = new Descriptor("arithmeticSin", false, double.class, double.class);
-    public static final Descriptor ARITHMETIC_COS = new Descriptor("arithmeticCos", false, double.class, double.class);
-    public static final Descriptor ARITHMETIC_TAN = new Descriptor("arithmeticTan", false, double.class, double.class);
+    public static final ForeignCallDescriptor ARITHMETIC_SIN = new ForeignCallDescriptor("arithmeticSin", double.class, double.class);
+    public static final ForeignCallDescriptor ARITHMETIC_COS = new ForeignCallDescriptor("arithmeticCos", double.class, double.class);
+    public static final ForeignCallDescriptor ARITHMETIC_TAN = new ForeignCallDescriptor("arithmeticTan", double.class, double.class);
 
-    @NodeIntrinsic(value = RuntimeCallNode.class, setStampFromReturnType = true)
-    public static double callDouble(@ConstantNodeParameter Descriptor descriptor, double value) {
+    @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true)
+    public static double callDouble(@ConstantNodeParameter ForeignCallDescriptor descriptor, double value) {
         if (descriptor == ARITHMETIC_SIN) {
             return Math.sin(value);
         }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Tue May 21 19:51:00 2013 +0200
@@ -26,10 +26,9 @@
 
 import java.util.*;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.extended.LocationNode.LocationIdentity;
 import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
 
@@ -42,10 +41,10 @@
 
     static class ReadCacheEntry {
 
-        public final LocationIdentity identity;
+        public final ResolvedJavaField identity;
         public final ValueNode object;
 
-        public ReadCacheEntry(LocationIdentity identity, ValueNode object) {
+        public ReadCacheEntry(ResolvedJavaField identity, ValueNode object) {
             this.identity = identity;
             this.object = object;
         }
@@ -83,7 +82,7 @@
         readCache = new HashMap<>(other.readCache);
     }
 
-    public void addReadCache(ValueNode object, LocationIdentity identity, ValueNode value) {
+    public void addReadCache(ValueNode object, ResolvedJavaField identity, ValueNode value) {
         ValueNode cacheObject;
         ObjectState obj = getObjectState(object);
         if (obj != null) {
@@ -95,7 +94,7 @@
         readCache.put(new ReadCacheEntry(identity, cacheObject), value);
     }
 
-    public ValueNode getReadCache(ValueNode object, LocationIdentity identity) {
+    public ValueNode getReadCache(ValueNode object, ResolvedJavaField identity) {
         ValueNode cacheObject;
         ObjectState obj = getObjectState(object);
         if (obj != null) {
@@ -115,16 +114,16 @@
         return cacheValue;
     }
 
-    public void killReadCache(LocationIdentity identity) {
-        if (identity == LocationNode.ANY_LOCATION) {
-            readCache.clear();
-        } else {
-            Iterator<Map.Entry<ReadCacheEntry, ValueNode>> iter = readCache.entrySet().iterator();
-            while (iter.hasNext()) {
-                Map.Entry<ReadCacheEntry, ValueNode> entry = iter.next();
-                if (entry.getKey().identity == identity) {
-                    iter.remove();
-                }
+    public void killReadCache() {
+        readCache.clear();
+    }
+
+    public void killReadCache(ResolvedJavaField identity) {
+        Iterator<Map.Entry<ReadCacheEntry, ValueNode>> iter = readCache.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry<ReadCacheEntry, ValueNode> entry = iter.next();
+            if (entry.getKey().identity == identity) {
+                iter.remove();
             }
         }
     }
@@ -187,7 +186,7 @@
             if (virtual instanceof VirtualInstanceNode) {
                 VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
                 for (int i = 0; i < entries.length; i++) {
-                    readCache.put(new ReadCacheEntry((LocationIdentity) instance.field(i), representation), values.get(pos + i));
+                    readCache.put(new ReadCacheEntry(instance.field(i), representation), values.get(pos + i));
                 }
             }
         } else {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Tue May 21 19:51:00 2013 +0200
@@ -75,7 +75,7 @@
         if (!readElimination) {
             boolean analyzableNodes = false;
             for (Node node : graph.getNodes()) {
-                if (node instanceof VirtualizableAllocation) {
+                if (node instanceof VirtualizableRoot) {
                     analyzableNodes = true;
                     break;
                 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue May 21 19:51:00 2013 +0200
@@ -59,9 +59,6 @@
     public static final DebugMetric METRIC_MATERIALIZATIONS_LOOP_END = Debug.metric("MaterializationsLoopEnd");
     public static final DebugMetric METRIC_ALLOCATION_REMOVED = Debug.metric("AllocationsRemoved");
 
-    public static final DebugMetric METRIC_STOREFIELD_RECORDED = Debug.metric("StoreFieldRecorded");
-    public static final DebugMetric METRIC_LOADFIELD_ELIMINATED = Debug.metric("LoadFieldEliminated");
-    public static final DebugMetric METRIC_LOADFIELD_NOT_ELIMINATED = Debug.metric("LoadFieldNotEliminated");
     public static final DebugMetric METRIC_MEMORYCHECKOINT = Debug.metric("MemoryCheckpoint");
 
     private final NodeBitMap usages;
@@ -148,44 +145,24 @@
         FixedWithNextNode lastFixedNode = null;
         for (Node node : nodeList) {
             boolean deleted;
-            if (usages.isMarked(node) || node instanceof VirtualizableAllocation) {
+            boolean isMarked = usages.isMarked(node);
+            if (isMarked || node instanceof VirtualizableRoot) {
                 trace("[[%s]] ", node);
-                deleted = processNode((ValueNode) node, lastFixedNode == null ? null : lastFixedNode.next(), state, effects);
+                FixedNode nextFixedNode = lastFixedNode == null ? null : lastFixedNode.next();
+                deleted = processNode((ValueNode) node, nextFixedNode, state, effects, isMarked);
             } else {
                 trace("%s ", node);
                 deleted = false;
             }
             if (GraalOptions.OptEarlyReadElimination) {
-                if (!deleted) {
-                    if (node instanceof StoreFieldNode) {
-                        METRIC_STOREFIELD_RECORDED.increment();
-                        StoreFieldNode store = (StoreFieldNode) node;
-                        ValueNode cachedValue = state.getReadCache(store.object(), (LocationIdentity) store.field());
-                        state.killReadCache((LocationIdentity) store.field());
-
-                        if (cachedValue == store.value()) {
-                            effects.deleteFixedNode(store);
-                            changed = true;
-                        } else {
-                            state.addReadCache(store.object(), (LocationIdentity) store.field(), store.value());
-                        }
-                    } else if (node instanceof LoadFieldNode) {
-                        LoadFieldNode load = (LoadFieldNode) node;
-                        ValueNode cachedValue = state.getReadCache(load.object(), (LocationIdentity) load.field());
-                        if (cachedValue != null) {
-                            METRIC_LOADFIELD_ELIMINATED.increment();
-                            effects.replaceAtUsages(load, cachedValue);
-                            state.addScalarAlias(load, cachedValue);
-                            changed = true;
-                        } else {
-                            METRIC_LOADFIELD_NOT_ELIMINATED.increment();
-                            state.addReadCache(load.object(), (LocationIdentity) load.field(), load);
-                        }
-                    } else if (node instanceof MemoryCheckpoint) {
-                        METRIC_MEMORYCHECKOINT.increment();
-                        MemoryCheckpoint checkpoint = (MemoryCheckpoint) node;
-                        for (LocationIdentity identity : checkpoint.getLocationIdentities()) {
-                            state.killReadCache(identity);
+                if (!deleted && node instanceof MemoryCheckpoint) {
+                    METRIC_MEMORYCHECKOINT.increment();
+                    MemoryCheckpoint checkpoint = (MemoryCheckpoint) node;
+                    for (LocationIdentity identity : checkpoint.getLocationIdentities()) {
+                        if (identity instanceof ResolvedJavaField) {
+                            state.killReadCache((ResolvedJavaField) identity);
+                        } else if (identity == LocationNode.ANY_LOCATION) {
+                            state.killReadCache();
                         }
                     }
                 }
@@ -198,7 +175,7 @@
         return state;
     }
 
-    private boolean processNode(final ValueNode node, FixedNode insertBefore, final BlockState state, final GraphEffectList effects) {
+    private boolean processNode(final ValueNode node, FixedNode insertBefore, final BlockState state, final GraphEffectList effects, boolean isMarked) {
         tool.reset(state, node, insertBefore);
         if (node instanceof Virtualizable) {
             ((Virtualizable) node).virtualize(tool);
@@ -209,89 +186,91 @@
             }
             return true;
         }
-        if (node instanceof StateSplit) {
-            StateSplit split = (StateSplit) node;
-            FrameState stateAfter = split.stateAfter();
-            if (stateAfter != null) {
-                if (stateAfter.usages().count() > 1) {
-                    stateAfter = (FrameState) stateAfter.copyWithInputs();
-                    split.setStateAfter(stateAfter);
-                }
-                final HashSet<ObjectState> virtual = new HashSet<>();
-                stateAfter.applyToNonVirtual(new NodeClosure<ValueNode>() {
+        if (isMarked) {
+            if (node instanceof StateSplit) {
+                StateSplit split = (StateSplit) node;
+                FrameState stateAfter = split.stateAfter();
+                if (stateAfter != null) {
+                    if (stateAfter.usages().count() > 1) {
+                        stateAfter = (FrameState) stateAfter.copyWithInputs();
+                        split.setStateAfter(stateAfter);
+                    }
+                    final HashSet<ObjectState> virtual = new HashSet<>();
+                    stateAfter.applyToNonVirtual(new NodeClosure<ValueNode>() {
 
-                    @Override
-                    public void apply(Node usage, ValueNode value) {
-                        ObjectState valueObj = state.getObjectState(value);
-                        if (valueObj != null) {
-                            virtual.add(valueObj);
-                            effects.replaceFirstInput(usage, value, valueObj.virtual);
-                        } else if (value instanceof VirtualObjectNode) {
-                            ObjectState virtualObj = null;
-                            for (ObjectState obj : state.getStates()) {
-                                if (value == obj.virtual) {
-                                    virtualObj = obj;
-                                    break;
+                        @Override
+                        public void apply(Node usage, ValueNode value) {
+                            ObjectState valueObj = state.getObjectState(value);
+                            if (valueObj != null) {
+                                virtual.add(valueObj);
+                                effects.replaceFirstInput(usage, value, valueObj.virtual);
+                            } else if (value instanceof VirtualObjectNode) {
+                                ObjectState virtualObj = null;
+                                for (ObjectState obj : state.getStates()) {
+                                    if (value == obj.virtual) {
+                                        virtualObj = obj;
+                                        break;
+                                    }
+                                }
+                                if (virtualObj != null) {
+                                    virtual.add(virtualObj);
                                 }
                             }
-                            if (virtualObj != null) {
-                                virtual.add(virtualObj);
-                            }
+                        }
+                    });
+                    for (ObjectState obj : state.getStates()) {
+                        if (obj.isVirtual() && obj.hasLocks()) {
+                            virtual.add(obj);
                         }
                     }
-                });
-                for (ObjectState obj : state.getStates()) {
-                    if (obj.isVirtual() && obj.hasLocks()) {
-                        virtual.add(obj);
-                    }
-                }
 
-                ArrayDeque<ObjectState> queue = new ArrayDeque<>(virtual);
-                while (!queue.isEmpty()) {
-                    ObjectState obj = queue.removeLast();
-                    if (obj.isVirtual()) {
-                        for (ValueNode field : obj.getEntries()) {
-                            ObjectState fieldObj = state.getObjectState(field);
-                            if (fieldObj != null) {
-                                if (fieldObj.isVirtual() && !virtual.contains(fieldObj)) {
-                                    virtual.add(fieldObj);
-                                    queue.addLast(fieldObj);
+                    ArrayDeque<ObjectState> queue = new ArrayDeque<>(virtual);
+                    while (!queue.isEmpty()) {
+                        ObjectState obj = queue.removeLast();
+                        if (obj.isVirtual()) {
+                            for (ValueNode field : obj.getEntries()) {
+                                ObjectState fieldObj = state.getObjectState(field);
+                                if (fieldObj != null) {
+                                    if (fieldObj.isVirtual() && !virtual.contains(fieldObj)) {
+                                        virtual.add(fieldObj);
+                                        queue.addLast(fieldObj);
+                                    }
                                 }
                             }
                         }
                     }
-                }
-                for (ObjectState obj : virtual) {
-                    EscapeObjectState v;
-                    if (obj.isVirtual()) {
-                        ValueNode[] fieldState = obj.getEntries().clone();
-                        for (int i = 0; i < fieldState.length; i++) {
-                            ObjectState valueObj = state.getObjectState(fieldState[i]);
-                            if (valueObj != null) {
-                                if (valueObj.isVirtual()) {
-                                    fieldState[i] = valueObj.virtual;
-                                } else {
-                                    fieldState[i] = valueObj.getMaterializedValue();
+                    for (ObjectState obj : virtual) {
+                        EscapeObjectState v;
+                        if (obj.isVirtual()) {
+                            ValueNode[] fieldState = obj.getEntries().clone();
+                            for (int i = 0; i < fieldState.length; i++) {
+                                ObjectState valueObj = state.getObjectState(fieldState[i]);
+                                if (valueObj != null) {
+                                    if (valueObj.isVirtual()) {
+                                        fieldState[i] = valueObj.virtual;
+                                    } else {
+                                        fieldState[i] = valueObj.getMaterializedValue();
+                                    }
                                 }
                             }
+                            v = new VirtualObjectState(obj.virtual, fieldState);
+                        } else {
+                            v = new MaterializedObjectState(obj.virtual, obj.getMaterializedValue());
                         }
-                        v = new VirtualObjectState(obj.virtual, fieldState);
-                    } else {
-                        v = new MaterializedObjectState(obj.virtual, obj.getMaterializedValue());
+                        effects.addVirtualMapping(stateAfter, v);
                     }
-                    effects.addVirtualMapping(stateAfter, v);
                 }
             }
-        }
-        for (ValueNode input : node.inputs().filter(ValueNode.class)) {
-            ObjectState obj = state.getObjectState(input);
-            if (obj != null) {
-                if (obj.isVirtual() && node instanceof MethodCallTargetNode) {
-                    Invoke invoke = ((MethodCallTargetNode) node).invoke();
-                    hints.put(invoke, 5d);
+            for (ValueNode input : node.inputs().filter(ValueNode.class)) {
+                ObjectState obj = state.getObjectState(input);
+                if (obj != null) {
+                    if (obj.isVirtual() && node instanceof MethodCallTargetNode) {
+                        Invoke invoke = ((MethodCallTargetNode) node).invoke();
+                        hints.put(invoke, 5d);
+                    }
+                    trace("replacing input %s at %s: %s", input, node, obj);
+                    replaceWithMaterialized(input, node, insertBefore, state, obj, effects, METRIC_MATERIALIZATIONS_UNHANDLED);
                 }
-                trace("replacing input %s at %s: %s", input, node, obj);
-                replaceWithMaterialized(input, node, insertBefore, state, obj, effects, METRIC_MATERIALIZATIONS_UNHANDLED);
             }
         }
         return false;
@@ -703,7 +682,7 @@
             }
         }
 
-        private void mergeReadCachePhi(PhiNode phi, LocationIdentity identity, List<BlockState> states) {
+        private void mergeReadCachePhi(PhiNode phi, ResolvedJavaField identity, List<BlockState> states) {
             ValueNode[] values = new ValueNode[phi.valueCount()];
             for (int i = 0; i < phi.valueCount(); i++) {
                 ValueNode value = states.get(i).getReadCache(phi.valueAt(i), identity);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Tue May 21 19:51:00 2013 +0200
@@ -181,4 +181,26 @@
             }
         }
     }
+
+    @Override
+    public void addReadCache(ValueNode object, ResolvedJavaField identity, ValueNode value) {
+        if (GraalOptions.OptEarlyReadElimination) {
+            state.addReadCache(object, identity, value);
+        }
+    }
+
+    @Override
+    public ValueNode getReadCache(ValueNode object, ResolvedJavaField identity) {
+        if (GraalOptions.OptEarlyReadElimination) {
+            return state.getReadCache(object, identity);
+        }
+        return null;
+    }
+
+    @Override
+    public void killReadCache(ResolvedJavaField identity) {
+        if (GraalOptions.OptEarlyReadElimination) {
+            state.killReadCache(identity);
+        }
+    }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java	Tue May 21 19:51:00 2013 +0200
@@ -115,7 +115,7 @@
      * @return the code as a String object
      */
     public final String getCode() {
-        return getSource().getCode().substring(charIndex, charLength);
+        return getSource().getCode().substring(charIndex, charIndex + charLength);
     }
 
     @Override
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java	Tue May 21 19:51:00 2013 +0200
@@ -24,7 +24,6 @@
 
 import java.io.*;
 import java.lang.annotation.*;
-import java.lang.reflect.*;
 import java.net.*;
 import java.util.*;
 
@@ -35,6 +34,10 @@
 
 import org.w3c.dom.*;
 
+import com.oracle.truffle.api.nodes.NodeUtil.NodeClass;
+import com.oracle.truffle.api.nodes.NodeUtil.NodeField;
+import com.oracle.truffle.api.nodes.NodeUtil.NodeFieldKind;
+
 /**
  * Utility class for creating output for the ideal graph visualizer.
  */
@@ -212,7 +215,7 @@
             setNodeProperty(node, "nodeType", (Node.class.isAssignableFrom(node.getClass()) ? Node.class.getSimpleName() : "other"));
             setNodeProperty(node, "nodeClass", node.getClass().getSimpleName());
             copyDebugProperties(node); // TODO: may overwrite property "name"? (currently allowed)
-            readNodeProperties(node);
+            readNodeProperties((Node) node);
         }
     }
 
@@ -254,23 +257,14 @@
         }
     }
 
-    private void readNodeProperties(Object node) {
-        Field[] fields = NodeUtil.getAllFields(node.getClass());
-        for (Field field : fields) {
-            if (Modifier.isStatic(field.getModifiers())) {
-                continue;
-            }
-            if (Node.class.isAssignableFrom(field.getType()) || (field.getType().getComponentType() != null && Node.class.isAssignableFrom(field.getType()))) {
-                continue;
-            }
-            String key = field.getName();
-            if (field.getAnnotation(HiddenField.class) == null && getPropertyElement(node, key) == null) {
-                try {
-                    field.setAccessible(true);
-                    Object value = field.get(node);
+    private void readNodeProperties(Node node) {
+        NodeField[] fields = NodeClass.get(node.getClass()).getFields();
+        for (NodeField field : fields) {
+            if (field.getKind() == NodeFieldKind.DATA) {
+                String key = field.getName();
+                if (getPropertyElement(node, key) == null) {
+                    Object value = field.loadValue(node);
                     setNodeProperty(node, key, value);
-                } catch (IllegalArgumentException | IllegalAccessException e) {
-                    assert false : e;
                 }
             }
         }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Tue May 21 19:43:53 2013 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Tue May 21 19:51:00 2013 +0200
@@ -37,104 +37,181 @@
  */
 public class NodeUtil {
 
-    public static final class NodeClass {
+    /**
+     * Interface that allows the customization of field offsets used for {@link Unsafe} field
+     * accesses.
+     */
+    public interface FieldOffsetProvider {
+
+        long objectFieldOffset(Field field);
+    }
+
+    private static final FieldOffsetProvider unsafeFieldOffsetProvider = new FieldOffsetProvider() {
+
+        @Override
+        public long objectFieldOffset(Field field) {
+            return unsafe.objectFieldOffset(field);
+        }
+    };
+
+    public static enum NodeFieldKind {
+        /** The single {@link Node#getParent() parent} field. */
+        PARENT,
+        /** A field annotated with {@link Child}. */
+        CHILD,
+        /** A field annotated with {@link Children}. */
+        CHILDREN,
+        /** A normal non-child data field of the node. */
+        DATA
+    }
+
+    /**
+     * Information about a field in a {@link Node} class.
+     */
+    public static final class NodeField {
+
+        private final NodeFieldKind kind;
+        private final Class<?> type;
+        private final String name;
+        private long offset;
+
+        protected NodeField(NodeFieldKind kind, Class<?> type, String name, long offset) {
+            this.kind = kind;
+            this.type = type;
+            this.name = name;
+            this.offset = offset;
+        }
 
-        private final Class parentClass;
-        private final long[] nodeFieldOffsets;
-        private final Class[] nodeFieldClasses;
-        private final long[] nodeArrayFieldOffsets;
-        private final Class[] nodeArrayFieldClasses;
-        private final long parentOffset;
-        private final long[] nodeDataFieldOffsets;
-        private final Class<?>[] nodeDataFieldClasses;
+        public NodeFieldKind getKind() {
+            return kind;
+        }
+
+        public Class<?> getType() {
+            return type;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public long getOffset() {
+            return offset;
+        }
+
+        public Object loadValue(Node node) {
+            if (type == boolean.class) {
+                return unsafe.getBoolean(node, offset);
+            } else if (type == byte.class) {
+                return unsafe.getByte(node, offset);
+            } else if (type == short.class) {
+                return unsafe.getShort(node, offset);
+            } else if (type == char.class) {
+                return unsafe.getChar(node, offset);
+            } else if (type == int.class) {
+                return unsafe.getInt(node, offset);
+            } else if (type == long.class) {
+                return unsafe.getLong(node, offset);
+            } else if (type == float.class) {
+                return unsafe.getFloat(node, offset);
+            } else if (type == double.class) {
+                return unsafe.getDouble(node, offset);
+            } else {
+                return unsafe.getObject(node, offset);
+            }
+        }
+    }
+
+    /**
+     * Information about a {@link Node} class. A single instance of this class is allocated for
+     * every subclass of {@link Node} that is used.
+     */
+    public static final class NodeClass {
 
         private static final Map<Class<?>, NodeClass> nodeClasses = new IdentityHashMap<>();
 
-        public static NodeClass get(Class<?> clazz) {
+        // The comprehensive list of all fields.
+        private final NodeField[] fields;
+        // Separate arrays for the frequently accessed field offsets.
+        private final long parentOffset;
+        private final long[] childOffsets;
+        private final long[] childrenOffsets;
+
+        public static NodeClass get(Class<? extends Node> clazz) {
             NodeClass nodeClass = nodeClasses.get(clazz);
             if (nodeClass == null) {
-                nodeClass = new NodeClass(clazz);
+                nodeClass = new NodeClass(clazz, unsafeFieldOffsetProvider);
                 nodeClasses.put(clazz, nodeClass);
             }
             return nodeClass;
         }
 
-        private NodeClass(Class<?> clazz) {
-            // scan object fields
-            Class<?> parentClassTmp = null;
-            List<Long> nodeFieldOffsetsList = new ArrayList<>();
-            List<Class<?>> nodeFieldClassesList = new ArrayList<>();
-            List<Long> nodeArrayFieldOffsetsList = new ArrayList<>();
-            List<Class<?>> nodeArrayFieldClassesList = new ArrayList<>();
-            List<Long> nodeDataFieldOffsetList = new ArrayList<>();
-            List<Class<?>> nodeDataFieldClassList = new ArrayList<>();
-            Field[] fields = getAllFields(clazz);
-            long parentOffsetTemp = -1;
-            for (Field field : fields) {
+        public NodeClass(Class<? extends Node> clazz, FieldOffsetProvider fieldOffsetProvider) {
+            List<NodeField> fieldsList = new ArrayList<>();
+            List<Long> parentOffsetsList = new ArrayList<>();
+            List<Long> childOffsetsList = new ArrayList<>();
+            List<Long> childrenOffsetsList = new ArrayList<>();
+
+            for (Field field : getAllFields(clazz)) {
                 if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
                     continue;
                 }
 
-                // Node fields
-                if (Node.class.isAssignableFrom(field.getType()) && field.getName().equals("parent")) {
-                    parentOffsetTemp = unsafe.objectFieldOffset(field);
-                    parentClassTmp = field.getType();
+                NodeFieldKind kind;
+                if (Node.class.isAssignableFrom(field.getType()) && field.getName().equals("parent") && field.getDeclaringClass() == Node.class) {
+                    kind = NodeFieldKind.PARENT;
+                    parentOffsetsList.add(fieldOffsetProvider.objectFieldOffset(field));
                 } else if (Node.class.isAssignableFrom(field.getType()) && field.getAnnotation(Child.class) != null) {
-                    nodeFieldOffsetsList.add(unsafe.objectFieldOffset(field));
-                    nodeFieldClassesList.add(field.getType());
-                } else if (field.getType().getComponentType() != null && Node.class.isAssignableFrom(field.getType().getComponentType()) && field.getAnnotation(Children.class) != null) {
-                    nodeArrayFieldOffsetsList.add(unsafe.objectFieldOffset(field));
-                    nodeArrayFieldClassesList.add(field.getType());
+                    kind = NodeFieldKind.CHILD;
+                    childOffsetsList.add(fieldOffsetProvider.objectFieldOffset(field));
+                } else if (field.getType().isArray() && Node.class.isAssignableFrom(field.getType().getComponentType()) && field.getAnnotation(Children.class) != null) {
+                    kind = NodeFieldKind.CHILDREN;
+                    childrenOffsetsList.add(fieldOffsetProvider.objectFieldOffset(field));
                 } else {
-                    nodeDataFieldOffsetList.add(unsafe.objectFieldOffset(field));
-                    nodeDataFieldClassList.add(field.getType());
+                    kind = NodeFieldKind.DATA;
                 }
+                fieldsList.add(new NodeField(kind, field.getType(), field.getName(), fieldOffsetProvider.objectFieldOffset(field)));
             }
-            this.parentClass = parentClassTmp;
-            this.nodeFieldOffsets = toLongArray(nodeFieldOffsetsList);
-            this.nodeFieldClasses = nodeFieldClassesList.toArray(new Class[nodeFieldClassesList.size()]);
-            this.nodeArrayFieldOffsets = toLongArray(nodeArrayFieldOffsetsList);
-            this.nodeArrayFieldClasses = nodeArrayFieldClassesList.toArray(new Class[nodeArrayFieldClassesList.size()]);
-            this.nodeDataFieldOffsets = toLongArray(nodeDataFieldOffsetList);
-            this.nodeDataFieldClasses = nodeDataFieldClassList.toArray(new Class<?>[nodeDataFieldClassList.size()]);
+            this.fields = fieldsList.toArray(new NodeField[fieldsList.size()]);
+            assert parentOffsetsList.size() == 1 : "must have exactly one parent field";
+            this.parentOffset = parentOffsetsList.get(0);
+            this.childOffsets = toLongArray(childOffsetsList);
+            this.childrenOffsets = toLongArray(childrenOffsetsList);
+        }
 
-            this.parentOffset = parentOffsetTemp;
+        public NodeField[] getFields() {
+            return fields;
         }
 
         public long getParentOffset() {
             return parentOffset;
         }
 
-        public long[] getNodeFieldOffsets() {
-            return nodeFieldOffsets;
+        public long[] getChildOffsets() {
+            return childOffsets;
         }
 
-        public long[] getNodeArrayFieldOffsets() {
-            return nodeArrayFieldOffsets;
+        public long[] getChildrenOffsets() {
+            return childrenOffsets;
         }
     }
 
-    public static class NodeIterator implements Iterator<Node> {
+    static class NodeIterator implements Iterator<Node> {
 
         private final Node node;
         private final NodeClass nodeClass;
         private final int childrenCount;
         private int index;
 
-        public NodeIterator(Node node) {
-            this(node, 0);
-        }
-
-        public NodeIterator(Node node, int index) {
+        protected NodeIterator(Node node) {
             this.node = node;
-            this.index = index;
+            this.index = 0;
             this.nodeClass = NodeClass.get(node.getClass());
             this.childrenCount = childrenCount();
         }
 
         private int childrenCount() {
-            int nodeCount = nodeClass.nodeFieldOffsets.length;
-            for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) {
+            int nodeCount = nodeClass.childOffsets.length;
+            for (long fieldOffset : nodeClass.childrenOffsets) {
                 Node[] children = ((Node[]) unsafe.getObject(node, fieldOffset));
                 if (children != null) {
                     nodeCount += children.length;
@@ -144,11 +221,11 @@
         }
 
         private Node nodeAt(int idx) {
-            int nodeCount = nodeClass.nodeFieldOffsets.length;
+            int nodeCount = nodeClass.childOffsets.length;
             if (idx < nodeCount) {
-                return (Node) unsafe.getObject(node, nodeClass.nodeFieldOffsets[idx]);
+                return (Node) unsafe.getObject(node, nodeClass.childOffsets[idx]);
             } else {
-                for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) {
+                for (long fieldOffset : nodeClass.childrenOffsets) {
                     Node[] nodeArray = (Node[]) unsafe.getObject(node, fieldOffset);
                     if (idx < nodeCount + nodeArray.length) {
                         return nodeArray[idx - nodeCount];
@@ -220,7 +297,7 @@
 
         unsafe.putObject(clone, nodeClass.parentOffset, null);
 
-        for (long fieldOffset : nodeClass.nodeFieldOffsets) {
+        for (long fieldOffset : nodeClass.childOffsets) {
             Node child = (Node) unsafe.getObject(orig, fieldOffset);
             if (child != null) {
                 Node clonedChild = cloneNode(child);
@@ -232,7 +309,7 @@
                 unsafe.putObject(clone, fieldOffset, clonedChild);
             }
         }
-        for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) {
+        for (long fieldOffset : nodeClass.childrenOffsets) {
             Node[] children = (Node[]) unsafe.getObject(orig, fieldOffset);
             if (children != null) {
                 Node[] clonedChildren = children.clone();
@@ -256,13 +333,13 @@
         List<Node> nodes = new ArrayList<>();
         NodeClass nodeClass = NodeClass.get(node.getClass());
 
-        for (long fieldOffset : nodeClass.nodeFieldOffsets) {
+        for (long fieldOffset : nodeClass.childOffsets) {
             Object child = unsafe.getObject(node, fieldOffset);
             if (child != null) {
                 nodes.add((Node) child);
             }
         }
-        for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) {
+        for (long fieldOffset : nodeClass.childrenOffsets) {
             Node[] children = (Node[]) unsafe.getObject(node, fieldOffset);
             if (children != null) {
                 nodes.addAll(Arrays.asList(children));
@@ -275,12 +352,12 @@
     public static void replaceChild(Node parent, Node oldChild, Node newChild) {
         NodeClass nodeClass = NodeClass.get(parent.getClass());
 
-        for (long fieldOffset : nodeClass.nodeFieldOffsets) {
+        for (long fieldOffset : nodeClass.childOffsets) {
             if (unsafe.getObject(parent, fieldOffset) == oldChild) {
                 unsafe.putObject(parent, fieldOffset, newChild);
             }
         }
-        for (long fieldOffset : nodeClass.nodeArrayFieldOffsets) {
+        for (long fieldOffset : nodeClass.childrenOffsets) {
             Node[] array = (Node[]) unsafe.getObject(parent, fieldOffset);
             if (array != null) {
                 for (int i = 0; i < array.length; i++) {
@@ -293,49 +370,8 @@
         }
     }
 
-    public static long[] getNodeDataFieldOffsets(Class<?> nodeClass) {
-        NodeClass clazz = NodeClass.get(nodeClass);
-        return Arrays.copyOf(clazz.nodeDataFieldOffsets, clazz.nodeDataFieldClasses.length);
-    }
-
-    public static Class[] getNodeDataFieldClasses(Class<?> nodeClass) {
-        NodeClass clazz = NodeClass.get(nodeClass);
-        return Arrays.copyOf(clazz.nodeDataFieldClasses, clazz.nodeDataFieldClasses.length);
-    }
-
-    public static long getNodeParentOffset(Class<?> nodeClass) {
-        NodeClass clazz = NodeClass.get(nodeClass);
-        return clazz.parentOffset;
-    }
-
-    /** Returns the number of Node field declarations in the class hierarchy. */
-    public static long[] getNodeFieldOffsets(Class<?> nodeClass) {
-        NodeClass clazz = NodeClass.get(nodeClass);
-        return Arrays.copyOf(clazz.nodeFieldOffsets, clazz.nodeFieldOffsets.length);
-    }
-
-    /** Returns the number of Node[] declaration in the class hierarchy. */
-    public static long[] getNodeFieldArrayOffsets(Class<?> nodeClass) {
-        NodeClass clazz = NodeClass.get(nodeClass);
-        return Arrays.copyOf(clazz.nodeArrayFieldOffsets, clazz.nodeArrayFieldOffsets.length);
-    }
-
-    public static Class[] getNodeFieldArrayClasses(Class<?> nodeClass) {
-        NodeClass clazz = NodeClass.get(nodeClass);
-        return Arrays.copyOf(clazz.nodeArrayFieldClasses, clazz.nodeArrayFieldClasses.length);
-    }
-
-    public static Class getNodeParentClass(Class<?> nodeClass) {
-        return NodeClass.get(nodeClass).parentClass;
-    }
-
-    public static Class[] getNodeFieldClasses(Class<?> nodeClass) {
-        NodeClass clazz = NodeClass.get(nodeClass);
-        return Arrays.copyOf(clazz.nodeFieldClasses, clazz.nodeFieldClasses.length);
-    }
-
     /** Returns all declared fields in the class hierarchy. */
-    public static Field[] getAllFields(Class<? extends Object> clazz) {
+    private static Field[] getAllFields(Class<? extends Object> clazz) {
         Field[] declaredFields = clazz.getDeclaredFields();
         if (clazz.getSuperclass() != null) {
             return concat(getAllFields(clazz.getSuperclass()), declaredFields);
@@ -472,44 +508,17 @@
         return nodeList;
     }
 
-    public static String printTreeToString(Node node) {
-        return printTreeToString(node, false);
-    }
-
-    private static String printTreeToString(Node node, boolean compact) {
-        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
-        if (compact) {
-            printCompactTree(new PrintStream(byteOut), node);
-        } else {
-            printTree(new PrintStream(byteOut), node);
-        }
-        try {
-            byteOut.flush();
-        } catch (IOException e) {
-        }
-        return new String(byteOut.toByteArray());
+    public static String printCompactTreeToString(Node node) {
+        StringWriter out = new StringWriter();
+        printCompactTree(new PrintWriter(out), null, node, 1);
+        return out.toString();
     }
 
-    /**
-     * Prints a human readable form of a {@link Node} AST to the given {@link PrintStream}. This
-     * print method does not check for cycles in the node structure.
-     * 
-     * @param p the {@link PrintStream} to print to.
-     * @param node the root node to write
-     */
-    public static void printTree(PrintStream p, Node node) {
-        printTree(p, node, new NodeTreeResolver());
+    public static void printCompactTree(OutputStream out, Node node) {
+        printCompactTree(new PrintWriter(out), null, node, 1);
     }
 
-    public static String printCompactTreeToString(Node node) {
-        return printTreeToString(node, true);
-    }
-
-    public static void printCompactTree(PrintStream p, Node node) {
-        printCompactTree(p, null, node, 1);
-    }
-
-    private static void printCompactTree(PrintStream p, Node parent, Node node, int level) {
+    private static void printCompactTree(PrintWriter p, Node parent, Node node, int level) {
         if (node == null) {
             return;
         }
@@ -519,32 +528,23 @@
         if (parent == null) {
             p.println(node.getClass().getSimpleName());
         } else {
-            String fieldName = null;
-            Field[] fields = NodeUtil.getAllFields(parent.getClass());
-            try {
-                for (Field field : fields) {
-                    field.setAccessible(true);
-                    Object value = field.get(parent);
-                    if (value == node) {
-                        fieldName = field.getName();
-                        break;
-                    } else if (value instanceof Node[]) {
-                        int index = 0;
-                        for (Node arrayNode : (Node[]) value) {
-                            if (arrayNode == node) {
-                                fieldName = field.getName() + "[" + index + "]";
-                                break;
-                            }
-                            index++;
+            String fieldName = "unknownField";
+            NodeField[] fields = NodeClass.get(parent.getClass()).fields;
+            for (NodeField field : fields) {
+                Object value = field.loadValue(parent);
+                if (value == node) {
+                    fieldName = field.getName();
+                    break;
+                } else if (value instanceof Node[]) {
+                    int index = 0;
+                    for (Node arrayNode : (Node[]) value) {
+                        if (arrayNode == node) {
+                            fieldName = field.getName() + "[" + index + "]";
+                            break;
                         }
+                        index++;
                     }
                 }
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-
-            if (fieldName == null) {
-                fieldName = "unknownField";
             }
             p.print(fieldName);
             p.print(" = ");
@@ -554,23 +554,33 @@
         for (Node child : node.getChildren()) {
             printCompactTree(p, node, child, level + 1);
         }
+        p.flush();
     }
 
     /**
-     * Prints a human readable form of a tree to the given {@link PrintStream}. The
-     * {@link TreeResolver} interface needs to be implemented to specify how the method can read the
-     * tree from plain a object.
+     * Prints a human readable form of a {@link Node} AST to the given {@link PrintStream}. This
+     * print method does not check for cycles in the node structure.
      * 
-     * @param p the {@link PrintStream} to print to.
-     * @param o the root object to be printed.
-     * @param resolver an implementation of a tree resolver
+     * @param out the stream to print to.
+     * @param node the root node to write
      */
-    public static void printTree(PrintStream p, Object o, TreeResolver resolver) {
-        printTree(p, o, resolver, 1);
-        p.println();
+    public static void printTree(OutputStream out, Node node) {
+        printTree(new PrintWriter(out), node);
     }
 
-    private static void printTree(PrintStream p, Object node, TreeResolver resolver, int level) {
+    public static String printTreeToString(Node node) {
+        StringWriter out = new StringWriter();
+        printTree(new PrintWriter(out), node);
+        return out.toString();
+    }
+
+    public static void printTree(PrintWriter p, Node node) {
+        printTree(p, node, 1);
+        p.println();
+        p.flush();
+    }
+
+    private static void printTree(PrintWriter p, Node node, int level) {
         if (node == null) {
             p.print("null");
             return;
@@ -578,204 +588,56 @@
 
         p.print(node.getClass().getSimpleName());
 
-        Field[] fields = NodeUtil.getAllFields(node.getClass());
+        ArrayList<NodeField> childFields = new ArrayList<>();
+        String sep = "";
         p.print("(");
-
-        ArrayList<Field> childFields = new ArrayList<>();
+        for (NodeField field : NodeClass.get(node.getClass()).fields) {
+            if (field.getKind() == NodeFieldKind.CHILD || field.getKind() == NodeFieldKind.CHILDREN) {
+                childFields.add(field);
+            } else if (field.getKind() == NodeFieldKind.DATA) {
+                p.print(sep);
+                sep = ", ";
 
-        boolean first = true;
-        for (int i = 0; i < fields.length; i++) {
-            Field field = fields[i];
-            if (Modifier.isStatic(field.getModifiers()) || resolver.isFiltered(field)) {
-                continue;
-            }
-            if (resolver.isChildObject(field) || resolver.isChildArrayObject(field)) {
-                childFields.add(field);
+                p.print(field.getName());
+                p.print(" = ");
+                p.print(field.loadValue(node));
             }
-            if (!resolver.isDataField(field)) {
-                continue;
-            }
-            if (!first) {
-                p.print(", ");
-            } else {
-                first = false;
-            }
-
-            Object value = getObjectValue(node, field.getType(), unsafe.objectFieldOffset(field));
-
-            p.print(field.getName());
-            p.print(" = ");
-            p.print(resolver.toString(value));
         }
         p.print(")");
 
         if (childFields.size() != 0) {
             p.print(" {");
-        }
-
-        // I refetch the fields to get declaration order.
-        for (int i = 0; i < childFields.size(); i++) {
-            Field field = childFields.get(i);
-            Class<?> fieldClass = field.getType();
-            String name = field.getName();
-
-            long offset = unsafe.objectFieldOffset(field);
-
-            Object value = getObjectValue(node, fieldClass, offset);
+            for (NodeField field : childFields) {
+                printNewLine(p, level);
+                p.print(field.getName());
 
-            printNewLine(p, level);
-            p.print(name);
-            if (value == null) {
-                p.print(" = null ");
-            } else if (resolver.isChildObject(field)) {
-                p.print(" = ");
-                printTree(p, value, resolver, level + 1);
-            } else if (resolver.isChildArrayObject(field)) {
-                Object[] objectArray = resolver.convertToArray(field, value);
-                if (objectArray.length == 0) {
-                    p.print(" = []");
-                } else {
+                Object value = field.loadValue(node);
+                if (value == null) {
+                    p.print(" = null ");
+                } else if (field.getKind() == NodeFieldKind.CHILD) {
+                    p.print(" = ");
+                    printTree(p, (Node) value, level + 1);
+                } else if (field.getKind() == NodeFieldKind.CHILDREN) {
+                    Node[] children = (Node[]) value;
                     p.print(" = [");
-                    for (int j = 0; j < objectArray.length; j++) {
-                        printTree(p, objectArray[j], resolver, level + 1);
-                        if (j < objectArray.length - 1) {
-                            p.print(", ");
-                        }
+                    sep = "";
+                    for (Node child : children) {
+                        p.print(sep);
+                        sep = ", ";
+                        printTree(p, child, level + 1);
                     }
                     p.print("]");
                 }
-            } else {
-                assert false;
             }
-        }
-
-        if (childFields.size() != 0) {
             printNewLine(p, level - 1);
             p.print("}");
         }
     }
 
-    private static Object getObjectValue(Object base, Class<?> fieldClass, long offset) {
-        if (fieldClass == boolean.class) {
-            return unsafe.getBoolean(base, offset);
-        } else if (fieldClass == byte.class) {
-            return unsafe.getByte(base, offset);
-        } else if (fieldClass == short.class) {
-            return unsafe.getShort(base, offset);
-        } else if (fieldClass == char.class) {
-            return unsafe.getChar(base, offset);
-        } else if (fieldClass == int.class) {
-            return unsafe.getInt(base, offset);
-        } else if (fieldClass == long.class) {
-            return unsafe.getLong(base, offset);
-        } else if (fieldClass == float.class) {
-            return unsafe.getFloat(base, offset);
-        } else if (fieldClass == double.class) {
-            return unsafe.getDouble(base, offset);
-        } else {
-            return unsafe.getObject(base, offset);
-        }
-    }
-
-    private static void printNewLine(PrintStream p, int level) {
+    private static void printNewLine(PrintWriter p, int level) {
         p.println();
         for (int i = 0; i < level; i++) {
             p.print("    ");
         }
     }
-
-    private static class NodeTreeResolver implements TreeResolver {
-
-        @Override
-        public boolean isFiltered(Field f) {
-            if (f.getName().equals("parent")) {
-                return true;
-            }
-            return f.isSynthetic();
-        }
-
-        @Override
-        public boolean isDataField(Field f) {
-            return !isChildArrayObject(f) && !isChildObject(f);
-        }
-
-        @Override
-        public boolean isChildObject(Field f) {
-            return Node.class.isAssignableFrom(f.getType()) && f.getAnnotation(Child.class) != null;
-        }
-
-        @Override
-        public boolean isChildArrayObject(Field f) {
-            return f.getType().getComponentType() != null && Node.class.isAssignableFrom(f.getType().getComponentType()) && f.getAnnotation(Children.class) != null;
-        }
-
-        @Override
-        public Object[] convertToArray(Field f, Object data) {
-            return (Object[]) data;
-        }
-
-        @Override
-        public String toString(Object o) {
-            return o == null ? "null" : o.toString();
-        }
-    }
-
-    /**
-     * Specifies how a tree can be built from plain objects.
-     */
-    public interface TreeResolver {
-
-        /**
-         * Returns true if a {@link Field} is filtered from the tree.
-         * 
-         * @param f the field to check
-         */
-        boolean isFiltered(Field f);
-
-        /**
-         * Returns true if a {@link Field} is a field that contains a data value which should not be
-         * traversed recursively.
-         * 
-         * @param f the field to check
-         * @return true if a the given field is a data field else false.
-         */
-        boolean isDataField(Field f);
-
-        /**
-         * Returns true if a {@link Field} is a field that contains an {@link Object} which should
-         * be recursively visited.
-         * 
-         * @param f the field to check
-         * @return true if a the given field is a child field else false.
-         */
-        boolean isChildObject(Field f);
-
-        /**
-         * Returns true if a {@link Field} is a field that contains any kind of list/array structure
-         * which itself holds values that should be recursively visited.
-         * 
-         * @param f the field to check
-         * @return true if a the given field is a child array/list field else false.
-         */
-        boolean isChildArrayObject(Field f);
-
-        /**
-         * Converts an given child array object to array which can be traversed. This is especially
-         * useful to convert any kind of list structure to a traversable array.
-         * 
-         * @param f the field for meta data needed to convert the data {@link Object}.
-         * @param value the actual value of the child array/list object.
-         * @return the converted {@link Object} array.
-         */
-        Object[] convertToArray(Field f, Object value);
-
-        /**
-         * Returns a human readable string for any data field object in the tree.
-         * 
-         * @param o the object to convert to string.
-         * @return the converted string
-         */
-        String toString(Object o);
-    }
-
 }
--- a/src/cpu/sparc/vm/c2_globals_sparc.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/cpu/sparc/vm/c2_globals_sparc.hpp	Tue May 21 19:51:00 2013 +0200
@@ -38,6 +38,7 @@
 define_pd_global(bool, ProfileTraps,                 true);
 define_pd_global(bool, UseOnStackReplacement,        true);
 define_pd_global(intx, TypeProfileWidth,             2   );
+define_pd_global(intx, MethodProfileWidth,           0   );
 #ifdef CC_INTERP
 define_pd_global(bool, ProfileInterpreter,           false);
 #else
--- a/src/cpu/x86/vm/c1_globals_x86.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/cpu/x86/vm/c1_globals_x86.hpp	Tue May 21 19:51:00 2013 +0200
@@ -56,6 +56,7 @@
 define_pd_global(uint64_t,MaxRAM,                    1ULL*G);
 define_pd_global(bool, CICompileOSR,                 true );
 define_pd_global(intx, TypeProfileWidth,             0);
+define_pd_global(intx, MethodProfileWidth,           0);
 #endif // !TIERED
 define_pd_global(bool, UseTypeProfile,               false);
 define_pd_global(bool, RoundFPResults,               true );
--- a/src/cpu/x86/vm/c2_globals_x86.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/cpu/x86/vm/c2_globals_x86.hpp	Tue May 21 19:51:00 2013 +0200
@@ -40,6 +40,7 @@
 define_pd_global(bool, ProfileTraps,                 true);
 define_pd_global(bool, UseOnStackReplacement,        true);
 define_pd_global(intx, TypeProfileWidth,             2   );
+define_pd_global(intx, MethodProfileWidth,           0   );
 #ifdef CC_INTERP
 define_pd_global(bool, ProfileInterpreter,           false);
 #else
--- a/src/cpu/x86/vm/graalGlobals_x86.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/cpu/x86/vm/graalGlobals_x86.hpp	Tue May 21 19:51:00 2013 +0200
@@ -58,6 +58,7 @@
 define_pd_global(intx, CodeCacheExpansionSize,       64*K );
 define_pd_global(uintx,CodeCacheMinBlockLength,      4);
 define_pd_global(intx, TypeProfileWidth,             8);
+define_pd_global(intx, MethodProfileWidth,           4);
 #endif
 
 #endif // CPU_X86_VM_GRAALGLOBALS_X86_HPP
--- a/src/cpu/x86/vm/interp_masm_x86_64.cpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/cpu/x86/vm/interp_masm_x86_64.cpp	Tue May 21 19:51:00 2013 +0200
@@ -1082,9 +1082,10 @@
     increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
 
     // The method data pointer needs to be updated to reflect the new target.
-    update_mdp_by_constant(mdp,
-                           in_bytes(VirtualCallData::
+    update_mdp_by_constant(mdp,
+                           in_bytes(VirtualCallData::
                                     virtual_call_data_size()));
+
     bind(profile_continue);
   }
 }
@@ -1116,13 +1117,38 @@
     bind(skip_receiver_profile);
 
     // The method data pointer needs to be updated to reflect the new target.
-    update_mdp_by_constant(mdp,
-                           in_bytes(VirtualCallData::
-                                    virtual_call_data_size()));
+#ifdef GRAAL
+    if (MethodProfileWidth == 0) {
+      update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size()));
+    }
+#else
+    update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size()));
+#endif
     bind(profile_continue);
   }
 }
 
+#ifdef GRAAL
+void InterpreterMacroAssembler::profile_called_method(Register method, Register mdp, Register reg2) {
+  assert_different_registers(method, mdp, reg2);
+  if (ProfileInterpreter && MethodProfileWidth > 0) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+    
+    Label done;
+    record_item_in_profile_helper(method, mdp, reg2, 0, done, MethodProfileWidth,
+      &VirtualCallData::method_offset, &VirtualCallData::method_count_offset, in_bytes(VirtualCallData::nonprofiled_receiver_count_offset()));
+    bind (done);
+
+    update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size()));
+    bind(profile_continue);
+  }
+}
+
+#endif
+
 // This routine creates a state machine for updating the multi-row
 // type profile at a virtual call site (or other type-sensitive bytecode).
 // The machine visits each row (of receiver/count) until the receiver type
@@ -1136,8 +1162,7 @@
 // See below for example code.
 void InterpreterMacroAssembler::record_klass_in_profile_helper(
                                         Register receiver, Register mdp,
-                                        Register reg2, int start_row,
-                                        Label& done, bool is_virtual_call) {
+                                        Register reg2, Label& done, bool is_virtual_call) {
   if (TypeProfileWidth == 0) {
     if (is_virtual_call) {
       increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
@@ -1147,14 +1172,33 @@
       increment_mdp_data_at(mdp, in_bytes(ReceiverTypeData::nonprofiled_receiver_count_offset()));
     }
 #endif
-    return;
-  }
+  } else {                                      
+    bool use_non_profiled_counter = !is_virtual_call || IS_GRAAL_DEFINED;
+    int non_profiled_offset = -1;
+    if (use_non_profiled_counter) {
+       non_profiled_offset = in_bytes(CounterData::count_offset());
+    #ifdef GRAAL
+      if (!is_virtual_call) {
+        non_profiled_offset = in_bytes(ReceiverTypeData::nonprofiled_receiver_count_offset());
+      }
+    #endif
+      assert(non_profiled_offset >= 0, "must be");
+    }
 
-  int last_row = VirtualCallData::row_limit() - 1;
+    record_item_in_profile_helper(receiver, mdp, reg2, 0, done, TypeProfileWidth,
+      &VirtualCallData::receiver_offset, &VirtualCallData::receiver_count_offset, non_profiled_offset);
+  }
+}
+
+void InterpreterMacroAssembler::record_item_in_profile_helper(Register item, Register mdp,
+                                        Register reg2, int start_row, Label& done, int total_rows,
+                                        OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn,
+                                        int non_profiled_offset) {
+  int last_row = total_rows - 1;
   assert(start_row <= last_row, "must be work left to do");
-  // Test this row for both the receiver and for null.
+  // Test this row for both the item and for null.
   // Take any of three different outcomes:
-  //   1. found receiver => increment count and goto done
+  //   1. found item => increment count and goto done
   //   2. found null => keep looking for case 1, maybe allocate this cell
   //   3. found something else => keep looking for cases 1 and 2
   // Case 3 is handled by a recursive call.
@@ -1162,36 +1206,30 @@
     Label next_test;
     bool test_for_null_also = (row == start_row);
 
-    // See if the receiver is receiver[n].
-    int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row));
-    test_mdp_data_at(mdp, recvr_offset, receiver,
+    // See if the item is item[n].
+    int item_offset = in_bytes(item_offset_fn(row));
+    test_mdp_data_at(mdp, item_offset, item,
                      (test_for_null_also ? reg2 : noreg),
                      next_test);
-    // (Reg2 now contains the receiver from the CallData.)
+    // (Reg2 now contains the item from the CallData.)
 
-    // The receiver is receiver[n].  Increment count[n].
-    int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row));
+    // The item is item[n].  Increment count[n].
+    int count_offset = in_bytes(item_count_offset_fn(row));
     increment_mdp_data_at(mdp, count_offset);
     jmp(done);
     bind(next_test);
 
     if (test_for_null_also) {
       Label found_null;
-      // Failed the equality check on receiver[n]...  Test for null.
+      // Failed the equality check on item[n]...  Test for null.
       testptr(reg2, reg2);
       if (start_row == last_row) {
         // The only thing left to do is handle the null case.
-        if (is_virtual_call GRAAL_ONLY(|| true)) {
+        if (non_profiled_offset >= 0) {
           jccb(Assembler::zero, found_null);
-          // Receiver did not match any saved receiver and there is no empty row for it.
+          // Item did not match any saved item and there is no empty row for it.
           // Increment total counter to indicate polymorphic case.
-          int offset = in_bytes(CounterData::count_offset());
-#ifdef GRAAL
-          if (!is_virtual_call) {
-            offset = in_bytes(ReceiverTypeData::nonprofiled_receiver_count_offset());
-          }
-#endif
-          increment_mdp_data_at(mdp, offset);
+          increment_mdp_data_at(mdp, non_profiled_offset);
           jmp(done);
           bind(found_null);
         } else {
@@ -1203,21 +1241,22 @@
       jcc(Assembler::zero, found_null);
 
       // Put all the "Case 3" tests here.
-      record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done, is_virtual_call);
+      record_item_in_profile_helper(item, mdp, reg2, start_row + 1, done, total_rows,
+        item_offset_fn, item_count_offset_fn, non_profiled_offset);
 
-      // Found a null.  Keep searching for a matching receiver,
+      // Found a null.  Keep searching for a matching item,
       // but remember that this is an empty (unused) slot.
       bind(found_null);
     }
   }
 
-  // In the fall-through case, we found no matching receiver, but we
-  // observed the receiver[start_row] is NULL.
+  // In the fall-through case, we found no matching item, but we
+  // observed the item[start_row] is NULL.
 
-  // Fill in the receiver field and increment the count.
-  int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row));
-  set_mdp_data_at(mdp, recvr_offset, receiver);
-  int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row));
+  // Fill in the item field and increment the count.
+  int item_offset = in_bytes(item_offset_fn(start_row));
+  set_mdp_data_at(mdp, item_offset, item);
+  int count_offset = in_bytes(item_count_offset_fn(start_row));
   movl(reg2, DataLayout::counter_increment);
   set_mdp_data_at(mdp, count_offset, reg2);
   if (start_row > 0) {
@@ -1255,7 +1294,7 @@
   assert(ProfileInterpreter, "must be profiling");
   Label done;
 
-  record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call);
+  record_klass_in_profile_helper(receiver, mdp, reg2, done, is_virtual_call);
 
   bind (done);
 }
@@ -1310,7 +1349,7 @@
     // The method data pointer needs to be updated.
     int mdp_delta = in_bytes(BitData::bit_data_size());
     if (TypeProfileCasts) {
-      mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
+      mdp_delta = in_bytes(ReceiverTypeData::receiver_type_data_size());
     }
     update_mdp_by_constant(mdp, mdp_delta);
 
@@ -1328,7 +1367,7 @@
 
     int count_offset = in_bytes(CounterData::count_offset());
     // Back up the address, since we have already bumped the mdp.
-    count_offset -= in_bytes(VirtualCallData::virtual_call_data_size());
+    count_offset -= in_bytes(ReceiverTypeData::receiver_type_data_size());
 
     // *Decrement* the counter.  We expect to see zero or small negatives.
     increment_mdp_data_at(mdp, count_offset, true);
@@ -1348,7 +1387,7 @@
     // The method data pointer needs to be updated.
     int mdp_delta = in_bytes(BitData::bit_data_size());
     if (TypeProfileCasts) {
-      mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
+      mdp_delta = in_bytes(ReceiverTypeData::receiver_type_data_size());
 
       // Record the object type.
       record_klass_in_profile(klass, mdp, reg2, false);
--- a/src/cpu/x86/vm/interp_masm_x86_64.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/cpu/x86/vm/interp_masm_x86_64.hpp	Tue May 21 19:51:00 2013 +0200
@@ -32,6 +32,7 @@
 
 // This file specializes the assember with interpreter-specific macros
 
+typedef ByteSize (*OffsetFunction)(uint);
 
 class InterpreterMacroAssembler: public MacroAssembler {
 #ifndef CC_INTERP
@@ -213,8 +214,11 @@
   void record_klass_in_profile(Register receiver, Register mdp,
                                Register reg2, bool is_virtual_call);
   void record_klass_in_profile_helper(Register receiver, Register mdp,
-                                      Register reg2, int start_row,
-                                      Label& done, bool is_virtual_call);
+                                      Register reg2, Label& done, bool is_virtual_call);
+  void record_item_in_profile_helper(Register item, Register mdp,
+                                        Register reg2, int start_row, Label& done, int total_rows,
+                                        OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn,
+                                        int non_profiled_offset);
 
   void update_mdp_by_offset(Register mdp_in, int offset_of_offset);
   void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp);
@@ -228,6 +232,7 @@
   void profile_virtual_call(Register receiver, Register mdp,
                             Register scratch2,
                             bool receiver_can_be_null = false);
+  void profile_called_method(Register method, Register mdp, Register reg2);
   void profile_ret(Register return_bci, Register mdp);
   void profile_null_seen(Register mdp);
   void profile_typecheck(Register mdp, Register klass, Register scratch);
--- a/src/cpu/x86/vm/templateTable_x86_64.cpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/cpu/x86/vm/templateTable_x86_64.cpp	Tue May 21 19:51:00 2013 +0200
@@ -3017,6 +3017,10 @@
 
   // get target Method* & entry point
   __ lookup_virtual_method(rax, index, method);
+#ifdef GRAAL
+  // r14: MethodDataPointer (r14 is callee saved)
+  __ profile_called_method(method, r14, r13);
+#endif
   __ jump_from_interpreted(method, rdx);
 }
 
@@ -3101,7 +3105,7 @@
   __ lookup_interface_method(// inputs: rec. class, interface, itable index
                              rdx, rax, rbx,
                              // outputs: method, scan temp. reg
-                             rbx, r13,
+                             rbx, r14,
                              no_such_interface);
 
   // rbx: Method* to call
@@ -3116,6 +3120,10 @@
   // do the call
   // rcx: receiver
   // rbx,: Method*
+#ifdef GRAAL
+  // r13: MethodDataPointer (r13 is callee saved)
+  __ profile_called_method(rbx, r13, r14);
+#endif
   __ jump_from_interpreted(rbx, rdx);
   __ should_not_reach_here();
 
--- a/src/share/vm/classfile/systemDictionary.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/classfile/systemDictionary.hpp	Tue May 21 19:51:00 2013 +0200
@@ -188,7 +188,7 @@
   do_klass(HotSpotCompiledCode_klass,             com_oracle_graal_hotspot_HotSpotCompiledCode,                 Opt) \
   do_klass(HotSpotCompiledNmethod_klass,          com_oracle_graal_hotspot_HotSpotCompiledNmethod,              Opt) \
   do_klass(HotSpotCompiledRuntimeStub_klass,      com_oracle_graal_hotspot_HotSpotCompiledRuntimeStub,          Opt) \
-  do_klass(HotSpotRuntimeCallTarget_klass,        com_oracle_graal_hotspot_HotSpotRuntimeCallTarget,            Opt) \
+  do_klass(HotSpotForeignCallLinkage_klass,       com_oracle_graal_hotspot_HotSpotForeignCallLinkage,           Opt) \
   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) \
--- a/src/share/vm/classfile/vmSymbols.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/classfile/vmSymbols.hpp	Tue May 21 19:51:00 2013 +0200
@@ -299,7 +299,7 @@
   template(com_oracle_graal_hotspot_HotSpotCompiledCode,             "com/oracle/graal/hotspot/HotSpotCompiledCode")                  \
   template(com_oracle_graal_hotspot_HotSpotCompiledNmethod,          "com/oracle/graal/hotspot/HotSpotCompiledNmethod")               \
   template(com_oracle_graal_hotspot_HotSpotCompiledRuntimeStub,      "com/oracle/graal/hotspot/HotSpotCompiledRuntimeStub")           \
-  template(com_oracle_graal_hotspot_HotSpotRuntimeCallTarget,        "com/oracle/graal/hotspot/HotSpotRuntimeCallTarget")             \
+  template(com_oracle_graal_hotspot_HotSpotForeignCallLinkage,       "com/oracle/graal/hotspot/HotSpotForeignCallLinkage")            \
   template(com_oracle_graal_hotspot_bridge_VMToCompiler,             "com/oracle/graal/hotspot/bridge/VMToCompiler")                  \
   template(com_oracle_graal_hotspot_bridge_CompilerToVMImpl,         "com/oracle/graal/hotspot/bridge/CompilerToVMImpl")              \
   template(com_oracle_graal_hotspot_meta_HotSpotCodeInfo,            "com/oracle/graal/hotspot/meta/HotSpotCodeInfo")                 \
--- a/src/share/vm/graal/graalCodeInstaller.cpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/graal/graalCodeInstaller.cpp	Tue May 21 19:51:00 2013 +0200
@@ -647,17 +647,17 @@
   InstanceKlass* target_klass = InstanceKlass::cast(target->klass());
 
   oop hotspot_method = NULL; // JavaMethod
-  oop global_stub = NULL;
+  oop foreign_call = NULL;
 
-  if (target_klass->is_subclass_of(SystemDictionary::HotSpotRuntimeCallTarget_klass())) {
-    global_stub = target;
+  if (target_klass->is_subclass_of(SystemDictionary::HotSpotForeignCallLinkage_klass())) {
+    foreign_call = target;
   } else {
     hotspot_method = target;
   }
 
   oop debug_info = CompilationResult_Call::debugInfo(site);
 
-  assert((hotspot_method ? 1 : 0) + (global_stub ? 1 : 0) == 1, "Call site needs exactly one type");
+  assert((hotspot_method ? 1 : 0) + (foreign_call ? 1 : 0) == 1, "Call site needs exactly one type");
 
   NativeInstruction* inst = nativeInstruction_at(_instructions->start() + pc_offset);
   jint next_pc_offset = 0x0;
@@ -704,24 +704,24 @@
     }
   }
 
-  if (global_stub != NULL) {
-    jlong global_stub_destination = HotSpotRuntimeCallTarget::address(global_stub);
+  if (foreign_call != NULL) {
+    jlong foreign_call_destination = HotSpotForeignCallLinkage::address(foreign_call);
     if (inst->is_call()) {
       // NOTE: for call without a mov, the offset must fit a 32-bit immediate
       //       see also CompilerToVM.getMaxCallTargetOffset()
       NativeCall* call = nativeCall_at((address) (inst));
-      call->set_destination((address) global_stub_destination);
+      call->set_destination((address) foreign_call_destination);
       _instructions->relocate(call->instruction_address(), runtime_call_Relocation::spec(), Assembler::call32_operand);
     } else if (inst->is_mov_literal64()) {
       NativeMovConstReg* mov = nativeMovConstReg_at((address) (inst));
-      mov->set_data((intptr_t) global_stub_destination);
+      mov->set_data((intptr_t) foreign_call_destination);
       _instructions->relocate(mov->instruction_address(), runtime_call_Relocation::spec(), Assembler::imm_operand);
     } else {
       NativeJump* jump = nativeJump_at((address) (inst));
-      jump->set_jump_destination((address) global_stub_destination);
+      jump->set_jump_destination((address) foreign_call_destination);
       _instructions->relocate((address)inst, runtime_call_Relocation::spec(), Assembler::call32_operand);
     }
-    TRACE_graal_3("relocating (stub)  at %p", inst);
+    TRACE_graal_3("relocating (foreign call)  at %p", inst);
   } else { // method != NULL
     assert(hotspot_method != NULL, "unexpected JavaMethod");
 #ifdef ASSERT
--- a/src/share/vm/graal/graalCompiler.cpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/graal/graalCompiler.cpp	Tue May 21 19:51:00 2013 +0200
@@ -95,7 +95,7 @@
       }
     }
     if (UseCompiler) {
-      VMToCompiler::startCompiler();
+      VMToCompiler::startCompiler(BootstrapGraal);
       _initialized = true;
       if (BootstrapGraal) {
         // We turn off CompileTheWorld and complete the VM startup so that
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Tue May 21 19:51:00 2013 +0200
@@ -687,6 +687,8 @@
   set_int("methodMaxLocalsOffset", in_bytes(ConstMethod::size_of_locals_offset()));
   set_int("methodConstMethodOffset", in_bytes(Method::const_offset()));
   set_int("constMethodMaxStackOffset", in_bytes(ConstMethod::max_stack_offset()));
+  set_int("constMethodConstantsOffset", in_bytes(ConstMethod::constants_offset()));
+  set_int("constantPoolHolderOffset", ConstantPool::pool_holder_offset_in_bytes());
   set_int("extraStackEntries", Method::extra_stack_entries());
   set_int("methodAccessFlagsOffset", in_bytes(Method::access_flags_offset()));
   set_int("methodIntrinsicIdOffset", Method::intrinsic_id_offset_in_bytes());
@@ -730,6 +732,7 @@
   set_int("dataLayoutCellSize", DataLayout::cell_size);
   set_int("bciProfileWidth", BciProfileWidth);
   set_int("typeProfileWidth", TypeProfileWidth);
+  set_int("methodProfileWidth", MethodProfileWidth);
 
   set_int("tlabAlignmentReserve", (int32_t)ThreadLocalAllocBuffer::alignment_reserve());
   set_long("tlabIntArrayMarkWord", (intptr_t)markOopDesc::prototype()->copy_set_hash(0x2));
--- a/src/share/vm/graal/graalJavaAccess.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/graal/graalJavaAccess.hpp	Tue May 21 19:51:00 2013 +0200
@@ -95,8 +95,8 @@
   start_class(HotSpotCompiledRuntimeStub)                                                                                                                      \
     oop_field(HotSpotCompiledRuntimeStub, stubName, "Ljava/lang/String;")                                                                                      \
   end_class                                                                                                                                                    \
-  start_class(HotSpotRuntimeCallTarget)                                                                                                                        \
-    long_field(HotSpotRuntimeCallTarget, address)                                                                                                              \
+  start_class(HotSpotForeignCallLinkage)                                                                                                                        \
+    long_field(HotSpotForeignCallLinkage, address)                                                                                                              \
   end_class                                                                                                                                                    \
   start_class(ExceptionHandler)                                                                                                                                \
     int_field(ExceptionHandler, startBCI)                                                                                                                      \
--- a/src/share/vm/graal/graalVMToCompiler.cpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/graal/graalVMToCompiler.cpp	Tue May 21 19:51:00 2013 +0200
@@ -123,12 +123,13 @@
   }
 }
 
-void VMToCompiler::startCompiler() {
+void VMToCompiler::startCompiler(jboolean bootstrap_enabled) {
   JavaThread* THREAD = JavaThread::current();
   JavaValue result(T_VOID);
   JavaCallArguments args;
   args.push_oop(instance());
-  JavaCalls::call_interface(&result, vmToCompilerKlass(), vmSymbols::startCompiler_name(), vmSymbols::void_method_signature(), &args, THREAD);
+  args.push_int(bootstrap_enabled);
+  JavaCalls::call_interface(&result, vmToCompilerKlass(), vmSymbols::startCompiler_name(), vmSymbols::bool_void_signature(), &args, THREAD);
   check_pending_exception("Error while calling startCompiler");
 }
 
--- a/src/share/vm/graal/graalVMToCompiler.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/graal/graalVMToCompiler.hpp	Tue May 21 19:51:00 2013 +0200
@@ -59,8 +59,8 @@
   // public abstract void shutdownCompiler();
   static void shutdownCompiler();
   
-  // public abstract void startCompiler();
-  static void startCompiler();
+  // public abstract void startCompiler(boolean bootstrapEnabled);
+  static void startCompiler(jboolean bootstrap_enabled);
   
   // public abstract void bootstrap();
   static void bootstrap();
--- a/src/share/vm/oops/methodData.cpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/oops/methodData.cpp	Tue May 21 19:51:00 2013 +0200
@@ -168,6 +168,18 @@
   }
 }
 
+#ifdef GRAAL
+void VirtualCallData::clean_weak_klass_links(BoolObjectClosure* is_alive_cl) {
+  ReceiverTypeData::clean_weak_klass_links(is_alive_cl);
+  for (uint row = 0; row < method_row_limit(); row++) {
+    Method* p = method(row);
+    if (p != NULL && !p->method_holder()->is_loader_alive(is_alive_cl)) {
+      clear_method_row(row);
+    }
+  }
+}
+#endif // GRAAL
+
 #ifndef PRODUCT
 void ReceiverTypeData::print_receiver_data_on(outputStream* st) {
   uint row;
--- a/src/share/vm/oops/methodData.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/oops/methodData.hpp	Tue May 21 19:51:00 2013 +0200
@@ -635,9 +635,17 @@
 protected:
   enum {
 #ifdef GRAAL
-    // Graal is interested in knowing the percentage of type checks
-    // involving a type not explicitly in the profile
-    nonprofiled_receiver_count_off_set = counter_cell_count,
+    // Description of the different counters
+    // ReceiverTypeData for instanceof/checkcast/aastore:
+    //   C1/C2: count is incremented on type overflow and decremented for failed type checks
+    //   Graal: count decremented for failed type checks and nonprofiled_count is incremented on type overflow
+    //          TODO (chaeubl): in fact, Graal should also increment the count for failed type checks to mimic the C1/C2 behavior
+    // VirtualCallData for invokevirtual/invokeinterface:
+    //   C1/C2: count is incremented on type overflow
+    //   Graal: count is incremented on type overflow, nonprofiled_count is increment on method overflow
+
+    // Graal is interested in knowing the percentage of type checks involving a type not explicitly in the profile
+    nonprofiled_count_off_set = counter_cell_count,
     receiver0_offset,
 #else
     receiver0_offset = counter_cell_count,
@@ -717,6 +725,13 @@
     set_count(0);
     set_receiver(row, NULL);
     set_receiver_count(row, 0);
+#ifdef GRAAL
+    if (!this->is_VirtualCallData()) {
+      // if this is a ReceiverTypeData for Graal, the nonprofiled_count
+      // must also be reset (see "Description of the different counters" above)
+      set_nonprofiled_count(0);
+    }
+#endif
   }
 
   // Code generation support
@@ -728,7 +743,10 @@
   }
 #ifdef GRAAL
   static ByteSize nonprofiled_receiver_count_offset() {
-    return cell_offset(nonprofiled_receiver_count_off_set);
+    return cell_offset(nonprofiled_count_off_set);
+  }
+  void set_nonprofiled_count(uint count) {
+    set_uint_at(nonprofiled_count_off_set, count);
   }
 #endif
   static ByteSize receiver_type_data_size() {
@@ -759,7 +777,7 @@
   static int static_cell_count() {
     // At this point we could add more profile state, e.g., for arguments.
     // But for now it's the same size as the base record type.
-    return ReceiverTypeData::static_cell_count();
+    return ReceiverTypeData::static_cell_count() GRAAL_ONLY(+ (uint) MethodProfileWidth * receiver_type_row_cell_count);
   }
 
   virtual int cell_count() {
@@ -771,6 +789,53 @@
     return cell_offset(static_cell_count());
   }
 
+#ifdef GRAAL
+  static ByteSize method_offset(uint row) {
+    return cell_offset(method_cell_index(row));
+  }
+  static ByteSize method_count_offset(uint row) {
+    return cell_offset(method_count_cell_index(row));
+  }
+  static int method_cell_index(uint row) {
+    return receiver0_offset + (row + TypeProfileWidth) * receiver_type_row_cell_count;
+  }
+  static int method_count_cell_index(uint row) {
+    return count0_offset + (row + TypeProfileWidth) * receiver_type_row_cell_count;
+  }
+  static uint method_row_limit() {
+    return MethodProfileWidth;
+  }
+
+  Method* method(uint row) {
+    assert(row < method_row_limit(), "oob");
+
+    Method* method = (Method*)intptr_at(method_cell_index(row));
+    assert(method == NULL || method->is_method(), "must be");
+    return method;
+  }
+
+  void set_method(uint row, Method* m) {
+    assert((uint)row < method_row_limit(), "oob");
+    set_intptr_at(method_cell_index(row), (uintptr_t)m);
+  }
+
+  void set_method_count(uint row, uint count) {
+    assert(row < method_row_limit(), "oob");
+    set_uint_at(method_count_cell_index(row), count);
+  }
+
+  void clear_method_row(uint row) {
+    assert(row < method_row_limit(), "oob");
+    // Clear total count - indicator of polymorphic call site (see comment for clear_row() in ReceiverTypeData).
+    set_nonprofiled_count(0);
+    set_method(row, NULL);
+    set_method_count(row, 0);
+  }
+
+  // GC support
+  virtual void clean_weak_klass_links(BoolObjectClosure* is_alive_closure);
+#endif
+
 #ifndef PRODUCT
   void print_data_on(outputStream* st);
 #endif
--- a/src/share/vm/runtime/globals.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/runtime/globals.hpp	Tue May 21 19:51:00 2013 +0200
@@ -2949,6 +2949,9 @@
   product_pd(intx, TypeProfileWidth,                                        \
           "number of receiver types to record in call/cast profile")        \
                                                                             \
+  product_pd(intx, MethodProfileWidth,                                      \
+          "number of methods to record in call profile")                    \
+                                                                            \
   develop(intx, BciProfileWidth,      2,                                    \
           "number of return bci's to record in ret profile")                \
                                                                             \
--- a/src/share/vm/utilities/macros.hpp	Tue May 21 19:43:53 2013 +0200
+++ b/src/share/vm/utilities/macros.hpp	Tue May 21 19:51:00 2013 +0200
@@ -182,6 +182,7 @@
 #ifdef GRAAL
 #define GRAAL_ONLY(code) code
 #define NOT_GRAAL(code)
+#define IS_GRAAL_DEFINED true
 #if !defined(COMPILER1) && !defined(COMPILER2)
 // Graal is the only compiler in the system and so will be used for compilation
 // requests issued by the compile broker.
@@ -197,6 +198,7 @@
 #else // !GRAAL
 #define GRAAL_ONLY(code)
 #define NOT_GRAAL(code) code
+#define IS_GRAAL_DEFINED false
 #define GRAALVM_ONLY(code)
 #define NOT_GRAALVM(code) code
 #endif // GRAAL