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 }