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