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