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