hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java
changeset 46807 8b2c620d7092
parent 46640 70bdce04c59b
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java	Fri Aug 11 23:12:11 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java	Fri Aug 11 17:36:26 2017 -0700
@@ -29,24 +29,46 @@
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.api.replacements.ClassSubstitution;
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
+import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.bytecode.BytecodeProvider;
+import org.graalvm.compiler.core.common.LIRKind;
+import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
+import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
+import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
+import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.core.common.type.StampFactory;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.GraalGraphError;
 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
+import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.debug.OpaqueNode;
+import org.graalvm.compiler.nodes.extended.ForeignCallNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
+import org.graalvm.compiler.nodes.graphbuilderconf.NodeIntrinsicPluginFactory;
+import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
+import org.graalvm.compiler.phases.common.FloatingReadPhase;
+import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
+import org.graalvm.compiler.phases.common.GuardLoweringPhase;
+import org.graalvm.compiler.phases.common.LoweringPhase;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.graalvm.word.LocationIdentity;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 import jdk.vm.ci.code.InstalledCode;
+import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 
 /**
@@ -158,6 +180,14 @@
         static int copyFirstL2R(byte[] left, byte[] right) {
             return copyFirstL2RBody(left, right);
         }
+
+        static int nonVoidIntrinsicWithCall(@SuppressWarnings("unused") int x, int y) {
+            return y;
+        }
+
+        static int nonVoidIntrinsicWithOptimizedSplit(int x) {
+            return x;
+        }
     }
 
     @ClassSubstitution(TestObject.class)
@@ -240,18 +270,45 @@
         private static String apply(Function<String, String> f, String value) {
             return f.apply(value);
         }
+
+        @MethodSubstitution(isStatic = true)
+        static int nonVoidIntrinsicWithCall(int x, int y) {
+            nonVoidIntrinsicWithCallStub(x);
+            return y;
+        }
+
+        @MethodSubstitution(isStatic = true)
+        static int nonVoidIntrinsicWithOptimizedSplit(int x) {
+            if (x == GraalDirectives.opaque(x)) {
+                nonVoidIntrinsicWithCallStub(x);
+            }
+            return x;
+        }
+
+        public static void nonVoidIntrinsicWithCallStub(int zLen) {
+            nonVoidIntrinsicWithCallStub(STUB_CALL, zLen);
+        }
+
+        static final ForeignCallDescriptor STUB_CALL = new ForeignCallDescriptor("stubCall", void.class, int.class);
+
+        @NodeIntrinsic(ForeignCallNode.class)
+        private static native void nonVoidIntrinsicWithCallStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, int zLen);
+
     }
 
     @Override
     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
         BytecodeProvider replacementBytecodeProvider = getSystemClassLoaderBytecodeProvider();
         Registration r = new Registration(invocationPlugins, TestObject.class, replacementBytecodeProvider);
-        new PluginFactory_ReplacementsParseTest().registerPlugins(invocationPlugins, null);
+        NodeIntrinsicPluginFactory.InjectionProvider injections = new DummyInjectionProvider();
+        new PluginFactory_ReplacementsParseTest().registerPlugins(invocationPlugins, injections);
         r.registerMethodSubstitution(TestObjectSubstitutions.class, "nextAfter", double.class, double.class);
         r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringize", Object.class);
         r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringizeId", Receiver.class);
         r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirst", byte[].class, byte[].class, boolean.class);
         r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirstL2R", byte[].class, byte[].class);
+        r.registerMethodSubstitution(TestObjectSubstitutions.class, "nonVoidIntrinsicWithCall", int.class, int.class);
+        r.registerMethodSubstitution(TestObjectSubstitutions.class, "nonVoidIntrinsicWithOptimizedSplit", int.class);
 
         if (replacementBytecodeProvider.supportsInvokedynamic()) {
             r.registerMethodSubstitution(TestObjectSubstitutions.class, "identity", String.class);
@@ -496,4 +553,108 @@
             inlineInvokeDecision = null;
         }
     }
+
+    public static int nonVoidIntrinsicWithCall(int x, int y) {
+        if (TestObject.nonVoidIntrinsicWithCall(x, y) == x) {
+            GraalDirectives.deoptimize();
+        }
+        return y;
+    }
+
+    /**
+     * This tests the case where an intrinsic ends with a runtime call but returns some kind of
+     * value. This requires that a FrameState is available after the {@link ForeignCallNode} since
+     * the return value must be computed on return from the call.
+     */
+    @Test
+    public void testNonVoidIntrinsicWithCall() {
+        testGraph("nonVoidIntrinsicWithCall");
+    }
+
+    public static int nonVoidIntrinsicWithOptimizedSplit(int x) {
+        if (TestObject.nonVoidIntrinsicWithOptimizedSplit(x) == x) {
+            GraalDirectives.deoptimize();
+        }
+        return x;
+    }
+
+    /**
+     * This is similar to {@link #testNonVoidIntrinsicWithCall()} but has a merge after the call
+     * which would normally capture the {@link FrameState} but in this case we force the merge to be
+     * optimized away.
+     */
+    @Test
+    public void testNonVoidIntrinsicWithOptimizedSplit() {
+        testGraph("nonVoidIntrinsicWithOptimizedSplit");
+    }
+
+    @SuppressWarnings("try")
+    private void testGraph(String name) {
+        StructuredGraph graph = parseEager(name, StructuredGraph.AllowAssumptions.YES);
+        try (DebugContext.Scope s0 = graph.getDebug().scope(name, graph)) {
+            for (OpaqueNode node : graph.getNodes().filter(OpaqueNode.class)) {
+                node.replaceAndDelete(node.getValue());
+            }
+            HighTierContext context = getDefaultHighTierContext();
+            CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+            new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+            new FloatingReadPhase().apply(graph);
+            canonicalizer.apply(graph, context);
+            new DeadCodeEliminationPhase().apply(graph);
+            new GuardLoweringPhase().apply(graph, getDefaultMidTierContext());
+            new FrameStateAssignmentPhase().apply(graph);
+        } catch (Throwable e) {
+            throw graph.getDebug().handle(e);
+        }
+    }
+
+    private class DummyInjectionProvider implements NodeIntrinsicPluginFactory.InjectionProvider {
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> T getInjectedArgument(Class<T> type) {
+            if (type == ForeignCallsProvider.class) {
+                return (T) new ForeignCallsProvider() {
+                    @Override
+                    public LIRKind getValueKind(JavaKind javaKind) {
+                        return null;
+                    }
+
+                    @Override
+                    public boolean isReexecutable(ForeignCallDescriptor descriptor) {
+                        return false;
+                    }
+
+                    @Override
+                    public LocationIdentity[] getKilledLocations(ForeignCallDescriptor descriptor) {
+                        return new LocationIdentity[0];
+                    }
+
+                    @Override
+                    public boolean canDeoptimize(ForeignCallDescriptor descriptor) {
+                        return false;
+                    }
+
+                    @Override
+                    public boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor) {
+                        return false;
+                    }
+
+                    @Override
+                    public ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor) {
+                        return null;
+                    }
+                };
+            }
+            if (type == SnippetReflectionProvider.class) {
+                return (T) getSnippetReflection();
+            }
+            return null;
+        }
+
+        @Override
+        public Stamp getInjectedStamp(Class<?> type, boolean nonNull) {
+            JavaKind kind = JavaKind.fromJavaClass(type);
+            return StampFactory.forKind(kind);
+        }
+    }
 }