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