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