1 /*
   2  * Copyright (c) 2012, 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.hotspot;
  26 
  27 import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Diagnose;
  28 import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.ExitVM;
  29 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAsFailure;
  30 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
  31 import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
  32 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
  33 
  34 import java.io.PrintStream;
  35 
  36 import jdk.internal.vm.compiler.collections.EconomicMap;
  37 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  38 import org.graalvm.compiler.code.CompilationResult;
  39 import org.graalvm.compiler.core.CompilationPrinter;
  40 import org.graalvm.compiler.core.CompilationWrapper;
  41 import org.graalvm.compiler.core.common.CompilationIdentifier;
  42 import org.graalvm.compiler.debug.CounterKey;
  43 import org.graalvm.compiler.debug.DebugCloseable;
  44 import org.graalvm.compiler.debug.DebugContext;
  45 import org.graalvm.compiler.debug.DebugDumpScope;
  46 import org.graalvm.compiler.debug.TimerKey;
  47 import org.graalvm.compiler.options.OptionKey;
  48 import org.graalvm.compiler.options.OptionValues;
  49 import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
  50 
  51 import jdk.vm.ci.code.BailoutException;
  52 import jdk.vm.ci.code.CodeCacheProvider;
  53 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
  54 import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
  55 import jdk.vm.ci.hotspot.HotSpotInstalledCode;
  56 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
  57 import jdk.vm.ci.hotspot.HotSpotNmethod;
  58 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
  59 import jdk.vm.ci.meta.ResolvedJavaMethod;
  60 import jdk.vm.ci.runtime.JVMCICompiler;
  61 
  62 public class CompilationTask {
  63 
  64     private final HotSpotJVMCIRuntime jvmciRuntime;
  65 
  66     private final HotSpotGraalCompiler compiler;
  67     private final HotSpotCompilationIdentifier compilationId;
  68 
  69     private HotSpotInstalledCode installedCode;
  70 
  71     /**
  72      * Specifies whether the compilation result is installed as the
  73      * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method.
  74      */
  75     private final boolean installAsDefault;
  76 
  77     private final boolean useProfilingInfo;
  78     private final boolean shouldRetainLocalVariables;
  79 
  80     final class HotSpotCompilationWrapper extends CompilationWrapper<HotSpotCompilationRequestResult> {
  81         CompilationResult result;
  82 
  83         HotSpotCompilationWrapper() {
  84             super(compiler.getGraalRuntime().getOutputDirectory(), compiler.getGraalRuntime().getCompilationProblemsPerAction());
  85         }
  86 
  87         @Override
  88         protected DebugContext createRetryDebugContext(OptionValues retryOptions, PrintStream logStream) {
  89             SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
  90             return DebugContext.create(retryOptions, logStream, new GraalDebugHandlersFactory(snippetReflection));
  91         }
  92 
  93         @Override
  94         public String toString() {
  95             return getMethod().format("%H.%n(%p)");
  96         }
  97 
  98         @Override
  99         protected HotSpotCompilationRequestResult handleException(Throwable t) {
 100             if (t instanceof BailoutException) {
 101                 BailoutException bailout = (BailoutException) t;
 102                 /*
 103                  * Handling of permanent bailouts: Permanent bailouts that can happen for example
 104                  * due to unsupported unstructured control flow in the bytecodes of a method must
 105                  * not be retried. Hotspot compile broker will ensure that no recompilation at the
 106                  * given tier will happen if retry is false.
 107                  */
 108                 return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent());
 109             }
 110 
 111             /*
 112              * Treat random exceptions from the compiler as indicating a problem compiling this
 113              * method. Report the result of toString instead of getMessage to ensure that the
 114              * exception type is included in the output in case there's no detail mesage.
 115              */
 116             return HotSpotCompilationRequestResult.failure(t.toString(), false);
 117         }
 118 
 119         @Override
 120         protected ExceptionAction lookupAction(OptionValues values, Throwable cause) {
 121             if (cause instanceof BailoutException) {
 122                 BailoutException bailout = (BailoutException) cause;
 123                 if (bailout.isPermanent()) {
 124                     // Respect current action if it has been explicitly set.
 125                     if (!CompilationBailoutAsFailure.hasBeenSet(values)) {
 126                         // Get more info for permanent bailouts during bootstrap.
 127                         if (compiler.getGraalRuntime().isBootstrapping()) {
 128                             return Diagnose;
 129                         }
 130 
 131                     }
 132                 }
 133                 if (!CompilationBailoutAsFailure.getValue(values)) {
 134                     return super.lookupAction(values, cause);
 135                 }
 136             }
 137 
 138             // Respect current action if it has been explicitly set.
 139             if (!CompilationFailureAction.hasBeenSet(values)) {
 140                 // Automatically exit on failure during bootstrap.
 141                 if (compiler.getGraalRuntime().isBootstrapping()) {
 142                     return ExitVM;
 143                 }
 144             }
 145             return super.lookupAction(values, cause);
 146         }
 147 
 148         @SuppressWarnings("try")
 149         @Override
 150         protected HotSpotCompilationRequestResult performCompilation(DebugContext debug) {
 151             HotSpotResolvedJavaMethod method = getMethod();
 152             int entryBCI = getEntryBCI();
 153             final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
 154             CompilationStatistics stats = CompilationStatistics.create(debug.getOptions(), method, isOSR);
 155 
 156             final CompilationPrinter printer = CompilationPrinter.begin(debug.getOptions(), compilationId, method, entryBCI);
 157 
 158             try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
 159                 result = compiler.compile(method, entryBCI, useProfilingInfo, shouldRetainLocalVariables, compilationId, debug);
 160             } catch (Throwable e) {
 161                 throw debug.handle(e);
 162             }
 163 
 164             if (result != null) {
 165                 try (DebugCloseable b = CodeInstallationTime.start(debug)) {
 166                     installMethod(debug, result);
 167                 }
 168                 // Installation is included in compilation time and memory usage reported by printer
 169                 printer.finish(result);
 170             }
 171             stats.finish(method, installedCode);
 172             if (result != null) {
 173                 // For compilation of substitutions the method in the compilation request might be
 174                 // different than the actual method parsed. The root of the compilation will always
 175                 // be the first method in the methods list, so use that instead.
 176                 ResolvedJavaMethod rootMethod = result.getMethods()[0];
 177                 int inlinedBytecodes = result.getBytecodeSize() - rootMethod.getCodeSize();
 178                 assert inlinedBytecodes >= 0 : rootMethod + " " + method;
 179                 return HotSpotCompilationRequestResult.success(inlinedBytecodes);
 180             }
 181             return null;
 182         }
 183 
 184     }
 185 
 186     public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault) {
 187         this(jvmciRuntime, compiler, request, useProfilingInfo, false, installAsDefault);
 188     }
 189 
 190     public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean shouldRetainLocalVariables,
 191                     boolean installAsDefault) {
 192         this.jvmciRuntime = jvmciRuntime;
 193         this.compiler = compiler;
 194         this.compilationId = new HotSpotCompilationIdentifier(request);
 195         this.useProfilingInfo = useProfilingInfo;
 196         this.shouldRetainLocalVariables = shouldRetainLocalVariables;
 197         this.installAsDefault = installAsDefault;
 198     }
 199 
 200     public OptionValues filterOptions(OptionValues options) {
 201         /*
 202          * Disable inlining if HotSpot has it disabled unless it's been explicitly set in Graal.
 203          */
 204         HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
 205         GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
 206         OptionValues newOptions = options;
 207         if (!config.inline) {
 208             EconomicMap<OptionKey<?>, Object> m = OptionValues.newOptionMap();
 209             if (Inline.getValue(options) && !Inline.hasBeenSet(options)) {
 210                 m.put(Inline, false);
 211             }
 212             if (InlineDuringParsing.getValue(options) && !InlineDuringParsing.hasBeenSet(options)) {
 213                 m.put(InlineDuringParsing, false);
 214             }
 215             if (!m.isEmpty()) {
 216                 newOptions = new OptionValues(options, m);
 217             }
 218         }
 219         return newOptions;
 220     }
 221 
 222     public HotSpotResolvedJavaMethod getMethod() {
 223         return getRequest().getMethod();
 224     }
 225 
 226     CompilationIdentifier getCompilationIdentifier() {
 227         return compilationId;
 228     }
 229 
 230     /**
 231      * Returns the HotSpot id of this compilation.
 232      *
 233      * @return HotSpot compile id
 234      */
 235     public int getId() {
 236         return getRequest().getId();
 237     }
 238 
 239     public int getEntryBCI() {
 240         return getRequest().getEntryBCI();
 241     }
 242 
 243     /**
 244      * @return the compilation id plus a trailing '%' if the compilation is an OSR to match
 245      *         PrintCompilation style output
 246      */
 247     public String getIdString() {
 248         if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) {
 249             return getId() + "%";
 250         } else {
 251             return Integer.toString(getId());
 252         }
 253     }
 254 
 255     public HotSpotInstalledCode getInstalledCode() {
 256         return installedCode;
 257     }
 258 
 259     /**
 260      * Time spent in compilation.
 261      */
 262     private static final TimerKey CompilationTime = DebugContext.timer("CompilationTime").doc("Time spent in compilation and code installation.");
 263 
 264     /**
 265      * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}.
 266      */
 267     private static final CounterKey CompiledBytecodes = DebugContext.counter("CompiledBytecodes");
 268 
 269     /**
 270      * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for
 271      * which {@linkplain CompilationResult#getTargetCode()} code was installed.
 272      */
 273     private static final CounterKey CompiledAndInstalledBytecodes = DebugContext.counter("CompiledAndInstalledBytecodes");
 274 
 275     /**
 276      * Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes.
 277      */
 278     private static final CounterKey InstalledCodeSize = DebugContext.counter("InstalledCodeSize");
 279 
 280     /**
 281      * Time spent in code installation.
 282      */
 283     public static final TimerKey CodeInstallationTime = DebugContext.timer("CodeInstallation");
 284 
 285     public HotSpotCompilationRequestResult runCompilation(OptionValues initialOptions) {
 286         OptionValues options = filterOptions(initialOptions);
 287         SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
 288         try (DebugContext debug = DebugContext.create(options, new GraalDebugHandlersFactory(snippetReflection))) {
 289             return runCompilation(debug);
 290         }
 291     }
 292 
 293     @SuppressWarnings("try")
 294     public HotSpotCompilationRequestResult runCompilation(DebugContext debug) {
 295         HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
 296         GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
 297         int entryBCI = getEntryBCI();
 298         boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
 299         HotSpotResolvedJavaMethod method = getMethod();
 300 
 301         if (installAsDefault || isOSR) {
 302             // If there is already compiled code for this method on our level we simply return.
 303             // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we
 304             // only need to check for that value.
 305             if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
 306                 return HotSpotCompilationRequestResult.failure("Already compiled", false);
 307             }
 308             if (HotSpotGraalCompilerFactory.shouldExclude(method)) {
 309                 return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false);
 310             }
 311         }
 312 
 313         HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper();
 314         try (DebugCloseable a = CompilationTime.start(debug)) {
 315             return compilation.run(debug);
 316         } finally {
 317             try {
 318                 int compiledBytecodes = 0;
 319                 int codeSize = 0;
 320 
 321                 if (compilation.result != null) {
 322                     compiledBytecodes = compilation.result.getBytecodeSize();
 323                     CompiledBytecodes.add(debug, compiledBytecodes);
 324                     if (installedCode != null) {
 325                         codeSize = installedCode.getSize();
 326                         CompiledAndInstalledBytecodes.add(debug, compiledBytecodes);
 327                         InstalledCodeSize.add(debug, codeSize);
 328                     }
 329                 }
 330             } catch (Throwable t) {
 331                 return compilation.handleException(t);
 332             }
 333         }
 334     }
 335 
 336     @SuppressWarnings("try")
 337     private void installMethod(DebugContext debug, final CompilationResult compResult) {
 338         final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache();
 339         HotSpotBackend backend = compiler.getGraalRuntime().getHostBackend();
 340         installedCode = null;
 341         Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult};
 342         try (DebugContext.Scope s = debug.scope("CodeInstall", context)) {
 343             HotSpotCompilationRequest request = getRequest();
 344             installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug,
 345                             request.getMethod(),
 346                             request,
 347                             compResult,
 348                             null,
 349                             installAsDefault,
 350                             context);
 351         } catch (Throwable e) {
 352             throw debug.handle(e);
 353         }
 354     }
 355 
 356     @Override
 357     public String toString() {
 358         return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]";
 359     }
 360 
 361     private HotSpotCompilationRequest getRequest() {
 362         return compilationId.getRequest();
 363     }
 364 }