blob: 2c558b1d2471fdf7d7ec59501c8d7daf6681289f [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 jdk.vm.ci.code.site.InfopointReason;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import org.junit.Ignore;
import org.junit.Test;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.debug.Debug.Scope;
import org.graalvm.compiler.debug.DebugDumpScope;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.FullInfopointNode;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
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;
public class InliningTest extends GraalCompilerTest {
@Test
public void testInvokeStaticInlining() {
assertInlined(getGraph("invokeStaticSnippet", false));
assertInlined(getGraph("invokeStaticOnInstanceSnippet", false));
}
@SuppressWarnings("all")
public static Boolean invokeStaticSnippet(boolean value) {
return Boolean.valueOf(value);
}
@SuppressWarnings({"all", "static"})
public static Boolean invokeStaticOnInstanceSnippet(Boolean obj, boolean value) {
return obj.valueOf(value);
}
@Test
public void testStaticBindableInlining() {
assertInlined(getGraph("invokeConstructorSnippet", false));
assertInlined(getGraph("invokeFinalMethodSnippet", false));
assertInlined(getGraph("invokeMethodOnFinalClassSnippet", false));
assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", false));
}
@Ignore("would need read elimination/EA before inlining")
@Test
public void testDependentStaticBindableInlining() {
assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", false));
assertInlined(getGraph("invokeMethodOnFieldSnippet", false));
}
@Test
public void testStaticBindableInliningIP() {
assertManyMethodInfopoints(assertInlined(getGraph("invokeConstructorSnippet", true)));
assertManyMethodInfopoints(assertInlined(getGraph("invokeFinalMethodSnippet", true)));
assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalClassSnippet", true)));
assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", true)));
}
@Ignore("would need read elimination/EA before inlining")
@Test
public void testDependentStaticBindableInliningIP() {
assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", true)));
assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFieldSnippet", true)));
}
@SuppressWarnings("all")
public static Object invokeConstructorSnippet(int value) {
return new SuperClass(value);
}
@SuppressWarnings("all")
public static int invokeFinalMethodSnippet(SuperClass superClass, SubClassA subClassA, FinalSubClass finalSubClass) {
return superClass.publicFinalMethod() + subClassA.publicFinalMethod() + finalSubClass.publicFinalMethod() + superClass.protectedFinalMethod() + subClassA.protectedFinalMethod() +
finalSubClass.protectedFinalMethod();
}
@SuppressWarnings("all")
public static int invokeMethodOnFinalClassSnippet(FinalSubClass finalSubClass) {
return finalSubClass.publicFinalMethod() + finalSubClass.publicNotOverriddenMethod() + finalSubClass.publicOverriddenMethod() + finalSubClass.protectedFinalMethod() +
finalSubClass.protectedNotOverriddenMethod() + finalSubClass.protectedOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeMethodOnStaticFinalFieldSnippet() {
return StaticFinalFields.NumberStaticFinalField.intValue() + StaticFinalFields.SuperClassStaticFinalField.publicOverriddenMethod() +
StaticFinalFields.FinalSubClassStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SingleImplementorStaticFinalField.publicOverriddenMethod() +
StaticFinalFields.MultipleImplementorsStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassAStaticFinalField.publicOverriddenMethod() +
StaticFinalFields.SubClassBStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassCStaticFinalField.publicOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeMethodOnFinalFieldSnippet() {
FinalFields fields = new FinalFields();
return fields.numberFinalField.intValue() + fields.superClassFinalField.publicOverriddenMethod() + fields.finalSubClassFinalField.publicOverriddenMethod() +
fields.singleImplementorFinalField.publicOverriddenMethod() + fields.multipleImplementorsFinalField.publicOverriddenMethod() +
fields.subClassAFinalField.publicOverriddenMethod() + fields.subClassBFinalField.publicOverriddenMethod() + fields.subClassCFinalField.publicOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeMethodOnFieldSnippet() {
Fields fields = new Fields();
return fields.numberField.intValue() + fields.superClassField.publicOverriddenMethod() + fields.finalSubClassField.publicOverriddenMethod() +
fields.singleImplementorField.publicOverriddenMethod() + fields.multipleImplementorsField.publicOverriddenMethod() + fields.subClassAField.publicOverriddenMethod() +
fields.subClassBField.publicOverriddenMethod() + fields.subClassCField.publicOverriddenMethod();
}
public interface Attributes {
int getLength();
}
public class NullAttributes implements Attributes {
@Override
public int getLength() {
return 0;
}
}
public class TenAttributes implements Attributes {
@Override
public int getLength() {
return 10;
}
}
public int getAttributesLength(Attributes a) {
return a.getLength();
}
@Test
public void testGuardedInline() {
NullAttributes nullAttributes = new NullAttributes();
for (int i = 0; i < 10000; i++) {
getAttributesLength(nullAttributes);
}
getAttributesLength(new TenAttributes());
test("getAttributesLength", nullAttributes);
test("getAttributesLength", (Object) null);
}
@Test
public void testClassHierarchyAnalysis() {
assertInlined(getGraph("invokeLeafClassMethodSnippet", false));
assertInlined(getGraph("invokeConcreteMethodSnippet", false));
assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", false));
// assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", false));
assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", false));
assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", false));
assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", false));
}
@Test
public void testClassHierarchyAnalysisIP() {
assertManyMethodInfopoints(assertInlined(getGraph("invokeLeafClassMethodSnippet", true)));
assertManyMethodInfopoints(assertInlined(getGraph("invokeConcreteMethodSnippet", true)));
assertManyMethodInfopoints(assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", true)));
//@formatter:off
// assertInlineInfopoints(assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", true)));
//@formatter:on
assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", true)));
assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", true)));
assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", true)));
}
@SuppressWarnings("all")
public static int invokeLeafClassMethodSnippet(SubClassA subClassA) {
return subClassA.publicFinalMethod() + subClassA.publicNotOverriddenMethod() + subClassA.publicOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeConcreteMethodSnippet(SuperClass superClass) {
return superClass.publicNotOverriddenMethod() + superClass.protectedNotOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeSingleImplementorInterfaceSnippet(SingleImplementorInterface testInterface) {
return testInterface.publicNotOverriddenMethod() + testInterface.publicOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeConcreteInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
return testInterface.publicNotOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeOverriddenInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
return testInterface.publicOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeOverriddenPublicMethodSnippet(SuperClass superClass) {
return superClass.publicOverriddenMethod();
}
@SuppressWarnings("all")
public static int invokeOverriddenProtectedMethodSnippet(SuperClass superClass) {
return superClass.protectedOverriddenMethod();
}
@SuppressWarnings("try")
private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) {
try (Scope s = Debug.scope("InliningTest", new DebugDumpScope(snippet, true))) {
ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
StructuredGraph graph = eagerInfopointMode ? parseDebug(method, AllowAssumptions.YES) : parseEager(method, AllowAssumptions.YES);
try (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(Debug.BASIC_LOG_LEVEL, graph, "Graph");
new CanonicalizerPhase().apply(graph, context);
new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
new CanonicalizerPhase().apply(graph, context);
new DeadCodeEliminationPhase().apply(graph);
return graph;
}
} catch (Throwable e) {
throw Debug.handle(e);
}
}
private static StructuredGraph assertInlined(StructuredGraph graph) {
return assertNotInGraph(graph, Invoke.class);
}
private static StructuredGraph assertNotInlined(StructuredGraph graph) {
return assertInGraph(graph, Invoke.class);
}
private static StructuredGraph assertNotInGraph(StructuredGraph graph, Class<?> clazz) {
for (Node node : graph.getNodes()) {
if (clazz.isInstance(node)) {
fail(node.toString());
}
}
return graph;
}
private static StructuredGraph assertInGraph(StructuredGraph graph, Class<?> clazz) {
for (Node node : graph.getNodes()) {
if (clazz.isInstance(node)) {
return graph;
}
}
fail("Graph does not contain a node of class " + clazz.getName());
return graph;
}
private static int[] countMethodInfopoints(StructuredGraph graph) {
int start = 0;
int end = 0;
for (FullInfopointNode ipn : graph.getNodes().filter(FullInfopointNode.class)) {
if (ipn.getReason() == InfopointReason.METHOD_START) {
++start;
} else if (ipn.getReason() == InfopointReason.METHOD_END) {
++end;
}
}
return new int[]{start, end};
}
private static StructuredGraph assertManyMethodInfopoints(StructuredGraph graph) {
int[] counts = countMethodInfopoints(graph);
if (counts[0] <= 1 || counts[1] <= 1) {
fail(String.format("Graph contains too few required method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
}
return graph;
}
private static StructuredGraph assertFewMethodInfopoints(StructuredGraph graph) {
int[] counts = countMethodInfopoints(graph);
if (counts[0] > 1 || counts[1] > 1) {
fail(String.format("Graph contains too many method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
}
return graph;
}
// some interfaces and classes for testing
private interface MultipleImplementorsInterface {
int publicNotOverriddenMethod();
int publicOverriddenMethod();
}
private interface SingleImplementorInterface {
int publicNotOverriddenMethod();
int publicOverriddenMethod();
}
private static class SuperClass implements MultipleImplementorsInterface {
protected int value;
SuperClass(int value) {
this.value = value;
}
@Override
public int publicNotOverriddenMethod() {
return value;
}
@Override
public int publicOverriddenMethod() {
return value;
}
protected int protectedNotOverriddenMethod() {
return value;
}
protected int protectedOverriddenMethod() {
return value;
}
public final int publicFinalMethod() {
return value + 255;
}
protected final int protectedFinalMethod() {
return value + 255;
}
}
private static class SubClassA extends SuperClass implements SingleImplementorInterface {
SubClassA(int value) {
super(value);
}
@Override
public int publicOverriddenMethod() {
return value + 2;
}
@Override
protected int protectedOverriddenMethod() {
return value * 2;
}
}
private static class SubClassB extends SuperClass {
SubClassB(int value) {
super(value);
}
@Override
public int publicOverriddenMethod() {
return value + 3;
}
@Override
protected int protectedOverriddenMethod() {
return value * 3;
}
}
private static class SubClassC extends SuperClass {
SubClassC(int value) {
super(value);
}
@Override
public int publicOverriddenMethod() {
return value + 4;
}
@Override
protected int protectedOverriddenMethod() {
return value * 4;
}
}
private static final class FinalSubClass extends SuperClass {
FinalSubClass(int value) {
super(value);
}
@Override
public int publicOverriddenMethod() {
return value + 5;
}
@Override
protected int protectedOverriddenMethod() {
return value * 5;
}
}
private static final class StaticFinalFields {
private static final Number NumberStaticFinalField = new Integer(1);
private static final SuperClass SuperClassStaticFinalField = new SubClassA(2);
private static final FinalSubClass FinalSubClassStaticFinalField = new FinalSubClass(3);
private static final SingleImplementorInterface SingleImplementorStaticFinalField = new SubClassA(4);
private static final MultipleImplementorsInterface MultipleImplementorsStaticFinalField = new SubClassC(5);
private static final SubClassA SubClassAStaticFinalField = new SubClassA(6);
private static final SubClassB SubClassBStaticFinalField = new SubClassB(7);
private static final SubClassC SubClassCStaticFinalField = new SubClassC(8);
}
private static final class FinalFields {
private final Number numberFinalField = new Integer(1);
private final SuperClass superClassFinalField = new SubClassA(2);
private final FinalSubClass finalSubClassFinalField = new FinalSubClass(3);
private final SingleImplementorInterface singleImplementorFinalField = new SubClassA(4);
private final MultipleImplementorsInterface multipleImplementorsFinalField = new SubClassC(5);
private final SubClassA subClassAFinalField = new SubClassA(6);
private final SubClassB subClassBFinalField = new SubClassB(7);
private final SubClassC subClassCFinalField = new SubClassC(8);
}
private static final class Fields {
private Number numberField = new Integer(1);
private SuperClass superClassField = new SubClassA(2);
private FinalSubClass finalSubClassField = new FinalSubClass(3);
private SingleImplementorInterface singleImplementorField = new SubClassA(4);
private MultipleImplementorsInterface multipleImplementorsField = new SubClassC(5);
private SubClassA subClassAField = new SubClassA(6);
private SubClassB subClassBField = new SubClassB(7);
private SubClassC subClassCField = new SubClassC(8);
}
}