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;
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;
return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent());
}
EventProvider.CompilerFailureEvent event = eventProvider.newCompilerFailureEvent();
if (event.shouldWrite()) {
event.setCompileId(getId());
event.setMessage(t.getMessage());
event.commit();
}
return HotSpotCompilationRequestResult.failure(t.toString(), false);
}
@Override
protected ExceptionAction lookupAction(OptionValues values, EnumOptionKey<ExceptionAction> actionKey, Throwable cause) {
if (!actionKey.hasBeenSet(values)) {
if (actionKey == CompilationFailureAction) {
if (Assertions.assertionsEnabled() || compiler.getGraalRuntime().isBootstrapping()) {
return ExitVM;
}
} else if (actionKey == CompilationBailoutAction && ((BailoutException) cause).isPermanent()) {
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))) {
compilationEvent.begin();
result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options, debug);
} catch (Throwable e) {
throw debug.handle(e);
} finally {
compilationEvent.end();
}
if (result != null) {
try (DebugCloseable b = CodeInstallationTime.start(debug)) {
installMethod(debug, result);
}
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;
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;
}
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() {
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();
EventProvider.CompilationEvent compilationEvent = eventProvider.newCompilationEvent();
if (installAsDefault) {
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);
}
}
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();
}
}