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 }