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