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 }