1 /*
   2  * Copyright (c) 2009, 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.core;
  24 
  25 import static org.graalvm.compiler.core.GraalCompilerOptions.EmitLIRRepeatCount;
  26 import static org.graalvm.compiler.core.common.GraalOptions.UseGraalInstrumentation;
  27 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Optional;
  28 
  29 import java.util.Collection;
  30 import java.util.List;
  31 
  32 import org.graalvm.compiler.code.CompilationResult;
  33 import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext;
  34 import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder;
  35 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
  36 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  37 import org.graalvm.compiler.core.common.util.CompilationAlarm;
  38 import org.graalvm.compiler.core.target.Backend;
  39 import org.graalvm.compiler.debug.Debug;
  40 import org.graalvm.compiler.debug.Debug.Scope;
  41 import org.graalvm.compiler.debug.DebugCloseable;
  42 import org.graalvm.compiler.debug.DebugCounter;
  43 import org.graalvm.compiler.debug.DebugTimer;
  44 import org.graalvm.compiler.debug.internal.method.MethodMetricsRootScopeInfo;
  45 import org.graalvm.compiler.lir.BailoutAndRestartBackendException;
  46 import org.graalvm.compiler.lir.LIR;
  47 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  48 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
  49 import org.graalvm.compiler.lir.framemap.FrameMap;
  50 import org.graalvm.compiler.lir.framemap.FrameMapBuilder;
  51 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
  52 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  53 import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext;
  54 import org.graalvm.compiler.lir.phases.LIRSuites;
  55 import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
  56 import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext;
  57 import org.graalvm.compiler.nodes.StructuredGraph;
  58 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  59 import org.graalvm.compiler.nodes.cfg.Block;
  60 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  61 import org.graalvm.compiler.options.OptionValue.OverrideScope;
  62 import org.graalvm.compiler.phases.OptimisticOptimizations;
  63 import org.graalvm.compiler.phases.PhaseSuite;
  64 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
  65 import org.graalvm.compiler.phases.common.instrumentation.ExtractInstrumentationPhase;
  66 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  67 import org.graalvm.compiler.phases.tiers.HighTierContext;
  68 import org.graalvm.compiler.phases.tiers.LowTierContext;
  69 import org.graalvm.compiler.phases.tiers.MidTierContext;
  70 import org.graalvm.compiler.phases.tiers.Suites;
  71 import org.graalvm.compiler.phases.tiers.TargetProvider;
  72 import org.graalvm.compiler.phases.util.Providers;
  73 
  74 import jdk.vm.ci.code.RegisterConfig;
  75 import jdk.vm.ci.code.TargetDescription;
  76 import jdk.vm.ci.code.site.ConstantReference;
  77 import jdk.vm.ci.code.site.DataPatch;
  78 import jdk.vm.ci.meta.Assumptions;
  79 import jdk.vm.ci.meta.JavaConstant;
  80 import jdk.vm.ci.meta.JavaKind;
  81 import jdk.vm.ci.meta.ProfilingInfo;
  82 import jdk.vm.ci.meta.ResolvedJavaField;
  83 import jdk.vm.ci.meta.ResolvedJavaMethod;
  84 import jdk.vm.ci.meta.VMConstant;
  85 
  86 /**
  87  * Static methods for orchestrating the compilation of a {@linkplain StructuredGraph graph}.
  88  */
  89 public class GraalCompiler {
  90 
  91     private static final DebugTimer CompilerTimer = Debug.timer("GraalCompiler");
  92     private static final DebugTimer FrontEnd = Debug.timer("FrontEnd");
  93     private static final DebugTimer BackEnd = Debug.timer("BackEnd");
  94     private static final DebugTimer EmitLIR = Debug.timer("EmitLIR");
  95     private static final DebugTimer EmitCode = Debug.timer("EmitCode");
  96     private static final LIRGenerationPhase LIR_GENERATION_PHASE = new LIRGenerationPhase();
  97 
  98     /**
  99      * Encapsulates all the inputs to a {@linkplain GraalCompiler#compile(Request) compilation}.
 100      */
 101     public static class Request<T extends CompilationResult> {
 102         public final StructuredGraph graph;
 103         public final ResolvedJavaMethod installedCodeOwner;
 104         public final Providers providers;
 105         public final Backend backend;
 106         public final PhaseSuite<HighTierContext> graphBuilderSuite;
 107         public final OptimisticOptimizations optimisticOpts;
 108         public final ProfilingInfo profilingInfo;
 109         public final Suites suites;
 110         public final LIRSuites lirSuites;
 111         public final T compilationResult;
 112         public final CompilationResultBuilderFactory factory;
 113 
 114         /**
 115          * @param graph the graph to be compiled
 116          * @param installedCodeOwner the method the compiled code will be associated with once
 117          *            installed. This argument can be null.
 118          * @param providers
 119          * @param backend
 120          * @param graphBuilderSuite
 121          * @param optimisticOpts
 122          * @param profilingInfo
 123          * @param suites
 124          * @param lirSuites
 125          * @param compilationResult
 126          * @param factory
 127          */
 128         public Request(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend, PhaseSuite<HighTierContext> graphBuilderSuite,
 129                         OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult, CompilationResultBuilderFactory factory) {
 130             this.graph = graph;
 131             this.installedCodeOwner = installedCodeOwner;
 132             this.providers = providers;
 133             this.backend = backend;
 134             this.graphBuilderSuite = graphBuilderSuite;
 135             this.optimisticOpts = optimisticOpts;
 136             this.profilingInfo = profilingInfo;
 137             this.suites = suites;
 138             this.lirSuites = lirSuites;
 139             this.compilationResult = compilationResult;
 140             this.factory = factory;
 141         }
 142 
 143         /**
 144          * Executes this compilation request.
 145          *
 146          * @return the result of the compilation
 147          */
 148         public T execute() {
 149             return GraalCompiler.compile(this);
 150         }
 151     }
 152 
 153     /**
 154      * Requests compilation of a given graph.
 155      *
 156      * @param graph the graph to be compiled
 157      * @param installedCodeOwner the method the compiled code will be associated with once
 158      *            installed. This argument can be null.
 159      * @return the result of the compilation
 160      */
 161     public static <T extends CompilationResult> T compileGraph(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend,
 162                     PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult,
 163                     CompilationResultBuilderFactory factory) {
 164         return compile(new Request<>(graph, installedCodeOwner, providers, backend, graphBuilderSuite, optimisticOpts, profilingInfo, suites, lirSuites, compilationResult, factory));
 165     }
 166 
 167     /**
 168      * Services a given compilation request.
 169      *
 170      * @return the result of the compilation
 171      */
 172     @SuppressWarnings("try")
 173     public static <T extends CompilationResult> T compile(Request<T> r) {
 174         try (Scope s = MethodMetricsRootScopeInfo.createRootScopeIfAbsent(r.installedCodeOwner);
 175                         CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod()) {
 176             assert !r.graph.isFrozen();
 177             try (Scope s0 = Debug.scope("GraalCompiler", r.graph, r.providers.getCodeCache()); DebugCloseable a = CompilerTimer.start()) {
 178                 emitFrontEnd(r.providers, r.backend, r.graph, r.graphBuilderSuite, r.optimisticOpts, r.profilingInfo, r.suites);
 179                 emitBackEnd(r.graph, null, r.installedCodeOwner, r.backend, r.compilationResult, r.factory, null, r.lirSuites);
 180             } catch (Throwable e) {
 181                 throw Debug.handle(e);
 182             }
 183             return r.compilationResult;
 184         }
 185     }
 186 
 187     /**
 188      * Builds the graph, optimizes it.
 189      */
 190     @SuppressWarnings("try")
 191     public static void emitFrontEnd(Providers providers, TargetProvider target, StructuredGraph graph, PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts,
 192                     ProfilingInfo profilingInfo, Suites suites) {
 193         try (Scope s = Debug.scope("FrontEnd"); DebugCloseable a = FrontEnd.start()) {
 194             HighTierContext highTierContext = new HighTierContext(providers, graphBuilderSuite, optimisticOpts);
 195             if (graph.start().next() == null) {
 196                 graphBuilderSuite.apply(graph, highTierContext);
 197                 new DeadCodeEliminationPhase(Optional).apply(graph);
 198             } else {
 199                 Debug.dump(Debug.INFO_LOG_LEVEL, graph, "initial state");
 200             }
 201             if (UseGraalInstrumentation.getValue()) {
 202                 new ExtractInstrumentationPhase().apply(graph, highTierContext);
 203             }
 204 
 205             suites.getHighTier().apply(graph, highTierContext);
 206             graph.maybeCompress();
 207 
 208             MidTierContext midTierContext = new MidTierContext(providers, target, optimisticOpts, profilingInfo);
 209             suites.getMidTier().apply(graph, midTierContext);
 210             graph.maybeCompress();
 211 
 212             LowTierContext lowTierContext = new LowTierContext(providers, target);
 213             suites.getLowTier().apply(graph, lowTierContext);
 214 
 215             Debug.dump(Debug.BASIC_LOG_LEVEL, graph.getLastSchedule(), "Final HIR schedule");
 216         } catch (Throwable e) {
 217             throw Debug.handle(e);
 218         }
 219     }
 220 
 221     @SuppressWarnings("try")
 222     public static <T extends CompilationResult> void emitBackEnd(StructuredGraph graph, Object stub, ResolvedJavaMethod installedCodeOwner, Backend backend, T compilationResult,
 223                     CompilationResultBuilderFactory factory, RegisterConfig registerConfig, LIRSuites lirSuites) {
 224         try (Scope s = Debug.scope("BackEnd", graph.getLastSchedule()); DebugCloseable a = BackEnd.start()) {
 225             // Repeatedly run the LIR code generation pass to improve statistical profiling results.
 226             for (int i = 0; i < EmitLIRRepeatCount.getValue(); i++) {
 227                 SchedulePhase dummySchedule = new SchedulePhase();
 228                 dummySchedule.apply(graph);
 229                 emitLIR(backend, graph, stub, registerConfig, lirSuites);
 230             }
 231 
 232             LIRGenerationResult lirGen = null;
 233             lirGen = emitLIR(backend, graph, stub, registerConfig, lirSuites);
 234             try (Scope s2 = Debug.scope("CodeGen", lirGen, lirGen.getLIR())) {
 235                 int bytecodeSize = graph.method() == null ? 0 : graph.getBytecodeSize();
 236                 compilationResult.setHasUnsafeAccess(graph.hasUnsafeAccess());
 237                 emitCode(backend, graph.getAssumptions(), graph.method(), graph.getMethods(), graph.getFields(), bytecodeSize, lirGen, compilationResult, installedCodeOwner, factory);
 238             } catch (Throwable e) {
 239                 throw Debug.handle(e);
 240             }
 241         } catch (Throwable e) {
 242             throw Debug.handle(e);
 243         }
 244     }
 245 
 246     @SuppressWarnings("try")
 247     public static LIRGenerationResult emitLIR(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites) {
 248         OverrideScope overrideScope = null;
 249         LIRSuites lirSuites0 = lirSuites;
 250         while (true) {
 251             try (OverrideScope scope = overrideScope) {
 252                 return emitLIR0(backend, graph, stub, registerConfig, lirSuites0);
 253             } catch (BailoutAndRestartBackendException e) {
 254                 if (BailoutAndRestartBackendException.Options.LIRUnlockBackendRestart.getValue() && e.shouldRestart()) {
 255                     overrideScope = e.getOverrideScope();
 256                     lirSuites0 = e.updateLIRSuites(lirSuites);
 257                     if (lirSuites0 != null) {
 258                         continue;
 259                     }
 260                 }
 261                 /*
 262                  * The BailoutAndRestartBackendException is permanent. If restart fails or is
 263                  * disabled we throw the bailout.
 264                  */
 265                 throw e;
 266             }
 267         }
 268     }
 269 
 270     @SuppressWarnings("try")
 271     private static LIRGenerationResult emitLIR0(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites) {
 272         try (Scope ds = Debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start()) {
 273             ScheduleResult schedule = graph.getLastSchedule();
 274             Block[] blocks = schedule.getCFG().getBlocks();
 275             Block startBlock = schedule.getCFG().getStartBlock();
 276             assert startBlock != null;
 277             assert startBlock.getPredecessorCount() == 0;
 278 
 279             LIR lir = null;
 280             AbstractBlockBase<?>[] codeEmittingOrder = null;
 281             AbstractBlockBase<?>[] linearScanOrder = null;
 282             try (Scope s = Debug.scope("ComputeLinearScanOrder", lir)) {
 283                 codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
 284                 linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
 285 
 286                 lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder);
 287                 Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After linear scan order");
 288             } catch (Throwable e) {
 289                 throw Debug.handle(e);
 290             }
 291             FrameMapBuilder frameMapBuilder = backend.newFrameMapBuilder(registerConfig);
 292             LIRGenerationResult lirGenRes = backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, graph, stub);
 293             LIRGeneratorTool lirGen = backend.newLIRGenerator(lirGenRes);
 294             NodeLIRBuilderTool nodeLirGen = backend.newNodeLIRBuilder(graph, lirGen);
 295 
 296             // LIR generation
 297             LIRGenerationContext context = new LIRGenerationContext(lirGen, nodeLirGen, graph, schedule);
 298             LIR_GENERATION_PHASE.apply(backend.getTarget(), lirGenRes, context);
 299 
 300             try (Scope s = Debug.scope("LIRStages", nodeLirGen, lir)) {
 301                 Debug.dump(Debug.BASIC_LOG_LEVEL, lir, "After LIR generation");
 302                 LIRGenerationResult result = emitLowLevel(backend.getTarget(), lirGenRes, lirGen, lirSuites, backend.newRegisterAllocationConfig(registerConfig));
 303                 Debug.dump(Debug.BASIC_LOG_LEVEL, lir, "Before code generation");
 304                 return result;
 305             } catch (Throwable e) {
 306                 throw Debug.handle(e);
 307             }
 308         } catch (Throwable e) {
 309             throw Debug.handle(e);
 310         }
 311     }
 312 
 313     protected static <T extends CompilationResult> String getCompilationUnitName(StructuredGraph graph, T compilationResult) {
 314         if (compilationResult != null && compilationResult.getName() != null) {
 315             return compilationResult.getName();
 316         }
 317         ResolvedJavaMethod method = graph.method();
 318         if (method == null) {
 319             return "<unknown>";
 320         }
 321         return method.format("%H.%n(%p)");
 322     }
 323 
 324     public static LIRGenerationResult emitLowLevel(TargetDescription target, LIRGenerationResult lirGenRes, LIRGeneratorTool lirGen, LIRSuites lirSuites,
 325                     RegisterAllocationConfig registerAllocationConfig) {
 326         PreAllocationOptimizationContext preAllocOptContext = new PreAllocationOptimizationContext(lirGen);
 327         lirSuites.getPreAllocationOptimizationStage().apply(target, lirGenRes, preAllocOptContext);
 328 
 329         AllocationContext allocContext = new AllocationContext(lirGen.getSpillMoveFactory(), registerAllocationConfig);
 330         lirSuites.getAllocationStage().apply(target, lirGenRes, allocContext);
 331 
 332         PostAllocationOptimizationContext postAllocOptContext = new PostAllocationOptimizationContext(lirGen);
 333         lirSuites.getPostAllocationOptimizationStage().apply(target, lirGenRes, postAllocOptContext);
 334 
 335         return lirGenRes;
 336     }
 337 
 338     @SuppressWarnings("try")
 339     public static void emitCode(Backend backend, Assumptions assumptions, ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods, Collection<ResolvedJavaField> accessedFields,
 340                     int bytecodeSize, LIRGenerationResult lirGenRes,
 341                     CompilationResult compilationResult, ResolvedJavaMethod installedCodeOwner, CompilationResultBuilderFactory factory) {
 342         try (DebugCloseable a = EmitCode.start()) {
 343             FrameMap frameMap = lirGenRes.getFrameMap();
 344             CompilationResultBuilder crb = backend.newCompilationResultBuilder(lirGenRes, frameMap, compilationResult, factory);
 345             backend.emitCode(crb, lirGenRes.getLIR(), installedCodeOwner);
 346             if (assumptions != null && !assumptions.isEmpty()) {
 347                 compilationResult.setAssumptions(assumptions.toArray());
 348             }
 349             if (rootMethod != null) {
 350                 compilationResult.setMethods(rootMethod, inlinedMethods);
 351                 compilationResult.setFields(accessedFields);
 352                 compilationResult.setBytecodeSize(bytecodeSize);
 353             }
 354             crb.finish();
 355             if (Debug.isCountEnabled()) {
 356                 List<DataPatch> ldp = compilationResult.getDataPatches();
 357                 JavaKind[] kindValues = JavaKind.values();
 358                 DebugCounter[] dms = new DebugCounter[kindValues.length];
 359                 for (int i = 0; i < dms.length; i++) {
 360                     dms[i] = Debug.counter("DataPatches-%s", kindValues[i]);
 361                 }
 362 
 363                 for (DataPatch dp : ldp) {
 364                     JavaKind kind = JavaKind.Illegal;
 365                     if (dp.reference instanceof ConstantReference) {
 366                         VMConstant constant = ((ConstantReference) dp.reference).getConstant();
 367                         if (constant instanceof JavaConstant) {
 368                             kind = ((JavaConstant) constant).getJavaKind();
 369                         }
 370                     }
 371                     dms[kind.ordinal()].add(1);
 372                 }
 373 
 374                 Debug.counter("CompilationResults").increment();
 375                 Debug.counter("CodeBytesEmitted").add(compilationResult.getTargetCodeSize());
 376                 Debug.counter("InfopointsEmitted").add(compilationResult.getInfopoints().size());
 377                 Debug.counter("DataPatches").add(ldp.size());
 378                 Debug.counter("ExceptionHandlersEmitted").add(compilationResult.getExceptionHandlers().size());
 379             }
 380 
 381             Debug.dump(Debug.BASIC_LOG_LEVEL, compilationResult, "After code generation");
 382         }
 383     }
 384 }