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