src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java
changeset 48190 25cfedf27edc
child 50858 2d3e99a72541
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java	Fri Dec 01 11:17:45 2017 -0800
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2012, 2016, 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 org.graalvm.compiler.core.test.inlining;
+
+import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine;
+import static org.graalvm.compiler.test.SubprocessUtil.java;
+import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments;
+
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugDumpScope;
+import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.DeoptimizeNode;
+import org.graalvm.compiler.nodes.InvokeNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
+import org.graalvm.compiler.nodes.java.TypeSwitchNode;
+import org.graalvm.compiler.phases.OptimisticOptimizations;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
+import org.graalvm.compiler.phases.common.inlining.InliningPhase;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.graalvm.compiler.test.SubprocessUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+public class PolymorphicInliningTest extends GraalCompilerTest {
+
+    @Test
+    public void testInSubprocess() throws InterruptedException, IOException {
+        String recursionPropName = getClass().getName() + ".recursion";
+        if (Boolean.getBoolean(recursionPropName)) {
+            testPolymorphicInlining();
+            testPolymorphicNotInlining();
+            testMegamorphicInlining();
+            testMegamorphicNotInlining();
+        } else {
+            List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
+            NotInlinableSubClass.class.getCanonicalName();
+            vmArgs.add("-XX:CompileCommand=dontinline,org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest$NotInlinableSubClass.publicOverriddenMethod");
+            vmArgs.add("-D" + recursionPropName + "=true");
+            SubprocessUtil.Subprocess proc = java(vmArgs, "com.oracle.mxtool.junit.MxJUnitWrapper", getClass().getName());
+            if (proc.exitCode != 0) {
+                Assert.fail(String.format("non-zero exit code %d for command:%n%s", proc.exitCode, proc));
+            }
+        }
+    }
+
+    public int polymorphicCallsite(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testPolymorphicInlining() {
+        for (int i = 0; i < 10000; i++) {
+            if (i % 2 == 0) {
+                polymorphicCallsite(Receivers.subClassA);
+            } else {
+                polymorphicCallsite(Receivers.subClassB);
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite", false);
+        // This callsite should be inlined with a TypeCheckedInliningViolated deoptimization.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 0);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1);
+        assertTrue(getNodeCount(graph, DeoptimizeNode.class) >= 1);
+    }
+
+    /**
+     * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding
+     * interference of the receiver type profile from different unit tests.
+     */
+    public int polymorphicCallsite1(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testPolymorphicNotInlining() {
+        for (int i = 0; i < 10000; i++) {
+            if (i % 2 == 0) {
+                polymorphicCallsite1(Receivers.subClassA);
+            } else {
+                polymorphicCallsite1(Receivers.notInlinableSubClass);
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite1", false);
+        // This callsite should not be inlined due to one of the potential callee method is not
+        // inlinable.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 1);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0);
+    }
+
+    /**
+     * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding
+     * interference of the receiver type profile from different unit tests.
+     */
+    public int polymorphicCallsite2(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testMegamorphicInlining() {
+        // Construct a receiver type profile that exceeds the max type width (by default 8 in JVMCI,
+        // specified by -XX:TypeProfileWidth).
+        for (int i = 0; i < 2000; i++) {
+            // Ensure the following receiver type is within the type profile.
+            polymorphicCallsite2(Receivers.subClassA);
+        }
+        for (int i = 0; i < 10000; i++) {
+            switch (i % 20) {
+                case 0:
+                case 1:
+                case 2:
+                case 3:
+                case 4:
+                case 5:
+                case 6:
+                case 7:
+                    // Probability: 40%
+                    // Ensure the probability is greater than
+                    // GraalOptions.MegamorphicInliningMinMethodProbability (by default 0.33D);
+                    polymorphicCallsite2(Receivers.subClassA);
+                    break;
+                case 8:
+                    polymorphicCallsite2(Receivers.subClassB);
+                    break;
+                case 9:
+                    polymorphicCallsite2(Receivers.subClassC);
+                    break;
+                case 10:
+                    polymorphicCallsite2(Receivers.subClassD);
+                    break;
+                case 11:
+                    polymorphicCallsite2(Receivers.subClassE);
+                    break;
+                case 12:
+                    polymorphicCallsite2(Receivers.subClassF);
+                    break;
+                case 13:
+                    polymorphicCallsite2(Receivers.subClassG);
+                    break;
+                case 14:
+                    polymorphicCallsite2(Receivers.subClassH);
+                    break;
+                default:
+                    // Probability: 25%
+                    polymorphicCallsite2(Receivers.notInlinableSubClass);
+                    break;
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite2", false);
+        // This callsite should be inlined with a fallback invocation.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 1);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1);
+    }
+
+    /**
+     * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding
+     * interference of the receiver type profile from different unit tests.
+     */
+    public int polymorphicCallsite3(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testMegamorphicNotInlining() {
+        for (int i = 0; i < 10000; i++) {
+            switch (i % 10) {
+                case 0:
+                case 1:
+                    polymorphicCallsite3(Receivers.subClassA);
+                    break;
+                case 2:
+                    polymorphicCallsite3(Receivers.subClassB);
+                    break;
+                case 3:
+                    polymorphicCallsite3(Receivers.subClassC);
+                    break;
+                case 4:
+                    polymorphicCallsite3(Receivers.subClassD);
+                    break;
+                case 5:
+                    polymorphicCallsite3(Receivers.subClassE);
+                    break;
+                case 6:
+                    polymorphicCallsite3(Receivers.subClassF);
+                    break;
+                case 7:
+                    polymorphicCallsite3(Receivers.subClassG);
+                    break;
+                case 8:
+                    polymorphicCallsite3(Receivers.subClassH);
+                    break;
+                default:
+                    polymorphicCallsite3(Receivers.notInlinableSubClass);
+                    break;
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite3", false);
+        // This callsite should not be inlined due to non of the potential callee method exceeds the
+        // probability specified by GraalOptions.MegamorphicInliningMinMethodProbability.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 1);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0);
+    }
+
+    @SuppressWarnings("try")
+    private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("InliningTest", new DebugDumpScope(snippet, true))) {
+            ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
+            Builder builder = builder(method, AllowAssumptions.YES, debug);
+            StructuredGraph graph = eagerInfopointMode ? parse(builder, getDebugGraphBuilderSuite()) : parse(builder, getEagerGraphBuilderSuite());
+            try (DebugContext.Scope s2 = debug.scope("Inlining", graph)) {
+                PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode
+                                ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true))
+                                : getDefaultGraphBuilderSuite();
+                HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
+                new CanonicalizerPhase().apply(graph, context);
+                new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
+                new CanonicalizerPhase().apply(graph, context);
+                new DeadCodeEliminationPhase().apply(graph);
+                return graph;
+            }
+        } catch (Throwable e) {
+            throw debug.handle(e);
+        }
+    }
+
+    private static int getNodeCount(StructuredGraph graph, Class<? extends Node> nodeClass) {
+        return graph.getNodes().filter(nodeClass).count();
+    }
+
+    private static final class Receivers {
+        static final SubClassA subClassA = new SubClassA();
+        static final SubClassB subClassB = new SubClassB();
+        static final SubClassC subClassC = new SubClassC();
+        static final SubClassD subClassD = new SubClassD();
+        static final SubClassE subClassE = new SubClassE();
+        static final SubClassF subClassF = new SubClassF();
+        static final SubClassG subClassG = new SubClassG();
+        static final SubClassH subClassH = new SubClassH();
+
+        static final NotInlinableSubClass notInlinableSubClass = new NotInlinableSubClass();
+    }
+
+    private abstract static class SuperClass {
+
+        public abstract int publicOverriddenMethod();
+
+    }
+
+    private static class SubClassA extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'A';
+        }
+
+    }
+
+    private static class SubClassB extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'B';
+        }
+
+    }
+
+    private static class SubClassC extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'C';
+        }
+
+    }
+
+    private static class SubClassD extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'D';
+        }
+
+    }
+
+    private static class SubClassE extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'E';
+        }
+
+    }
+
+    private static class SubClassF extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'F';
+        }
+
+    }
+
+    private static class SubClassG extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'G';
+        }
+
+    }
+
+    private static class SubClassH extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'H';
+        }
+
+    }
+
+    private static final class NotInlinableSubClass extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'X';
+        }
+
+    }
+
+}