--- /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';
+ }
+
+ }
+
+}