1 /*
   2  * Copyright (c) 2009, 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.core;
  26 
  27 import java.util.Collection;
  28 import java.util.List;
  29 
  30 import jdk.internal.vm.compiler.collections.EconomicSet;
  31 import org.graalvm.compiler.code.CompilationResult;
  32 import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext;
  33 import org.graalvm.compiler.core.common.GraalOptions;
  34 import org.graalvm.compiler.core.common.PermanentBailoutException;
  35 import org.graalvm.compiler.core.common.RetryableBailoutException;
  36 import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder;
  37 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
  38 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  39 import org.graalvm.compiler.core.common.util.CompilationAlarm;
  40 import org.graalvm.compiler.core.target.Backend;
  41 import org.graalvm.compiler.debug.CounterKey;
  42 import org.graalvm.compiler.debug.DebugCloseable;
  43 import org.graalvm.compiler.debug.DebugContext;
  44 import org.graalvm.compiler.debug.GraalError;
  45 import org.graalvm.compiler.debug.MethodFilter;
  46 import org.graalvm.compiler.debug.TimerKey;
  47 import org.graalvm.compiler.lir.LIR;
  48 import org.graalvm.compiler.lir.alloc.OutOfRegistersException;
  49 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  50 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
  51 import org.graalvm.compiler.lir.framemap.FrameMap;
  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.LIRSuites;
  57 import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
  58 import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext;
  59 import org.graalvm.compiler.nodes.StructuredGraph;
  60 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  61 import org.graalvm.compiler.nodes.cfg.Block;
  62 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  63 import org.graalvm.compiler.phases.OptimisticOptimizations;
  64 import org.graalvm.compiler.phases.PhaseSuite;
  65 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
  66 import org.graalvm.compiler.phases.tiers.HighTierContext;
  67 import org.graalvm.compiler.phases.tiers.LowTierContext;
  68 import org.graalvm.compiler.phases.tiers.MidTierContext;
  69 import org.graalvm.compiler.phases.tiers.Suites;
  70 import org.graalvm.compiler.phases.tiers.TargetProvider;
  71 import org.graalvm.compiler.phases.util.Providers;
  72 
  73 import jdk.vm.ci.code.RegisterConfig;
  74 import jdk.vm.ci.code.TargetDescription;
  75 import jdk.vm.ci.code.site.ConstantReference;
  76 import jdk.vm.ci.code.site.DataPatch;
  77 import jdk.vm.ci.meta.Assumptions;
  78 import jdk.vm.ci.meta.JavaConstant;
  79 import jdk.vm.ci.meta.JavaKind;
  80 import jdk.vm.ci.meta.ProfilingInfo;
  81 import jdk.vm.ci.meta.ResolvedJavaField;
  82 import jdk.vm.ci.meta.ResolvedJavaMethod;
  83 import jdk.vm.ci.meta.VMConstant;
  84 
  85 /**
  86  * Static methods for orchestrating the compilation of a {@linkplain StructuredGraph graph}.
  87  */
  88 public class GraalCompiler {
  89 
  90     private static final TimerKey CompilerTimer = DebugContext.timer("GraalCompiler").doc("Time spent in compilation (excludes code installation).");
  91     private static final TimerKey FrontEnd = DebugContext.timer("FrontEnd").doc("Time spent processing HIR.");
  92     private static final TimerKey EmitLIR = DebugContext.timer("EmitLIR").doc("Time spent generating LIR from HIR.");
  93     private static final TimerKey EmitCode = DebugContext.timer("EmitCode").doc("Time spent generating machine code from LIR.");
  94     private static final TimerKey BackEnd = DebugContext.timer("BackEnd").doc("Time spent in EmitLIR and EmitCode.");
  95 
  96     /**
  97      * Encapsulates all the inputs to a {@linkplain GraalCompiler#compile(Request) compilation}.
  98      */
  99     public static class Request<T extends CompilationResult> {
 100         public final StructuredGraph graph;
 101         public final ResolvedJavaMethod installedCodeOwner;
 102         public final Providers providers;
 103         public final Backend backend;
 104         public final PhaseSuite<HighTierContext> graphBuilderSuite;
 105         public final OptimisticOptimizations optimisticOpts;
 106         public final ProfilingInfo profilingInfo;
 107         public final Suites suites;
 108         public final LIRSuites lirSuites;
 109         public final T compilationResult;
 110         public final CompilationResultBuilderFactory factory;
 111         public final boolean verifySourcePositions;
 112 
 113         /**
 114          * @param graph the graph to be compiled
 115          * @param installedCodeOwner the method the compiled code will be associated with once
 116          *            installed. This argument can be null.
 117          * @param providers
 118          * @param backend
 119          * @param graphBuilderSuite
 120          * @param optimisticOpts
 121          * @param profilingInfo
 122          * @param suites
 123          * @param lirSuites
 124          * @param compilationResult
 125          * @param factory
 126          */
 127         public Request(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend, PhaseSuite<HighTierContext> graphBuilderSuite,
 128                         OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult, CompilationResultBuilderFactory factory,
 129                         boolean verifySourcePositions) {
 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             this.verifySourcePositions = verifySourcePositions;
 142         }
 143 
 144         /**
 145          * Executes this compilation request.
 146          *
 147          * @return the result of the compilation
 148          */
 149         public T execute() {
 150             return GraalCompiler.compile(this);
 151         }
 152     }
 153 
 154     /**
 155      * Requests compilation of a given graph.
 156      *
 157      * @param graph the graph to be compiled
 158      * @param installedCodeOwner the method the compiled code will be associated with once
 159      *            installed. This argument can be null.
 160      * @return the result of the compilation
 161      */
 162     public static <T extends CompilationResult> T compileGraph(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend,
 163                     PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult,
 164                     CompilationResultBuilderFactory factory, boolean verifySourcePositions) {
 165         return compile(new Request<>(graph, installedCodeOwner, providers, backend, graphBuilderSuite, optimisticOpts, profilingInfo, suites, lirSuites, compilationResult, factory,
 166                         verifySourcePositions));
 167     }
 168 
 169     /**
 170      * Services a given compilation request.
 171      *
 172      * @return the result of the compilation
 173      */
 174     @SuppressWarnings("try")
 175     public static <T extends CompilationResult> T compile(Request<T> r) {
 176         DebugContext debug = r.graph.getDebug();
 177         try (CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod(r.graph.getOptions())) {
 178             assert !r.graph.isFrozen();
 179             try (DebugContext.Scope s0 = debug.scope("GraalCompiler", r.graph, r.providers.getCodeCache()); DebugCloseable a = CompilerTimer.start(debug)) {
 180                 emitFrontEnd(r.providers, r.backend, r.graph, r.graphBuilderSuite, r.optimisticOpts, r.profilingInfo, r.suites);
 181                 emitBackEnd(r.graph, null, r.installedCodeOwner, r.backend, r.compilationResult, r.factory, null, r.lirSuites);
 182                 if (r.verifySourcePositions) {
 183                     assert r.graph.verifySourcePositions(true);
 184                 }
 185             } catch (Throwable e) {
 186                 throw debug.handle(e);
 187             }
 188             checkForRequestedCrash(r.graph);
 189             return r.compilationResult;
 190         }
 191     }
 192 
 193     /**
 194      * Checks whether the {@link GraalCompilerOptions#CrashAt} option indicates that the compilation
 195      * of {@code graph} should result in an exception.
 196      *
 197      * @param graph a graph currently being compiled
 198      * @throws RuntimeException if the value of {@link GraalCompilerOptions#CrashAt} matches
 199      *             {@code graph.method()} or {@code graph.name}
 200      */
 201     private static void checkForRequestedCrash(StructuredGraph graph) {
 202         String value = GraalCompilerOptions.CrashAt.getValue(graph.getOptions());
 203         if (value != null) {
 204             boolean bailout = false;
 205             boolean permanentBailout = false;
 206             String methodPattern = value;
 207             if (value.endsWith(":Bailout")) {
 208                 methodPattern = value.substring(0, value.length() - ":Bailout".length());
 209                 bailout = true;
 210             } else if (value.endsWith(":PermanentBailout")) {
 211                 methodPattern = value.substring(0, value.length() - ":PermanentBailout".length());
 212                 permanentBailout = true;
 213             }
 214             String crashLabel = null;
 215             if (graph.name != null && graph.name.contains(methodPattern)) {
 216                 crashLabel = graph.name;
 217             }
 218             if (crashLabel == null) {
 219                 ResolvedJavaMethod method = graph.method();
 220                 MethodFilter[] filters = MethodFilter.parse(methodPattern);
 221                 for (MethodFilter filter : filters) {
 222                     if (filter.matches(method)) {
 223                         crashLabel = method.format("%H.%n(%p)");
 224                     }
 225                 }
 226             }
 227             if (crashLabel != null) {
 228                 if (permanentBailout) {
 229                     throw new PermanentBailoutException("Forced crash after compiling " + crashLabel);
 230                 }
 231                 if (bailout) {
 232                     throw new RetryableBailoutException("Forced crash after compiling " + crashLabel);
 233                 }
 234                 throw new RuntimeException("Forced crash after compiling " + crashLabel);
 235             }
 236         }
 237     }
 238 
 239     /**
 240      * Builds the graph, optimizes it.
 241      */
 242     @SuppressWarnings("try")
 243     public static void emitFrontEnd(Providers providers, TargetProvider target, StructuredGraph graph, PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts,
 244                     ProfilingInfo profilingInfo, Suites suites) {
 245         DebugContext debug = graph.getDebug();
 246         try (DebugContext.Scope s = debug.scope("FrontEnd"); DebugCloseable a = FrontEnd.start(debug)) {
 247             HighTierContext highTierContext = new HighTierContext(providers, graphBuilderSuite, optimisticOpts);
 248             if (graph.start().next() == null) {
 249                 graphBuilderSuite.apply(graph, highTierContext);
 250                 new DeadCodeEliminationPhase(DeadCodeEliminationPhase.Optionality.Optional).apply(graph);
 251                 debug.dump(DebugContext.BASIC_LEVEL, graph, "After parsing");
 252             } else {
 253                 debug.dump(DebugContext.INFO_LEVEL, graph, "initial state");
 254             }
 255 
 256             suites.getHighTier().apply(graph, highTierContext);
 257             graph.maybeCompress();
 258             debug.dump(DebugContext.BASIC_LEVEL, graph, "After high tier");
 259 
 260             MidTierContext midTierContext = new MidTierContext(providers, target, optimisticOpts, profilingInfo);
 261             suites.getMidTier().apply(graph, midTierContext);
 262             graph.maybeCompress();
 263             debug.dump(DebugContext.BASIC_LEVEL, graph, "After mid tier");
 264 
 265             LowTierContext lowTierContext = new LowTierContext(providers, target);
 266             suites.getLowTier().apply(graph, lowTierContext);
 267             debug.dump(DebugContext.BASIC_LEVEL, graph, "After low tier");
 268 
 269             debug.dump(DebugContext.BASIC_LEVEL, graph.getLastSchedule(), "Final HIR schedule");
 270             graph.logInliningTree();
 271         } catch (Throwable e) {
 272             throw debug.handle(e);
 273         } finally {
 274             graph.checkCancellation();
 275         }
 276     }
 277 
 278     @SuppressWarnings("try")
 279     public static <T extends CompilationResult> void emitBackEnd(StructuredGraph graph, Object stub, ResolvedJavaMethod installedCodeOwner, Backend backend, T compilationResult,
 280                     CompilationResultBuilderFactory factory, RegisterConfig registerConfig, LIRSuites lirSuites) {
 281         DebugContext debug = graph.getDebug();
 282         try (DebugContext.Scope s = debug.scope("BackEnd", graph.getLastSchedule()); DebugCloseable a = BackEnd.start(debug)) {
 283             LIRGenerationResult lirGen = null;
 284             lirGen = emitLIR(backend, graph, stub, registerConfig, lirSuites);
 285             try (DebugContext.Scope s2 = debug.scope("CodeGen", lirGen, lirGen.getLIR())) {
 286                 int bytecodeSize = graph.method() == null ? 0 : graph.getBytecodeSize();
 287                 compilationResult.setHasUnsafeAccess(graph.hasUnsafeAccess());
 288                 emitCode(backend, graph.getAssumptions(), graph.method(), graph.getMethods(), graph.getFields(), bytecodeSize, lirGen, compilationResult, installedCodeOwner, factory);
 289             } catch (Throwable e) {
 290                 throw debug.handle(e);
 291             }
 292         } catch (Throwable e) {
 293             throw debug.handle(e);
 294         } finally {
 295             graph.checkCancellation();
 296         }
 297     }
 298 
 299     @SuppressWarnings("try")
 300     public static LIRGenerationResult emitLIR(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites) {
 301         String registerPressure = GraalOptions.RegisterPressure.getValue(graph.getOptions());
 302         String[] allocationRestrictedTo = registerPressure == null ? null : registerPressure.split(",");
 303         try {
 304             return emitLIR0(backend, graph, stub, registerConfig, lirSuites, allocationRestrictedTo);
 305         } catch (OutOfRegistersException e) {
 306             if (allocationRestrictedTo != null) {
 307                 allocationRestrictedTo = null;
 308                 return emitLIR0(backend, graph, stub, registerConfig, lirSuites, allocationRestrictedTo);
 309             }
 310             /* If the re-execution fails we convert the exception into a "hard" failure */
 311             throw new GraalError(e);
 312         } finally {
 313             graph.checkCancellation();
 314         }
 315     }
 316 
 317     @SuppressWarnings("try")
 318     private static LIRGenerationResult emitLIR0(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites,
 319                     String[] allocationRestrictedTo) {
 320         DebugContext debug = graph.getDebug();
 321         try (DebugContext.Scope ds = debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start(debug)) {
 322             assert !graph.hasValueProxies();
 323             ScheduleResult schedule = graph.getLastSchedule();
 324             Block[] blocks = schedule.getCFG().getBlocks();
 325             Block startBlock = schedule.getCFG().getStartBlock();
 326             assert startBlock != null;
 327             assert startBlock.getPredecessorCount() == 0;
 328 
 329             AbstractBlockBase<?>[] codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock);
 330             AbstractBlockBase<?>[] linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock);
 331             LIR lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder, graph.getOptions(), graph.getDebug());
 332 
 333             FrameMapBuilder frameMapBuilder = backend.newFrameMapBuilder(registerConfig);
 334             LIRGenerationResult lirGenRes = backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, graph, stub);
 335             LIRGeneratorTool lirGen = backend.newLIRGenerator(lirGenRes);
 336             NodeLIRBuilderTool nodeLirGen = backend.newNodeLIRBuilder(graph, lirGen);
 337 
 338             // LIR generation
 339             LIRGenerationContext context = new LIRGenerationContext(lirGen, nodeLirGen, graph, schedule);
 340             new LIRGenerationPhase().apply(backend.getTarget(), lirGenRes, context);
 341 
 342             try (DebugContext.Scope s = debug.scope("LIRStages", nodeLirGen, lirGenRes, lir)) {
 343                 // Dump LIR along with HIR (the LIR is looked up from context)
 344                 debug.dump(DebugContext.BASIC_LEVEL, graph.getLastSchedule(), "After LIR generation");
 345                 LIRGenerationResult result = emitLowLevel(backend.getTarget(), lirGenRes, lirGen, lirSuites, backend.newRegisterAllocationConfig(registerConfig, allocationRestrictedTo));
 346                 return result;
 347             } catch (Throwable e) {
 348                 throw debug.handle(e);
 349             }
 350         } catch (Throwable e) {
 351             throw debug.handle(e);
 352         } finally {
 353             graph.checkCancellation();
 354         }
 355     }
 356 
 357     protected static <T extends CompilationResult> String getCompilationUnitName(StructuredGraph graph, T compilationResult) {
 358         if (compilationResult != null && compilationResult.getName() != null) {
 359             return compilationResult.getName();
 360         }
 361         ResolvedJavaMethod method = graph.method();
 362         if (method == null) {
 363             return "<unknown>";
 364         }
 365         return method.format("%H.%n(%p)");
 366     }
 367 
 368     public static LIRGenerationResult emitLowLevel(TargetDescription target, LIRGenerationResult lirGenRes, LIRGeneratorTool lirGen, LIRSuites lirSuites,
 369                     RegisterAllocationConfig registerAllocationConfig) {
 370         DebugContext debug = lirGenRes.getLIR().getDebug();
 371         PreAllocationOptimizationContext preAllocOptContext = new PreAllocationOptimizationContext(lirGen);
 372         lirSuites.getPreAllocationOptimizationStage().apply(target, lirGenRes, preAllocOptContext);
 373         debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After PreAllocationOptimizationStage");
 374 
 375         AllocationContext allocContext = new AllocationContext(lirGen.getSpillMoveFactory(), registerAllocationConfig);
 376         lirSuites.getAllocationStage().apply(target, lirGenRes, allocContext);
 377         debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After AllocationStage");
 378 
 379         PostAllocationOptimizationContext postAllocOptContext = new PostAllocationOptimizationContext(lirGen);
 380         lirSuites.getPostAllocationOptimizationStage().apply(target, lirGenRes, postAllocOptContext);
 381         debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After PostAllocationOptimizationStage");
 382 
 383         return lirGenRes;
 384     }
 385 
 386     @SuppressWarnings("try")
 387     public static void emitCode(Backend backend, Assumptions assumptions, ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods, EconomicSet<ResolvedJavaField> accessedFields,
 388                     int bytecodeSize, LIRGenerationResult lirGenRes,
 389                     CompilationResult compilationResult, ResolvedJavaMethod installedCodeOwner, CompilationResultBuilderFactory factory) {
 390         DebugContext debug = lirGenRes.getLIR().getDebug();
 391         try (DebugCloseable a = EmitCode.start(debug)) {
 392             FrameMap frameMap = lirGenRes.getFrameMap();
 393             CompilationResultBuilder crb = backend.newCompilationResultBuilder(lirGenRes, frameMap, compilationResult, factory);
 394             backend.emitCode(crb, lirGenRes.getLIR(), installedCodeOwner);
 395             if (assumptions != null && !assumptions.isEmpty()) {
 396                 compilationResult.setAssumptions(assumptions.toArray());
 397             }
 398             if (rootMethod != null) {
 399                 compilationResult.setMethods(rootMethod, inlinedMethods);
 400                 compilationResult.setFields(accessedFields);
 401                 compilationResult.setBytecodeSize(bytecodeSize);
 402             }
 403             crb.finish();
 404             if (debug.isCountEnabled()) {
 405                 List<DataPatch> ldp = compilationResult.getDataPatches();
 406                 JavaKind[] kindValues = JavaKind.values();
 407                 CounterKey[] dms = new CounterKey[kindValues.length];
 408                 for (int i = 0; i < dms.length; i++) {
 409                     dms[i] = DebugContext.counter("DataPatches-%s", kindValues[i]);
 410                 }
 411 
 412                 for (DataPatch dp : ldp) {
 413                     JavaKind kind = JavaKind.Illegal;
 414                     if (dp.reference instanceof ConstantReference) {
 415                         VMConstant constant = ((ConstantReference) dp.reference).getConstant();
 416                         if (constant instanceof JavaConstant) {
 417                             kind = ((JavaConstant) constant).getJavaKind();
 418                         }
 419                     }
 420                     dms[kind.ordinal()].add(debug, 1);
 421                 }
 422 
 423                 DebugContext.counter("CompilationResults").increment(debug);
 424                 DebugContext.counter("CodeBytesEmitted").add(debug, compilationResult.getTargetCodeSize());
 425                 DebugContext.counter("InfopointsEmitted").add(debug, compilationResult.getInfopoints().size());
 426                 DebugContext.counter("DataPatches").add(debug, ldp.size());
 427                 DebugContext.counter("ExceptionHandlersEmitted").add(debug, compilationResult.getExceptionHandlers().size());
 428             }
 429 
 430             debug.dump(DebugContext.BASIC_LEVEL, compilationResult, "After code generation");
 431         }
 432     }
 433 }