All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.runtime.EngineData Maven / Gradle / Ivy

/*
 * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.runtime;

import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.ArgumentTypeSpeculation;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.BackgroundCompilation;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.Compilation;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.CompilationFailureAction;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.CompilationStatisticDetails;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.CompilationStatistics;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.CompileAOTOnCreate;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.CompileImmediately;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.CompileOnly;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.FirstTierCompilationThreshold;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.FirstTierMinInvokeThreshold;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.LastTierCompilationThreshold;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.MinInvokeThreshold;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.Mode;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.MultiTier;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.PriorityQueue;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.Profiling;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.PropagateLoopCountToLexicalSingleCaller;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.PropagateLoopCountToLexicalSingleCallerMaxDepth;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.ReturnTypeSpeculation;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SingleTierCompilationThreshold;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.Splitting;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingAllowForcedSplits;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingDumpDecisions;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingGrowthLimit;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxCalleeSize;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxPropagationDepth;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingTraceEvents;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilation;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilationDetails;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceDeoptimizeFrame;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceSplitting;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceSplittingSummary;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceTransferToInterpreter;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraversingQueueFirstTierBonus;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraversingQueueFirstTierPriority;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraversingQueueWeightingBothTiers;
import static com.oracle.truffle.runtime.OptimizedTruffleRuntime.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.function.Function;
import java.util.logging.Level;

import org.graalvm.collections.Pair;
import org.graalvm.options.OptionValues;
import org.graalvm.polyglot.SandboxPolicy;

import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.TruffleOptions;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.runtime.OptimizedRuntimeOptions.EngineModeEnum;
import com.oracle.truffle.runtime.OptimizedRuntimeOptions.ExceptionAction;
import com.oracle.truffle.runtime.debug.StatisticsListener;

/**
 * Class used to store data used by the compiler in the Engine. Enables "global" compiler state per
 * engine. One-to-one relationship with a polyglot Engine instance.
 */
public final class EngineData {

    int splitLimit;
    int splitCount;
    public final long id;
    private Function loggerFactory;
    @CompilationFinal OptionValues engineOptions;
    final TruffleSplittingStrategy.SplitStatisticsData splittingStatistics;
    @CompilationFinal public StatisticsListener statisticsListener;

    /*
     * Important while visible, options must not be modified except in loadOptions.
     */
    // splitting options
    @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;

    // compilation options
    @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 boolean traceDeoptimizeFrame;
    @CompilationFinal public boolean compileAOTOnCreate;
    @CompilationFinal public boolean firstTierOnly;

    // compilation queue options
    @CompilationFinal public boolean priorityQueue;
    @CompilationFinal public boolean weightingBothTiers;
    @CompilationFinal public boolean traversingFirstTierPriority;
    @CompilationFinal public double traversingFirstTierBonus;
    @CompilationFinal public boolean propagateCallAndLoopCount;
    @CompilationFinal public int propagateCallAndLoopCountMaxDepth;

    // computed fields.
    @CompilationFinal public int callThresholdInInterpreter;
    @CompilationFinal public int callAndLoopThresholdInInterpreter;
    @CompilationFinal public int callThresholdInFirstTier;
    @CompilationFinal public int callAndLoopThresholdInFirstTier;
    @CompilationFinal public long interpreterCallStackHeadRoom;

    // Cached parsed CompileOnly includes and excludes
    private volatile Pair, List> parsedCompileOnly;
    private Map compilerOptions;

    private volatile Object polyglotEngine;

    /*
     * Extension data for dynamically bound engine extensions.
     */
    private volatile Map, Object> engineLocals;

    EngineData(Object polyglotEngine, OptionValues runtimeOptions, Function loggerFactory, SandboxPolicy sandboxPolicy) {
        Objects.requireNonNull(polyglotEngine);
        Objects.requireNonNull(runtimeOptions);
        this.polyglotEngine = polyglotEngine;
        this.id = OptimizedRuntimeAccessor.ENGINE.getEngineId(polyglotEngine);
        this.loggerFactory = loggerFactory;
        this.loadOptions(runtimeOptions, sandboxPolicy);

        // the splittingStatistics requires options to be initialized
        this.splittingStatistics = new TruffleSplittingStrategy.SplitStatisticsData();
    }

