1 /*
   2  * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.core.test.inlining;
  26 
  27 import org.graalvm.compiler.core.common.GraalOptions;
  28 import org.graalvm.compiler.core.test.GraalCompilerTest;
  29 import org.graalvm.compiler.debug.DebugContext;
  30 import org.graalvm.compiler.debug.DebugDumpScope;
  31 import org.graalvm.compiler.debug.TTY;
  32 import org.graalvm.compiler.graph.Node;
  33 import org.graalvm.compiler.nodes.FullInfopointNode;
  34 import org.graalvm.compiler.nodes.Invoke;
  35 import org.graalvm.compiler.nodes.StructuredGraph;
  36 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  37 import org.graalvm.compiler.nodes.StructuredGraph.Builder;
  38 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
  39 import org.graalvm.compiler.options.OptionValues;
  40 import org.graalvm.compiler.phases.OptimisticOptimizations;
  41 import org.graalvm.compiler.phases.PhaseSuite;
  42 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  43 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
  44 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
  45 import org.graalvm.compiler.phases.tiers.HighTierContext;
  46 import org.junit.Assert;
  47 import org.junit.Ignore;
  48 import org.junit.Test;
  49 
  50 import jdk.vm.ci.code.site.InfopointReason;
  51 import jdk.vm.ci.meta.ResolvedJavaMethod;
  52 
  53 import java.util.regex.Pattern;
  54 
  55 public class InliningTest extends GraalCompilerTest {
  56 
  57     @Test
  58     public void testInvokeStaticInlining() {
  59         assertInlined(getGraph("invokeStaticSnippet", false));
  60         assertInlined(getGraph("invokeStaticOnInstanceSnippet", false));
  61     }
  62 
  63     @SuppressWarnings("all")
  64     public static Boolean invokeStaticSnippet(boolean value) {
  65         return Boolean.valueOf(value);
  66     }
  67 
  68     @SuppressWarnings({"all", "static"})
  69     public static Boolean invokeStaticOnInstanceSnippet(Boolean obj, boolean value) {
  70         return obj.valueOf(value);
  71     }
  72 
  73     @Test
  74     public void testStaticBindableInlining() {
  75         assertInlined(getGraph("invokeConstructorSnippet", false));
  76         assertInlined(getGraph("invokeFinalMethodSnippet", false));
  77         assertInlined(getGraph("invokeMethodOnFinalClassSnippet", false));
  78         assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", false));
  79     }
  80 
  81     @Ignore("would need read elimination/EA before inlining")
  82     @Test
  83     public void testDependentStaticBindableInlining() {
  84         assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", false));
  85         assertInlined(getGraph("invokeMethodOnFieldSnippet", false));
  86     }
  87 
  88     @Test
  89     public void testStaticBindableInliningIP() {
  90         assertManyMethodInfopoints(assertInlined(getGraph("invokeConstructorSnippet", true)));
  91         assertManyMethodInfopoints(assertInlined(getGraph("invokeFinalMethodSnippet", true)));
  92         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalClassSnippet", true)));
  93         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", true)));
  94     }
  95 
  96     @Ignore("would need read elimination/EA before inlining")
  97     @Test
  98     public void testDependentStaticBindableInliningIP() {
  99         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", true)));
 100         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFieldSnippet", true)));
 101     }
 102 
 103     @SuppressWarnings("all")
 104     public static Object invokeConstructorSnippet(int value) {
 105         return new SuperClass(value);
 106     }
 107 
 108     @SuppressWarnings("all")
 109     public static int invokeFinalMethodSnippet(SuperClass superClass, SubClassA subClassA, FinalSubClass finalSubClass) {
 110         return superClass.publicFinalMethod() + subClassA.publicFinalMethod() + finalSubClass.publicFinalMethod() + superClass.protectedFinalMethod() + subClassA.protectedFinalMethod() +
 111                         finalSubClass.protectedFinalMethod();
 112     }
 113 
 114     @SuppressWarnings("all")
 115     public static int invokeMethodOnFinalClassSnippet(FinalSubClass finalSubClass) {
 116         return finalSubClass.publicFinalMethod() + finalSubClass.publicNotOverriddenMethod() + finalSubClass.publicOverriddenMethod() + finalSubClass.protectedFinalMethod() +
 117                         finalSubClass.protectedNotOverriddenMethod() + finalSubClass.protectedOverriddenMethod();
 118     }
 119 
 120     @SuppressWarnings("all")
 121     public static int invokeMethodOnStaticFinalFieldSnippet() {
 122         return StaticFinalFields.NumberStaticFinalField.intValue() + StaticFinalFields.SuperClassStaticFinalField.publicOverriddenMethod() +
 123                         StaticFinalFields.FinalSubClassStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SingleImplementorStaticFinalField.publicOverriddenMethod() +
 124                         StaticFinalFields.MultipleImplementorsStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassAStaticFinalField.publicOverriddenMethod() +
 125                         StaticFinalFields.SubClassBStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassCStaticFinalField.publicOverriddenMethod();
 126     }
 127 
 128     @SuppressWarnings("all")
 129     public static int invokeMethodOnFinalFieldSnippet() {
 130         FinalFields fields = new FinalFields();
 131         return fields.numberFinalField.intValue() + fields.superClassFinalField.publicOverriddenMethod() + fields.finalSubClassFinalField.publicOverriddenMethod() +
 132                         fields.singleImplementorFinalField.publicOverriddenMethod() + fields.multipleImplementorsFinalField.publicOverriddenMethod() +
 133                         fields.subClassAFinalField.publicOverriddenMethod() + fields.subClassBFinalField.publicOverriddenMethod() + fields.subClassCFinalField.publicOverriddenMethod();
 134     }
 135 
 136     @SuppressWarnings("all")
 137     public static int invokeMethodOnFieldSnippet() {
 138         Fields fields = new Fields();
 139         return fields.numberField.intValue() + fields.superClassField.publicOverriddenMethod() + fields.finalSubClassField.publicOverriddenMethod() +
 140                         fields.singleImplementorField.publicOverriddenMethod() + fields.multipleImplementorsField.publicOverriddenMethod() + fields.subClassAField.publicOverriddenMethod() +
 141                         fields.subClassBField.publicOverriddenMethod() + fields.subClassCField.publicOverriddenMethod();
 142     }
 143 
 144     public interface Attributes {
 145 
 146         int getLength();
 147     }
 148 
 149     public class NullAttributes implements Attributes {
 150 
 151         @Override
 152         public int getLength() {
 153             return 0;
 154         }
 155 
 156     }
 157 
 158     public class TenAttributes implements Attributes {
 159 
 160         @Override
 161         public int getLength() {
 162             return 10;
 163         }
 164 
 165     }
 166 
 167     public int getAttributesLength(Attributes a) {
 168         return a.getLength();
 169     }
 170 
 171     @Test
 172     public void testGuardedInline() {
 173         NullAttributes nullAttributes = new NullAttributes();
 174         for (int i = 0; i < 10000; i++) {
 175             getAttributesLength(nullAttributes);
 176         }
 177         getAttributesLength(new TenAttributes());
 178 
 179         test("getAttributesLength", nullAttributes);
 180         test("getAttributesLength", (Object) null);
 181     }
 182 
 183     @Test
 184     public void testClassHierarchyAnalysis() {
 185         assertInlined(getGraph("invokeLeafClassMethodSnippet", false));
 186         assertInlined(getGraph("invokeConcreteMethodSnippet", false));
 187         assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", false));
 188         // assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", false));
 189 
 190         assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", false));
 191         assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", false));
 192         assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", false));
 193     }
 194 
 195     @Test
 196     public void testClassHierarchyAnalysisIP() {
 197         assertManyMethodInfopoints(assertInlined(getGraph("invokeLeafClassMethodSnippet", true)));
 198         assertManyMethodInfopoints(assertInlined(getGraph("invokeConcreteMethodSnippet", true)));
 199         assertManyMethodInfopoints(assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", true)));
 200         //@formatter:off
 201         // assertInlineInfopoints(assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", true)));
 202         //@formatter:on
 203 
 204         assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", true)));
 205         assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", true)));
 206         assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", true)));
 207     }
 208 
 209     public static void traceInliningTest() {
 210         callTrivial();
 211     }
 212 
 213     private static void callTrivial() {
 214         callNonTrivial();
 215     }
 216 
 217     private static double callNonTrivial() {
 218         double x = 0.0;
 219         for (int i = 0; i < 10; i++) {
 220             x += i * 1.21;
 221         }
 222         return x;
 223     }
 224 
 225     @Test
 226     @SuppressWarnings("try")
 227     public void testTracing() {
 228         OptionValues options = new OptionValues(getInitialOptions(), GraalOptions.TraceInlining, true);
 229         StructuredGraph graph;
 230         try (TTY.Filter f = new TTY.Filter()) {
 231             graph = getGraph("traceInliningTest", options, false);
 232         }
 233         String inliningTree = graph.getInliningLog().formatAsTree(false);
 234         String expectedRegex = "compilation of org.graalvm.compiler.core.test.inlining.InliningTest.traceInliningTest.*: \\R" +
 235                         "  at .*org.graalvm.compiler.core.test.inlining.InliningTest.traceInliningTest.*: <GraphBuilderPhase> org.graalvm.compiler.core.test.inlining.InliningTest.callTrivial.*: yes, inline method\\R" +
 236                         "    at .*org.graalvm.compiler.core.test.inlining.InliningTest.callTrivial.*: .*\\R" +
 237                         "       .*<GraphBuilderPhase> org.graalvm.compiler.core.test.inlining.InliningTest.callNonTrivial.*: .*(.*\\R)*" +
 238                         "       .*<InliningPhase> org.graalvm.compiler.core.test.inlining.InliningTest.callNonTrivial.*: .*(.*\\R)*";
 239         Pattern expectedPattern = Pattern.compile(expectedRegex, Pattern.MULTILINE);
 240         Assert.assertTrue("Got: " + inliningTree, expectedPattern.matcher(inliningTree).matches());
 241     }
 242 
 243     @SuppressWarnings("all")
 244     public static int invokeLeafClassMethodSnippet(SubClassA subClassA) {
 245         return subClassA.publicFinalMethod() + subClassA.publicNotOverriddenMethod() + subClassA.publicOverriddenMethod();
 246     }
 247 
 248     @SuppressWarnings("all")
 249     public static int invokeConcreteMethodSnippet(SuperClass superClass) {
 250         return superClass.publicNotOverriddenMethod() + superClass.protectedNotOverriddenMethod();
 251     }
 252 
 253     @SuppressWarnings("all")
 254     public static int invokeSingleImplementorInterfaceSnippet(SingleImplementorInterface testInterface) {
 255         return testInterface.publicNotOverriddenMethod() + testInterface.publicOverriddenMethod();
 256     }
 257 
 258     @SuppressWarnings("all")
 259     public static int invokeConcreteInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
 260         return testInterface.publicNotOverriddenMethod();
 261     }
 262 
 263     @SuppressWarnings("all")
 264     public static int invokeOverriddenInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
 265         return testInterface.publicOverriddenMethod();
 266     }
 267 
 268     @SuppressWarnings("all")
 269     public static int invokeOverriddenPublicMethodSnippet(SuperClass superClass) {
 270         return superClass.publicOverriddenMethod();
 271     }
 272 
 273     @SuppressWarnings("all")
 274     public static int invokeOverriddenProtectedMethodSnippet(SuperClass superClass) {
 275         return superClass.protectedOverriddenMethod();
 276     }
 277 
 278     private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) {
 279         return getGraph(snippet, null, eagerInfopointMode);
 280     }
 281 
 282     @SuppressWarnings("try")
 283     private StructuredGraph getGraph(final String snippet, OptionValues options, final boolean eagerInfopointMode) {
 284         DebugContext debug = options == null ? getDebugContext() : getDebugContext(options, null, null);
 285         try (DebugContext.Scope s = debug.scope("InliningTest", new DebugDumpScope(snippet, true))) {
 286             ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
 287             Builder builder = builder(method, AllowAssumptions.YES, debug);
 288             StructuredGraph graph = eagerInfopointMode ? parse(builder, getDebugGraphBuilderSuite()) : parse(builder, getEagerGraphBuilderSuite());
 289             try (DebugContext.Scope s2 = debug.scope("Inlining", graph)) {
 290                 PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode
 291                                 ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true))
 292                                 : getDefaultGraphBuilderSuite();
 293                 HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
 294                 debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
 295                 new CanonicalizerPhase().apply(graph, context);
 296                 new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
 297                 debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
 298                 new CanonicalizerPhase().apply(graph, context);
 299                 new DeadCodeEliminationPhase().apply(graph);
 300                 return graph;
 301             }
 302         } catch (Throwable e) {
 303             throw debug.handle(e);
 304         }
 305     }
 306 
 307     private static StructuredGraph assertInlined(StructuredGraph graph) {
 308         return assertNotInGraph(graph, Invoke.class);
 309     }
 310 
 311     private static StructuredGraph assertNotInlined(StructuredGraph graph) {
 312         return assertInGraph(graph, Invoke.class);
 313     }
 314 
 315     private static StructuredGraph assertNotInGraph(StructuredGraph graph, Class<?> clazz) {
 316         for (Node node : graph.getNodes()) {
 317             if (clazz.isInstance(node)) {
 318                 fail(node.toString());
 319             }
 320         }
 321         return graph;
 322     }
 323 
 324     private static StructuredGraph assertInGraph(StructuredGraph graph, Class<?> clazz) {
 325         for (Node node : graph.getNodes()) {
 326             if (clazz.isInstance(node)) {
 327                 return graph;
 328             }
 329         }
 330         fail("Graph does not contain a node of class " + clazz.getName());
 331         return graph;
 332     }
 333 
 334     private static int[] countMethodInfopoints(StructuredGraph graph) {
 335         int start = 0;
 336         int end = 0;
 337         for (FullInfopointNode ipn : graph.getNodes().filter(FullInfopointNode.class)) {
 338             if (ipn.getReason() == InfopointReason.METHOD_START) {
 339                 ++start;
 340             } else if (ipn.getReason() == InfopointReason.METHOD_END) {
 341                 ++end;
 342             }
 343         }
 344         return new int[]{start, end};
 345     }
 346 
 347     private static StructuredGraph assertManyMethodInfopoints(StructuredGraph graph) {
 348         int[] counts = countMethodInfopoints(graph);
 349         if (counts[0] <= 1 || counts[1] <= 1) {
 350             fail(String.format("Graph contains too few required method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
 351         }
 352         return graph;
 353     }
 354 
 355     private static StructuredGraph assertFewMethodInfopoints(StructuredGraph graph) {
 356         int[] counts = countMethodInfopoints(graph);
 357         if (counts[0] > 1 || counts[1] > 1) {
 358             fail(String.format("Graph contains too many method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
 359         }
 360         return graph;
 361     }
 362 
 363     // some interfaces and classes for testing
 364     private interface MultipleImplementorsInterface {
 365 
 366         int publicNotOverriddenMethod();
 367 
 368         int publicOverriddenMethod();
 369     }
 370 
 371     private interface SingleImplementorInterface {
 372 
 373         int publicNotOverriddenMethod();
 374 
 375         int publicOverriddenMethod();
 376     }
 377 
 378     private static class SuperClass implements MultipleImplementorsInterface {
 379 
 380         protected int value;
 381 
 382         SuperClass(int value) {
 383             this.value = value;
 384         }
 385 
 386         @Override
 387         public int publicNotOverriddenMethod() {
 388             return value;
 389         }
 390 
 391         @Override
 392         public int publicOverriddenMethod() {
 393             return value;
 394         }
 395 
 396         protected int protectedNotOverriddenMethod() {
 397             return value;
 398         }
 399 
 400         protected int protectedOverriddenMethod() {
 401             return value;
 402         }
 403 
 404         public final int publicFinalMethod() {
 405             return value + 255;
 406         }
 407 
 408         protected final int protectedFinalMethod() {
 409             return value + 255;
 410         }
 411     }
 412 
 413     private static class SubClassA extends SuperClass implements SingleImplementorInterface {
 414 
 415         SubClassA(int value) {
 416             super(value);
 417         }
 418 
 419         @Override
 420         public int publicOverriddenMethod() {
 421             return value + 2;
 422         }
 423 
 424         @Override
 425         protected int protectedOverriddenMethod() {
 426             return value * 2;
 427         }
 428     }
 429 
 430     private static class SubClassB extends SuperClass {
 431 
 432         SubClassB(int value) {
 433             super(value);
 434         }
 435 
 436         @Override
 437         public int publicOverriddenMethod() {
 438             return value + 3;
 439         }
 440 
 441         @Override
 442         protected int protectedOverriddenMethod() {
 443             return value * 3;
 444         }
 445     }
 446 
 447     private static class SubClassC extends SuperClass {
 448 
 449         SubClassC(int value) {
 450             super(value);
 451         }
 452 
 453         @Override
 454         public int publicOverriddenMethod() {
 455             return value + 4;
 456         }
 457 
 458         @Override
 459         protected int protectedOverriddenMethod() {
 460             return value * 4;
 461         }
 462     }
 463 
 464     private static final class FinalSubClass extends SuperClass {
 465 
 466         FinalSubClass(int value) {
 467             super(value);
 468         }
 469 
 470         @Override
 471         public int publicOverriddenMethod() {
 472             return value + 5;
 473         }
 474 
 475         @Override
 476         protected int protectedOverriddenMethod() {
 477             return value * 5;
 478         }
 479     }
 480 
 481     private static final class StaticFinalFields {
 482 
 483         private static final Number NumberStaticFinalField = Integer.valueOf(1);
 484         private static final SuperClass SuperClassStaticFinalField = new SubClassA(2);
 485         private static final FinalSubClass FinalSubClassStaticFinalField = new FinalSubClass(3);
 486         private static final SingleImplementorInterface SingleImplementorStaticFinalField = new SubClassA(4);
 487         private static final MultipleImplementorsInterface MultipleImplementorsStaticFinalField = new SubClassC(5);
 488         private static final SubClassA SubClassAStaticFinalField = new SubClassA(6);
 489         private static final SubClassB SubClassBStaticFinalField = new SubClassB(7);
 490         private static final SubClassC SubClassCStaticFinalField = new SubClassC(8);
 491     }
 492 
 493     private static final class FinalFields {
 494 
 495         private final Number numberFinalField = Integer.valueOf(1);
 496         private final SuperClass superClassFinalField = new SubClassA(2);
 497         private final FinalSubClass finalSubClassFinalField = new FinalSubClass(3);
 498         private final SingleImplementorInterface singleImplementorFinalField = new SubClassA(4);
 499         private final MultipleImplementorsInterface multipleImplementorsFinalField = new SubClassC(5);
 500         private final SubClassA subClassAFinalField = new SubClassA(6);
 501         private final SubClassB subClassBFinalField = new SubClassB(7);
 502         private final SubClassC subClassCFinalField = new SubClassC(8);
 503     }
 504 
 505     private static final class Fields {
 506 
 507         private Number numberField = Integer.valueOf(1);
 508         private SuperClass superClassField = new SubClassA(2);
 509         private FinalSubClass finalSubClassField = new FinalSubClass(3);
 510         private SingleImplementorInterface singleImplementorField = new SubClassA(4);
 511         private MultipleImplementorsInterface multipleImplementorsField = new SubClassC(5);
 512         private SubClassA subClassAField = new SubClassA(6);
 513         private SubClassB subClassBField = new SubClassB(7);
 514         private SubClassC subClassCField = new SubClassC(8);
 515     }
 516 }