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