    public static IllegalArgumentException sandboxPolicyException(SandboxPolicy sandboxPolicy, String reason, String fix) {
        Objects.requireNonNull(sandboxPolicy);
        Objects.requireNonNull(reason);
        Objects.requireNonNull(fix);
        String spawnIsolateHelp;
        if (sandboxPolicy.isStricterOrEqual(SandboxPolicy.ISOLATED)) {
            spawnIsolateHelp = " If you switch to a less strict sandbox policy you can still spawn an isolate with an isolated heap using Builder.option(\"engine.SpawnIsolate\",\"true\").";
        } else {
            spawnIsolateHelp = "";
        }
        String message = String.format("The validation for the given sandbox policy %s failed. %s " +
                        "In order to resolve this %s or switch to a less strict sandbox policy using Builder.sandbox(SandboxPolicy).%s", sandboxPolicy, reason, fix, spawnIsolateHelp);
        return new IllegalArgumentException(message);
    }

    public void preinitializeContext() {
        OptimizedRuntimeAccessor.ENGINE.preinitializeContext(this.polyglotEngine);
    }

    public void finalizeStore() {
        OptimizedRuntimeAccessor.ENGINE.finalizeStore(this.polyglotEngine);
    }

    public Object getEngineLock() {
        return OptimizedRuntimeAccessor.ENGINE.getEngineLock(this.polyglotEngine);
    }

    public Object getEngineLogHandler() {
        return OptimizedRuntimeAccessor.ENGINE.getEngineLogHandler(this.polyglotEngine);
    }

    @SuppressWarnings("unchecked")
    public  T getEngineLocal(Class symbol) {
        Map, Object> data = this.engineLocals;
        if (data == null) {
            return null;
        }
        return (T) data.get(symbol);
    }

    public void clearEngineLocal(Class symbol) {
        Map, Object> data = this.engineLocals;
        if (data == null) {
            return;
        }
        data.remove(symbol);
    }

    public  void putEngineLocal(Class symbol, T value) {
        Map, 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 == engine;
        getRuntime().getEngineCacheSupport().onEngineCreated(this);
    }

