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 }