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 static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine; 28 import static org.graalvm.compiler.test.SubprocessUtil.java; 29 import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments; 30 31 import jdk.vm.ci.meta.ResolvedJavaMethod; 32 import org.graalvm.compiler.core.test.GraalCompilerTest; 33 import org.graalvm.compiler.debug.DebugContext; 34 import org.graalvm.compiler.debug.DebugDumpScope; 35 import org.graalvm.compiler.graph.Node; 36 import org.graalvm.compiler.nodes.DeoptimizeNode; 37 import org.graalvm.compiler.nodes.InvokeNode; 38 import org.graalvm.compiler.nodes.StructuredGraph; 39 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 40 import org.graalvm.compiler.nodes.StructuredGraph.Builder; 41 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 42 import org.graalvm.compiler.nodes.java.TypeSwitchNode; 43 import org.graalvm.compiler.phases.OptimisticOptimizations; 44 import org.graalvm.compiler.phases.PhaseSuite; 45 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 46 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; 47 import org.graalvm.compiler.phases.common.inlining.InliningPhase; 48 import org.graalvm.compiler.phases.tiers.HighTierContext; 49 import org.graalvm.compiler.test.SubprocessUtil; 50 import org.junit.Assert; 51 import org.junit.Assume; 52 import org.junit.Before; 53 import org.junit.Test; 54 55 import java.io.IOException; 56 import java.util.List; 57 58 public class PolymorphicInliningTest extends GraalCompilerTest { 59 60 @Before 61 public void checkJavaAgent() { 62 Assume.assumeFalse("Java Agent found -> skipping", SubprocessUtil.isJavaAgentAttached()); 63 } 64 65 @Test 66 public void testInSubprocess() throws InterruptedException, IOException { 67 String recursionPropName = getClass().getName() + ".recursion"; 68 if (Boolean.getBoolean(recursionPropName)) { 69 testPolymorphicInlining(); 70 testPolymorphicNotInlining(); 71 testMegamorphicInlining(); 72 testMegamorphicNotInlining(); 73 } else { 74 List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine()); 75 NotInlinableSubClass.class.getCanonicalName(); 76 vmArgs.add("-XX:CompileCommand=dontinline,org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest$NotInlinableSubClass.publicOverriddenMethod"); 77 vmArgs.add("-D" + recursionPropName + "=true"); 78 SubprocessUtil.Subprocess proc = java(vmArgs, "com.oracle.mxtool.junit.MxJUnitWrapper", getClass().getName()); 79 if (proc.exitCode != 0) { 80 Assert.fail(String.format("non-zero exit code %d for command:%n%s", proc.exitCode, proc)); 81 } 82 } 83 } 84 85 public int polymorphicCallsite(SuperClass receiver) { 86 return receiver.publicOverriddenMethod(); 87 } 88 89 public void testPolymorphicInlining() { 90 for (int i = 0; i < 10000; i++) { 91 if (i % 2 == 0) { 92 polymorphicCallsite(Receivers.subClassA); 93 } else { 94 polymorphicCallsite(Receivers.subClassB); 95 } 96 } 97 StructuredGraph graph = getGraph("polymorphicCallsite", false); 98 // This callsite should be inlined with a TypeCheckedInliningViolated deoptimization. 99 assertTrue(getNodeCount(graph, InvokeNode.class) == 0); 100 assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1); 101 assertTrue(getNodeCount(graph, DeoptimizeNode.class) >= 1); 102 } 103 104 /** 105 * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding 106 * interference of the receiver type profile from different unit tests. 107 */ 108 public int polymorphicCallsite1(SuperClass receiver) { 109 return receiver.publicOverriddenMethod(); 110 } 111 112 public void testPolymorphicNotInlining() { 113 for (int i = 0; i < 10000; i++) { 114 if (i % 2 == 0) { 115 polymorphicCallsite1(Receivers.subClassA); 116 } else { 117 polymorphicCallsite1(Receivers.notInlinableSubClass); 118 } 119 } 120 StructuredGraph graph = getGraph("polymorphicCallsite1", false); 121 // This callsite should not be inlined due to one of the potential callee method is not 122 // inlinable. 123 assertTrue(getNodeCount(graph, InvokeNode.class) == 1); 124 assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0); 125 } 126 127 /** 128 * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding 129 * interference of the receiver type profile from different unit tests. 130 */ 131 public int polymorphicCallsite2(SuperClass receiver) { 132 return receiver.publicOverriddenMethod(); 133 } 134 135 public void testMegamorphicInlining() { 136 // Construct a receiver type profile that exceeds the max type width (by default 8 in JVMCI, 137 // specified by -XX:TypeProfileWidth). 138 for (int i = 0; i < 2000; i++) { 139 // Ensure the following receiver type is within the type profile. 140 polymorphicCallsite2(Receivers.subClassA); 141 } 142 for (int i = 0; i < 10000; i++) { 143 switch (i % 20) { 144 case 0: 145 case 1: 146 case 2: 147 case 3: 148 case 4: 149 case 5: 150 case 6: 151 case 7: 152 // Probability: 40% 153 // Ensure the probability is greater than 154 // GraalOptions.MegamorphicInliningMinMethodProbability (by default 0.33D); 155 polymorphicCallsite2(Receivers.subClassA); 156 break; 157 case 8: 158 polymorphicCallsite2(Receivers.subClassB); 159 break; 160 case 9: 161 polymorphicCallsite2(Receivers.subClassC); 162 break; 163 case 10: 164 polymorphicCallsite2(Receivers.subClassD); 165 break; 166 case 11: 167 polymorphicCallsite2(Receivers.subClassE); 168 break; 169 case 12: 170 polymorphicCallsite2(Receivers.subClassF); 171 break; 172 case 13: 173 polymorphicCallsite2(Receivers.subClassG); 174 break; 175 case 14: 176 polymorphicCallsite2(Receivers.subClassH); 177 break; 178 default: 179 // Probability: 25% 180 polymorphicCallsite2(Receivers.notInlinableSubClass); 181 break; 182 } 183 } 184 StructuredGraph graph = getGraph("polymorphicCallsite2", false); 185 // This callsite should be inlined with a fallback invocation. 186 assertTrue(getNodeCount(graph, InvokeNode.class) == 1); 187 assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1); 188 } 189 190 /** 191 * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding 192 * interference of the receiver type profile from different unit tests. 193 */ 194 public int polymorphicCallsite3(SuperClass receiver) { 195 return receiver.publicOverriddenMethod(); 196 } 197 198 public void testMegamorphicNotInlining() { 199 for (int i = 0; i < 10000; i++) { 200 switch (i % 10) { 201 case 0: 202 case 1: 203 polymorphicCallsite3(Receivers.subClassA); 204 break; 205 case 2: 206 polymorphicCallsite3(Receivers.subClassB); 207 break; 208 case 3: 209 polymorphicCallsite3(Receivers.subClassC); 210 break; 211 case 4: 212 polymorphicCallsite3(Receivers.subClassD); 213 break; 214 case 5: 215 polymorphicCallsite3(Receivers.subClassE); 216 break; 217 case 6: 218 polymorphicCallsite3(Receivers.subClassF); 219 break; 220 case 7: 221 polymorphicCallsite3(Receivers.subClassG); 222 break; 223 case 8: 224 polymorphicCallsite3(Receivers.subClassH); 225 break; 226 default: 227 polymorphicCallsite3(Receivers.notInlinableSubClass); 228 break; 229 } 230 } 231 StructuredGraph graph = getGraph("polymorphicCallsite3", false); 232 // This callsite should not be inlined due to non of the potential callee method exceeds the 233 // probability specified by GraalOptions.MegamorphicInliningMinMethodProbability. 234 assertTrue(getNodeCount(graph, InvokeNode.class) == 1); 235 assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0); 236 } 237 238 @SuppressWarnings("try") 239 private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) { 240 DebugContext debug = getDebugContext(); 241 try (DebugContext.Scope s = debug.scope("InliningTest", new DebugDumpScope(snippet, true))) { 242 ResolvedJavaMethod method = getResolvedJavaMethod(snippet); 243 Builder builder = builder(method, AllowAssumptions.YES, debug); 244 StructuredGraph graph = eagerInfopointMode ? parse(builder, getDebugGraphBuilderSuite()) : parse(builder, getEagerGraphBuilderSuite()); 245 try (DebugContext.Scope s2 = debug.scope("Inlining", graph)) { 246 PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode 247 ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true)) 248 : getDefaultGraphBuilderSuite(); 249 HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL); 250 debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph"); 251 new CanonicalizerPhase().apply(graph, context); 252 new InliningPhase(new CanonicalizerPhase()).apply(graph, context); 253 debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph"); 254 new CanonicalizerPhase().apply(graph, context); 255 new DeadCodeEliminationPhase().apply(graph); 256 return graph; 257 } 258 } catch (Throwable e) { 259 throw debug.handle(e); 260 } 261 } 262 263 private static int getNodeCount(StructuredGraph graph, Class<? extends Node> nodeClass) { 264 return graph.getNodes().filter(nodeClass).count(); 265 } 266 267 private static final class Receivers { 268 static final SubClassA subClassA = new SubClassA(); 269 static final SubClassB subClassB = new SubClassB(); 270 static final SubClassC subClassC = new SubClassC(); 271 static final SubClassD subClassD = new SubClassD(); 272 static final SubClassE subClassE = new SubClassE(); 273 static final SubClassF subClassF = new SubClassF(); 274 static final SubClassG subClassG = new SubClassG(); 275 static final SubClassH subClassH = new SubClassH(); 276 277 static final NotInlinableSubClass notInlinableSubClass = new NotInlinableSubClass(); 278 } 279 280 private abstract static class SuperClass { 281 282 public abstract int publicOverriddenMethod(); 283 284 } 285 286 private static class SubClassA extends SuperClass { 287 288 @Override 289 public int publicOverriddenMethod() { 290 return 'A'; 291 } 292 293 } 294 295 private static class SubClassB extends SuperClass { 296 297 @Override 298 public int publicOverriddenMethod() { 299 return 'B'; 300 } 301 302 } 303 304 private static class SubClassC extends SuperClass { 305 306 @Override 307 public int publicOverriddenMethod() { 308 return 'C'; 309 } 310 311 } 312 313 private static class SubClassD extends SuperClass { 314 315 @Override 316 public int publicOverriddenMethod() { 317 return 'D'; 318 } 319 320 } 321 322 private static class SubClassE extends SuperClass { 323 324 @Override 325 public int publicOverriddenMethod() { 326 return 'E'; 327 } 328 329 } 330 331 private static class SubClassF extends SuperClass { 332 333 @Override 334 public int publicOverriddenMethod() { 335 return 'F'; 336 } 337 338 } 339 340 private static class SubClassG extends SuperClass { 341 342 @Override 343 public int publicOverriddenMethod() { 344 return 'G'; 345 } 346 347 } 348 349 private static class SubClassH extends SuperClass { 350 351 @Override 352 public int publicOverriddenMethod() { 353 return 'H'; 354 } 355 356 } 357 358 private static final class NotInlinableSubClass extends SuperClass { 359 360 @Override 361 public int publicOverriddenMethod() { 362 return 'X'; 363 } 364 365 } 366 367 }