blob: 55ff31d737d8ca031660c10b1b5192d54660abe6 [file] [log] [blame]
/*
* 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';
}
}
}