    void onEnginePatch(OptionValues newRuntimeOptions, Function newLoggerFactory, SandboxPolicy sandboxPolicy) {
        this.loggerFactory = newLoggerFactory;
        loadOptions(newRuntimeOptions, sandboxPolicy);
        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);
        /*
         * The PolyglotEngine must be reset only after listeners are closed. The PolyglotEngine
         * reset disables logging.
         */
        this.polyglotEngine = null;
    }

    private void loadOptions(OptionValues options, SandboxPolicy sandboxPolicy) {
        this.engineOptions = options;

        // splitting 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);

        // compilation options
        this.compilation = options.get(Compilation);
        this.compileOnly = options.get(CompileOnly);
        this.compileImmediately = options.get(CompileImmediately);
        this.multiTier = !compileImmediately && options.get(MultiTier);
        this.compileAOTOnCreate = options.get(CompileAOTOnCreate);
        this.firstTierOnly = options.get(Mode) == EngineModeEnum.LATENCY;
        this.propagateCallAndLoopCount = options.get(PropagateLoopCountToLexicalSingleCaller);
        this.propagateCallAndLoopCountMaxDepth = options.get(PropagateLoopCountToLexicalSingleCallerMaxDepth);

        // compilation queue options
        priorityQueue = options.get(PriorityQueue);
        weightingBothTiers = options.get(TraversingQueueWeightingBothTiers);
        traversingFirstTierPriority = options.get(TraversingQueueFirstTierPriority);
        // See usage of traversingFirstTierBonus for explanation of this formula.
        traversingFirstTierBonus = options.get(TraversingQueueFirstTierBonus) * options.get(LastTierCompilationThreshold) / options.get(FirstTierCompilationThreshold);

        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) && !compileAOTOnCreate;
        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(OptimizedTruffleRuntime.getRuntime()) : null;
        this.profilingEnabled = options.get(Profiling);
        this.traceTransferToInterpreter = options.get(TraceTransferToInterpreter);
        this.traceDeoptimizeFrame = options.get(TraceDeoptimizeFrame);
        this.compilationFailureAction = options.get(CompilationFailureAction);
        validateOptions(sandboxPolicy);
        parsedCompileOnly = null;

        Map compilerOptionValues = OptimizedTruffleRuntime.CompilerOptionsDescriptors.extractOptions(engineOptions);
        updateCompilerOptions(compilerOptionValues);
        this.compilerOptions = compilerOptionValues;
    }

    /**
     * Update compiler options based on runtime options. Note there is no support for compiler
     * options yet.
     */
    private void updateCompilerOptions(Map options) {
        if (compilationFailureAction == ExceptionAction.ExitVM) {
            options.put("compiler.DiagnoseFailure", "true");
        } else if (compilationFailureAction == ExceptionAction.Diagnose) {
            options.put("compiler.DiagnoseFailure", "true");
        } else if (compilationFailureAction == ExceptionAction.Throw) {
            options.put("compiler.DiagnoseFailure", "true");
        }
        if (TruffleOptions.AOT && traceTransferToInterpreter) {
            options.put("compiler.NodeSourcePositions", "true");
        }
        if (callTargetStatistics || callTargetStatisticDetails) {
            options.put("compiler.LogInlinedTargets", "true");
        }
    }

    public Map getCompilerOptions() {
        return compilerOptions;
    }

    /**
     * Checks if a {@link OptimizedCallTarget} should be compiled. The
     * {@link OptimizedRuntimeOptions#Compilation Compilation} and
     * {@link OptimizedRuntimeOptions#CompileOnly CompileOnly} options are used to determine if the
     * calltarget should be compiled.
     */
    boolean acceptForCompilation(OptimizedCallTarget target) {
        if (!compilation) {
            return false;
        }
        Pair, List> value = getCompileOnly();
        if (value != null) {
            String name = target.getName();
            List 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;
    }

    /**
     * Returns the include and exclude sets for the {@link OptimizedRuntimeOptions#CompileOnly
     * CompileOnly} option. The returned value is {@code null} if the {@code CompileOnly} option is
     * not specified. Otherwise the {@link Pair#getLeft() left} value is the include set and the
     * {@link Pair#getRight() right} value is the exclude set.
     */
    private Pair, List> getCompileOnly() {
        if (compileOnly == null) {
            return null;
        }
        Pair, List> result = parsedCompileOnly;
        if (result == null) {
            List includesList = new ArrayList<>();
            List 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 getCallTargets() {
        if (polyglotEngine == null) {
            throw new IllegalStateException("The polyglot engine is closed.");
        }
        return (Collection) OptimizedRuntimeAccessor.ENGINE.findCallTargets(polyglotEngine);
    }

    private void validateOptions(SandboxPolicy sandboxPolicy) {
        if (sandboxPolicy.isStricterOrEqual(SandboxPolicy.CONSTRAINED) && compilationFailureAction != ExceptionAction.Silent && compilationFailureAction != ExceptionAction.Print) {
            throw OptimizedRuntimeAccessor.ENGINE.createPolyglotEngineException(
                            sandboxPolicyException(sandboxPolicy, "The engine.CompilationFailureAction option is set to " + compilationFailureAction.name() + ", but must be set to Silent or Print.",
                                            "use the default value (Silent) by removing Builder.option(\"engine.CompilationFailureAction\", ...) or set it to Print"));
        }
        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(SingleTierCompilationThreshold));
        }
    }

    private int computeCallAndLoopThresholdInInterpreter(OptionValues options) {
        if (compileImmediately) {
            return 0;
        }
        if (multiTier) {
            return options.get(FirstTierCompilationThreshold);
        } else {
            return options.get(SingleTierCompilationThreshold);
        }
    }

    private int computeCallThresholdInFirstTier(OptionValues options) {
        if (compileImmediately) {
            return 0;
        }
        return Math.min(options.get(MinInvokeThreshold), options.get(LastTierCompilationThreshold));
    }

    private int computeCallAndLoopThresholdInFirstTier(OptionValues options) {
        if (compileImmediately) {
            return 0;
        }
        return options.get(LastTierCompilationThreshold);
    }

    public TruffleLogger getEngineLogger() {
        return getLogger("engine");
    }

    public TruffleLogger getLogger(String loggerId) {
        return polyglotEngine != null ? loggerFactory.apply(loggerId) : null;
    }

    @SuppressWarnings("static-method")
    public void mergeLoadedSources(Source[] sources) {
        OptimizedRuntimeAccessor.SOURCE.mergeLoadedSources(sources);
    }

    @SuppressWarnings("static-method")
    public Object enterLanguage(TruffleLanguage language) {
        return OptimizedRuntimeAccessor.ENGINE.enterLanguageFromRuntime(language);
    }

    @SuppressWarnings("static-method")
    public void leaveLanguage(TruffleLanguage language, Object prev) {
        OptimizedRuntimeAccessor.ENGINE.leaveLanguageFromRuntime(language, prev);
    }

    @SuppressWarnings("static-method")
    public TruffleLanguage getLanguage(OptimizedCallTarget target) {
        RootNode root = target.getRootNode();
        return OptimizedRuntimeAccessor.NODES.getLanguage(root);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy