1 /* 2 * Copyright (c) 2016, 2019, 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.microbenchmarks.lir; 26 27 import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getGraph; 28 import static org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil.getMethodFromMethodSpec; 29 30 import java.lang.annotation.Annotation; 31 import java.lang.annotation.ElementType; 32 import java.lang.annotation.Inherited; 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.lang.annotation.Target; 36 import java.lang.reflect.Field; 37 import java.lang.reflect.Method; 38 39 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 40 import org.graalvm.compiler.api.test.Graal; 41 import org.graalvm.compiler.code.CompilationResult; 42 import org.graalvm.compiler.core.GraalCompiler; 43 import org.graalvm.compiler.core.GraalCompiler.Request; 44 import org.graalvm.compiler.core.LIRGenerationPhase; 45 import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext; 46 import org.graalvm.compiler.core.common.CompilationIdentifier; 47 import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder; 48 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; 49 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 50 import org.graalvm.compiler.core.gen.LIRCompilerBackend; 51 import org.graalvm.compiler.core.gen.LIRGenerationProvider; 52 import org.graalvm.compiler.core.target.Backend; 53 import org.graalvm.compiler.debug.DebugContext; 54 import org.graalvm.compiler.debug.DebugHandlersFactory; 55 import org.graalvm.compiler.lir.LIR; 56 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; 57 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 58 import org.graalvm.compiler.lir.gen.LIRGeneratorTool; 59 import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext; 60 import org.graalvm.compiler.lir.phases.LIRPhase; 61 import org.graalvm.compiler.lir.phases.LIRSuites; 62 import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext; 63 import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext; 64 import org.graalvm.compiler.microbenchmarks.graal.util.GraalState; 65 import org.graalvm.compiler.microbenchmarks.graal.util.GraalUtil; 66 import org.graalvm.compiler.microbenchmarks.graal.util.MethodSpec; 67 import org.graalvm.compiler.nodes.StructuredGraph; 68 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 69 import org.graalvm.compiler.nodes.cfg.Block; 70 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 71 import org.graalvm.compiler.nodes.spi.LoweringProvider; 72 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; 73 import org.graalvm.compiler.options.OptionValues; 74 import org.graalvm.compiler.phases.OptimisticOptimizations; 75 import org.graalvm.compiler.phases.PhaseSuite; 76 import org.graalvm.compiler.phases.tiers.HighTierContext; 77 import org.graalvm.compiler.phases.tiers.Suites; 78 import org.graalvm.compiler.phases.tiers.TargetProvider; 79 import org.graalvm.compiler.phases.util.Providers; 80 import org.graalvm.compiler.runtime.RuntimeProvider; 81 import org.openjdk.jmh.annotations.Level; 82 import org.openjdk.jmh.annotations.Param; 83 import org.openjdk.jmh.annotations.Scope; 84 import org.openjdk.jmh.annotations.Setup; 85 import org.openjdk.jmh.annotations.State; 86 87 import jdk.vm.ci.code.CodeCacheProvider; 88 import jdk.vm.ci.code.RegisterConfig; 89 import jdk.vm.ci.code.TargetDescription; 90 import jdk.vm.ci.meta.ConstantReflectionProvider; 91 import jdk.vm.ci.meta.MetaAccessProvider; 92 import jdk.vm.ci.meta.ResolvedJavaMethod; 93 import jdk.vm.ci.meta.SpeculationLog; 94 95 /** 96 * State providing a new copy of a graph for each invocation of a benchmark. Subclasses of this 97 * class are annotated with {@link MethodSpec} to specify the Java method that will be parsed to 98 * obtain the original graph. 99 */ 100 @State(Scope.Thread) 101 public abstract class GraalCompilerState { 102 103 /** 104 * Original graph from which the per-benchmark invocation {@link #graph} is cloned. 105 */ 106 private StructuredGraph originalGraph; 107 108 /** 109 * The graph processed by the benchmark. 110 */ 111 private final OptionValues options; 112 private final DebugContext debug; 113 private StructuredGraph graph; 114 private final Backend backend; 115 private final Providers providers; 116 117 /** 118 * We only allow inner classes to subclass this to ensure that the {@link Setup} methods are 119 * executed in the right order. 120 */ 121 @SuppressWarnings("try") 122 protected GraalCompilerState() { 123 this.options = Graal.getRequiredCapability(OptionValues.class); 124 this.backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend(); 125 this.providers = backend.getProviders(); 126 this.debug = DebugContext.create(options, DebugHandlersFactory.LOADER); 127 } 128 129 protected boolean useProfilingInfo() { 130 return false; 131 } 132 133 @SuppressWarnings("try") 134 protected void initializeMethod() { 135 GraalState graal = new GraalState(); 136 ResolvedJavaMethod method = graal.metaAccess.lookupJavaMethod(getMethod()); 137 StructuredGraph structuredGraph = null; 138 try (DebugContext.Scope s = debug.scope("GraphState", method)) { 139 structuredGraph = preprocessOriginal(getGraph(graal, method, useProfilingInfo())); 140 } catch (Throwable t) { 141 debug.handle(t); 142 } 143 this.originalGraph = structuredGraph; 144 } 145 146 protected Method getMethod() { 147 Class<?> c = getClass(); 148 if (isMethodSpecAnnotationPresent(c)) { 149 return getMethodFromMethodSpec(c); 150 } 151 return findParamField(this); 152 } 153 154 protected boolean isMethodSpecAnnotationPresent(Class<?> startClass) { 155 Class<?> c = startClass; 156 while (c != null) { 157 if (c.isAnnotationPresent(MethodSpec.class)) { 158 return true; 159 } 160 c = c.getSuperclass(); 161 } 162 return false; 163 } 164 165 /** 166 * Declares {@link GraalCompilerState#getMethodFromString(String) method description field}. The 167 * field must be a {@link String} and have a {@link Param} annotation. 168 */ 169 @Inherited 170 @Target({ElementType.FIELD}) 171 @Retention(RetentionPolicy.RUNTIME) 172 public @interface MethodDescString { 173 } 174 175 private static Method findParamField(Object obj) { 176 Class<?> c = obj.getClass(); 177 Class<? extends Annotation> annotationClass = MethodDescString.class; 178 try { 179 for (Field f : c.getFields()) { 180 if (f.isAnnotationPresent(annotationClass)) { 181 // these checks could be done by an annotation processor 182 if (!f.getType().equals(String.class)) { 183 throw new RuntimeException("Found a field annotated with " + annotationClass.getSimpleName() + " in " + c + " which is not a " + String.class.getSimpleName()); 184 } 185 if (!f.isAnnotationPresent(Param.class)) { 186 throw new RuntimeException("Found a field annotated with " + annotationClass.getSimpleName() + " in " + c + " which is not annotated with " + Param.class.getSimpleName()); 187 } 188 String methodName; 189 methodName = (String) f.get(obj); 190 assert methodName != null; 191 return getMethodFromString(methodName); 192 } 193 } 194 } catch (Exception e) { 195 throw new RuntimeException(e); 196 } 197 throw new RuntimeException("Could not find class annotated with " + annotationClass.getSimpleName() + " in hierarchy of " + c); 198 } 199 200 /** 201 * Gets a {@link Method} from a method description string. The format is as follows: 202 * 203 * <pre> 204 * ClassName#MethodName 205 * ClassName#MethodName(ClassName, ClassName, ...) 206 * </pre> 207 * 208 * <code>CodeName</code> is passed to {@link Class#forName(String)}. <br> 209 * <b>Examples:</b> 210 * 211 * <pre> 212 * java.lang.String#equals 213 * java.lang.String#equals(java.lang.Object) 214 * </pre> 215 */ 216 protected static Method getMethodFromString(String methodDesc) { 217 try { 218 String[] s0 = methodDesc.split("#", 2); 219 if (s0.length != 2) { 220 throw new RuntimeException("Missing method description? " + methodDesc); 221 } 222 String className = s0[0]; 223 Class<?> clazz = Class.forName(className); 224 String[] s1 = s0[1].split("\\(", 2); 225 String name = s1[0]; 226 Class<?>[] parameters = null; 227 if (s1.length > 1) { 228 String parametersPart = s1[1]; 229 if (parametersPart.charAt(parametersPart.length() - 1) != ')') { 230 throw new RuntimeException("Missing closing ')'? " + methodDesc); 231 } 232 String[] s2 = parametersPart.substring(0, parametersPart.length() - 1).split(","); 233 parameters = new Class<?>[s2.length]; 234 for (int i = 0; i < s2.length; i++) { 235 parameters[i] = Class.forName(s2[i]); 236 } 237 } 238 return GraalUtil.getMethod(clazz, name, parameters); 239 } catch (ClassNotFoundException e) { 240 throw new RuntimeException(e); 241 } 242 } 243 244 protected StructuredGraph preprocessOriginal(StructuredGraph structuredGraph) { 245 return structuredGraph; 246 } 247 248 protected OptionValues getOptions() { 249 return options; 250 } 251 252 protected Suites createSuites(OptionValues opts) { 253 return backend.getSuites().getDefaultSuites(opts).copy(); 254 } 255 256 protected LIRSuites createLIRSuites(OptionValues opts) { 257 return backend.getSuites().getDefaultLIRSuites(opts).copy(); 258 } 259 260 protected Backend getBackend() { 261 return backend; 262 } 263 264 protected Providers getProviders() { 265 return providers; 266 } 267 268 protected SnippetReflectionProvider getSnippetReflection() { 269 return Graal.getRequiredCapability(SnippetReflectionProvider.class); 270 } 271 272 protected TargetDescription getTarget() { 273 return getTargetProvider().getTarget(); 274 } 275 276 protected TargetProvider getTargetProvider() { 277 return getBackend(); 278 } 279 280 protected CodeCacheProvider getCodeCache() { 281 return getProviders().getCodeCache(); 282 } 283 284 protected ConstantReflectionProvider getConstantReflection() { 285 return getProviders().getConstantReflection(); 286 } 287 288 protected MetaAccessProvider getMetaAccess() { 289 return getProviders().getMetaAccess(); 290 } 291 292 protected LoweringProvider getLowerer() { 293 return getProviders().getLowerer(); 294 } 295 296 protected PhaseSuite<HighTierContext> getDefaultGraphBuilderSuite() { 297 // defensive copying 298 return backend.getSuites().getDefaultGraphBuilderSuite().copy(); 299 } 300 301 protected LIRSuites getLIRSuites() { 302 return request.lirSuites; 303 } 304 305 private Request<CompilationResult> request; 306 private LIRGenerationResult lirGenRes; 307 private LIRGeneratorTool lirGenTool; 308 private NodeLIRBuilderTool nodeLirGen; 309 private RegisterConfig registerConfig; 310 private ScheduleResult schedule; 311 private AbstractBlockBase<?>[] codeEmittingOrder; 312 private AbstractBlockBase<?>[] linearScanOrder; 313 314 /** 315 * Copies the {@link #originalGraph original graph} and prepares the {@link #request}. 316 * 317 * The {@link Suites} can be changed by overriding {@link #createSuites}. {@link LIRSuites} can 318 * be changed by overriding {@link #createLIRSuites}. 319 */ 320 protected final void prepareRequest() { 321 assert originalGraph != null : "call initialzeMethod first"; 322 CompilationIdentifier compilationId = backend.getCompilationIdentifier(originalGraph.method()); 323 graph = originalGraph.copyWithIdentifier(compilationId, originalGraph.getDebug()); 324 assert !graph.isFrozen(); 325 ResolvedJavaMethod installedCodeOwner = graph.method(); 326 request = new Request<>(graph, installedCodeOwner, getProviders(), getBackend(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, 327 graph.getProfilingInfo(), createSuites(getOptions()), createLIRSuites(getOptions()), new CompilationResult(graph.compilationId()), CompilationResultBuilderFactory.Default, 328 true); 329 } 330 331 /** 332 * Executes the high-level (FrontEnd) part of the compiler. 333 */ 334 protected final void emitFrontEnd() { 335 GraalCompiler.emitFrontEnd(request.providers, request.backend, request.graph, request.graphBuilderSuite, request.optimisticOpts, request.profilingInfo, request.suites); 336 request.graph.freeze(); 337 } 338 339 /** 340 * Executes the low-level (BackEnd) part of the compiler. 341 */ 342 protected final void emitBackEnd() { 343 emitLIR(); 344 emitCode(); 345 } 346 347 /** 348 * Generates {@link LIR} and executes the {@link LIR} pipeline. 349 */ 350 protected final void emitLIR() { 351 generateLIR(); 352 emitLowLevel(); 353 } 354 355 /** 356 * Generates the initial {@link LIR}. 357 */ 358 protected final void generateLIR() { 359 preLIRGeneration(); 360 lirGeneration(); 361 } 362 363 /** 364 * Sets up {@link LIR} generation. 365 */ 366 protected final void preLIRGeneration() { 367 assert request.graph.isFrozen() : "Graph not frozen."; 368 Object stub = null; 369 schedule = request.graph.getLastSchedule(); 370 ControlFlowGraph cfg = deepCopy(schedule.getCFG()); 371 Block[] blocks = cfg.getBlocks(); 372 Block startBlock = cfg.getStartBlock(); 373 assert startBlock != null; 374 assert startBlock.getPredecessorCount() == 0; 375 376 codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock); 377 linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock); 378 379 LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder, getGraphOptions(), getGraphDebug()); 380 LIRGenerationProvider lirBackend = (LIRGenerationProvider) request.backend; 381 RegisterAllocationConfig registerAllocationConfig = request.backend.newRegisterAllocationConfig(registerConfig, null); 382 lirGenRes = lirBackend.newLIRGenerationResult(graph.compilationId(), lir, registerAllocationConfig, request.graph, stub); 383 lirGenTool = lirBackend.newLIRGenerator(lirGenRes); 384 nodeLirGen = lirBackend.newNodeLIRBuilder(request.graph, lirGenTool); 385 } 386 387 protected OptionValues getGraphOptions() { 388 return graph.getOptions(); 389 } 390 391 protected DebugContext getGraphDebug() { 392 return graph.getDebug(); 393 } 394 395 private static ControlFlowGraph deepCopy(ControlFlowGraph cfg) { 396 return ControlFlowGraph.compute(cfg.graph, true, true, true, true); 397 } 398 399 /** 400 * Executes the {@link LIRGenerationPhase}. 401 */ 402 protected final void lirGeneration() { 403 LIRGenerationContext context = new LIRGenerationContext(lirGenTool, nodeLirGen, request.graph, schedule); 404 new LIRGenerationPhase().apply(request.backend.getTarget(), lirGenRes, context); 405 } 406 407 /** 408 * Executes the low-level compiler stages. 409 */ 410 protected final void emitLowLevel() { 411 preAllocationStage(); 412 allocationStage(); 413 postAllocationStage(); 414 } 415 416 /** 417 * Executes a {@link LIRPhase} within a given {@code context}. 418 */ 419 protected <C> void applyLIRPhase(LIRPhase<C> phase, C context) { 420 phase.apply(request.backend.getTarget(), lirGenRes, context); 421 } 422 423 /** 424 * Executes the {@link PreAllocationStage}. 425 * 426 * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites}. 427 */ 428 protected final void preAllocationStage() { 429 applyLIRPhase(getLIRSuites().getPreAllocationOptimizationStage(), createPreAllocationOptimizationContext()); 430 } 431 432 protected PreAllocationOptimizationContext createPreAllocationOptimizationContext() { 433 return new PreAllocationOptimizationContext(lirGenTool); 434 } 435 436 /** 437 * Executes the {@link AllocationStage}. 438 * 439 * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites}. 440 */ 441 protected final void allocationStage() { 442 applyLIRPhase(getLIRSuites().getAllocationStage(), createAllocationContext()); 443 } 444 445 protected AllocationContext createAllocationContext() { 446 return new AllocationContext(lirGenTool.getSpillMoveFactory(), lirGenRes.getRegisterAllocationConfig()); 447 } 448 449 /** 450 * Executes the {@link PostAllocationStage}. 451 * 452 * {@link LIRPhase phases} can be changed by overriding {@link #createLIRSuites}. 453 */ 454 protected final void postAllocationStage() { 455 applyLIRPhase(getLIRSuites().getPostAllocationOptimizationStage(), createPostAllocationOptimizationContext()); 456 } 457 458 protected PostAllocationOptimizationContext createPostAllocationOptimizationContext() { 459 return new PostAllocationOptimizationContext(lirGenTool); 460 } 461 462 /** 463 * Emits the machine code. 464 */ 465 protected final void emitCode() { 466 int bytecodeSize = request.graph.method() == null ? 0 : request.graph.getBytecodeSize(); 467 SpeculationLog speculationLog = null; 468 request.compilationResult.setHasUnsafeAccess(request.graph.hasUnsafeAccess()); 469 LIRCompilerBackend.emitCode(request.backend, request.graph.getAssumptions(), request.graph.method(), request.graph.getMethods(), request.graph.getFields(), 470 speculationLog, bytecodeSize, lirGenRes, 471 request.compilationResult, request.installedCodeOwner, request.factory); 472 } 473 474 protected StructuredGraph graph() { 475 return graph; 476 } 477 478 protected LIR getLIR() { 479 return lirGenRes.getLIR(); 480 } 481 482 public abstract static class Compile extends GraalCompilerState { 483 484 @Setup(Level.Trial) 485 public void init() { 486 initializeMethod(); 487 } 488 489 @Setup(Level.Invocation) 490 public void setup() { 491 prepareRequest(); 492 } 493 494 public CompilationResult compile() { 495 emitFrontEnd(); 496 emitBackEnd(); 497 return super.request.compilationResult; 498 } 499 500 } 501 502 public abstract static class FrontEndOnly extends GraalCompilerState { 503 504 @Setup(Level.Trial) 505 public void init() { 506 initializeMethod(); 507 } 508 509 @Setup(Level.Invocation) 510 public void setup() { 511 prepareRequest(); 512 } 513 514 public StructuredGraph compile() { 515 emitFrontEnd(); 516 return super.graph; 517 } 518 519 } 520 521 public abstract static class BackEndOnly extends GraalCompilerState { 522 523 @Setup(Level.Trial) 524 public void init() { 525 initializeMethod(); 526 } 527 528 /** 529 * Cannot do this {@link Level#Trial only once} since {@link #emitCode()} closes the 530 * {@link CompilationResult}. 531 */ 532 @Setup(Level.Invocation) 533 public void setupGraph() { 534 prepareRequest(); 535 emitFrontEnd(); 536 } 537 538 public CompilationResult compile() { 539 emitBackEnd(); 540 return super.request.compilationResult; 541 } 542 } 543 544 public abstract static class PreAllocationStage extends GraalCompilerState { 545 /** 546 * No need to rebuild the graph for every invocation since it is not altered by the backend. 547 */ 548 @Setup(Level.Trial) 549 public void setupGraph() { 550 initializeMethod(); 551 prepareRequest(); 552 emitFrontEnd(); 553 } 554 555 @Setup(Level.Invocation) 556 public void setup() { 557 generateLIR(); 558 } 559 560 public LIRGenerationResult compile() { 561 preAllocationStage(); 562 return super.lirGenRes; 563 } 564 } 565 566 public abstract static class AllocationStage extends GraalCompilerState { 567 /** 568 * No need to rebuild the graph for every invocation since it is not altered by the backend. 569 */ 570 @Setup(Level.Trial) 571 public void setupGraph() { 572 initializeMethod(); 573 prepareRequest(); 574 emitFrontEnd(); 575 } 576 577 @Setup(Level.Invocation) 578 public void setup() { 579 generateLIR(); 580 preAllocationStage(); 581 } 582 583 public LIRGenerationResult compile() { 584 allocationStage(); 585 return super.lirGenRes; 586 } 587 } 588 589 public abstract static class PostAllocationStage extends GraalCompilerState { 590 /** 591 * No need to rebuild the graph for every invocation since it is not altered by the backend. 592 */ 593 @Setup(Level.Trial) 594 public void setupGraph() { 595 initializeMethod(); 596 prepareRequest(); 597 emitFrontEnd(); 598 } 599 600 @Setup(Level.Invocation) 601 public void setup() { 602 generateLIR(); 603 preAllocationStage(); 604 allocationStage(); 605 } 606 607 public LIRGenerationResult compile() { 608 postAllocationStage(); 609 return super.lirGenRes; 610 } 611 } 612 }