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 }