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