changeset 16389:c68c5fafef92

Merge
author Tom Rodriguez <tom.rodriguez@oracle.com>
date Wed, 02 Jul 2014 13:40:10 -0700
parents 31e242cad4d1 (diff) 8057279ec60e (current diff)
children ae8f4016792a
files
diffstat 20 files changed, 322 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Wed Jul 02 13:40:10 2014 -0700
@@ -865,14 +865,14 @@
     @HotSpotVMField(name = "Array<Klass*>::_length", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int metaspaceArrayLengthOffset;
     @HotSpotVMField(name = "Array<Klass*>::_data[0]", type = "Klass*", get = HotSpotVMField.Type.OFFSET) @Stable public int metaspaceArrayBaseOffset;
 
-    @HotSpotVMField(name = "InstanceKlass::_graal_node_class", type = "oop", get = HotSpotVMField.Type.OFFSET) @Stable public int klassNodeClassOffset;
-    @HotSpotVMField(name = "InstanceKlass::_source_file_name_index", type = "u2", get = HotSpotVMField.Type.OFFSET) @Stable public int klassSourceFileNameIndexOffset;
-    @HotSpotVMField(name = "InstanceKlass::_init_state", type = "u1", get = HotSpotVMField.Type.OFFSET) @Stable public int klassStateOffset;
+    @HotSpotVMField(name = "InstanceKlass::_graal_node_class", type = "oop", get = HotSpotVMField.Type.OFFSET) @Stable public int instanceKlassNodeClassOffset;
+    @HotSpotVMField(name = "InstanceKlass::_source_file_name_index", type = "u2", get = HotSpotVMField.Type.OFFSET) @Stable public int instanceKlassSourceFileNameIndexOffset;
+    @HotSpotVMField(name = "InstanceKlass::_init_state", type = "u1", get = HotSpotVMField.Type.OFFSET) @Stable public int instanceKlassInitStateOffset;
     @HotSpotVMField(name = "InstanceKlass::_constants", type = "ConstantPool*", get = HotSpotVMField.Type.OFFSET) @Stable public int instanceKlassConstantsOffset;
     @HotSpotVMField(name = "InstanceKlass::_fields", type = "Array<u2>*", get = HotSpotVMField.Type.OFFSET) @Stable public int instanceKlassFieldsOffset;
 
-    @HotSpotVMConstant(name = "InstanceKlass::linked") @Stable public int klassStateLinked;
-    @HotSpotVMConstant(name = "InstanceKlass::fully_initialized") @Stable public int klassStateFullyInitialized;
+    @HotSpotVMConstant(name = "InstanceKlass::linked") @Stable public int instanceKlassStateLinked;
+    @HotSpotVMConstant(name = "InstanceKlass::fully_initialized") @Stable public int instanceKlassStateFullyInitialized;
 
     @HotSpotVMField(name = "ObjArrayKlass::_element_klass", type = "Klass*", get = HotSpotVMField.Type.OFFSET) @Stable public int arrayClassElementOffset;
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java	Wed Jul 02 13:40:10 2014 -0700
@@ -111,7 +111,7 @@
         ResolvedJavaMethod initMethod = null;
         try {
             Class<?> rjm = ResolvedJavaMethod.class;
-            makeGraphMethod = metaAccess.lookupJavaMethod(ReplacementsImpl.class.getDeclaredMethod("makeGraph", rjm, rjm, rjm, SnippetInliningPolicy.class, FrameStateProcessing.class));
+            makeGraphMethod = metaAccess.lookupJavaMethod(ReplacementsImpl.class.getDeclaredMethod("makeGraph", rjm, rjm, SnippetInliningPolicy.class, FrameStateProcessing.class));
             initMethod = metaAccess.lookupJavaMethod(SnippetTemplate.AbstractTemplates.class.getDeclaredMethod("template", Arguments.class));
         } catch (NoSuchMethodException | SecurityException e) {
             throw new GraalInternalError(e);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Wed Jul 02 13:40:10 2014 -0700
@@ -555,6 +555,10 @@
         }
 
         long[] values = runtime().getCompilerToVM().getLineNumberTable(metaspaceMethod);
+        if (values.length == 0) {
+            // Empty table so treat is as non-existent
+            return null;
+        }
         assert values.length % 2 == 0;
         int[] bci = new int[values.length / 2];
         int[] line = new int[values.length / 2];
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Wed Jul 02 13:40:10 2014 -0700
@@ -293,14 +293,12 @@
 
     @Override
     public boolean isInitialized() {
-        final int state = getState();
-        return state == runtime().getConfig().klassStateFullyInitialized;
+        return isArray() ? true : getInitState() == runtime().getConfig().instanceKlassStateFullyInitialized;
     }
 
     @Override
     public boolean isLinked() {
-        final int state = getState();
-        return state >= runtime().getConfig().klassStateLinked;
+        return isArray() ? true : getInitState() >= runtime().getConfig().instanceKlassStateLinked;
     }
 
     /**
@@ -309,8 +307,9 @@
      *
      * @return state field value of this type
      */
-    private int getState() {
-        return unsafe.getByte(getMetaspaceKlass() + runtime().getConfig().klassStateOffset) & 0xFF;
+    private int getInitState() {
+        assert !isArray() : "_init_state only exists in InstanceKlass";
+        return unsafe.getByte(getMetaspaceKlass() + runtime().getConfig().instanceKlassInitStateOffset) & 0xFF;
     }
 
     @Override
@@ -356,6 +355,22 @@
 
     @Override
     public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
+        ResolvedJavaMethod resolvedMethod = resolveMethodInternal(method, callerType);
+        if (resolvedMethod == null || resolvedMethod.isAbstract()) {
+            return null;
+        }
+        return resolvedMethod;
+    }
+
+    /**
+     * Resolve the method against the current type and return the method found, including any
+     * abstract methods.
+     *
+     * @param method
+     * @param callerType
+     * @return the method found
+     */
+    private ResolvedJavaMethod resolveMethodInternal(ResolvedJavaMethod method, ResolvedJavaType callerType) {
         assert !callerType.isArray();
         if (!method.isAbstract() && method.getDeclaringClass().equals(this) && method.isPublic()) {
             return method;
@@ -369,11 +384,7 @@
         if (resolvedMetaspaceMethod == 0) {
             return null;
         }
-        HotSpotResolvedJavaMethod resolvedMethod = HotSpotResolvedJavaMethod.fromMetaspace(resolvedMetaspaceMethod);
-        if (resolvedMethod.isAbstract()) {
-            return null;
-        }
-        return resolvedMethod;
+        return HotSpotResolvedJavaMethod.fromMetaspace(resolvedMetaspaceMethod);
     }
 
     public ConstantPool constantPool() {
@@ -464,21 +475,24 @@
         /*
          * Sometimes the receiver type in the graph hasn't stabilized to a subtype of declared
          * holder, usually because of phis, so make sure that the type is related to the declared
-         * type before using it for lookup.
+         * type before using it for lookup. Unlinked types should also be ignored because we can't
+         * resolve the proper method to invoke. Generally unlinked types in invokes should result in
+         * a deopt instead since they can't really be used if they aren't linked yet.
          */
-        if (!declaredHolder.isAssignableFrom(this) || this.isArray() || this.equals(declaredHolder)) {
+        if (!declaredHolder.isAssignableFrom(this) || this.isArray() || this.equals(declaredHolder) || !isLinked() || isInterface()) {
             return hmethod.uniqueConcreteMethod(declaredHolder);
         }
         /*
          * The holder may be a subtype of the decaredHolder so make sure to resolve the method to
          * the correct method for the subtype.
          */
-        HotSpotResolvedJavaMethod newMethod = (HotSpotResolvedJavaMethod) resolveMethod(hmethod, this);
-        if (newMethod != null && !hmethod.equals(newMethod)) {
-            hmethod = newMethod;
+        HotSpotResolvedJavaMethod resolvedMethod = (HotSpotResolvedJavaMethod) resolveMethodInternal(hmethod, this);
+        if (resolvedMethod == null) {
+            // The type isn't known to implement the method.
+            return null;
         }
 
-        return hmethod.uniqueConcreteMethod(this);
+        return resolvedMethod.uniqueConcreteMethod(this);
     }
 
     /**
@@ -680,7 +694,7 @@
     @Override
     public String getSourceFileName() {
         HotSpotVMConfig config = runtime().getConfig();
-        final int sourceFileNameIndex = unsafe.getChar(getMetaspaceKlass() + config.klassSourceFileNameIndexOffset);
+        final int sourceFileNameIndex = unsafe.getChar(getMetaspaceKlass() + config.instanceKlassSourceFileNameIndexOffset);
         if (sourceFileNameIndex == 0) {
             return null;
         }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotNodeSubstitutions.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotNodeSubstitutions.java	Wed Jul 02 13:40:10 2014 -0700
@@ -42,6 +42,6 @@
         // so we are guaranteed to read a non-null value here. As long as NodeClass
         // is final, the stamp of the PiNode below will automatically be exact.
         Word klass = loadHub(node);
-        return piCastNonNull(klass.readObject(Word.signed(klassNodeClassOffset()), KLASS_NODE_CLASS), NodeClass.class);
+        return piCastNonNull(klass.readObject(Word.signed(instanceKlassNodeClassOffset()), KLASS_NODE_CLASS), NodeClass.class);
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Wed Jul 02 13:40:10 2014 -0700
@@ -586,21 +586,26 @@
     public static final LocationIdentity CLASS_STATE_LOCATION = new NamedLocationIdentity("ClassState");
 
     @Fold
-    public static int klassStateOffset() {
-        return config().klassStateOffset;
+    public static int instanceKlassInitStateOffset() {
+        return config().instanceKlassInitStateOffset;
     }
 
     @Fold
-    public static int klassStateFullyInitialized() {
-        return config().klassStateFullyInitialized;
+    public static int instanceKlassStateFullyInitialized() {
+        return config().instanceKlassStateFullyInitialized;
     }
 
-    public static boolean isKlassFullyInitialized(Word hub) {
-        return readKlassState(hub) == klassStateFullyInitialized();
+    /**
+     *
+     * @param hub the hub of an InstanceKlass
+     * @return true is the InstanceKlass represented by hub is fully initialized
+     */
+    public static boolean isInstanceKlassFullyInitialized(Word hub) {
+        return readInstanceKlassState(hub) == instanceKlassStateFullyInitialized();
     }
 
-    public static byte readKlassState(Word hub) {
-        return hub.readByte(klassStateOffset(), CLASS_STATE_LOCATION);
+    private static byte readInstanceKlassState(Word hub) {
+        return hub.readByte(instanceKlassInitStateOffset(), CLASS_STATE_LOCATION);
     }
 
     @Fold
@@ -621,8 +626,8 @@
     public static final LocationIdentity KLASS_NODE_CLASS = new NamedLocationIdentity("KlassNodeClass");
 
     @Fold
-    public static int klassNodeClassOffset() {
-        return config().klassNodeClassOffset;
+    public static int instanceKlassNodeClassOffset() {
+        return config().instanceKlassNodeClassOffset;
     }
 
     @Fold
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Wed Jul 02 13:40:10 2014 -0700
@@ -154,7 +154,7 @@
     public static Object allocateInstanceDynamic(Class<?> type, @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister, @ConstantParameter String typeContext) {
         Word hub = loadWordFromObject(type, klassOffset());
         if (probability(FAST_PATH_PROBABILITY, !hub.equal(Word.zero()))) {
-            if (probability(FAST_PATH_PROBABILITY, isKlassFullyInitialized(hub))) {
+            if (probability(FAST_PATH_PROBABILITY, isInstanceKlassFullyInitialized(hub))) {
                 int layoutHelper = readLayoutHelper(hub);
                 /*
                  * src/share/vm/oops/klass.hpp: For instances, layout helper is a positive number,
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java	Wed Jul 02 13:40:10 2014 -0700
@@ -99,7 +99,7 @@
         int sizeInBytes = hub.readInt(klassInstanceSizeOffset(), LocationIdentity.FINAL_LOCATION);
         Word thread = registerAsWord(threadRegister);
         if (!forceSlowPath() && inlineContiguousAllocationSupported()) {
-            if (isKlassFullyInitialized(hub)) {
+            if (isInstanceKlassFullyInitialized(hub)) {
                 Word memory = refillAllocate(thread, intArrayHub, sizeInBytes, logging());
                 if (memory.notEqual(0)) {
                     Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Jul 02 13:40:10 2014 -0700
@@ -680,9 +680,25 @@
                 return new StoreFieldNode(receiver, field, value);
             }
 
+            /**
+             * Ensure that concrete classes are at least linked before generating an invoke.
+             * Interfaces may never be linked so simply return true for them.
+             *
+             * @param target
+             * @return true if the declared holder is an interface or is linked
+             */
+            private boolean callTargetIsResolved(JavaMethod target) {
+                if (target instanceof ResolvedJavaMethod) {
+                    ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target;
+                    ResolvedJavaType resolvedType = resolvedTarget.getDeclaringClass();
+                    return resolvedType.isInterface() || resolvedType.isLinked();
+                }
+                return false;
+            }
+
             @Override
             protected void genInvokeStatic(JavaMethod target) {
-                if (target instanceof ResolvedJavaMethod) {
+                if (callTargetIsResolved(target)) {
                     ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target;
                     ResolvedJavaType holder = resolvedTarget.getDeclaringClass();
                     if (!holder.isInitialized() && ResolveClassBeforeStaticInvoke.getValue()) {
@@ -698,7 +714,7 @@
 
             @Override
             protected void genInvokeInterface(JavaMethod target) {
-                if (target instanceof ResolvedJavaMethod) {
+                if (callTargetIsResolved(target)) {
                     ValueNode[] args = frameState.popArguments(target.getSignature().getParameterSlots(true), target.getSignature().getParameterCount(true));
                     appendInvoke(InvokeKind.Interface, (ResolvedJavaMethod) target, args);
                 } else {
@@ -722,7 +738,7 @@
 
             @Override
             protected void genInvokeVirtual(JavaMethod target) {
-                if (target instanceof ResolvedJavaMethod) {
+                if (callTargetIsResolved(target)) {
                     /*
                      * Special handling for runtimes that rewrite an invocation of
                      * MethodHandle.invoke(...) or MethodHandle.invokeExact(...) to a static
@@ -749,7 +765,7 @@
 
             @Override
             protected void genInvokeSpecial(JavaMethod target) {
-                if (target instanceof ResolvedJavaMethod) {
+                if (callTargetIsResolved(target)) {
                     assert target != null;
                     assert target.getSignature() != null;
                     ValueNode[] args = frameState.popArguments(target.getSignature().getParameterSlots(true), target.getSignature().getParameterCount(true));
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/DynamicNewInstanceNode.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/DynamicNewInstanceNode.java	Wed Jul 02 13:40:10 2014 -0700
@@ -41,7 +41,7 @@
     public Node canonical(CanonicalizerTool tool) {
         if (clazz.isConstant()) {
             ResolvedJavaType type = tool.getConstantReflection().asJavaType(clazz.asConstant());
-            if (type != null && type.isInitialized()) {
+            if (type != null && type.isInitialized() && !type.isArray() && !type.isInterface() && !type.isPrimitive()) {
                 return new NewInstanceNode(type, fillContents());
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Wed Jul 02 13:40:10 2014 -0700
@@ -42,20 +42,20 @@
 
     /**
      * Constructs a NewInstanceNode.
-     * 
+     *
      * @param type the class being allocated
      * @param fillContents determines whether the new object's fields should be initialized to
      *            zero/null.
      */
     public NewInstanceNode(ResolvedJavaType type, boolean fillContents) {
         super(StampFactory.exactNonNull(type), fillContents);
-        assert !type.isArray();
+        assert !type.isArray() && !type.isInterface() && !type.isPrimitive();
         this.instanceClass = type;
     }
 
     /**
      * Gets the instance class being allocated by this node.
-     * 
+     *
      * @return the instance class allocated
      */
     public ResolvedJavaType instanceClass() {
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java	Wed Jul 02 13:40:10 2014 -0700
@@ -55,7 +55,7 @@
     @Override
     protected StructuredGraph parse(Method m) {
         ResolvedJavaMethod resolvedMethod = getMetaAccess().lookupJavaMethod(m);
-        return installer.makeGraph(resolvedMethod, null, resolvedMethod, inliningPolicy.get(), FrameStateProcessing.CollapseFrameForSingleSideEffect);
+        return installer.makeGraph(resolvedMethod, null, inliningPolicy.get(), FrameStateProcessing.CollapseFrameForSingleSideEffect);
     }
 
     @Test
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java	Wed Jul 02 13:40:10 2014 -0700
@@ -61,7 +61,7 @@
     @Override
     protected StructuredGraph parse(Method m) {
         ResolvedJavaMethod resolvedMethod = getMetaAccess().lookupJavaMethod(m);
-        return installer.makeGraph(resolvedMethod, null, resolvedMethod, inliningPolicy.get(), FrameStateProcessing.CollapseFrameForSingleSideEffect);
+        return installer.makeGraph(resolvedMethod, null, inliningPolicy.get(), FrameStateProcessing.CollapseFrameForSingleSideEffect);
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ReplacementsParseTest.java	Wed Jul 02 13:40:10 2014 -0700
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.replacements.test;
+
+import org.junit.*;
+
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.runtime.*;
+
+public class ReplacementsParseTest extends GraalCompilerTest {
+
+    static class TestMethods {
+        static double next(double v) {
+            return Math.nextAfter(v, 1.0);
+        }
+
+        static double next2(double v) {
+            return Math.nextAfter(v, 1.0);
+        }
+
+        static double nextAfter(double x, double d) {
+            return Math.nextAfter(x, d);
+        }
+
+    }
+
+    @ClassSubstitution(TestMethods.class)
+    static class TestMethodsSubstitutions {
+
+        @MethodSubstitution(isStatic = true)
+        static double next(double v) {
+            return TestMethods.next(v);
+        }
+
+        @MethodSubstitution(isStatic = true)
+        static double next2(double v) {
+            return next2(v);
+        }
+
+        @MethodSubstitution(isStatic = true)
+        static double nextAfter(double x, double d) {
+            double xx = (x == -0.0 ? 0.0 : x);
+            assert !Double.isNaN(xx);
+            return Math.nextAfter(xx, d);
+        }
+    }
+
+    private static boolean substitutionsInstalled;
+
+    public ReplacementsParseTest() {
+        if (!substitutionsInstalled) {
+            Replacements replacements = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getProviders().getReplacements();
+            replacements.registerSubstitutions(TestMethods.class, TestMethodsSubstitutions.class);
+            substitutionsInstalled = true;
+        }
+    }
+
+    /**
+     * Ensure that calling the original method from the substitution binds correctly.
+     */
+    @Test
+    public void test1() {
+        test("test1Snippet", 1.0);
+    }
+
+    public double test1Snippet(double d) {
+        return TestMethods.next(d);
+    }
+
+    /**
+     * Ensure that calling the substitution method binds to the original method properly.
+     */
+    @Test
+    public void test2() {
+        test("test2Snippet", 1.0);
+    }
+
+    public double test2Snippet(double d) {
+        return TestMethods.next2(d);
+    }
+
+    /**
+     * Ensure that substitution methods with assertions in them don't complain when the exception
+     * constructor is deleted.
+     */
+
+    @Test
+    public void testNextAfter() {
+        double[] inArray = new double[1024];
+        double[] outArray = new double[1024];
+        for (int i = 0; i < inArray.length; i++) {
+            inArray[i] = -0.0;
+        }
+        test("doNextAfter", inArray, outArray);
+    }
+
+    public void doNextAfter(double[] outArray, double[] inArray) {
+        for (int i = 0; i < inArray.length; i++) {
+            double direction = (i & 1) == 0 ? Double.POSITIVE_INFINITY : -Double.NEGATIVE_INFINITY;
+            outArray[i] = TestMethods.nextAfter(inArray[i], direction);
+        }
+    }
+}
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/WordTest.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/WordTest.java	Wed Jul 02 13:40:10 2014 -0700
@@ -51,7 +51,7 @@
     @Override
     protected StructuredGraph parse(Method m) {
         ResolvedJavaMethod resolvedMethod = getMetaAccess().lookupJavaMethod(m);
-        return installer.makeGraph(resolvedMethod, null, resolvedMethod, inliningPolicy.get(), FrameStateProcessing.CollapseFrameForSingleSideEffect);
+        return installer.makeGraph(resolvedMethod, null, inliningPolicy.get(), FrameStateProcessing.CollapseFrameForSingleSideEffect);
     }
 
     @Test
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ArraySubstitutions.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ArraySubstitutions.java	Wed Jul 02 13:40:10 2014 -0700
@@ -36,6 +36,9 @@
     public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException {
         // The error cases must be handled here since DynamicNewArrayNode can only deoptimize the
         // caller in response to exceptions.
+        if (length < 0) {
+            throw new NegativeArraySizeException();
+        }
         if (componentType == void.class) {
             throw new IllegalArgumentException();
         }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Wed Jul 02 13:40:10 2014 -0700
@@ -207,7 +207,7 @@
     public void inline(InvokeNode invoke, SnippetReflectionProvider snippetReflection) {
         ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod();
         ReplacementsImpl repl = new ReplacementsImpl(providers, snippetReflection, new Assumptions(false), providers.getCodeCache().getTarget());
-        StructuredGraph calleeGraph = repl.makeGraph(method, null, method, null, FrameStateProcessing.CollapseFrameForSingleSideEffect);
+        StructuredGraph calleeGraph = repl.makeGraph(method, null, null, FrameStateProcessing.CollapseFrameForSingleSideEffect);
         InliningUtil.inline(invoke, calleeGraph, false, null);
     }
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Wed Jul 02 13:40:10 2014 -0700
@@ -242,7 +242,7 @@
             try (TimerCloseable a = SnippetPreparationTime.start()) {
                 FrameStateProcessing frameStateProcessing = method.getAnnotation(Snippet.class).removeAllFrameStates() ? FrameStateProcessing.Removal
                                 : FrameStateProcessing.CollapseFrameForSingleSideEffect;
-                StructuredGraph newGraph = makeGraph(method, recursiveEntry, recursiveEntry, inliningPolicy(method), frameStateProcessing);
+                StructuredGraph newGraph = makeGraph(method, recursiveEntry, inliningPolicy(method), frameStateProcessing);
                 Debug.metric("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount());
                 if (!UseSnippetGraphCache) {
                     return newGraph;
@@ -278,7 +278,7 @@
         }
         StructuredGraph graph = graphs.get(substitute);
         if (graph == null) {
-            graph = makeGraph(substitute, original, substitute, inliningPolicy(substitute), FrameStateProcessing.None);
+            graph = makeGraph(substitute, original, inliningPolicy(substitute), FrameStateProcessing.None);
             graph.freeze();
             graphs.putIfAbsent(substitute, graph);
             graph = graphs.get(substitute);
@@ -405,15 +405,15 @@
      * @param policy the inlining policy to use during preprocessing
      * @param frameStateProcessing controls how {@link FrameState FrameStates} should be handled.
      */
-    public StructuredGraph makeGraph(ResolvedJavaMethod method, ResolvedJavaMethod original, ResolvedJavaMethod recursiveEntry, SnippetInliningPolicy policy, FrameStateProcessing frameStateProcessing) {
-        return createGraphMaker(method, original, recursiveEntry, frameStateProcessing).makeGraph(policy);
+    public StructuredGraph makeGraph(ResolvedJavaMethod method, ResolvedJavaMethod original, SnippetInliningPolicy policy, FrameStateProcessing frameStateProcessing) {
+        return createGraphMaker(method, original, frameStateProcessing).makeGraph(policy);
     }
 
     /**
      * Can be overridden to return an object that specializes various parts of graph preprocessing.
      */
-    protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original, ResolvedJavaMethod recursiveEntry, FrameStateProcessing frameStateProcessing) {
-        return new GraphMaker(substitute, original, recursiveEntry, frameStateProcessing);
+    protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original, FrameStateProcessing frameStateProcessing) {
+        return new GraphMaker(substitute, original, frameStateProcessing);
     }
 
     /**
@@ -437,24 +437,20 @@
         protected final ResolvedJavaMethod method;
 
         /**
-         * The method which is used when a call to {@link #recursiveEntry} is found.
+         * The original method which {@link #method} is substituting. Calls to {@link #method} or
+         * {@link #substitutedMethod} will be replaced with a forced inline of
+         * {@link #substitutedMethod}.
          */
         protected final ResolvedJavaMethod substitutedMethod;
 
         /**
-         * The method which is used to detect a recursive call.
-         */
-        protected final ResolvedJavaMethod recursiveEntry;
-
-        /**
          * Controls how FrameStates are processed.
          */
         private FrameStateProcessing frameStateProcessing;
 
-        protected GraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod, ResolvedJavaMethod recursiveEntry, FrameStateProcessing frameStateProcessing) {
+        protected GraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod, FrameStateProcessing frameStateProcessing) {
             this.method = substitute;
             this.substitutedMethod = substitutedMethod;
-            this.recursiveEntry = recursiveEntry;
             this.frameStateProcessing = frameStateProcessing;
         }
 
@@ -484,9 +480,9 @@
                 NodeIntrinsificationVerificationPhase.verify(graph);
             }
             int sideEffectCount = 0;
-            assert (sideEffectCount = graph.getNodes().filter(e -> e instanceof StateSplit && ((StateSplit) e).hasSideEffect()).count()) >= 0;
+            assert (sideEffectCount = graph.getNodes().filter(e -> hasSideEffect(e)).count()) >= 0;
             new ConvertDeoptimizeToGuardPhase().apply(graph);
-            assert sideEffectCount == graph.getNodes().filter(e -> e instanceof StateSplit && ((StateSplit) e).hasSideEffect()).count() : "deleted side effecting node";
+            assert sideEffectCount == graph.getNodes().filter(e -> hasSideEffect(e)).count() : "deleted side effecting node";
 
             switch (frameStateProcessing) {
                 case Removal:
@@ -503,6 +499,35 @@
             new DeadCodeEliminationPhase().apply(graph);
         }
 
+        /**
+         * Filter nodes has side effects and shouldn't be deleted from snippets when converting
+         * deoptimizations to guards. Currently this only allows exception constructors to be
+         * eliminated to cover the case when Java assertions are in the inlined code.
+         *
+         * @param node
+         * @return true for nodes that have side effects and are unsafe to delete
+         */
+        private boolean hasSideEffect(Node node) {
+            if (node instanceof StateSplit) {
+                if (((StateSplit) node).hasSideEffect()) {
+                    if (node instanceof Invoke) {
+                        CallTargetNode callTarget = ((Invoke) node).callTarget();
+                        if (callTarget instanceof MethodCallTargetNode) {
+                            ResolvedJavaMethod targetMethod = ((MethodCallTargetNode) callTarget).targetMethod();
+                            if (targetMethod.isConstructor()) {
+                                ResolvedJavaType throwableType = providers.getMetaAccess().lookupJavaType(Throwable.class);
+                                return !throwableType.isAssignableFrom(targetMethod.getDeclaringClass());
+                            }
+                        }
+                    }
+                    // Not an exception constructor call
+                    return true;
+                }
+            }
+            // Not a StateSplit
+            return false;
+        }
+
         private static final int MAX_GRAPH_INLINING_DEPTH = 100; // more than enough
 
         private StructuredGraph parseGraph(final ResolvedJavaMethod methodToParse, final SnippetInliningPolicy policy, int inliningDepth) {
@@ -585,7 +610,11 @@
                         continue;
                     }
                     ResolvedJavaMethod callee = callTarget.targetMethod();
-                    if (callee.equals(recursiveEntry)) {
+                    if (substitutedMethod != null && (callee.equals(method) || callee.equals(substitutedMethod))) {
+                        /*
+                         * Ensure that calls to the original method inside of a substitution ends up
+                         * calling it instead of the Graal substitution.
+                         */
                         if (isInlinable(substitutedMethod)) {
                             final StructuredGraph originalGraph = buildInitialGraph(substitutedMethod);
                             Mark mark = graph.getMark();
--- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java	Wed Jul 02 15:04:25 2014 +0200
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java	Wed Jul 02 13:40:10 2014 -0700
@@ -33,10 +33,11 @@
 public class GraalJUnitCore {
 
     /**
-     * Run the tests contained in the classes named in the <code>args</code>. If all tests run
-     * successfully, exit with a status of 0. Otherwise exit with a status of 1. Write feedback
-     * while tests are running and write stack traces for all failed tests after the tests all
-     * complete.
+     * Run the tests contained in the classes named in the <code>args</code>. A single test method
+     * can be specified by adding #method after the class name. Only a single test can be run in
+     * this way. If all tests run successfully, exit with a status of 0. Otherwise exit with a
+     * status of 1. Write feedback while tests are running and write stack traces for all failed
+     * tests after the tests all complete.
      *
      * @param args names of classes in which to find tests to run
      */
@@ -46,6 +47,7 @@
         system.out().println("GraalJUnitCore");
         system.out().println("JUnit version " + Version.id());
         List<Class<?>> classes = new ArrayList<>();
+        String methodName = null;
         List<Failure> missingClasses = new ArrayList<>();
         boolean verbose = false;
         boolean enableTiming = false;
@@ -70,6 +72,27 @@
                 }
 
             } else {
+                /*
+                 * Entries of the form class#method are handled specially. Only one can be specified
+                 * on the command line as there's no obvious way to build a runner for multiple
+                 * ones.
+                 */
+                if (methodName != null) {
+                    system.out().println("Only a single class and method can be specified: " + each);
+                    System.exit(1);
+                } else if (each.contains("#")) {
+                    String[] pair = each.split("#");
+                    if (pair.length != 2) {
+                        system.out().println("Malformed class and method request: " + each);
+                        System.exit(1);
+                    } else if (classes.size() != 0) {
+                        system.out().println("Only a single class and method can be specified: " + each);
+                        System.exit(1);
+                    } else {
+                        methodName = pair[1];
+                        each = pair[0];
+                    }
+                }
                 try {
                     classes.add(Class.forName(each));
                 } catch (ClassNotFoundException e) {
@@ -99,7 +122,13 @@
             graalListener = new GCAfterTestDecorator(graalListener);
         }
         junitCore.addListener(GraalTextListener.createRunListener(graalListener));
-        Result result = junitCore.run(classes.toArray(new Class[0]));
+        Request request;
+        if (methodName == null) {
+            request = Request.classes(classes.toArray(new Class[0]));
+        } else {
+            request = Request.method(classes.get(0), methodName);
+        }
+        Result result = junitCore.run(request);
         for (Failure each : missingClasses) {
             result.getFailures().add(each);
         }
--- a/mx/mx_graal.py	Wed Jul 02 15:04:25 2014 +0200
+++ b/mx/mx_graal.py	Wed Jul 02 13:40:10 2014 -0700
@@ -974,15 +974,31 @@
         projectscp = mx.classpath([pcp.name for pcp in mx.projects_opt_limit_to_suites() if pcp.javaCompliance <= mx.java().javaCompliance])
     else:
         projs = set()
-        for t in tests:
-            found = False
+        if len(tests) == 1 and '#' in tests[0]:
+            words = tests[0].split('#')
+            if len(words) != 2:
+                mx.abort("Method specification is class#method: " + tests[0])
+            t, method = words
             for c, p in candidates.iteritems():
                 if t in c:
                     found = True
-                    classes.append(c)
+                    # this code assumes a single test will be handled by GraalJUnitCore
+                    classes.append(c + '#' + method)
                     projs.add(p.name)
             if not found:
                 mx.log('warning: no tests matched by substring "' + t)
+        else:
+            for t in tests:
+                if '#' in t:
+                    mx.abort('Method specifications can only be used in a single test: ' + t)
+                found = False
+                for c, p in candidates.iteritems():
+                    if t in c:
+                        found = True
+                        classes.append(c)
+                        projs.add(p.name)
+                if not found:
+                    mx.log('warning: no tests matched by substring "' + t)
         projectscp = mx.classpath(projs)
 
     if whitelist:
@@ -1253,6 +1269,12 @@
                 t.abort(test.name + ' Failed')
             tasks.append(t.stop())
 
+    # ensure -Xbatch still works
+    with VM('graal', 'product'):
+        t = Task('DaCapo_pmd:BatchMode:product')
+        dacapo(['-Xbatch', 'pmd'])
+        tasks.append(t.stop())
+
     if args.jacocout is not None:
         jacocoreport([args.jacocout])