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