blob: e0596989d9390114d1aa89f032739eac582d79f3 [file] [log] [blame]
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.hotspot;
import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Diagnose;
import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.ExitVM;
import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAction;
import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
import java.util.List;
import jdk.internal.vm.compiler.collections.EconomicMap;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.compiler.core.CompilationPrinter;
import org.graalvm.compiler.core.CompilationWrapper;
import org.graalvm.compiler.core.common.CompilationIdentifier;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.CounterKey;
import org.graalvm.compiler.debug.DebugCloseable;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.DebugDumpScope;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.TimerKey;
import org.graalvm.compiler.options.EnumOptionKey;
import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
import jdk.vm.ci.code.BailoutException;
import jdk.vm.ci.code.CodeCacheProvider;
import jdk.vm.ci.hotspot.EventProvider;
import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
import jdk.vm.ci.hotspot.HotSpotInstalledCode;
import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotNmethod;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import jdk.vm.ci.runtime.JVMCICompiler;
import jdk.vm.ci.services.JVMCIServiceLocator;
public class CompilationTask {
private static final EventProvider eventProvider;
static {
List<EventProvider> providers = JVMCIServiceLocator.getProviders(EventProvider.class);
if (providers.size() > 1) {
throw new GraalError("Multiple %s providers found: %s", EventProvider.class.getName(), providers);
} else if (providers.isEmpty()) {
eventProvider = EventProvider.createEmptyEventProvider();
} else {
eventProvider = providers.get(0);
}
}
private final HotSpotJVMCIRuntime jvmciRuntime;
private final HotSpotGraalCompiler compiler;
private final HotSpotCompilationIdentifier compilationId;
private HotSpotInstalledCode installedCode;
/**
* Specifies whether the compilation result is installed as the
* {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method.
*/
private final boolean installAsDefault;
private final boolean useProfilingInfo;
private final OptionValues options;
final class HotSpotCompilationWrapper extends CompilationWrapper<HotSpotCompilationRequestResult> {
private final EventProvider.CompilationEvent compilationEvent;
CompilationResult result;
HotSpotCompilationWrapper(EventProvider.CompilationEvent compilationEvent) {
super(compiler.getGraalRuntime().getOutputDirectory(), compiler.getGraalRuntime().getCompilationProblemsPerAction());
this.compilationEvent = compilationEvent;
}
@Override
protected DebugContext createRetryDebugContext(OptionValues retryOptions) {
SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
return DebugContext.create(retryOptions, new GraalDebugHandlersFactory(snippetReflection));
}
@Override
public String toString() {
return getMethod().format("%H.%n(%p)");
}
@Override
protected HotSpotCompilationRequestResult handleException(Throwable t) {
if (t instanceof BailoutException) {
BailoutException bailout = (BailoutException) t;
/*
* Handling of permanent bailouts: Permanent bailouts that can happen for example
* due to unsupported unstructured control flow in the bytecodes of a method must
* not be retried. Hotspot compile broker will ensure that no recompilation at the
* given tier will happen if retry is false.
*/
return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent());
}
// Log a failure event.
EventProvider.CompilerFailureEvent event = eventProvider.newCompilerFailureEvent();
if (event.shouldWrite()) {
event.setCompileId(getId());
event.setMessage(t.getMessage());
event.commit();
}
/*
* Treat random exceptions from the compiler as indicating a problem compiling this
* method. Report the result of toString instead of getMessage to ensure that the
* exception type is included in the output in case there's no detail mesage.
*/
return HotSpotCompilationRequestResult.failure(t.toString(), false);
}
@Override
protected ExceptionAction lookupAction(OptionValues values, EnumOptionKey<ExceptionAction> actionKey, Throwable cause) {
// Respect current action if it has been explicitly set.
if (!actionKey.hasBeenSet(values)) {
if (actionKey == CompilationFailureAction) {
// Automatically exit on non-bailout during bootstrap
// or when assertions are enabled.
if (Assertions.assertionsEnabled() || compiler.getGraalRuntime().isBootstrapping()) {
return ExitVM;
}
} else if (actionKey == CompilationBailoutAction && ((BailoutException) cause).isPermanent()) {
// Get more info for permanent bailouts during bootstrap
// or when assertions are enabled.
assert CompilationBailoutAction.getDefaultValue() == ExceptionAction.Silent;
if (Assertions.assertionsEnabled() || compiler.getGraalRuntime().isBootstrapping()) {
return Diagnose;
}
}
}
return super.lookupAction(values, actionKey, cause);
}
@SuppressWarnings("try")
@Override
protected HotSpotCompilationRequestResult performCompilation(DebugContext debug) {
HotSpotResolvedJavaMethod method = getMethod();
int entryBCI = getEntryBCI();
final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
CompilationStatistics stats = CompilationStatistics.create(options, method, isOSR);
final CompilationPrinter printer = CompilationPrinter.begin(options, compilationId, method, entryBCI);
try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
// Begin the compilation event.
compilationEvent.begin();
result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options, debug);
} catch (Throwable e) {
throw debug.handle(e);
} finally {
// End the compilation event.
compilationEvent.end();
}
if (result != null) {
try (DebugCloseable b = CodeInstallationTime.start(debug)) {
installMethod(debug, result);
}
// Installation is included in compilation time and memory usage reported by printer
printer.finish(result);
}
stats.finish(method, installedCode);
if (result != null) {
return HotSpotCompilationRequestResult.success(result.getBytecodeSize() - method.getCodeSize());
}
return null;
}
}
public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault,
OptionValues options) {
this.jvmciRuntime = jvmciRuntime;
this.compiler = compiler;
this.compilationId = new HotSpotCompilationIdentifier(request);
this.useProfilingInfo = useProfilingInfo;
this.installAsDefault = installAsDefault;
/*
* Disable inlining if HotSpot has it disabled unless it's been explicitly set in Graal.
*/
HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
OptionValues newOptions = options;
if (!config.inline) {
EconomicMap<OptionKey<?>, Object> m = OptionValues.newOptionMap();
if (Inline.getValue(options) && !Inline.hasBeenSet(options)) {
m.put(Inline, false);
}
if (InlineDuringParsing.getValue(options) && !InlineDuringParsing.hasBeenSet(options)) {
m.put(InlineDuringParsing, false);
}
if (!m.isEmpty()) {
newOptions = new OptionValues(options, m);
}
}
this.options = newOptions;
}
public HotSpotResolvedJavaMethod getMethod() {
return getRequest().getMethod();
}
CompilationIdentifier getCompilationIdentifier() {
return compilationId;
}
/**
* Returns the HotSpot id of this compilation.
*
* @return HotSpot compile id
*/
public int getId() {
return getRequest().getId();
}
public int getEntryBCI() {
return getRequest().getEntryBCI();
}
/**
* @return the compilation id plus a trailing '%' if the compilation is an OSR to match
* PrintCompilation style output
*/
public String getIdString() {
if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) {
return getId() + "%";
} else {
return Integer.toString(getId());
}
}
public HotSpotInstalledCode getInstalledCode() {
return installedCode;
}
/**
* Time spent in compilation.
*/
private static final TimerKey CompilationTime = DebugContext.timer("CompilationTime").doc("Time spent in compilation and code installation.");
/**
* Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}.
*/
private static final CounterKey CompiledBytecodes = DebugContext.counter("CompiledBytecodes");
/**
* Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for
* which {@linkplain CompilationResult#getTargetCode()} code was installed.
*/
private static final CounterKey CompiledAndInstalledBytecodes = DebugContext.counter("CompiledAndInstalledBytecodes");
/**
* Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes.
*/
private static final CounterKey InstalledCodeSize = DebugContext.counter("InstalledCodeSize");
/**
* Time spent in code installation.
*/
public static final TimerKey CodeInstallationTime = DebugContext.timer("CodeInstallation");
public HotSpotCompilationRequestResult runCompilation() {
SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
try (DebugContext debug = DebugContext.create(options, new GraalDebugHandlersFactory(snippetReflection))) {
return runCompilation(debug);
}
}
@SuppressWarnings("try")
public HotSpotCompilationRequestResult runCompilation(DebugContext debug) {
HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
int entryBCI = getEntryBCI();
boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
HotSpotResolvedJavaMethod method = getMethod();
// Log a compilation event.
EventProvider.CompilationEvent compilationEvent = eventProvider.newCompilationEvent();
if (installAsDefault) {
// If there is already compiled code for this method on our level we simply return.
// JVMCI compiles are always at the highest compile level, even in non-tiered mode so we
// only need to check for that value.
if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
return HotSpotCompilationRequestResult.failure("Already compiled", false);
}
if (HotSpotGraalCompilerFactory.checkGraalCompileOnlyFilter(method.getDeclaringClass().toJavaName(), method.getName(), method.getSignature().toString(),
HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) != HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) {
return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false);
}
}
HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper(compilationEvent);
try (DebugCloseable a = CompilationTime.start(debug)) {
return compilation.run(debug);
} finally {
try {
int compiledBytecodes = 0;
int codeSize = 0;
if (compilation.result != null) {
compiledBytecodes = compilation.result.getBytecodeSize();
CompiledBytecodes.add(debug, compiledBytecodes);
if (installedCode != null) {
codeSize = installedCode.getSize();
CompiledAndInstalledBytecodes.add(debug, compiledBytecodes);
InstalledCodeSize.add(debug, codeSize);
}
}
// Log a compilation event.
if (compilationEvent.shouldWrite()) {
compilationEvent.setMethod(method.format("%H.%n(%p)"));
compilationEvent.setCompileId(getId());
compilationEvent.setCompileLevel(config.compilationLevelFullOptimization);
compilationEvent.setSucceeded(compilation.result != null && installedCode != null);
compilationEvent.setIsOsr(isOSR);
compilationEvent.setCodeSize(codeSize);
compilationEvent.setInlinedBytes(compiledBytecodes);
compilationEvent.commit();
}
} catch (Throwable t) {
return compilation.handleException(t);
}
}
}
@SuppressWarnings("try")
private void installMethod(DebugContext debug, final CompilationResult compResult) {
final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache();
HotSpotBackend backend = compiler.getGraalRuntime().getHostBackend();
installedCode = null;
Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult};
try (DebugContext.Scope s = debug.scope("CodeInstall", context)) {
installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug, getRequest().getMethod(), getRequest(), compResult,
getRequest().getMethod().getSpeculationLog(), null, installAsDefault, context);
} catch (Throwable e) {
throw debug.handle(e);
}
}
@Override
public String toString() {
return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]";
}
private HotSpotCompilationRequest getRequest() {
return compilationId.getRequest();
}
}