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.CompilationBailoutAsFailure;
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.io.PrintStream;
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.CounterKey;
import org.graalvm.compiler.debug.DebugCloseable;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.DebugDumpScope;
import org.graalvm.compiler.debug.TimerKey;
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.HotSpotCompilationRequest;
import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
import jdk.vm.ci.hotspot.HotSpotInstalledCode;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotNmethod;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.runtime.JVMCICompiler;
public class CompilationTask {
private final HotSpotJVMCIRuntime jvmciRuntime;
private final HotSpotGraalCompiler compiler;
private final HotSpotCompilationIdentifier compilationId;
private HotSpotInstalledCode installedCode;
private final boolean installAsDefault;
private final boolean useProfilingInfo;
private final boolean shouldRetainLocalVariables;
final class HotSpotCompilationWrapper extends CompilationWrapper<HotSpotCompilationRequestResult> {
CompilationResult result;
HotSpotCompilationWrapper() {
super(compiler.getGraalRuntime().getOutputDirectory(), compiler.getGraalRuntime().getCompilationProblemsPerAction());
}
@Override
protected DebugContext createRetryDebugContext(OptionValues retryOptions, PrintStream logStream) {
SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
return DebugContext.create(retryOptions, logStream, 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;
return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent());
}
return HotSpotCompilationRequestResult.failure(t.toString(), false);
}
@Override
protected ExceptionAction lookupAction(OptionValues values, Throwable cause) {
if (cause instanceof BailoutException) {
BailoutException bailout = (BailoutException) cause;
if (bailout.isPermanent()) {
if (!CompilationBailoutAsFailure.hasBeenSet(values)) {
if (compiler.getGraalRuntime().isBootstrapping()) {
return Diagnose;
}
}
}
if (!CompilationBailoutAsFailure.getValue(values)) {
return super.lookupAction(values, cause);
}
}
if (!CompilationFailureAction.hasBeenSet(values)) {
if (compiler.getGraalRuntime().isBootstrapping()) {
return ExitVM;
}
}
return super.lookupAction(values, 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(debug.getOptions(), method, isOSR);
final CompilationPrinter printer = CompilationPrinter.begin(debug.getOptions(), compilationId, method, entryBCI);
try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
result = compiler.compile(method, entryBCI, useProfilingInfo, shouldRetainLocalVariables, compilationId, debug);
} catch (Throwable e) {
throw debug.handle(e);
}
if (result != null) {
try (DebugCloseable b = CodeInstallationTime.start(debug)) {
installMethod(debug, result);
}
printer.finish(result);
}
stats.finish(method, installedCode);
if (result != null) {
ResolvedJavaMethod rootMethod = result.getMethods()[0];
int inlinedBytecodes = result.getBytecodeSize() - rootMethod.getCodeSize();
assert inlinedBytecodes >= 0 : rootMethod + " " + method;
return HotSpotCompilationRequestResult.success(inlinedBytecodes);
}
return null;
}
}
public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault) {
this(jvmciRuntime, compiler, request, useProfilingInfo, false, installAsDefault);
}
public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean shouldRetainLocalVariables,
boolean installAsDefault) {
this.jvmciRuntime = jvmciRuntime;
this.compiler = compiler;
this.compilationId = new HotSpotCompilationIdentifier(request);
this.useProfilingInfo = useProfilingInfo;
this.shouldRetainLocalVariables = shouldRetainLocalVariables;
this.installAsDefault = installAsDefault;
}
public OptionValues filterOptions(OptionValues options) {
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);
}
}
return newOptions;
}
public HotSpotResolvedJavaMethod getMethod() {
return getRequest().getMethod();
}
CompilationIdentifier getCompilationIdentifier() {
return compilationId;
}
public int getId() {
return getRequest().getId();
}
public int getEntryBCI() {
return getRequest().getEntryBCI();
}
public String getIdString() {
if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) {
return getId() + "%";
} else {
return Integer.toString(getId());
}
}
public HotSpotInstalledCode getInstalledCode() {
return installedCode;
}
private static final TimerKey CompilationTime = DebugContext.timer("CompilationTime").doc("Time spent in compilation and code installation.");
private static final CounterKey CompiledBytecodes = DebugContext.counter("CompiledBytecodes");
private static final CounterKey CompiledAndInstalledBytecodes = DebugContext.counter("CompiledAndInstalledBytecodes");
private static final CounterKey InstalledCodeSize = DebugContext.counter("InstalledCodeSize");
public static final TimerKey CodeInstallationTime = DebugContext.timer("CodeInstallation");
public HotSpotCompilationRequestResult runCompilation(OptionValues initialOptions) {
OptionValues options = filterOptions(initialOptions);
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();
if (installAsDefault || isOSR) {
if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
return HotSpotCompilationRequestResult.failure("Already compiled", false);
}
if (HotSpotGraalCompilerFactory.shouldExclude(method)) {
return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false);
}
}
HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper();
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);
}
}
} 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)) {
HotSpotCompilationRequest request = getRequest();
installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug,
request.getMethod(),
request,
compResult,
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();
}
}