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 package org.graalvm.compiler.core.test.inlining;
  24 
  25 import jdk.vm.ci.code.site.InfopointReason;
  26 import jdk.vm.ci.meta.ResolvedJavaMethod;
  27 
  28 import org.junit.Ignore;
  29 import org.junit.Test;
  30 
  31 import org.graalvm.compiler.core.test.GraalCompilerTest;
  32 import org.graalvm.compiler.debug.Debug;
  33 import org.graalvm.compiler.debug.Debug.Scope;
  34 import org.graalvm.compiler.debug.DebugDumpScope;
  35 import org.graalvm.compiler.graph.Node;
  36 import org.graalvm.compiler.nodes.FullInfopointNode;
  37 import org.graalvm.compiler.nodes.Invoke;
  38 import org.graalvm.compiler.nodes.StructuredGraph;
  39 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  40 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
  41 import org.graalvm.compiler.phases.OptimisticOptimizations;
  42 import org.graalvm.compiler.phases.PhaseSuite;
  43 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  44 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
  45 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
  46 import org.graalvm.compiler.phases.tiers.HighTierContext;
  47 
  48 public class InliningTest extends GraalCompilerTest {
  49 
  50     @Test
  51     public void testInvokeStaticInlining() {
  52         assertInlined(getGraph("invokeStaticSnippet", false));
  53         assertInlined(getGraph("invokeStaticOnInstanceSnippet", false));
  54     }
  55 
  56     @SuppressWarnings("all")
  57     public static Boolean invokeStaticSnippet(boolean value) {
  58         return Boolean.valueOf(value);
  59     }
  60 
  61     @SuppressWarnings({"all", "static"})
  62     public static Boolean invokeStaticOnInstanceSnippet(Boolean obj, boolean value) {
  63         return obj.valueOf(value);
  64     }
  65 
  66     @Test
  67     public void testStaticBindableInlining() {
  68         assertInlined(getGraph("invokeConstructorSnippet", false));
  69         assertInlined(getGraph("invokeFinalMethodSnippet", false));
  70         assertInlined(getGraph("invokeMethodOnFinalClassSnippet", false));
  71         assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", false));
  72     }
  73 
  74     @Ignore("would need read elimination/EA before inlining")
  75     @Test
  76     public void testDependentStaticBindableInlining() {
  77         assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", false));
  78         assertInlined(getGraph("invokeMethodOnFieldSnippet", false));
  79     }
  80 
  81     @Test
  82     public void testStaticBindableInliningIP() {
  83         assertManyMethodInfopoints(assertInlined(getGraph("invokeConstructorSnippet", true)));
  84         assertManyMethodInfopoints(assertInlined(getGraph("invokeFinalMethodSnippet", true)));
  85         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalClassSnippet", true)));
  86         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", true)));
  87     }
  88 
  89     @Ignore("would need read elimination/EA before inlining")
  90     @Test
  91     public void testDependentStaticBindableInliningIP() {
  92         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", true)));
  93         assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFieldSnippet", true)));
  94     }
  95 
  96     @SuppressWarnings("all")
  97     public static Object invokeConstructorSnippet(int value) {
  98         return new SuperClass(value);
  99     }
 100 
 101     @SuppressWarnings("all")
 102     public static int invokeFinalMethodSnippet(SuperClass superClass, SubClassA subClassA, FinalSubClass finalSubClass) {
 103         return superClass.publicFinalMethod() + subClassA.publicFinalMethod() + finalSubClass.publicFinalMethod() + superClass.protectedFinalMethod() + subClassA.protectedFinalMethod() +
 104                         finalSubClass.protectedFinalMethod();
 105     }
 106 
 107     @SuppressWarnings("all")
 108     public static int invokeMethodOnFinalClassSnippet(FinalSubClass finalSubClass) {
 109         return finalSubClass.publicFinalMethod() + finalSubClass.publicNotOverriddenMethod() + finalSubClass.publicOverriddenMethod() + finalSubClass.protectedFinalMethod() +
 110                         finalSubClass.protectedNotOverriddenMethod() + finalSubClass.protectedOverriddenMethod();
 111     }
 112 
 113     @SuppressWarnings("all")
 114     public static int invokeMethodOnStaticFinalFieldSnippet() {
 115         return StaticFinalFields.NumberStaticFinalField.intValue() + StaticFinalFields.SuperClassStaticFinalField.publicOverriddenMethod() +
 116                         StaticFinalFields.FinalSubClassStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SingleImplementorStaticFinalField.publicOverriddenMethod() +
 117                         StaticFinalFields.MultipleImplementorsStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassAStaticFinalField.publicOverriddenMethod() +
 118                         StaticFinalFields.SubClassBStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassCStaticFinalField.publicOverriddenMethod();
 119     }
 120 
 121     @SuppressWarnings("all")
 122     public static int invokeMethodOnFinalFieldSnippet() {
 123         FinalFields fields = new FinalFields();
 124         return fields.numberFinalField.intValue() + fields.superClassFinalField.publicOverriddenMethod() + fields.finalSubClassFinalField.publicOverriddenMethod() +
 125                         fields.singleImplementorFinalField.publicOverriddenMethod() + fields.multipleImplementorsFinalField.publicOverriddenMethod() +
 126                         fields.subClassAFinalField.publicOverriddenMethod() + fields.subClassBFinalField.publicOverriddenMethod() + fields.subClassCFinalField.publicOverriddenMethod();
 127     }
 128 
 129     @SuppressWarnings("all")
 130     public static int invokeMethodOnFieldSnippet() {
 131         Fields fields = new Fields();
 132         return fields.numberField.intValue() + fields.superClassField.publicOverriddenMethod() + fields.finalSubClassField.publicOverriddenMethod() +
 133                         fields.singleImplementorField.publicOverriddenMethod() + fields.multipleImplementorsField.publicOverriddenMethod() + fields.subClassAField.publicOverriddenMethod() +
 134                         fields.subClassBField.publicOverriddenMethod() + fields.subClassCField.publicOverriddenMethod();
 135     }
 136 
 137     public interface Attributes {
 138 
 139         int getLength();
 140     }
 141 
 142     public class NullAttributes implements Attributes {
 143 
 144         @Override
 145         public int getLength() {
 146             return 0;
 147         }
 148 
 149     }
 150 
 151     public class TenAttributes implements Attributes {
 152 
 153         @Override
 154         public int getLength() {
 155             return 10;
 156         }
 157 
 158     }
 159 
 160     public int getAttributesLength(Attributes a) {
 161         return a.getLength();
 162     }
 163 
 164     @Test
 165     public void testGuardedInline() {
 166         NullAttributes nullAttributes = new NullAttributes();
 167         for (int i = 0; i < 10000; i++) {
 168             getAttributesLength(nullAttributes);
 169         }
 170         getAttributesLength(new TenAttributes());
 171 
 172         test("getAttributesLength", nullAttributes);
 173         test("getAttributesLength", (Object) null);
 174     }
 175 
 176     @Test
 177     public void testClassHierarchyAnalysis() {
 178         assertInlined(getGraph("invokeLeafClassMethodSnippet", false));
 179         assertInlined(getGraph("invokeConcreteMethodSnippet", false));
 180         assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", false));
 181         // assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", false));
 182 
 183         assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", false));
 184         assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", false));
 185         assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", false));
 186     }
 187 
 188     @Test
 189     public void testClassHierarchyAnalysisIP() {
 190         assertManyMethodInfopoints(assertInlined(getGraph("invokeLeafClassMethodSnippet", true)));
 191         assertManyMethodInfopoints(assertInlined(getGraph("invokeConcreteMethodSnippet", true)));
 192         assertManyMethodInfopoints(assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", true)));
 193         //@formatter:off
 194         // assertInlineInfopoints(assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", true)));
 195         //@formatter:on
 196 
 197         assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", true)));
 198         assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", true)));
 199         assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", true)));
 200     }
 201 
 202     @SuppressWarnings("all")
 203     public static int invokeLeafClassMethodSnippet(SubClassA subClassA) {
 204         return subClassA.publicFinalMethod() + subClassA.publicNotOverriddenMethod() + subClassA.publicOverriddenMethod();
 205     }
 206 
 207     @SuppressWarnings("all")
 208     public static int invokeConcreteMethodSnippet(SuperClass superClass) {
 209         return superClass.publicNotOverriddenMethod() + superClass.protectedNotOverriddenMethod();
 210     }
 211 
 212     @SuppressWarnings("all")
 213     public static int invokeSingleImplementorInterfaceSnippet(SingleImplementorInterface testInterface) {
 214         return testInterface.publicNotOverriddenMethod() + testInterface.publicOverriddenMethod();
 215     }
 216 
 217     @SuppressWarnings("all")
 218     public static int invokeConcreteInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
 219         return testInterface.publicNotOverriddenMethod();
 220     }
 221 
 222     @SuppressWarnings("all")
 223     public static int invokeOverriddenInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
 224         return testInterface.publicOverriddenMethod();
 225     }
 226 
 227     @SuppressWarnings("all")
 228     public static int invokeOverriddenPublicMethodSnippet(SuperClass superClass) {
 229         return superClass.publicOverriddenMethod();
 230     }
 231 
 232     @SuppressWarnings("all")
 233     public static int invokeOverriddenProtectedMethodSnippet(SuperClass superClass) {
 234         return superClass.protectedOverriddenMethod();
 235     }
 236 
 237     @SuppressWarnings("try")
 238     private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) {
 239         try (Scope s = Debug.scope("InliningTest", new DebugDumpScope(snippet, true))) {
 240             ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
 241             StructuredGraph graph = eagerInfopointMode ? parseDebug(method, AllowAssumptions.YES) : parseEager(method, AllowAssumptions.YES);
 242             try (Scope s2 = Debug.scope("Inlining", graph)) {
 243                 PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode
 244                                 ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true))
 245                                 : getDefaultGraphBuilderSuite();
 246                 HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
 247                 Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
 248                 new CanonicalizerPhase().apply(graph, context);
 249                 new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
 250                 Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "Graph");
 251                 new CanonicalizerPhase().apply(graph, context);
 252                 new DeadCodeEliminationPhase().apply(graph);
 253                 return graph;
 254             }
 255         } catch (Throwable e) {
 256             throw Debug.handle(e);
 257         }
 258     }
 259 
 260     private static StructuredGraph assertInlined(StructuredGraph graph) {
 261         return assertNotInGraph(graph, Invoke.class);
 262     }
 263 
 264     private static StructuredGraph assertNotInlined(StructuredGraph graph) {
 265         return assertInGraph(graph, Invoke.class);
 266     }
 267 
 268     private static StructuredGraph assertNotInGraph(StructuredGraph graph, Class<?> clazz) {
 269         for (Node node : graph.getNodes()) {
 270             if (clazz.isInstance(node)) {
 271                 fail(node.toString());
 272             }
 273         }
 274         return graph;
 275     }
 276 
 277     private static StructuredGraph assertInGraph(StructuredGraph graph, Class<?> clazz) {
 278         for (Node node : graph.getNodes()) {
 279             if (clazz.isInstance(node)) {
 280                 return graph;
 281             }
 282         }
 283         fail("Graph does not contain a node of class " + clazz.getName());
 284         return graph;
 285     }
 286 
 287     private static int[] countMethodInfopoints(StructuredGraph graph) {
 288         int start = 0;
 289         int end = 0;
 290         for (FullInfopointNode ipn : graph.getNodes().filter(FullInfopointNode.class)) {
 291             if (ipn.getReason() == InfopointReason.METHOD_START) {
 292                 ++start;
 293             } else if (ipn.getReason() == InfopointReason.METHOD_END) {
 294                 ++end;
 295             }
 296         }
 297         return new int[]{start, end};
 298     }
 299 
 300     private static StructuredGraph assertManyMethodInfopoints(StructuredGraph graph) {
 301         int[] counts = countMethodInfopoints(graph);
 302         if (counts[0] <= 1 || counts[1] <= 1) {
 303             fail(String.format("Graph contains too few required method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
 304         }
 305         return graph;
 306     }
 307 
 308     private static StructuredGraph assertFewMethodInfopoints(StructuredGraph graph) {
 309         int[] counts = countMethodInfopoints(graph);
 310         if (counts[0] > 1 || counts[1] > 1) {
 311             fail(String.format("Graph contains too many method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
 312         }
 313         return graph;
 314     }
 315 
 316     // some interfaces and classes for testing
 317     private interface MultipleImplementorsInterface {
 318 
 319         int publicNotOverriddenMethod();
 320 
 321         int publicOverriddenMethod();
 322     }
 323 
 324     private interface SingleImplementorInterface {
 325 
 326         int publicNotOverriddenMethod();
 327 
 328         int publicOverriddenMethod();
 329     }
 330 
 331     private static class SuperClass implements MultipleImplementorsInterface {
 332 
 333         protected int value;
 334 
 335         SuperClass(int value) {
 336             this.value = value;
 337         }
 338 
 339         @Override
 340         public int publicNotOverriddenMethod() {
 341             return value;
 342         }
 343 
 344         @Override
 345         public int publicOverriddenMethod() {
 346             return value;
 347         }
 348 
 349         protected int protectedNotOverriddenMethod() {
 350             return value;
 351         }
 352 
 353         protected int protectedOverriddenMethod() {
 354             return value;
 355         }
 356 
 357         public final int publicFinalMethod() {
 358             return value + 255;
 359         }
 360 
 361         protected final int protectedFinalMethod() {
 362             return value + 255;
 363         }
 364     }
 365 
 366     private static class SubClassA extends SuperClass implements SingleImplementorInterface {
 367 
 368         SubClassA(int value) {
 369             super(value);
 370         }
 371 
 372         @Override
 373         public int publicOverriddenMethod() {
 374             return value + 2;
 375         }
 376 
 377         @Override
 378         protected int protectedOverriddenMethod() {
 379             return value * 2;
 380         }
 381     }
 382 
 383     private static class SubClassB extends SuperClass {
 384 
 385         SubClassB(int value) {
 386             super(value);
 387         }
 388 
 389         @Override
 390         public int publicOverriddenMethod() {
 391             return value + 3;
 392         }
 393 
 394         @Override
 395         protected int protectedOverriddenMethod() {
 396             return value * 3;
 397         }
 398     }
 399 
 400     private static class SubClassC extends SuperClass {
 401 
 402         SubClassC(int value) {
 403             super(value);
 404         }
 405 
 406         @Override
 407         public int publicOverriddenMethod() {
 408             return value + 4;
 409         }
 410 
 411         @Override
 412         protected int protectedOverriddenMethod() {
 413             return value * 4;
 414         }
 415     }
 416 
 417     private static final class FinalSubClass extends SuperClass {
 418 
 419         FinalSubClass(int value) {
 420             super(value);
 421         }
 422 
 423         @Override
 424         public int publicOverriddenMethod() {
 425             return value + 5;
 426         }
 427 
 428         @Override
 429         protected int protectedOverriddenMethod() {
 430             return value * 5;
 431         }
 432     }
 433 
 434     private static final class StaticFinalFields {
 435 
 436         private static final Number NumberStaticFinalField = new Integer(1);
 437         private static final SuperClass SuperClassStaticFinalField = new SubClassA(2);
 438         private static final FinalSubClass FinalSubClassStaticFinalField = new FinalSubClass(3);
 439         private static final SingleImplementorInterface SingleImplementorStaticFinalField = new SubClassA(4);
 440         private static final MultipleImplementorsInterface MultipleImplementorsStaticFinalField = new SubClassC(5);
 441         private static final SubClassA SubClassAStaticFinalField = new SubClassA(6);
 442         private static final SubClassB SubClassBStaticFinalField = new SubClassB(7);
 443         private static final SubClassC SubClassCStaticFinalField = new SubClassC(8);
 444     }
 445 
 446     private static final class FinalFields {
 447 
 448         private final Number numberFinalField = new Integer(1);
 449         private final SuperClass superClassFinalField = new SubClassA(2);
 450         private final FinalSubClass finalSubClassFinalField = new FinalSubClass(3);
 451         private final SingleImplementorInterface singleImplementorFinalField = new SubClassA(4);
 452         private final MultipleImplementorsInterface multipleImplementorsFinalField = new SubClassC(5);
 453         private final SubClassA subClassAFinalField = new SubClassA(6);
 454         private final SubClassB subClassBFinalField = new SubClassB(7);
 455         private final SubClassC subClassCFinalField = new SubClassC(8);
 456     }
 457 
 458     private static final class Fields {
 459 
 460         private Number numberField = new Integer(1);
 461         private SuperClass superClassField = new SubClassA(2);
 462         private FinalSubClass finalSubClassField = new FinalSubClass(3);
 463         private SingleImplementorInterface singleImplementorField = new SubClassA(4);
 464         private MultipleImplementorsInterface multipleImplementorsField = new SubClassC(5);
 465         private SubClassA subClassAField = new SubClassA(6);
 466         private SubClassB subClassBField = new SubClassB(7);
 467         private SubClassC subClassCField = new SubClassC(8);
 468     }
 469 }