1/* 2 * Copyright (c) 2011, 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 */ 23package org.graalvm.compiler.core.test; 24 25import static jdk.vm.ci.runtime.JVMCICompiler.INVOCATION_ENTRY_BCI; 26import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes; 27import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_NO_EXCEPTION; 28import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION; 29 30import java.lang.annotation.ElementType; 31import java.lang.annotation.Retention; 32import java.lang.annotation.RetentionPolicy; 33import java.lang.annotation.Target; 34import java.lang.reflect.Constructor; 35import java.lang.reflect.Executable; 36import java.lang.reflect.InvocationTargetException; 37import java.lang.reflect.Method; 38import java.util.ArrayList; 39import java.util.Arrays; 40import java.util.Collection; 41import java.util.Collections; 42import java.util.EnumMap; 43import java.util.HashMap; 44import java.util.List; 45import java.util.ListIterator; 46import java.util.Map; 47import java.util.Set; 48import java.util.function.Supplier; 49 50import org.graalvm.compiler.api.directives.GraalDirectives; 51import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 52import org.graalvm.compiler.api.test.Graal; 53import org.graalvm.compiler.code.CompilationResult; 54import org.graalvm.compiler.core.CompilationPrinter; 55import org.graalvm.compiler.core.GraalCompiler; 56import org.graalvm.compiler.core.GraalCompiler.Request; 57import org.graalvm.compiler.core.common.CompilationIdentifier; 58import org.graalvm.compiler.core.common.type.StampFactory; 59import org.graalvm.compiler.core.target.Backend; 60import org.graalvm.compiler.debug.DebugContext; 61import org.graalvm.compiler.debug.DebugDumpHandler; 62import org.graalvm.compiler.debug.DebugDumpScope; 63import org.graalvm.compiler.debug.DebugHandlersFactory; 64import org.graalvm.compiler.debug.GraalError; 65import org.graalvm.compiler.debug.TTY; 66import org.graalvm.compiler.graph.Node; 67import org.graalvm.compiler.graph.NodeClass; 68import org.graalvm.compiler.graph.NodeMap; 69import org.graalvm.compiler.java.BytecodeParser; 70import org.graalvm.compiler.java.ComputeLoopFrequenciesClosure; 71import org.graalvm.compiler.java.GraphBuilderPhase; 72import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; 73import org.graalvm.compiler.lir.phases.LIRSuites; 74import org.graalvm.compiler.nodeinfo.NodeInfo; 75import org.graalvm.compiler.nodeinfo.NodeSize; 76import org.graalvm.compiler.nodeinfo.Verbosity; 77import org.graalvm.compiler.nodes.BreakpointNode; 78import org.graalvm.compiler.nodes.Cancellable; 79import org.graalvm.compiler.nodes.ConstantNode; 80import org.graalvm.compiler.nodes.FixedWithNextNode; 81import org.graalvm.compiler.nodes.FrameState; 82import org.graalvm.compiler.nodes.FullInfopointNode; 83import org.graalvm.compiler.nodes.InvokeNode; 84import org.graalvm.compiler.nodes.InvokeWithExceptionNode; 85import org.graalvm.compiler.nodes.ParameterNode; 86import org.graalvm.compiler.nodes.ProxyNode; 87import org.graalvm.compiler.nodes.ReturnNode; 88import org.graalvm.compiler.nodes.StructuredGraph; 89import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 90import org.graalvm.compiler.nodes.StructuredGraph.Builder; 91import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 92import org.graalvm.compiler.nodes.ValueNode; 93import org.graalvm.compiler.nodes.cfg.Block; 94import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 95import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; 96import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; 97import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; 98import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; 99import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; 100import org.graalvm.compiler.nodes.java.AccessFieldNode; 101import org.graalvm.compiler.nodes.spi.LoweringProvider; 102import org.graalvm.compiler.nodes.spi.Replacements; 103import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 104import org.graalvm.compiler.options.OptionValues; 105import org.graalvm.compiler.phases.BasePhase; 106import org.graalvm.compiler.phases.OptimisticOptimizations; 107import org.graalvm.compiler.phases.Phase; 108import org.graalvm.compiler.phases.PhaseSuite; 109import org.graalvm.compiler.phases.common.CanonicalizerPhase; 110import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase; 111import org.graalvm.compiler.phases.schedule.SchedulePhase; 112import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy; 113import org.graalvm.compiler.phases.tiers.HighTierContext; 114import org.graalvm.compiler.phases.tiers.MidTierContext; 115import org.graalvm.compiler.phases.tiers.Suites; 116import org.graalvm.compiler.phases.tiers.TargetProvider; 117import org.graalvm.compiler.phases.util.Providers; 118import org.graalvm.compiler.printer.GraalDebugHandlersFactory; 119import org.graalvm.compiler.runtime.RuntimeProvider; 120import org.graalvm.compiler.test.AddExports; 121import org.graalvm.compiler.test.GraalTest; 122import org.graalvm.compiler.test.JLModule; 123import org.junit.After; 124import org.junit.Assert; 125import org.junit.Test; 126import org.junit.internal.AssumptionViolatedException; 127 128import jdk.vm.ci.code.Architecture; 129import jdk.vm.ci.code.BailoutException; 130import jdk.vm.ci.code.CodeCacheProvider; 131import jdk.vm.ci.code.InstalledCode; 132import jdk.vm.ci.code.TargetDescription; 133import jdk.vm.ci.meta.Assumptions.Assumption; 134import jdk.vm.ci.meta.ConstantReflectionProvider; 135import jdk.vm.ci.meta.DeoptimizationReason; 136import jdk.vm.ci.meta.JavaKind; 137import jdk.vm.ci.meta.JavaType; 138import jdk.vm.ci.meta.MetaAccessProvider; 139import jdk.vm.ci.meta.ProfilingInfo; 140import jdk.vm.ci.meta.ResolvedJavaMethod; 141import jdk.vm.ci.meta.ResolvedJavaType; 142import jdk.vm.ci.meta.SpeculationLog; 143 144/** 145 * Base class for Graal compiler unit tests. 146 * <p> 147 * White box tests for Graal compiler transformations use this pattern: 148 * <ol> 149 * <li>Create a graph by {@linkplain #parseEager parsing} a method.</li> 150 * <li>Manually modify the graph (e.g. replace a parameter node with a constant).</li> 151 * <li>Apply a transformation to the graph.</li> 152 * <li>Assert that the transformed graph is equal to an expected graph.</li> 153 * </ol> 154 * <p> 155 * See {@link InvokeHintsTest} as an example of a white box test. 156 * <p> 157 * Black box tests use the {@link #test(String, Object...)} or 158 * {@link #testN(int, String, Object...)} to execute some method in the interpreter and compare its 159 * result against that produced by a Graal compiled version of the method. 160 * <p> 161 * These tests will be run by the {@code mx unittest} command. 162 */ 163@AddExports({"java.base/jdk.internal.org.objectweb.asm", "java.base/jdk.internal.org.objectweb.asm.tree"}) 164public abstract class GraalCompilerTest extends GraalTest { 165 166 /** 167 * Gets the initial option values provided by the Graal runtime. These are option values 168 * typically parsed from the command line. 169 */ 170 public static OptionValues getInitialOptions() { 171 return Graal.getRequiredCapability(OptionValues.class); 172 } 173 174 private static final int BAILOUT_RETRY_LIMIT = 1; 175 private final Providers providers; 176 private final Backend backend; 177 178 /** 179 * Representative class for the {@code java.base} module. 180 */ 181 public static final Class<?> JAVA_BASE = Class.class; 182 183 /** 184 * Exports the package named {@code packageName} declared in {@code moduleMember}'s module to 185 * this object's module. This must be called before accessing packages that are no longer public 186 * as of JDK 9. 187 */ 188 protected final void exportPackage(Class<?> moduleMember, String packageName) { 189 if (!Java8OrEarlier) { 190 JLModule.exportPackageTo(moduleMember, packageName, getClass()); 191 } 192 } 193 194 /** 195 * Denotes a test method that must be inlined by the {@link BytecodeParser}. 196 */ 197 @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) 198 @Retention(RetentionPolicy.RUNTIME) 199 public @interface BytecodeParserForceInline { 200 } 201 202 /** 203 * Denotes a test method that must never be inlined by the {@link BytecodeParser}. 204 */ 205 @Retention(RetentionPolicy.RUNTIME) 206 @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) 207 public @interface BytecodeParserNeverInline { 208 /** 209 * Specifies if the call should be implemented with {@link InvokeWithExceptionNode} instead 210 * of {@link InvokeNode}. 211 */ 212 boolean invokeWithException() default false; 213 } 214 215 /** 216 * Can be overridden by unit tests to verify properties of the graph. 217 * 218 * @param graph the graph at the end of HighTier 219 */ 220 protected boolean checkHighTierGraph(StructuredGraph graph) { 221 return true; 222 } 223 224 /** 225 * Can be overridden by unit tests to verify properties of the graph. 226 * 227 * @param graph the graph at the end of MidTier 228 */ 229 protected boolean checkMidTierGraph(StructuredGraph graph) { 230 return true; 231 } 232 233 /** 234 * Can be overridden by unit tests to verify properties of the graph. 235 * 236 * @param graph the graph at the end of LowTier 237 */ 238 protected boolean checkLowTierGraph(StructuredGraph graph) { 239 return true; 240 } 241 242 protected static void breakpoint() { 243 } 244 245 @SuppressWarnings("unused") 246 protected static void breakpoint(int arg0) { 247 } 248 249 protected static void shouldBeOptimizedAway() { 250 } 251 252 protected Suites createSuites(OptionValues opts) { 253 Suites ret = backend.getSuites().getDefaultSuites(opts).copy(); 254 ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(ConvertDeoptimizeToGuardPhase.class, true); 255 if (iter == null) { 256 /* 257 * in the economy configuration, we don't have the ConvertDeoptimizeToGuard phase, so we 258 * just select the first CanonicalizerPhase in HighTier 259 */ 260 iter = ret.getHighTier().findPhase(CanonicalizerPhase.class); 261 } 262 iter.add(new Phase() { 263 264 @Override 265 protected void run(StructuredGraph graph) { 266 ComputeLoopFrequenciesClosure.compute(graph); 267 } 268 269 @Override 270 public float codeSizeIncrease() { 271 return NodeSize.IGNORE_SIZE_CONTRACT_FACTOR; 272 } 273 274 @Override 275 protected CharSequence getName() { 276 return "ComputeLoopFrequenciesPhase"; 277 } 278 }); 279 ret.getHighTier().appendPhase(new Phase() { 280 281 @Override 282 protected void run(StructuredGraph graph) { 283 assert checkHighTierGraph(graph) : "failed HighTier graph check"; 284 } 285 286 @Override 287 public float codeSizeIncrease() { 288 return NodeSize.IGNORE_SIZE_CONTRACT_FACTOR; 289 } 290 291 @Override 292 protected CharSequence getName() { 293 return "CheckGraphPhase"; 294 } 295 }); 296 ret.getMidTier().appendPhase(new Phase() { 297 298 @Override 299 protected void run(StructuredGraph graph) { 300 assert checkMidTierGraph(graph) : "failed MidTier graph check"; 301 } 302 303 @Override 304 public float codeSizeIncrease() { 305 return NodeSize.IGNORE_SIZE_CONTRACT_FACTOR; 306 } 307 308 @Override 309 protected CharSequence getName() { 310 return "CheckGraphPhase"; 311 } 312 }); 313 ret.getLowTier().appendPhase(new Phase() { 314 315 @Override 316 protected void run(StructuredGraph graph) { 317 assert checkLowTierGraph(graph) : "failed LowTier graph check"; 318 } 319 320 @Override 321 public float codeSizeIncrease() { 322 return NodeSize.IGNORE_SIZE_CONTRACT_FACTOR; 323 } 324 325 @Override 326 protected CharSequence getName() { 327 return "CheckGraphPhase"; 328 } 329 }); 330 return ret; 331 } 332 333 protected LIRSuites createLIRSuites(OptionValues opts) { 334 LIRSuites ret = backend.getSuites().getDefaultLIRSuites(opts).copy(); 335 return ret; 336 } 337 338 public GraalCompilerTest() { 339 this.backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend(); 340 this.providers = getBackend().getProviders(); 341 } 342 343 /** 344 * Set up a test for a non-default backend. The test should check (via {@link #getBackend()} ) 345 * whether the desired backend is available. 346 * 347 * @param arch the name of the desired backend architecture 348 */ 349 public GraalCompilerTest(Class<? extends Architecture> arch) { 350 RuntimeProvider runtime = Graal.getRequiredCapability(RuntimeProvider.class); 351 Backend b = runtime.getBackend(arch); 352 if (b != null) { 353 this.backend = b; 354 } else { 355 // Fall back to the default/host backend 356 this.backend = runtime.getHostBackend(); 357 } 358 this.providers = backend.getProviders(); 359 } 360 361 /** 362 * Set up a test for a non-default backend. 363 * 364 * @param backend the desired backend 365 */ 366 public GraalCompilerTest(Backend backend) { 367 this.backend = backend; 368 this.providers = backend.getProviders(); 369 } 370 371 @Override 372 @After 373 public void afterTest() { 374 if (invocationPluginExtensions != null) { 375 synchronized (this) { 376 if (invocationPluginExtensions != null) { 377 extendedInvocationPlugins.removeTestPlugins(invocationPluginExtensions); 378 extendedInvocationPlugins = null; 379 invocationPluginExtensions = null; 380 } 381 } 382 } 383 super.afterTest(); 384 } 385 386 /** 387 * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if 388 * none currently exists. Debug contexts created by this method will have their 389 * {@link DebugDumpHandler}s closed in {@link #afterTest()}. 390 */ 391 protected DebugContext getDebugContext() { 392 return getDebugContext(getInitialOptions()); 393 } 394 395 @Override 396 protected Collection<DebugHandlersFactory> getDebugHandlersFactories() { 397 return Collections.singletonList(new GraalDebugHandlersFactory(getSnippetReflection())); 398 } 399 400 protected void assertEquals(StructuredGraph expected, StructuredGraph graph) { 401 assertEquals(expected, graph, false, true); 402 } 403 404 protected int countUnusedConstants(StructuredGraph graph) { 405 int total = 0; 406 for (ConstantNode node : getConstantNodes(graph)) { 407 if (node.hasNoUsages()) { 408 total++; 409 } 410 } 411 return total; 412 } 413 414 protected int getNodeCountExcludingUnusedConstants(StructuredGraph graph) { 415 return graph.getNodeCount() - countUnusedConstants(graph); 416 } 417 418 protected void assertEquals(StructuredGraph expected, StructuredGraph graph, boolean excludeVirtual, boolean checkConstants) { 419 String expectedString = getCanonicalGraphString(expected, excludeVirtual, checkConstants); 420 String actualString = getCanonicalGraphString(graph, excludeVirtual, checkConstants); 421 String mismatchString = compareGraphStrings(expected, expectedString, graph, actualString); 422 423 if (!excludeVirtual && getNodeCountExcludingUnusedConstants(expected) != getNodeCountExcludingUnusedConstants(graph)) { 424 expected.getDebug().dump(DebugContext.BASIC_LEVEL, expected, "Node count not matching - expected"); 425 graph.getDebug().dump(DebugContext.BASIC_LEVEL, graph, "Node count not matching - actual"); 426 Assert.fail("Graphs do not have the same number of nodes: " + expected.getNodeCount() + " vs. " + graph.getNodeCount() + "\n" + mismatchString); 427 } 428 if (!expectedString.equals(actualString)) { 429 expected.getDebug().dump(DebugContext.BASIC_LEVEL, expected, "mismatching graphs - expected"); 430 graph.getDebug().dump(DebugContext.BASIC_LEVEL, graph, "mismatching graphs - actual"); 431 Assert.fail(mismatchString); 432 } 433 } 434 435 private static String compareGraphStrings(StructuredGraph expectedGraph, String expectedString, StructuredGraph actualGraph, String actualString) { 436 if (!expectedString.equals(actualString)) { 437 String[] expectedLines = expectedString.split("\n"); 438 String[] actualLines = actualString.split("\n"); 439 int diffIndex = -1; 440 int limit = Math.min(actualLines.length, expectedLines.length); 441 String marker = " <<<"; 442 for (int i = 0; i < limit; i++) { 443 if (!expectedLines[i].equals(actualLines[i])) { 444 diffIndex = i; 445 break; 446 } 447 } 448 if (diffIndex == -1) { 449 // Prefix is the same so add some space after the prefix 450 diffIndex = limit; 451 if (actualLines.length == limit) { 452 actualLines = Arrays.copyOf(actualLines, limit + 1); 453 actualLines[diffIndex] = ""; 454 } else { 455 assert expectedLines.length == limit; 456 expectedLines = Arrays.copyOf(expectedLines, limit + 1); 457 expectedLines[diffIndex] = ""; 458 } 459 } 460 // Place a marker next to the first line that differs 461 expectedLines[diffIndex] = expectedLines[diffIndex] + marker; 462 actualLines[diffIndex] = actualLines[diffIndex] + marker; 463 String ediff = String.join("\n", expectedLines); 464 String adiff = String.join("\n", actualLines); 465 return "mismatch in graphs:\n========= expected (" + expectedGraph + ") =========\n" + ediff + "\n\n========= actual (" + actualGraph + ") =========\n" + adiff; 466 } else { 467 return "mismatch in graphs"; 468 } 469 } 470 471 protected void assertOptimizedAway(StructuredGraph g) { 472 Assert.assertEquals(0, g.getNodes().filter(NotOptimizedNode.class).count()); 473 } 474 475 protected void assertConstantReturn(StructuredGraph graph, int value) { 476 String graphString = getCanonicalGraphString(graph, false, true); 477 Assert.assertEquals("unexpected number of ReturnNodes: " + graphString, graph.getNodes(ReturnNode.TYPE).count(), 1); 478 ValueNode result = graph.getNodes(ReturnNode.TYPE).first().result(); 479 Assert.assertTrue("unexpected ReturnNode result node: " + graphString, result.isConstant()); 480 Assert.assertEquals("unexpected ReturnNode result kind: " + graphString, result.asJavaConstant().getJavaKind(), JavaKind.Int); 481 Assert.assertEquals("unexpected ReturnNode result: " + graphString, result.asJavaConstant().asInt(), value); 482 } 483 484 protected static String getCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants) { 485 SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.EARLIEST); 486 schedule.apply(graph); 487 ScheduleResult scheduleResult = graph.getLastSchedule(); 488 489 NodeMap<Integer> canonicalId = graph.createNodeMap(); 490 int nextId = 0; 491 492 List<String> constantsLines = new ArrayList<>(); 493 494 StringBuilder result = new StringBuilder(); 495 for (Block block : scheduleResult.getCFG().getBlocks()) { 496 result.append("Block ").append(block).append(' '); 497 if (block == scheduleResult.getCFG().getStartBlock()) { 498 result.append("* "); 499 } 500 result.append("-> "); 501 for (Block succ : block.getSuccessors()) { 502 result.append(succ).append(' '); 503 } 504 result.append('\n'); 505 for (Node node : scheduleResult.getBlockToNodesMap().get(block)) { 506 if (node instanceof ValueNode && node.isAlive()) { 507 if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof FullInfopointNode || node instanceof ParameterNode)) { 508 if (node instanceof ConstantNode) { 509 String name = checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName(); 510 if (excludeVirtual) { 511 constantsLines.add(name); 512 } else { 513 constantsLines.add(name + " (" + filteredUsageCount(node) + ")"); 514 } 515 } else { 516 int id; 517 if (canonicalId.get(node) != null) { 518 id = canonicalId.get(node); 519 } else { 520 id = nextId++; 521 canonicalId.set(node, id); 522 } 523 String name = node.getClass().getSimpleName(); 524 result.append(" ").append(id).append('|').append(name); 525 if (node instanceof AccessFieldNode) { 526 result.append('#'); 527 result.append(((AccessFieldNode) node).field()); 528 } 529 if (!excludeVirtual) { 530 result.append(" ("); 531 result.append(filteredUsageCount(node)); 532 result.append(')'); 533 } 534 result.append('\n'); 535 } 536 } 537 } 538 } 539 } 540 541 StringBuilder constantsLinesResult = new StringBuilder(); 542 constantsLinesResult.append(constantsLines.size()).append(" constants:\n"); 543 Collections.sort(constantsLines); 544 for (String s : constantsLines) { 545 constantsLinesResult.append(s); 546 constantsLinesResult.append('\n'); 547 } 548 549 return constantsLinesResult.toString() + result.toString(); 550 } 551 552 /** 553 * @return usage count excluding {@link FrameState} usages 554 */ 555 private static int filteredUsageCount(Node node) { 556 return node.usages().filter(n -> !(n instanceof FrameState)).count(); 557 } 558 559 /** 560 * @param graph 561 * @return a scheduled textual dump of {@code graph} . 562 */ 563 protected static String getScheduledGraphString(StructuredGraph graph) { 564 SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.EARLIEST); 565 schedule.apply(graph); 566 ScheduleResult scheduleResult = graph.getLastSchedule(); 567 568 StringBuilder result = new StringBuilder(); 569 Block[] blocks = scheduleResult.getCFG().getBlocks(); 570 for (Block block : blocks) { 571 result.append("Block ").append(block).append(' '); 572 if (block == scheduleResult.getCFG().getStartBlock()) { 573 result.append("* "); 574 } 575 result.append("-> "); 576 for (Block succ : block.getSuccessors()) { 577 result.append(succ).append(' '); 578 } 579 result.append('\n'); 580 for (Node node : scheduleResult.getBlockToNodesMap().get(block)) { 581 result.append(String.format("%1S\n", node)); 582 } 583 } 584 return result.toString(); 585 } 586 587 protected Backend getBackend() { 588 return backend; 589 } 590 591 protected final Providers getProviders() { 592 return providers; 593 } 594 595 protected HighTierContext getDefaultHighTierContext() { 596 return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL); 597 } 598 599 protected MidTierContext getDefaultMidTierContext() { 600 return new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, null); 601 } 602 603 protected SnippetReflectionProvider getSnippetReflection() { 604 return Graal.getRequiredCapability(SnippetReflectionProvider.class); 605 } 606 607 protected TargetDescription getTarget() { 608 return getTargetProvider().getTarget(); 609 } 610 611 protected TargetProvider getTargetProvider() { 612 return getBackend(); 613 } 614 615 protected CodeCacheProvider getCodeCache() { 616 return getProviders().getCodeCache(); 617 } 618 619 protected ConstantReflectionProvider getConstantReflection() { 620 return getProviders().getConstantReflection(); 621 } 622 623 protected MetaAccessProvider getMetaAccess() { 624 return getProviders().getMetaAccess(); 625 } 626 627 protected LoweringProvider getLowerer() { 628 return getProviders().getLowerer(); 629 } 630 631 protected CompilationIdentifier getCompilationId(ResolvedJavaMethod method) { 632 return getBackend().getCompilationIdentifier(method); 633 } 634 635 protected CompilationIdentifier getOrCreateCompilationId(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph) { 636 if (graph != null) { 637 return graph.compilationId(); 638 } 639 return getCompilationId(installedCodeOwner); 640 } 641 642 protected void testN(int n, final String name, final Object... args) { 643 final List<Throwable> errors = new ArrayList<>(n); 644 Thread[] threads = new Thread[n]; 645 for (int i = 0; i < n; i++) { 646 Thread t = new Thread(i + ":" + name) { 647 648 @Override 649 public void run() { 650 try { 651 test(name, args); 652 } catch (Throwable e) { 653 errors.add(e); 654 } 655 } 656 }; 657 threads[i] = t; 658 t.start(); 659 } 660 for (int i = 0; i < n; i++) { 661 try { 662 threads[i].join(); 663 } catch (InterruptedException e) { 664 errors.add(e); 665 } 666 } 667 if (!errors.isEmpty()) { 668 throw new MultiCauseAssertionError(errors.size() + " failures", errors.toArray(new Throwable[errors.size()])); 669 } 670 } 671 672 protected Object referenceInvoke(ResolvedJavaMethod method, Object receiver, Object... args) 673 throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { 674 return invoke(method, receiver, args); 675 } 676 677 public static class Result { 678 679 public final Object returnValue; 680 public final Throwable exception; 681 682 public Result(Object returnValue, Throwable exception) { 683 this.returnValue = returnValue; 684 this.exception = exception; 685 } 686 687 @Override 688 public String toString() { 689 return exception == null ? returnValue == null ? "null" : returnValue.toString() : "!" + exception; 690 } 691 } 692 693 /** 694 * Called before a test is executed. 695 */ 696 protected void before(@SuppressWarnings("unused") ResolvedJavaMethod method) { 697 } 698 699 /** 700 * Called after a test is executed. 701 */ 702 protected void after() { 703 } 704 705 protected Result executeExpected(ResolvedJavaMethod method, Object receiver, Object... args) { 706 before(method); 707 try { 708 // This gives us both the expected return value as well as ensuring that the method to 709 // be compiled is fully resolved 710 return new Result(referenceInvoke(method, receiver, args), null); 711 } catch (InvocationTargetException e) { 712 return new Result(null, e.getTargetException()); 713 } catch (Exception e) { 714 throw new RuntimeException(e); 715 } finally { 716 after(); 717 } 718 } 719 720 protected Result executeActual(ResolvedJavaMethod method, Object receiver, Object... args) { 721 return executeActual(getInitialOptions(), method, receiver, args); 722 } 723 724 protected Result executeActual(OptionValues options, ResolvedJavaMethod method, Object receiver, Object... args) { 725 before(method); 726 Object[] executeArgs = argsWithReceiver(receiver, args); 727 728 checkArgs(method, executeArgs); 729 730 InstalledCode compiledMethod = getCode(method, options); 731 try { 732 return new Result(compiledMethod.executeVarargs(executeArgs), null); 733 } catch (Throwable e) { 734 return new Result(null, e); 735 } finally { 736 after(); 737 } 738 } 739 740 protected void checkArgs(ResolvedJavaMethod method, Object[] args) { 741 JavaType[] sig = method.toParameterTypes(); 742 Assert.assertEquals(sig.length, args.length); 743 for (int i = 0; i < args.length; i++) { 744 JavaType javaType = sig[i]; 745 JavaKind kind = javaType.getJavaKind(); 746 Object arg = args[i]; 747 if (kind == JavaKind.Object) { 748 if (arg != null && javaType instanceof ResolvedJavaType) { 749 ResolvedJavaType resolvedJavaType = (ResolvedJavaType) javaType; 750 Assert.assertTrue(resolvedJavaType + " from " + getMetaAccess().lookupJavaType(arg.getClass()), resolvedJavaType.isAssignableFrom(getMetaAccess().lookupJavaType(arg.getClass()))); 751 } 752 } else { 753 Assert.assertNotNull(arg); 754 Assert.assertEquals(kind.toBoxedJavaClass(), arg.getClass()); 755 } 756 } 757 } 758 759 /** 760 * Prepends a non-null receiver argument to a given list or args. 761 * 762 * @param receiver the receiver argument to prepend if it is non-null 763 */ 764 protected Object[] argsWithReceiver(Object receiver, Object... args) { 765 Object[] executeArgs; 766 if (receiver == null) { 767 executeArgs = args; 768 } else { 769 executeArgs = new Object[args.length + 1]; 770 executeArgs[0] = receiver; 771 for (int i = 0; i < args.length; i++) { 772 executeArgs[i + 1] = args[i]; 773 } 774 } 775 return applyArgSuppliers(executeArgs); 776 } 777 778 protected final Result test(String name, Object... args) { 779 return test(getInitialOptions(), name, args); 780 } 781 782 protected final Result test(OptionValues options, String name, Object... args) { 783 try { 784 ResolvedJavaMethod method = getResolvedJavaMethod(name); 785 Object receiver = method.isStatic() ? null : this; 786 return test(options, method, receiver, args); 787 } catch (AssumptionViolatedException e) { 788 // Suppress so that subsequent calls to this method within the 789 // same Junit @Test annotated method can proceed. 790 return null; 791 } 792 } 793 794 /** 795 * Type denoting a lambda that supplies a fresh value each time it is called. This is useful 796 * when supplying an argument to {@link GraalCompilerTest#test(String, Object...)} where the 797 * test modifies the state of the argument (e.g., updates a field). 798 */ 799 @FunctionalInterface 800 public interface ArgSupplier extends Supplier<Object> { 801 } 802 803 /** 804 * Convenience method for using an {@link ArgSupplier} lambda in a varargs list. 805 */ 806 public static Object supply(ArgSupplier supplier) { 807 return supplier; 808 } 809 810 protected Result test(ResolvedJavaMethod method, Object receiver, Object... args) { 811 return test(getInitialOptions(), method, receiver, args); 812 } 813 814 protected Result test(OptionValues options, ResolvedJavaMethod method, Object receiver, Object... args) { 815 Result expect = executeExpected(method, receiver, args); 816 if (getCodeCache() != null) { 817 testAgainstExpected(options, method, expect, receiver, args); 818 } 819 return expect; 820 } 821 822 /** 823 * Process a given set of arguments, converting any {@link ArgSupplier} argument to the argument 824 * it supplies. 825 */ 826 protected Object[] applyArgSuppliers(Object... args) { 827 Object[] res = args; 828 for (int i = 0; i < args.length; i++) { 829 if (args[i] instanceof ArgSupplier) { 830 if (res == args) { 831 res = args.clone(); 832 } 833 res[i] = ((ArgSupplier) args[i]).get(); 834 } 835 } 836 return res; 837 } 838 839 protected final void testAgainstExpected(ResolvedJavaMethod method, Result expect, Object receiver, Object... args) { 840 testAgainstExpected(getInitialOptions(), method, expect, Collections.<DeoptimizationReason> emptySet(), receiver, args); 841 } 842 843 protected void testAgainstExpected(ResolvedJavaMethod method, Result expect, Set<DeoptimizationReason> shouldNotDeopt, Object receiver, Object... args) { 844 testAgainstExpected(getInitialOptions(), method, expect, shouldNotDeopt, receiver, args); 845 } 846 847 protected final void testAgainstExpected(OptionValues options, ResolvedJavaMethod method, Result expect, Object receiver, Object... args) { 848 testAgainstExpected(options, method, expect, Collections.<DeoptimizationReason> emptySet(), receiver, args); 849 } 850 851 protected void testAgainstExpected(OptionValues options, ResolvedJavaMethod method, Result expect, Set<DeoptimizationReason> shouldNotDeopt, Object receiver, Object... args) { 852 Result actual = executeActualCheckDeopt(options, method, shouldNotDeopt, receiver, args); 853 assertEquals(expect, actual); 854 } 855 856 protected Result executeActualCheckDeopt(OptionValues options, ResolvedJavaMethod method, Set<DeoptimizationReason> shouldNotDeopt, Object receiver, Object... args) { 857 Map<DeoptimizationReason, Integer> deoptCounts = new EnumMap<>(DeoptimizationReason.class); 858 ProfilingInfo profile = method.getProfilingInfo(); 859 for (DeoptimizationReason reason : shouldNotDeopt) { 860 deoptCounts.put(reason, profile.getDeoptimizationCount(reason)); 861 } 862 Result actual = executeActual(options, method, receiver, args); 863 profile = method.getProfilingInfo(); // profile can change after execution 864 for (DeoptimizationReason reason : shouldNotDeopt) { 865 Assert.assertEquals((int) deoptCounts.get(reason), profile.getDeoptimizationCount(reason)); 866 } 867 return actual; 868 } 869 870 protected void assertEquals(Result expect, Result actual) { 871 if (expect.exception != null) { 872 Assert.assertTrue("expected " + expect.exception, actual.exception != null); 873 Assert.assertEquals("Exception class", expect.exception.getClass(), actual.exception.getClass()); 874 Assert.assertEquals("Exception message", expect.exception.getMessage(), actual.exception.getMessage()); 875 } else { 876 if (actual.exception != null) { 877 throw new AssertionError("expected " + expect.returnValue + " but got an exception", actual.exception); 878 } 879 assertDeepEquals(expect.returnValue, actual.returnValue); 880 } 881 } 882 883 private Map<ResolvedJavaMethod, InstalledCode> cache = new HashMap<>(); 884 885 /** 886 * Gets installed code for a given method, compiling it first if necessary. The graph is parsed 887 * {@link #parseEager eagerly}. 888 */ 889 protected final InstalledCode getCode(ResolvedJavaMethod method) { 890 return getCode(method, null, false, false, getInitialOptions()); 891 } 892 893 protected final InstalledCode getCode(ResolvedJavaMethod method, OptionValues options) { 894 return getCode(method, null, false, false, options); 895 } 896 897 /** 898 * Gets installed code for a given method, compiling it first if necessary. 899 * 900 * @param installedCodeOwner the method the compiled code will be associated with when installed 901 * @param graph the graph to be compiled. If null, a graph will be obtained from 902 * {@code installedCodeOwner} via {@link #parseForCompile(ResolvedJavaMethod)}. 903 */ 904 protected final InstalledCode getCode(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph) { 905 return getCode(installedCodeOwner, graph, false, false, graph == null ? getInitialOptions() : graph.getOptions()); 906 } 907 908 /** 909 * Gets installed code for a given method and graph, compiling it first if necessary. 910 * 911 * @param installedCodeOwner the method the compiled code will be associated with when installed 912 * @param graph the graph to be compiled. If null, a graph will be obtained from 913 * {@code installedCodeOwner} via {@link #parseForCompile(ResolvedJavaMethod)}. 914 * @param forceCompile specifies whether to ignore any previous code cached for the (method, 915 * key) pair 916 */ 917 protected final InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile) { 918 return getCode(installedCodeOwner, graph, forceCompile, false, graph == null ? getInitialOptions() : graph.getOptions()); 919 } 920 921 /** 922 * Gets installed code for a given method and graph, compiling it first if necessary. 923 * 924 * @param installedCodeOwner the method the compiled code will be associated with when installed 925 * @param graph the graph to be compiled. If null, a graph will be obtained from 926 * {@code installedCodeOwner} via {@link #parseForCompile(ResolvedJavaMethod)}. 927 * @param forceCompile specifies whether to ignore any previous code cached for the (method, 928 * key) pair 929 * @param installAsDefault specifies whether to install as the default implementation 930 * @param options the options that will be used in {@link #parseForCompile(ResolvedJavaMethod)} 931 */ 932 @SuppressWarnings("try") 933 protected InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) { 934 if (!forceCompile && graph == null) { 935 InstalledCode cached = cache.get(installedCodeOwner); 936 if (cached != null) { 937 if (cached.isValid()) { 938 return cached; 939 } 940 } 941 } 942 // loop for retrying compilation 943 for (int retry = 0; retry <= BAILOUT_RETRY_LIMIT; retry++) { 944 final CompilationIdentifier id = getOrCreateCompilationId(installedCodeOwner, graph); 945 946 InstalledCode installedCode = null; 947 StructuredGraph graphToCompile = graph == null ? parseForCompile(installedCodeOwner, id, options) : graph; 948 DebugContext debug = graphToCompile.getDebug(); 949 950 try (AllocSpy spy = AllocSpy.open(installedCodeOwner); DebugContext.Scope ds = debug.scope("Compiling", new DebugDumpScope(id.toString(CompilationIdentifier.Verbosity.ID), true))) { 951 CompilationPrinter printer = CompilationPrinter.begin(options, id, installedCodeOwner, INVOCATION_ENTRY_BCI); 952 CompilationResult compResult = compile(installedCodeOwner, graphToCompile, new CompilationResult(graphToCompile.compilationId()), id, options); 953 printer.finish(compResult); 954 955 try (DebugContext.Scope s = debug.scope("CodeInstall", getCodeCache(), installedCodeOwner, compResult); 956 DebugContext.Activation a = debug.activate()) { 957 try { 958 if (installAsDefault) { 959 installedCode = addDefaultMethod(debug, installedCodeOwner, compResult); 960 } else { 961 installedCode = addMethod(debug, installedCodeOwner, compResult); 962 } 963 if (installedCode == null) { 964 throw new GraalError("Could not install code for " + installedCodeOwner.format("%H.%n(%p)")); 965 } 966 } catch (BailoutException e) { 967 if (retry <= BAILOUT_RETRY_LIMIT && graph == null && !e.isPermanent()) { 968 // retry (if there is no predefined graph) 969 TTY.println(String.format("Restart compilation %s (%s) due to a non-permanent bailout!", installedCodeOwner, id)); 970 continue; 971 } 972 throw e; 973 } 974 } catch (Throwable e) { 975 throw debug.handle(e); 976 } 977 } catch (Throwable e) { 978 throw debug.handle(e); 979 } 980 981 if (!forceCompile) { 982 cache.put(installedCodeOwner, installedCode); 983 } 984 return installedCode; 985 } 986 throw GraalError.shouldNotReachHere(); 987 } 988 989 /** 990 * Used to produce a graph for a method about to be compiled by 991 * {@link #compile(ResolvedJavaMethod, StructuredGraph)} if the second parameter to that method 992 * is null. 993 * 994 * The default implementation in {@link GraalCompilerTest} is to call {@link #parseEager}. 995 */ 996 protected StructuredGraph parseForCompile(ResolvedJavaMethod method, OptionValues options) { 997 return parseEager(method, AllowAssumptions.YES, getCompilationId(method), options); 998 } 999 1000 protected final StructuredGraph parseForCompile(ResolvedJavaMethod method, DebugContext debug) { 1001 return parseEager(method, AllowAssumptions.YES, debug); 1002 } 1003 1004 protected final StructuredGraph parseForCompile(ResolvedJavaMethod method) { 1005 return parseEager(method, AllowAssumptions.YES, getCompilationId(method), getInitialOptions()); 1006 } 1007 1008 protected StructuredGraph parseForCompile(ResolvedJavaMethod method, CompilationIdentifier compilationId, OptionValues options) { 1009 return parseEager(method, AllowAssumptions.YES, compilationId, options); 1010 } 1011 1012 /** 1013 * Compiles a given method. 1014 * 1015 * @param installedCodeOwner the method the compiled code will be associated with when installed 1016 * @param graph the graph to be compiled for {@code installedCodeOwner}. If null, a graph will 1017 * be obtained from {@code installedCodeOwner} via 1018 * {@link #parseForCompile(ResolvedJavaMethod)}. 1019 */ 1020 protected final CompilationResult compile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph) { 1021 OptionValues options = graph == null ? getInitialOptions() : graph.getOptions(); 1022 CompilationIdentifier compilationId = getOrCreateCompilationId(installedCodeOwner, graph); 1023 return compile(installedCodeOwner, graph, new CompilationResult(compilationId), compilationId, options); 1024 } 1025 1026 protected final CompilationResult compile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, CompilationIdentifier compilationId) { 1027 OptionValues options = graph == null ? getInitialOptions() : graph.getOptions(); 1028 return compile(installedCodeOwner, graph, new CompilationResult(compilationId), compilationId, options); 1029 } 1030 1031 protected final CompilationResult compile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, OptionValues options) { 1032 assert graph == null || graph.getOptions() == options; 1033 CompilationIdentifier compilationId = getOrCreateCompilationId(installedCodeOwner, graph); 1034 return compile(installedCodeOwner, graph, new CompilationResult(compilationId), compilationId, options); 1035 } 1036 1037 /** 1038 * Compiles a given method. 1039 * 1040 * @param installedCodeOwner the method the compiled code will be associated with when installed 1041 * @param graph the graph to be compiled for {@code installedCodeOwner}. If null, a graph will 1042 * be obtained from {@code installedCodeOwner} via 1043 * {@link #parseForCompile(ResolvedJavaMethod)}. 1044 * @param compilationId 1045 */ 1046 @SuppressWarnings("try") 1047 protected CompilationResult compile(ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, CompilationResult compilationResult, CompilationIdentifier compilationId, OptionValues options) { 1048 StructuredGraph graphToCompile = graph == null ? parseForCompile(installedCodeOwner, compilationId, options) : graph; 1049 lastCompiledGraph = graphToCompile; 1050 DebugContext debug = graphToCompile.getDebug(); 1051 try (DebugContext.Scope s = debug.scope("Compile", graphToCompile)) { 1052 assert options != null; 1053 Request<CompilationResult> request = new Request<>(graphToCompile, installedCodeOwner, getProviders(), getBackend(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, 1054 graphToCompile.getProfilingInfo(), createSuites(options), createLIRSuites(options), compilationResult, CompilationResultBuilderFactory.Default); 1055 return GraalCompiler.compile(request); 1056 } catch (Throwable e) { 1057 throw debug.handle(e); 1058 } 1059 } 1060 1061 protected StructuredGraph lastCompiledGraph; 1062 1063 protected SpeculationLog getSpeculationLog() { 1064 return null; 1065 } 1066 1067 protected InstalledCode addMethod(DebugContext debug, final ResolvedJavaMethod method, final CompilationResult compilationResult) { 1068 return backend.addInstalledCode(debug, method, null, compilationResult); 1069 } 1070 1071 protected InstalledCode addDefaultMethod(DebugContext debug, final ResolvedJavaMethod method, final CompilationResult compilationResult) { 1072 return backend.createDefaultInstalledCode(debug, method, compilationResult); 1073 } 1074 1075 private final Map<ResolvedJavaMethod, Executable> methodMap = new HashMap<>(); 1076 1077 /** 1078 * Converts a reflection {@link Method} to a {@link ResolvedJavaMethod}. 1079 */ 1080 protected ResolvedJavaMethod asResolvedJavaMethod(Executable method) { 1081 ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(method); 1082 methodMap.put(javaMethod, method); 1083 return javaMethod; 1084 } 1085 1086 protected ResolvedJavaMethod getResolvedJavaMethod(String methodName) { 1087 return asResolvedJavaMethod(getMethod(methodName)); 1088 } 1089 1090 protected ResolvedJavaMethod getResolvedJavaMethod(Class<?> clazz, String methodName) { 1091 return asResolvedJavaMethod(getMethod(clazz, methodName)); 1092 } 1093 1094 protected ResolvedJavaMethod getResolvedJavaMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { 1095 return asResolvedJavaMethod(getMethod(clazz, methodName, parameterTypes)); 1096 } 1097 1098 /** 1099 * Gets the reflection {@link Method} from which a given {@link ResolvedJavaMethod} was created 1100 * or null if {@code javaMethod} does not correspond to a reflection method. 1101 */ 1102 protected Executable lookupMethod(ResolvedJavaMethod javaMethod) { 1103 return methodMap.get(javaMethod); 1104 } 1105 1106 protected Object invoke(ResolvedJavaMethod javaMethod, Object receiver, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { 1107 Executable method = lookupMethod(javaMethod); 1108 Assert.assertTrue(method != null); 1109 if (!method.isAccessible()) { 1110 method.setAccessible(true); 1111 } 1112 if (method instanceof Method) { 1113 return ((Method) method).invoke(receiver, applyArgSuppliers(args)); 1114 } 1115 assert receiver == null : "no receiver for constructor invokes"; 1116 return ((Constructor<?>) method).newInstance(applyArgSuppliers(args)); 1117 } 1118 1119 /** 1120 * Parses a Java method in {@linkplain GraphBuilderConfiguration#getDefault default} mode to 1121 * produce a graph. 1122 * 1123 * @param methodName the name of the method in {@code this.getClass()} to be parsed 1124 * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph 1125 */ 1126 protected final StructuredGraph parseProfiled(String methodName, AllowAssumptions allowAssumptions) { 1127 ResolvedJavaMethod method = getResolvedJavaMethod(methodName); 1128 return parse(builder(method, allowAssumptions), getDefaultGraphBuilderSuite()); 1129 } 1130 1131 /** 1132 * Parses a Java method in {@linkplain GraphBuilderConfiguration#getDefault default} mode to 1133 * produce a graph. 1134 * 1135 * @param method the method to be parsed 1136 * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph 1137 */ 1138 protected final StructuredGraph parseProfiled(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) { 1139 return parse(builder(method, allowAssumptions), getDefaultGraphBuilderSuite()); 1140 } 1141 1142 /** 1143 * Parses a Java method with {@linkplain GraphBuilderConfiguration#withEagerResolving(boolean)} 1144 * set to true to produce a graph. 1145 * 1146 * @param methodName the name of the method in {@code this.getClass()} to be parsed 1147 * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph 1148 */ 1149 protected final StructuredGraph parseEager(String methodName, AllowAssumptions allowAssumptions) { 1150 ResolvedJavaMethod method = getResolvedJavaMethod(methodName); 1151 return parse(builder(method, allowAssumptions), getEagerGraphBuilderSuite()); 1152 } 1153 1154 /** 1155 * Parses a Java method with {@linkplain GraphBuilderConfiguration#withEagerResolving(boolean)} 1156 * set to true to produce a graph. 1157 * 1158 * @param methodName the name of the method in {@code this.getClass()} to be parsed 1159 * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph 1160 * @param options the option values to be used when compiling the graph 1161 */ 1162 protected final StructuredGraph parseEager(String methodName, AllowAssumptions allowAssumptions, OptionValues options) { 1163 ResolvedJavaMethod method = getResolvedJavaMethod(methodName); 1164 return parse(builder(method, allowAssumptions, options), getEagerGraphBuilderSuite()); 1165 } 1166 1167 protected final StructuredGraph parseEager(String methodName, AllowAssumptions allowAssumptions, DebugContext debug) { 1168 ResolvedJavaMethod method = getResolvedJavaMethod(methodName); 1169 return parse(builder(method, allowAssumptions, debug), getEagerGraphBuilderSuite()); 1170 } 1171 1172 /** 1173 * Parses a Java method with {@linkplain GraphBuilderConfiguration#withEagerResolving(boolean)} 1174 * set to true to produce a graph. 1175 * 1176 * @param method the method to be parsed 1177 * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph 1178 */ 1179 protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) { 1180 return parse(builder(method, allowAssumptions), getEagerGraphBuilderSuite()); 1181 } 1182 1183 protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, DebugContext debug) { 1184 return parse(builder(method, allowAssumptions, debug), getEagerGraphBuilderSuite()); 1185 } 1186 1187 /** 1188 * Parses a Java method with {@linkplain GraphBuilderConfiguration#withEagerResolving(boolean)} 1189 * set to true to produce a graph. 1190 * 1191 * @param method the method to be parsed 1192 * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph 1193 * @param options the option values to be used when compiling the graph 1194 */ 1195 protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, OptionValues options) { 1196 return parse(builder(method, allowAssumptions, options), getEagerGraphBuilderSuite()); 1197 } 1198 1199 /** 1200 * Parses a Java method with {@linkplain GraphBuilderConfiguration#withEagerResolving(boolean)} 1201 * set to true to produce a graph. 1202 * 1203 * @param method the method to be parsed 1204 * @param allowAssumptions specifies if {@link Assumption}s can be made compiling the graph 1205 * @param compilationId the compilation identifier to be associated with the graph 1206 * @param options the option values to be used when compiling the graph 1207 */ 1208 protected final StructuredGraph parseEager(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) { 1209 return parse(builder(method, allowAssumptions, compilationId, options), getEagerGraphBuilderSuite()); 1210 } 1211 1212 protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, DebugContext debug) { 1213 OptionValues options = debug.getOptions(); 1214 return new Builder(options, debug, allowAssumptions).method(method).compilationId(getCompilationId(method)); 1215 } 1216 1217 protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) { 1218 OptionValues options = getInitialOptions(); 1219 return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(getCompilationId(method)); 1220 } 1221 1222 protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) { 1223 return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(compilationId); 1224 } 1225 1226 protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, OptionValues options) { 1227 return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(getCompilationId(method)); 1228 } 1229 1230 protected PhaseSuite<HighTierContext> getDebugGraphBuilderSuite() { 1231 return getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true)); 1232 } 1233 1234 @SuppressWarnings("try") 1235 protected StructuredGraph parse(StructuredGraph.Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) { 1236 ResolvedJavaMethod javaMethod = builder.getMethod(); 1237 if (builder.getCancellable() == null) { 1238 builder.cancellable(getCancellable(javaMethod)); 1239 } 1240 assert javaMethod.getAnnotation(Test.class) == null : "shouldn't parse method with @Test annotation: " + javaMethod; 1241 StructuredGraph graph = builder.build(); 1242 DebugContext debug = graph.getDebug(); 1243 try (DebugContext.Scope ds = debug.scope("Parsing", javaMethod, graph)) { 1244 graphBuilderSuite.apply(graph, getDefaultHighTierContext()); 1245 return graph; 1246 } catch (Throwable e) { 1247 throw debug.handle(e); 1248 } 1249 } 1250 1251 protected PhaseSuite<HighTierContext> getEagerGraphBuilderSuite() { 1252 return getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withEagerResolving(true)); 1253 } 1254 1255 /** 1256 * Gets the cancellable that should be associated with a graph being created by any of the 1257 * {@code parse...()} methods. 1258 * 1259 * @param method the method being parsed into a graph 1260 */ 1261 protected Cancellable getCancellable(ResolvedJavaMethod method) { 1262 return null; 1263 } 1264 1265 protected Plugins getDefaultGraphBuilderPlugins() { 1266 PhaseSuite<HighTierContext> suite = backend.getSuites().getDefaultGraphBuilderSuite(); 1267 Plugins defaultPlugins = ((GraphBuilderPhase) suite.findPhase(GraphBuilderPhase.class).previous()).getGraphBuilderConfig().getPlugins(); 1268 // defensive copying 1269 return new Plugins(defaultPlugins); 1270 } 1271 1272 protected PhaseSuite<HighTierContext> getDefaultGraphBuilderSuite() { 1273 // defensive copying 1274 return backend.getSuites().getDefaultGraphBuilderSuite().copy(); 1275 } 1276 1277 /** 1278 * Registers extra invocation plugins for this test. The extra plugins are removed in the 1279 * {@link #afterTest()} method. 1280 * 1281 * Subclasses overriding this method should always call the same method on the super class in 1282 * case it wants to register plugins. 1283 * 1284 * @param invocationPlugins 1285 */ 1286 protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) { 1287 invocationPlugins.register(new InvocationPlugin() { 1288 @Override 1289 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 1290 b.add(new BreakpointNode()); 1291 return true; 1292 } 1293 }, GraalCompilerTest.class, "breakpoint"); 1294 invocationPlugins.register(new InvocationPlugin() { 1295 @Override 1296 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg0) { 1297 b.add(new BreakpointNode(arg0)); 1298 return true; 1299 } 1300 }, GraalCompilerTest.class, "breakpoint", int.class); 1301 invocationPlugins.register(new InvocationPlugin() { 1302 @Override 1303 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 1304 b.add(new NotOptimizedNode()); 1305 return true; 1306 } 1307 }, GraalCompilerTest.class, "shouldBeOptimizedAway"); 1308 } 1309 1310 /** 1311 * The {@link #testN(int, String, Object...)} method means multiple threads trying to initialize 1312 * this field. 1313 */ 1314 private volatile InvocationPlugins invocationPluginExtensions; 1315 1316 private InvocationPlugins extendedInvocationPlugins; 1317 1318 protected PhaseSuite<HighTierContext> getCustomGraphBuilderSuite(GraphBuilderConfiguration gbConf) { 1319 PhaseSuite<HighTierContext> suite = getDefaultGraphBuilderSuite(); 1320 ListIterator<BasePhase<? super HighTierContext>> iterator = suite.findPhase(GraphBuilderPhase.class); 1321 initializeInvocationPluginExtensions(); 1322 GraphBuilderConfiguration gbConfCopy = editGraphBuilderConfiguration(gbConf.copy()); 1323 iterator.remove(); 1324 iterator.add(new GraphBuilderPhase(gbConfCopy)); 1325 return suite; 1326 } 1327 1328 private void initializeInvocationPluginExtensions() { 1329 if (invocationPluginExtensions == null) { 1330 synchronized (this) { 1331 if (invocationPluginExtensions == null) { 1332 InvocationPlugins invocationPlugins = new InvocationPlugins(); 1333 registerInvocationPlugins(invocationPlugins); 1334 extendedInvocationPlugins = getReplacements().getGraphBuilderPlugins().getInvocationPlugins(); 1335 extendedInvocationPlugins.addTestPlugins(invocationPlugins, null); 1336 invocationPluginExtensions = invocationPlugins; 1337 } 1338 } 1339 } 1340 } 1341 1342 protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) { 1343 conf.getPlugins().prependInlineInvokePlugin(new InlineInvokePlugin() { 1344 1345 @Override 1346 public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { 1347 BytecodeParserNeverInline neverInline = method.getAnnotation(BytecodeParserNeverInline.class); 1348 if (neverInline != null) { 1349 return neverInline.invokeWithException() ? DO_NOT_INLINE_WITH_EXCEPTION : DO_NOT_INLINE_NO_EXCEPTION; 1350 } 1351 if (method.getAnnotation(BytecodeParserForceInline.class) != null) { 1352 return InlineInfo.createStandardInlineInfo(method); 1353 } 1354 return bytecodeParserShouldInlineInvoke(b, method, args); 1355 } 1356 }); 1357 return conf; 1358 } 1359 1360 /** 1361 * Supplements {@link BytecodeParserForceInline} and {@link BytecodeParserNeverInline} in terms 1362 * of allowing a test to influence the inlining decision made during bytecode parsing. 1363 * 1364 * @see InlineInvokePlugin#shouldInlineInvoke(GraphBuilderContext, ResolvedJavaMethod, 1365 * ValueNode[]) 1366 */ 1367 @SuppressWarnings("unused") 1368 protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { 1369 return null; 1370 } 1371 1372 @NodeInfo 1373 public static class NotOptimizedNode extends FixedWithNextNode { 1374 private static final NodeClass<NotOptimizedNode> TYPE = NodeClass.create(NotOptimizedNode.class); 1375 1376 protected NotOptimizedNode() { 1377 super(TYPE, StampFactory.forVoid()); 1378 } 1379 1380 } 1381 1382 protected Replacements getReplacements() { 1383 return getProviders().getReplacements(); 1384 } 1385 1386 /** 1387 * Inject a probability for a branch condition into the profiling information of this test case. 1388 * 1389 * @param p the probability that cond is true 1390 * @param cond the condition of the branch 1391 * @return cond 1392 */ 1393 protected static boolean branchProbability(double p, boolean cond) { 1394 return GraalDirectives.injectBranchProbability(p, cond); 1395 } 1396 1397 /** 1398 * Inject an iteration count for a loop condition into the profiling information of this test 1399 * case. 1400 * 1401 * @param i the iteration count of the loop 1402 * @param cond the condition of the loop 1403 * @return cond 1404 */ 1405 protected static boolean iterationCount(double i, boolean cond) { 1406 return GraalDirectives.injectIterationCount(i, cond); 1407 } 1408 1409 /** 1410 * Test if the current test runs on the given platform. The name must match the name given in 1411 * the {@link Architecture#getName()}. 1412 * 1413 * @param name The name to test 1414 * @return true if we run on the architecture given by name 1415 */ 1416 protected boolean isArchitecture(String name) { 1417 return name.equals(backend.getTarget().arch.getName()); 1418 } 1419 1420 /** 1421 * This method should be called in "timeout" tests which JUnit runs in a different thread. 1422 */ 1423 public static void initializeForTimeout() { 1424 // timeout tests run in a separate thread which needs the DebugEnvironment to be 1425 // initialized 1426 // DebugEnvironment.ensureInitialized(getInitialOptions()); 1427 } 1428} 1429