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 }