package org.graalvm.compiler.truffle.runtime;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.ArgumentTypeSpeculation;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.BackgroundCompilation;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.Compilation;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompilationExceptionsAreFatal;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompilationExceptionsArePrinted;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompilationExceptionsAreThrown;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompilationFailureAction;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompilationStatisticDetails;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompilationStatistics;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompilationThreshold;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompileImmediately;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.CompileOnly;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.FirstTierCompilationThreshold;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.FirstTierMinInvokeThreshold;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.Inlining;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.MinInvokeThreshold;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.Mode;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.MultiTier;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.PerformanceWarningsAreFatal;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.Profiling;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.ReturnTypeSpeculation;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.Splitting;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.SplittingAllowForcedSplits;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.SplittingDumpDecisions;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.SplittingGrowthLimit;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.SplittingMaxCalleeSize;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.SplittingMaxPropagationDepth;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.SplittingTraceEvents;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.TraceCompilation;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.TraceCompilationDetails;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.TraceSplitting;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.TraceSplittingSummary;
import static org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.TraceTransferToInterpreter;
import static org.graalvm.compiler.truffle.runtime.GraalTruffleRuntime.getRuntime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.logging.Level;
import org.graalvm.collections.Pair;
import org.graalvm.compiler.truffle.options.PolyglotCompilerOptions;
import org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.EngineModeEnum;
import org.graalvm.compiler.truffle.options.PolyglotCompilerOptions.ExceptionAction;
import org.graalvm.compiler.truffle.runtime.debug.StatisticsListener;
import org.graalvm.options.OptionValues;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
public final class EngineData {
private static final AtomicLong engineCounter = new AtomicLong();
int splitLimit;
int splitCount;
public final long id;
private Function<String, TruffleLogger> loggerFactory;
@CompilationFinal OptionValues engineOptions;
final TruffleSplittingStrategy.SplitStatisticsData splittingStatistics;
@CompilationFinal public StatisticsListener statisticsListener;
@CompilationFinal public boolean splitting;
@CompilationFinal public boolean splittingAllowForcedSplits;
@CompilationFinal public boolean splittingDumpDecisions;
@CompilationFinal public boolean splittingTraceEvents;
@CompilationFinal public boolean traceSplittingSummary;
@CompilationFinal public boolean traceSplits;
@CompilationFinal public int splittingMaxCalleeSize;
@CompilationFinal public int splittingMaxPropagationDepth;
@CompilationFinal public double splittingGrowthLimit;
@CompilationFinal public boolean inlining;
@CompilationFinal public boolean compilation;
@CompilationFinal public boolean compileImmediately;
@CompilationFinal public boolean multiTier;
@CompilationFinal public boolean returnTypeSpeculation;
@CompilationFinal public boolean argumentTypeSpeculation;
@CompilationFinal public boolean traceCompilation;
@CompilationFinal public boolean traceCompilationDetails;
@CompilationFinal public boolean backgroundCompilation;
@CompilationFinal public ExceptionAction compilationFailureAction;
@CompilationFinal public String compileOnly;
@CompilationFinal public boolean callTargetStatistics;
@CompilationFinal public boolean callTargetStatisticDetails;
@CompilationFinal public boolean profilingEnabled;
@CompilationFinal public boolean traceTransferToInterpreter;
@CompilationFinal public int callThresholdInInterpreter;
@CompilationFinal public int callAndLoopThresholdInInterpreter;
@CompilationFinal public int callThresholdInFirstTier;
@CompilationFinal public int callAndLoopThresholdInFirstTier;
private volatile Pair<List<String>, List<String>> parsedCompileOnly;
private Object polyglotEngine;
private volatile Map<Class<?>, Object> engineLocals;
EngineData(OptionValues options, Function<String, TruffleLogger> loggerFactory) {
Objects.requireNonNull(options);
this.id = engineCounter.incrementAndGet();
this.loggerFactory = loggerFactory;
this.loadOptions(options);
this.splittingStatistics = new TruffleSplittingStrategy.SplitStatisticsData();
}
public void preinitializeContext() {
GraalRuntimeAccessor.ENGINE.preinitializeContext(this.polyglotEngine);
}
public void finalizeStore() {
GraalRuntimeAccessor.ENGINE.finalizeStore(this.polyglotEngine);
}
public Object getEngineLock() {
return GraalRuntimeAccessor.ENGINE.getEngineLock(this.polyglotEngine);
}
@SuppressWarnings("unchecked")
public <T> T getEngineLocal(Class<T> symbol) {
Map<Class<?>, Object> data = this.engineLocals;
if (data == null) {
return null;
}
return (T) data.get(symbol);
}
public void clearEngineLocal(Class<?> symbol) {
Map<Class<?>, Object> data = this.engineLocals;
if (data == null) {
return;
}
data.remove(symbol);
}
public <T> void putEngineLocal(Class<T> symbol, T value) {
Map<Class<?>, Object> data = this.engineLocals;
if (data == null) {
synchronized (this) {
data = this.engineLocals;
if (data == null) {
this.engineLocals = data = new ConcurrentHashMap<>();
}
}
}
Object prev = data.putIfAbsent(symbol, symbol.cast(value));
if (prev != null) {
throw new IllegalArgumentException("Cannot set engine local. Key " + symbol + " is already defined.");
}
}
void onEngineCreated(Object engine) {
assert this.polyglotEngine == null;
this.polyglotEngine = engine;
getRuntime().getEngineCacheSupport().onEngineCreated(this);
}
void onEnginePatch(OptionValues newOptions, Function<String, TruffleLogger> newLoggerFactory) {
this.loggerFactory = newLoggerFactory;
loadOptions(newOptions);
getRuntime().getEngineCacheSupport().onEnginePatch(this);
}
public Object getPolyglotEngine() {
return polyglotEngine;
}
boolean onEngineClosing() {
return getRuntime().getEngineCacheSupport().onEngineClosing(this);
}
void onEngineClosed() {
getRuntime().getListener().onEngineClosed(this);
getRuntime().getEngineCacheSupport().onEngineClosed(this);
this.polyglotEngine = null;
}
private void loadOptions(OptionValues options) {
this.engineOptions = options;
this.splitting = options.get(Splitting) && options.get(Mode) != EngineModeEnum.LATENCY;
this.splittingAllowForcedSplits = options.get(SplittingAllowForcedSplits);
this.splittingDumpDecisions = options.get(SplittingDumpDecisions);
this.splittingMaxCalleeSize = options.get(SplittingMaxCalleeSize);
this.splittingMaxPropagationDepth = options.get(SplittingMaxPropagationDepth);
this.splittingTraceEvents = options.get(SplittingTraceEvents);
this.traceSplittingSummary = options.get(TraceSplittingSummary);
this.traceSplits = options.get(TraceSplitting);
this.splittingGrowthLimit = options.get(SplittingGrowthLimit);
this.inlining = options.get(Inlining) && options.get(Mode) != EngineModeEnum.LATENCY;
this.compilation = options.get(Compilation);
this.compileOnly = options.get(CompileOnly);
this.compileImmediately = options.get(CompileImmediately);
this.multiTier = !compileImmediately && options.get(MultiTier);
this.returnTypeSpeculation = options.get(ReturnTypeSpeculation);
this.argumentTypeSpeculation = options.get(ArgumentTypeSpeculation);
this.traceCompilation = options.get(TraceCompilation);
this.traceCompilationDetails = options.get(TraceCompilationDetails);
this.backgroundCompilation = options.get(BackgroundCompilation);
this.callThresholdInInterpreter = computeCallThresholdInInterpreter(options);
this.callAndLoopThresholdInInterpreter = computeCallAndLoopThresholdInInterpreter(options);
this.callThresholdInFirstTier = computeCallThresholdInFirstTier(options);
this.callAndLoopThresholdInFirstTier = computeCallAndLoopThresholdInFirstTier(options);
this.callTargetStatisticDetails = options.get(CompilationStatisticDetails);
this.callTargetStatistics = options.get(CompilationStatistics) || this.callTargetStatisticDetails;
this.statisticsListener = this.callTargetStatistics ? StatisticsListener.createEngineListener(GraalTruffleRuntime.getRuntime()) : null;
this.profilingEnabled = options.get(Profiling);
this.traceTransferToInterpreter = options.get(TraceTransferToInterpreter);
this.compilationFailureAction = computeCompilationFailureAction(options);
validateOptions();
parsedCompileOnly = null;
}
boolean acceptForCompilation(RootNode rootNode) {
if (!compilation) {
return false;
}
Pair<List<String>, List<String>> value = getCompileOnly();
if (value != null) {
String name = rootNode.getName();
List<String> includes = value.getLeft();
boolean included = includes.isEmpty();
if (name != null) {
for (int i = 0; !included && i < includes.size(); i++) {
if (name.contains(includes.get(i))) {
included = true;
}
}
}
if (!included) {
return false;
}
if (name != null) {
for (String exclude : value.getRight()) {
if (name.contains(exclude)) {
return false;
}
}
}
}
return true;
}
private Pair<List<String>, List<String>> getCompileOnly() {
if (compileOnly == null) {
return null;
}
Pair<List<String>, List<String>> result = parsedCompileOnly;
if (result == null) {
List<String> includesList = new ArrayList<>();
List<String> excludesList = new ArrayList<>();
String[] items = compileOnly.split(",");
for (String item : items) {
if (item.startsWith("~")) {
excludesList.add(item.substring(1));
} else {
includesList.add(item);
}
}
result = Pair.create(includesList, excludesList);
parsedCompileOnly = result;
}
return result;
}
public OptionValues getEngineOptions() {
return engineOptions;
}
@SuppressWarnings({"static-method", "unchecked"})
public Collection<OptimizedCallTarget> getCallTargets() {
if (polyglotEngine == null) {
throw new IllegalStateException("No polyglot engine initialized.");
}
return (Collection<OptimizedCallTarget>) GraalRuntimeAccessor.ENGINE.findCallTargets(polyglotEngine);
}
private static ExceptionAction computeCompilationFailureAction(OptionValues options) {
ExceptionAction action = options.get(CompilationFailureAction);
if (action.ordinal() < ExceptionAction.Print.ordinal() && options.get(CompilationExceptionsArePrinted)) {
action = ExceptionAction.Print;
}
if (action.ordinal() < ExceptionAction.Throw.ordinal() && options.get(CompilationExceptionsAreThrown)) {
action = ExceptionAction.Throw;
}
if (action.ordinal() < ExceptionAction.ExitVM.ordinal() && options.get(CompilationExceptionsAreFatal)) {
action = ExceptionAction.ExitVM;
}
if (action.ordinal() < ExceptionAction.ExitVM.ordinal() && !options.get(PerformanceWarningsAreFatal).isEmpty()) {
action = ExceptionAction.ExitVM;
}
return action;
}
private void validateOptions() {
if (compilationFailureAction == ExceptionAction.Throw && backgroundCompilation) {
getEngineLogger().log(Level.WARNING, "The 'Throw' value of the 'engine.CompilationFailureAction' option requires the 'engine.BackgroundCompilation' option to be set to 'false'.");
}
}
private int computeCallThresholdInInterpreter(OptionValues options) {
if (compileImmediately) {
return 0;
}
if (multiTier) {
return Math.min(options.get(FirstTierMinInvokeThreshold), options.get(FirstTierCompilationThreshold));
} else {
return Math.min(options.get(MinInvokeThreshold), options.get(CompilationThreshold));
}
}
private int computeCallAndLoopThresholdInInterpreter(OptionValues options) {
if (compileImmediately) {
return 0;
}
if (multiTier) {
return options.get(FirstTierCompilationThreshold);
} else {
return options.get(CompilationThreshold);
}
}
private int computeCallThresholdInFirstTier(OptionValues options) {
if (compileImmediately) {
return 0;
}
return Math.min(options.get(MinInvokeThreshold), options.get(CompilationThreshold));
}
private int computeCallAndLoopThresholdInFirstTier(OptionValues options) {
if (compileImmediately) {
return 0;
}
return options.get(CompilationThreshold);
}
public TruffleLogger getEngineLogger() {
return getLogger("engine");
}
public TruffleLogger getLogger(String loggerId) {
return loggerFactory.apply(loggerId);
}
@SuppressWarnings("static-method")
public void mergeLoadedSources(Source[] sources) {
GraalRuntimeAccessor.SOURCE.mergeLoadedSources(sources);
}
}