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