package com.oracle.svm.truffle;
import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsingMaxDepth;
import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createStandardInlineInfo;
import java.lang.annotation.Annotation;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
import org.graalvm.compiler.nodes.spi.Replacements;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.compiler.truffle.common.CompilableTruffleAST;
import org.graalvm.compiler.truffle.common.OptimizedAssumptionDependency;
import org.graalvm.compiler.truffle.compiler.EconomyPartialEvaluatorConfiguration;
import org.graalvm.compiler.truffle.compiler.PartialEvaluator;
import org.graalvm.compiler.truffle.compiler.PartialEvaluatorConfiguration;
import org.graalvm.compiler.truffle.compiler.TruffleCompilerConfiguration;
import org.graalvm.compiler.truffle.compiler.TruffleCompilerImpl;
import org.graalvm.compiler.truffle.compiler.TruffleTierConfiguration;
import org.graalvm.compiler.truffle.compiler.nodes.asserts.NeverPartOfCompilationNode;
import org.graalvm.compiler.truffle.compiler.substitutions.KnownTruffleTypes;
import org.graalvm.compiler.truffle.runtime.BackgroundCompileQueue;
import org.graalvm.compiler.truffle.runtime.OptimizedAssumption;
import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget;
import org.graalvm.compiler.truffle.runtime.OptimizedDirectCallNode;
import org.graalvm.compiler.truffle.runtime.TruffleCallBoundary;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.meta.HostedProviders;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Delete;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.deopt.Deoptimizer;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.HostedOptionValues;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.stack.JavaStackWalker;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.graal.GraalSupport;
import com.oracle.svm.graal.hosted.GraalFeature;
import com.oracle.svm.graal.hosted.GraalFeature.CallTreeNode;
import com.oracle.svm.graal.hosted.GraalFeature.RuntimeBytecodeParser;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl;
import com.oracle.svm.hosted.SVMHost;
import com.oracle.svm.hosted.meta.HostedType;
import com.oracle.svm.hosted.phases.IntrinsifyMethodHandlesInvocationPlugin;
import com.oracle.svm.hosted.phases.ExperimentalNativeImageInlineDuringParsingSupport;
import com.oracle.svm.hosted.snippets.ReflectionPlugins;
import com.oracle.svm.hosted.snippets.SubstrateGraphBuilderPlugins;
import com.oracle.svm.truffle.api.SubstrateOptimizedCallTarget;
import com.oracle.svm.truffle.api.SubstratePartialEvaluator;
import com.oracle.svm.truffle.api.SubstrateTruffleCompiler;
import com.oracle.svm.truffle.api.SubstrateTruffleCompilerImpl;
import com.oracle.svm.truffle.api.SubstrateTruffleRuntime;
import com.oracle.svm.truffle.isolated.IsolateAwareTruffleCompiler;
import com.oracle.svm.truffle.isolated.IsolatedTruffleRuntimeSupport;
import com.oracle.svm.util.ReflectionUtil;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleRuntime;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.impl.DefaultTruffleRuntime;
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.library.DefaultExportProvider;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.GenerateLibrary;
import com.oracle.truffle.api.library.Library;
import com.oracle.truffle.api.library.LibraryExport;
import com.oracle.truffle.api.library.LibraryFactory;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.Layout;
import com.oracle.truffle.api.profiles.Profile;
import com.oracle.truffle.api.utilities.TriState;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
public final class TruffleFeature implements com.oracle.svm.core.graal.GraalFeature {
public static class Options {
@Option(help = "Check that CompilerAsserts.neverPartOfCompilation is not reachable for runtime compilation")
public static final HostedOptionKey<Boolean> TruffleCheckNeverPartOfCompilation = new HostedOptionKey<>(true);
@Option(help = "Enforce that the Truffle runtime provides the only implementation of Frame")
public static final HostedOptionKey<Boolean> TruffleCheckFrameImplementation = new HostedOptionKey<>(true);
@Option(help = "Fail if a method known as not suitable for partial evaluation is reachable for runtime compilation")
public static final HostedOptionKey<Boolean> TruffleCheckBlackListedMethods = new HostedOptionKey<>(false);
@Option(help = "Inline trivial methods in Truffle graphs during native image generation")
public static final HostedOptionKey<Boolean> TruffleInlineDuringParsing = new HostedOptionKey<>(true);
}
private boolean firstAnalysisRun;
public static final class IsEnabled implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return ImageSingletons.contains(TruffleFeature.class);
}
}
public static final class IsCreateProcessDisabled implements BooleanSupplier {
static boolean query() {
try {
Class<?> clazz = Class.forName("com.oracle.truffle.polyglot.PolyglotEngineImpl");
final boolean allowCreateProcess = ReflectionUtil.readField(clazz, "ALLOW_CREATE_PROCESS", null);
return !allowCreateProcess;
} catch (ReflectiveOperationException e) {
throw VMError.shouldNotReachHere(e);
}
}
static final boolean ALLOW_CREATE_PROCESS = query();
@Override
public boolean getAsBoolean() {
return ALLOW_CREATE_PROCESS;
}
}
public static class Support {
public SubstrateOptimizedCallTarget createOptimizedCallTarget(OptimizedCallTarget sourceCallTarget, RootNode rootNode) {
return new SubstrateOptimizedCallTarget(sourceCallTarget, rootNode);
}
public SubstratePartialEvaluator createPartialEvaluator(TruffleCompilerConfiguration config, GraphBuilderConfiguration graphBuilderConfigForRoot) {
return new SubstratePartialEvaluator(config, graphBuilderConfigForRoot);
}
@SuppressWarnings("unused")
public void registerInterpreterEntryMethodsAsCompiled(PartialEvaluator partialEvaluator, BeforeAnalysisAccess access) {
}
public SubstrateTruffleCompiler createTruffleCompiler(SubstrateTruffleRuntime runtime) {
SubstrateTruffleCompiler compiler = createSubstrateTruffleCompilerImpl(runtime, "community");
if (SubstrateOptions.supportCompileInIsolates()) {
compiler = new IsolateAwareTruffleCompiler(compiler);
}
return compiler;
}
protected static SubstrateTruffleCompiler createSubstrateTruffleCompilerImpl(SubstrateTruffleRuntime runtime, String compilerConfigurationName) {
GraalFeature graalFeature = ImageSingletons.lookup(GraalFeature.class);
SnippetReflectionProvider snippetReflectionProvider = graalFeature.getHostedProviders().getSnippetReflection();
final GraphBuilderConfiguration.Plugins graphBuilderPlugins = graalFeature.getHostedProviders().getGraphBuilderPlugins();
final TruffleTierConfiguration firstTier = new TruffleTierConfiguration(new EconomyPartialEvaluatorConfiguration(), GraalSupport.getRuntimeConfig().getBackendForNormalMethod(),
GraalSupport.getFirstTierProviders(), GraalSupport.getFirstTierSuites(), GraalSupport.getFirstTierLirSuites());
PartialEvaluatorConfiguration peConfig = TruffleCompilerImpl.createPartialEvaluatorConfiguration(compilerConfigurationName);
final TruffleTierConfiguration lastTier = new TruffleTierConfiguration(peConfig, GraalSupport.getRuntimeConfig().getBackendForNormalMethod(),
GraalSupport.getRuntimeConfig().getProviders(), GraalSupport.getSuites(), GraalSupport.getLIRSuites());
final TruffleCompilerConfiguration truffleCompilerConfig = new TruffleCompilerConfiguration(runtime, graphBuilderPlugins, snippetReflectionProvider, firstTier, lastTier);
return new SubstrateTruffleCompilerImpl(truffleCompilerConfig);
}
public static boolean isIsolatedCompilation() {
return !SubstrateUtil.HOSTED && SubstrateOptions.shouldCompileInIsolates();
}
public Consumer<OptimizedAssumptionDependency> registerOptimizedAssumptionDependency(JavaConstant optimizedAssumptionConstant) {
if (isIsolatedCompilation()) {
return IsolatedTruffleRuntimeSupport.registerOptimizedAssumptionDependency(optimizedAssumptionConstant);
}
Object target = SubstrateObjectConstant.asObject(optimizedAssumptionConstant);
OptimizedAssumption assumption = (OptimizedAssumption) KnownIntrinsics.convertUnknownValue(target, Object.class);
return assumption.registerDependency();
}
public JavaConstant getCallTargetForCallNode(JavaConstant callNodeConstant) {
if (isIsolatedCompilation()) {
return IsolatedTruffleRuntimeSupport.getCallTargetForCallNode(callNodeConstant);
}
Object target = SubstrateObjectConstant.asObject(callNodeConstant);
OptimizedDirectCallNode callNode = (OptimizedDirectCallNode) KnownIntrinsics.convertUnknownValue(target, Object.class);
OptimizedCallTarget callTarget = callNode.getCallTarget();
return SubstrateObjectConstant.forObject(callTarget);
}
public BackgroundCompileQueue createBackgroundCompileQueue(@SuppressWarnings("unused") SubstrateTruffleRuntime runtime) {
return new BackgroundCompileQueue(runtime);
}
public CompilableTruffleAST asCompilableTruffleAST(JavaConstant constant) {
if (isIsolatedCompilation()) {
return IsolatedTruffleRuntimeSupport.asCompilableTruffleAST(constant);
}
return (CompilableTruffleAST) KnownIntrinsics.convertUnknownValue(SubstrateObjectConstant.asObject(OptimizedCallTarget.class, constant), Object.class);
}
@SuppressWarnings("unused")
public boolean tryLog(SubstrateTruffleRuntime runtime, String loggerId, CompilableTruffleAST compilable, String message) {
if (isIsolatedCompilation()) {
return IsolatedTruffleRuntimeSupport.tryLog(loggerId, compilable, message);
}
return false;
}
public TriState tryIsSuppressedFailure(CompilableTruffleAST compilable, Supplier<String> serializedException) {
if (isIsolatedCompilation()) {
return IsolatedTruffleRuntimeSupport.tryIsSuppressedFailure(compilable, serializedException);
}
return TriState.UNDEFINED;
}
}
private Support support;
private ClassLoader imageClassLoader;
private final Set<ResolvedJavaMethod> blacklistMethods;
private final Set<GraalFeature.CallTreeNode> blacklistViolations;
private final Set<ResolvedJavaMethod> warnMethods;
private final Set<GraalFeature.CallTreeNode> warnViolations;
private final Set<GraalFeature.CallTreeNode> neverPartOfCompilationViolations;
public TruffleFeature() {
blacklistMethods = new HashSet<>();
blacklistViolations = new TreeSet<>(TruffleFeature::blacklistViolationComparator);
warnMethods = new HashSet<>();
warnViolations = new TreeSet<>(TruffleFeature::blacklistViolationComparator);
neverPartOfCompilationViolations = new TreeSet<>(TruffleFeature::blacklistViolationComparator);
}
public static TruffleFeature getSingleton() {
return ImageSingletons.lookup(TruffleFeature.class);
}
public static void setSupport(Support support) {
getSingleton().support = support;
}
public static Support getSupport() {
return getSingleton().support;
}
@Override
public List<Class<? extends Feature>> getRequiredFeatures() {
return Arrays.asList(GraalFeature.class, NodeClassFeature.class);
}
private static void initializeTruffleReflectively(ClassLoader imageClassLoader) {
invokeStaticMethod("com.oracle.truffle.api.impl.Accessor", "getTVMCI", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "initializeNativeImageState", Collections.singletonList(ClassLoader.class), imageClassLoader);
invokeStaticMethod("com.oracle.truffle.polyglot.InstrumentCache", "initializeNativeImageState", Collections.singletonList(ClassLoader.class), imageClassLoader);
invokeStaticMethod("com.oracle.truffle.api.impl.TruffleLocator", "initializeNativeImageState", Collections.emptyList());
}
public static void removeTruffleLanguage(String mimeType) {
invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "removeLanguageFromNativeImage", Collections.singletonList(String.class), mimeType);
}
private static Collection<Class<?>> getLanguageClasses() {
return invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "getLanguageClasses", Collections.emptyList());
}
@SuppressWarnings("unchecked")
private static <T> T invokeStaticMethod(String className, String methodName, Collection<Class<?>> parameterTypes, Object... args) {
try {
Class<?> clazz = Class.forName(className);
Method method = ReflectionUtil.lookupMethod(clazz, methodName, parameterTypes.toArray(new Class<?>[0]));
return (T) method.invoke(null, args);
} catch (ReflectiveOperationException e) {
throw VMError.shouldNotReachHere(e);
}
}
@Override
public void afterRegistration(AfterRegistrationAccess a) {
if (support == null) {
support = new Support();
}
imageClassLoader = a.getApplicationClassLoader();
ImageSingletons.lookup(ExperimentalNativeImageInlineDuringParsingSupport.class).disableNativeImageInlineDuringParsing();
TruffleRuntime runtime = Truffle.getRuntime();
UserError.guarantee(runtime != null, "TruffleRuntime not available via Truffle.getRuntime()");
UserError.guarantee(runtime instanceof SubstrateTruffleRuntime || runtime instanceof DefaultTruffleRuntime,
"Unsupported TruffleRuntime %s (only SubstrateTruffleRuntime or DefaultTruffleRuntime allowed)",
runtime.getClass().getName());
RuntimeClassInitialization.initializeAtBuildTime("com.oracle.graalvm.locator", "Truffle classes are always initialized at build time");
if (useTruffleCompiler()) {
SubstrateTruffleRuntime truffleRuntime = (SubstrateTruffleRuntime) runtime;
truffleRuntime.resetHosted();
}
for (TruffleLanguage.Provider provider : ServiceLoader.load(TruffleLanguage.Provider.class)) {
RuntimeClassInitialization.initializeAtBuildTime(provider.getClass());
}
for (TruffleInstrument.Provider provider : ServiceLoader.load(TruffleInstrument.Provider.class)) {
RuntimeClassInitialization.initializeAtBuildTime(provider.getClass());
}
initializeTruffleReflectively(imageClassLoader);
invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory", "reinitializeNativeImageState", Collections.emptyList());
}
@Override
public void cleanup() {
TruffleRuntime runtime = Truffle.getRuntime();
if (runtime instanceof SubstrateTruffleRuntime) {
((SubstrateTruffleRuntime) runtime).resetNativeImageState();
} else if (!(runtime instanceof DefaultTruffleRuntime)) {
throw VMError.shouldNotReachHere("Only SubstrateTruffleRuntime and DefaultTruffleRuntime supported");
}
invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "resetNativeImageState", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.polyglot.InstrumentCache", "resetNativeImageState", Collections.emptyList());
invokeStaticMethod("org.graalvm.polyglot.Engine$ImplHolder", "resetPreInitializedEngine", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.api.impl.TruffleLocator", "resetNativeImageState", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory", "resetNativeImageState", Collections.singletonList(ClassLoader.class), imageClassLoader);
invokeStaticMethod("com.oracle.truffle.api.nodes.Node", "resetNativeImageState", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.api.source.Source", "resetNativeImageState", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.object.LayoutImpl", "resetNativeImageState", Collections.emptyList());
}
public static boolean useTruffleCompiler() {
return Truffle.getRuntime() instanceof SubstrateTruffleRuntime;
}
@Override
public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, InvocationPlugins invocationPlugins, boolean analysis, boolean hosted) {
Registration r = new Registration(invocationPlugins, Profile.class);
r.register0("isProfilingEnabled", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
boolean constantBoolean;
if (profilingEnabled == null) {
constantBoolean = false;
} else {
constantBoolean = profilingEnabled;
}
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(constantBoolean));
return true;
}
});
if (analysis || hosted) {
r = new Registration(invocationPlugins, CompilerDirectives.class);
SubstrateGraphBuilderPlugins.registerCastExact(r);
}
}
private void registerNeverPartOfCompilation(InvocationPlugins plugins) {
Registration r = new Registration(plugins, CompilerAsserts.class);
r.setAllowOverwrite(true);
r.register0("neverPartOfCompilation", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
return handleNeverPartOfCompilation(b, targetMethod, null);
}
});
r.register1("neverPartOfCompilation", String.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
return handleNeverPartOfCompilation(b, targetMethod, message);
}
});
}
private boolean handleNeverPartOfCompilation(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ValueNode messageNode) {
String message = "CompilerAsserts.neverPartOfCompilation()";
if (messageNode != null && messageNode.isConstant()) {
message = messageNode.asConstant().toValueString();
}
NeverPartOfCompilationNode neverPartOfCompilation = b.add(new NeverPartOfCompilationNode(message));
if (Options.TruffleCheckNeverPartOfCompilation.getValue()) {
if (neverPartOfCompilation.stateAfter().getMethod().getDeclaringClass().equals(targetMethod.getDeclaringClass())) {
} else {
CallTreeNode callerNode = ((RuntimeBytecodeParser) b).getCallTreeNode();
CallTreeNode calleeNode = new CallTreeNode(targetMethod, targetMethod, callerNode, callerNode.getLevel() + 1, GraalFeature.buildSourceReference(neverPartOfCompilation.stateAfter()));
neverPartOfCompilationViolations.add(calleeNode);
}
}
return true;
}
private Boolean profilingEnabled;
@SuppressWarnings("deprecation")
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
BeforeAnalysisAccessImpl config = (BeforeAnalysisAccessImpl) access;
getLanguageClasses().forEach(RuntimeReflection::registerForReflectiveInstantiation);
config.registerHierarchyForReflectiveInstantiation(DefaultExportProvider.class);
config.registerHierarchyForReflectiveInstantiation(TruffleInstrument.class);
registerDynamicObjectFields(config);
config.registerSubtypeReachabilityHandler(TruffleFeature::registerTruffleLibrariesAsInHeap, LibraryFactory.class);
config.registerSubtypeReachabilityHandler(TruffleFeature::registerTruffleLibrariesAsInHeap, LibraryExport.class);
if (useTruffleCompiler()) {
SubstrateTruffleRuntime truffleRuntime = (SubstrateTruffleRuntime) Truffle.getRuntime();
GraalFeature graalFeature = ImageSingletons.lookup(GraalFeature.class);
SnippetReflectionProvider snippetReflection = graalFeature.getHostedProviders().getSnippetReflection();
SubstrateTruffleCompiler truffleCompiler = truffleRuntime.initTruffleCompiler();
truffleRuntime.lookupCallMethods(config.getMetaAccess());
PartialEvaluator partialEvaluator = truffleCompiler.getPartialEvaluator();
registerKnownTruffleFields(config, partialEvaluator.getKnownTruffleTypes());
support.registerInterpreterEntryMethodsAsCompiled(partialEvaluator, access);
GraphBuilderConfiguration graphBuilderConfig = partialEvaluator.getConfigPrototype();
if (Options.TruffleInlineDuringParsing.getValue()) {
graphBuilderConfig.getPlugins().appendInlineInvokePlugin(
new TruffleParsingInlineInvokePlugin(config.getHostVM(), graalFeature.getHostedProviders().getReplacements(), graphBuilderConfig.getPlugins().getInvocationPlugins(),
partialEvaluator, method -> includeCallee(method, null, null)));
}
registerNeverPartOfCompilation(graphBuilderConfig.getPlugins().getInvocationPlugins());
graphBuilderConfig.getPlugins().getInvocationPlugins().closeRegistration();
HostedProviders newHostedProviders = new HostedProviders(
partialEvaluator.getProviders().getMetaAccess(),
partialEvaluator.getProviders().getCodeCache(),
partialEvaluator.getProviders().getConstantReflection(),
new HostedTruffleConstantFieldProvider(partialEvaluator.getProviders().getConstantFieldProvider()),
partialEvaluator.getProviders().getForeignCalls(),
partialEvaluator.getProviders().getLowerer(),
partialEvaluator.getProviders().getReplacements(),
partialEvaluator.getProviders().getStampProvider(),
snippetReflection,
graalFeature.getHostedProviders().getWordTypes(),
graalFeature.getHostedProviders().getPlatformConfigurationProvider(),
graalFeature.getHostedProviders().getMetaAccessExtensionProvider());
newHostedProviders.setGraphBuilderPlugins(graphBuilderConfig.getPlugins());
graalFeature.initializeRuntimeCompilationConfiguration(newHostedProviders, graphBuilderConfig, this::includeCallee, this::deoptimizeOnException);
for (ResolvedJavaMethod method : partialEvaluator.getCompilationRootMethods()) {
graalFeature.prepareMethodForRuntimeCompilation(method, config);
}
initializeMethodBlacklist(config.getMetaAccess());
for (ResolvedJavaMethod method : truffleRuntime.getAnyFrameMethod()) {
graalFeature.requireFrameInformationForMethod(method);
config.registerAsCompiled((AnalysisMethod) method);
}
this.profilingEnabled = Truffle.getRuntime().isProfilingEnabled();
invokeStaticMethod("com.oracle.truffle.polyglot.PolyglotEngineImpl", "resetFallbackEngine", Collections.emptyList());
}
firstAnalysisRun = true;
}
private static void registerTruffleLibrariesAsInHeap(DuringAnalysisAccess access, Class<?> clazz) {
assert access.isReachable(clazz) : clazz;
assert LibraryFactory.class.isAssignableFrom(clazz) || LibraryExport.class.isAssignableFrom(clazz) : clazz;
if (!Modifier.isAbstract(clazz.getModifiers())) {
access.registerAsInHeap(clazz);
}
}
static class TruffleParsingInlineInvokePlugin implements InlineInvokePlugin {
private final SVMHost hostVM;
private final Replacements replacements;
private final InvocationPlugins invocationPlugins;
private final PartialEvaluator partialEvaluator;
private final Predicate<ResolvedJavaMethod> includeMethodPredicate;
TruffleParsingInlineInvokePlugin(SVMHost hostVM, Replacements replacements, InvocationPlugins invocationPlugins, PartialEvaluator partialEvaluator,
Predicate<ResolvedJavaMethod> includeMethodPredicate) {
this.hostVM = hostVM;
this.replacements = replacements;
this.invocationPlugins = invocationPlugins;
this.partialEvaluator = partialEvaluator;
this.includeMethodPredicate = includeMethodPredicate;
}
@Override
public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments) {
if (SubstrateUtil.NativeImageLoadingShield.isNeverInline(original)) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
} else if (invocationPlugins.lookupInvocation(original) != null) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
} else if (original.getAnnotation(ExplodeLoop.class) != null) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
} else if (builder.getMethod().getAnnotation(ExplodeLoop.class) != null) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
} else if (replacements.hasSubstitution(original)) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
}
if (ImageSingletons.lookup(IntrinsifyMethodHandlesInvocationPlugin.IntrinsificationRegistry.class).hasIntrinsifications((AnalysisMethod) original)) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
} else if (ImageSingletons.lookup(ReflectionPlugins.ReflectionPluginRegistry.class).hasIntrinsifications((AnalysisMethod) original)) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
}
for (ResolvedJavaMethod m : partialEvaluator.getNeverInlineMethods()) {
if (original.equals(m)) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
}
}
if (original.getCode() != null && includeMethodPredicate.test(original) && hostVM.isAnalysisTrivialMethod((AnalysisMethod) original) &&
builder.getDepth() < InlineDuringParsingMaxDepth.getValue(HostedOptionValues.singleton())) {
return createStandardInlineInfo(original);
}
return null;
}
}
@Override
public void duringAnalysis(DuringAnalysisAccess access) {
if (firstAnalysisRun) {
firstAnalysisRun = false;
Object keep = invokeStaticMethod("com.oracle.truffle.polyglot.PolyglotContextImpl", "resetSingleContextState", Collections.singleton(boolean.class), false);
invokeStaticMethod("org.graalvm.polyglot.Engine$ImplHolder", "preInitializeEngine", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.polyglot.PolyglotContextImpl", "restoreSingleContextState", Collections.singleton(Object.class), keep);
access.requireAnalysisIteration();
}
for (AnalysisType type : ((DuringAnalysisAccessImpl) access).getBigBang().getUniverse().getTypes()) {
if (!access.isReachable(type.getJavaClass())) {
continue;
}
initializeTruffleLibrariesAtBuildTime(type);
initializeDynamicObjectLayouts(type);
}
}
private static void initializeTruffleLibrariesAtBuildTime(AnalysisType type) {
if (type.isAnnotationPresent(GenerateLibrary.class)) {
LibraryFactory.resolve(type.getJavaClass().asSubclass(Library.class));
}
if (type.getDeclaredAnnotationsByType(ExportLibrary.class).length != 0) {
invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", "lookup", Collections.singleton(Class.class), type.getJavaClass());
}
}
private final Set<Class<?>> dynamicObjectClasses = new HashSet<>();
private void initializeDynamicObjectLayouts(AnalysisType type) {
if (type.isInstantiated()) {
Class<?> javaClass = type.getJavaClass();
if (DynamicObject.class.isAssignableFrom(javaClass) && dynamicObjectClasses.add(javaClass)) {
Layout.newLayout().type(javaClass.asSubclass(DynamicObject.class)).build();
}
}
}
private static void registerKnownTruffleFields(BeforeAnalysisAccessImpl config, KnownTruffleTypes knownTruffleFields) {
for (Class<?> klass = knownTruffleFields.getClass(); klass != Object.class; klass = klass.getSuperclass()) {
for (Field field : klass.getDeclaredFields()) {
if (Modifier.isPublic(field.getModifiers())) {
try {
Object value = field.get(knownTruffleFields);
if (value != null && value instanceof ResolvedJavaField) {
config.registerAsAccessed((AnalysisField) value);
}
} catch (IllegalAccessException ex) {
throw VMError.shouldNotReachHere(ex);
}
}
}
}
}
private static void registerDynamicObjectFields(BeforeAnalysisAccessImpl config) {
Class<?> dynamicFieldClass = config.findClassByName(DynamicObject.class.getName().concat("$DynamicField"));
if (dynamicFieldClass == null) {
throw VMError.shouldNotReachHere("DynamicObject.DynamicField annotation not found.");
}
for (Field field : config.findAnnotatedFields(dynamicFieldClass.asSubclass(Annotation.class))) {
config.registerAsUnsafeAccessed(field);
}
}
private boolean includeCallee(GraalFeature.CallTreeNode calleeNode, List<AnalysisMethod> implementationMethods) {
return includeCallee(calleeNode.getImplementationMethod(), calleeNode, implementationMethods);
}
private boolean includeCallee(ResolvedJavaMethod implementationMethod, GraalFeature.CallTreeNode calleeNode, List<AnalysisMethod> implementationMethods) {
Uninterruptible uninterruptibleAnnotation = implementationMethod.getAnnotation(Uninterruptible.class);
if (implementationMethod.getAnnotation(CompilerDirectives.TruffleBoundary.class) != null) {
return false;
} else if (SubstrateUtil.NativeImageLoadingShield.isNeverInline(implementationMethod)) {
return false;
} else if (uninterruptibleAnnotation != null && !uninterruptibleAnnotation.mayBeInlined()) {
return false;
} else if (implementationMethod.getAnnotation(TruffleCallBoundary.class) != null) {
return false;
} else if (calleeNode != null && implementationMethods.size() > 4 && isBlacklisted(calleeNode.getTargetMethod())) {
blacklistViolations.add(new GraalFeature.CallTreeNode(calleeNode.getTargetMethod(), calleeNode.getTargetMethod(), calleeNode.getParent(), calleeNode.getLevel(),
calleeNode.getSourceReference()));
return false;
} else if (isBlacklisted(implementationMethod)) {
if (calleeNode != null) {
blacklistViolations.add(calleeNode);
}
return false;
} else if (warnMethods.contains(implementationMethod)) {
if (calleeNode != null) {
warnViolations.add(calleeNode);
}
}
return true;
}
private boolean isBlacklisted(ResolvedJavaMethod method) {
if (!((AnalysisMethod) method).allowRuntimeCompilation()) {
return true;
}
if (method.isSynchronized() && method.getName().equals("fillInStackTrace")) {
return true;
}
return blacklistMethods.contains(method);
}
@SuppressWarnings("deprecation")
private boolean deoptimizeOnException(ResolvedJavaMethod method) {
if (method == null) {
return false;
}
CompilerDirectives.TruffleBoundary truffleBoundary = method.getAnnotation(CompilerDirectives.TruffleBoundary.class);
return truffleBoundary != null && truffleBoundary.transferToInterpreterOnException();
}
private void initializeMethodBlacklist(MetaAccessProvider metaAccess) {
blacklistMethod(metaAccess, Object.class, "clone");
blacklistMethod(metaAccess, Object.class, "equals", Object.class);
blacklistMethod(metaAccess, Object.class, "hashCode");
blacklistMethod(metaAccess, Object.class, "toString");
blacklistMethod(metaAccess, String.class, "valueOf", Object.class);
blacklistMethod(metaAccess, String.class, "getBytes");
blacklistMethod(metaAccess, Throwable.class, "initCause", Throwable.class);
blacklistMethod(metaAccess, Throwable.class, "addSuppressed", Throwable.class);
blacklistMethod(metaAccess, System.class, "getProperty", String.class);
blacklistAllMethods(metaAccess, AssertionError.class);
blacklistAllMethods(metaAccess, BigInteger.class);
blacklistAllMethods(metaAccess, BigDecimal.class);
blacklistAllMethods(metaAccess, Comparable.class);
blacklistAllMethods(metaAccess, Comparator.class);
blacklistAllMethods(metaAccess, Collection.class);
blacklistAllMethods(metaAccess, List.class);
blacklistAllMethods(metaAccess, Set.class);
blacklistAllMethods(metaAccess, Map.class);
blacklistAllMethods(metaAccess, Map.Entry.class);
blacklistAllMethods(metaAccess, TreeMap.class);
blacklistAllMethods(metaAccess, HashMap.class);
blacklistAllMethods(metaAccess, ConcurrentHashMap.class);
blacklistAllMethods(metaAccess, WeakHashMap.class);
blacklistAllMethods(metaAccess, IdentityHashMap.class);
blacklistAllMethods(metaAccess, Iterable.class);
blacklistAllMethods(metaAccess, Iterator.class);
blacklistAllMethods(metaAccess, ListIterator.class);
blacklistAllMethods(metaAccess, ReentrantLock.class);
whitelistMethod(metaAccess, BigInteger.class, "signum");
whitelistMethod(metaAccess, ReentrantLock.class, "isLocked");
whitelistMethod(metaAccess, ReentrantLock.class, "isHeldByCurrentThread");
blacklistAllMethods(metaAccess, StringBuffer.class);
blacklistAllMethods(metaAccess, Vector.class);
blacklistAllMethods(metaAccess, Hashtable.class);
warnAllMethods(metaAccess, JavaStackWalker.class);
warnAllMethods(metaAccess, Deoptimizer.class);
warnAllMethods(metaAccess, Heap.getHeap().getClass());
}
private void blacklistAllMethods(MetaAccessProvider metaAccess, Class<?> clazz) {
for (Executable m : clazz.getDeclaredMethods()) {
blacklistMethods.add(metaAccess.lookupJavaMethod(m));
}
for (Executable m : clazz.getDeclaredConstructors()) {
blacklistMethods.add(metaAccess.lookupJavaMethod(m));
}
}
private void blacklistMethod(MetaAccessProvider metaAccess, Class<?> clazz, String name, Class<?>... parameterTypes) {
try {
blacklistMethods.add(metaAccess.lookupJavaMethod(clazz.getDeclaredMethod(name, parameterTypes)));
} catch (NoSuchMethodException ex) {
throw VMError.shouldNotReachHere(ex);
}
}
private void whitelistMethod(MetaAccessProvider metaAccess, Class<?> clazz, String name, Class<?>... parameterTypes) {
try {
if (!blacklistMethods.remove(metaAccess.lookupJavaMethod(clazz.getDeclaredMethod(name, parameterTypes)))) {
throw VMError.shouldNotReachHere();
}
} catch (NoSuchMethodException ex) {
throw VMError.shouldNotReachHere(ex);
}
}
private void warnAllMethods(MetaAccessProvider metaAccess, Class<?> clazz) {
for (Executable m : clazz.getDeclaredMethods()) {
if (m.getAnnotations().length == 0 && !m.getName().startsWith("get") && !m.getName().startsWith("set")) {
warnMethods.add(metaAccess.lookupJavaMethod(m));
}
}
for (Executable m : clazz.getDeclaredConstructors()) {
if (m.getAnnotations().length == 0) {
warnMethods.add(metaAccess.lookupJavaMethod(m));
}
}
}
private static int blacklistViolationComparator(GraalFeature.CallTreeNode n1, GraalFeature.CallTreeNode n2) {
int result = n1.getTargetMethod().getQualifiedName().compareTo(n2.getTargetMethod().getQualifiedName());
if (result == 0) {
result = n1.getSourceReference().compareTo(n2.getSourceReference());
}
return result;
}
@Override
public void beforeCompilation(BeforeCompilationAccess config) {
BeforeCompilationAccessImpl access = (BeforeCompilationAccessImpl) config;
boolean failBlackListViolations = Options.TruffleCheckBlackListedMethods.getValue();
boolean printBlackListViolations = GraalFeature.Options.PrintRuntimeCompileMethods.getValue() || failBlackListViolations;
if (printBlackListViolations && blacklistViolations.size() > 0) {
System.out.println();
System.out.println("=== Found " + blacklistViolations.size() + " compilation blacklist violations ===");
System.out.println();
for (GraalFeature.CallTreeNode node : blacklistViolations) {
System.out.println("Blacklisted method");
System.out.println(node.getImplementationMethod().format(" %H.%n(%p)"));
System.out.println("called from");
for (GraalFeature.CallTreeNode cur = node; cur != null; cur = cur.getParent()) {
System.out.println(" " + cur.getSourceReference());
}
}
if (failBlackListViolations) {
throw VMError.shouldNotReachHere("Blacklisted methods are reachable for runtime compilation");
}
}
if (warnViolations.size() > 0) {
GraalFeature.CallTreeNode printNode = null;
int printLength = Integer.MAX_VALUE;
for (GraalFeature.CallTreeNode warnNode : warnViolations) {
int warnLength = 0;
for (GraalFeature.CallTreeNode cur = warnNode; cur != null; cur = cur.getParent()) {
warnLength++;
}
if (warnLength < printLength) {
printNode = warnNode;
printLength = warnLength;
}
}
System.out.println("WARNING: suspicious method reachable for runtime compilation: " + printNode.getImplementationMethod().format("%H.%n(%p)"));
System.out.println("Check the complete tree of reachable methods using the option " + GraalFeature.Options.PrintRuntimeCompileMethods.getDescriptor().getFieldName());
System.out.println("Suspicious method is called from");
for (GraalFeature.CallTreeNode cur = printNode; cur != null; cur = cur.getParent()) {
System.out.println(" " + cur.getSourceReference());
}
}
if (neverPartOfCompilationViolations.size() > 0) {
System.out.println("ERROR: CompilerAsserts.neverPartOfCompilation reachable for runtime compilation from " + neverPartOfCompilationViolations.size() + " places:");
for (GraalFeature.CallTreeNode neverPartOfCompilationNode : neverPartOfCompilationViolations) {
System.out.println("called from");
for (GraalFeature.CallTreeNode cur = neverPartOfCompilationNode; cur != null; cur = cur.getParent()) {
System.out.println(" " + cur.getSourceReference());
}
}
throw VMError.shouldNotReachHere("CompilerAsserts.neverPartOfCompilation reachable for runtime compilation");
}
if (Options.TruffleCheckFrameImplementation.getValue() && useTruffleCompiler()) {
Optional<? extends ResolvedJavaType> optionalFrameType = access.getMetaAccess().optionalLookupJavaType(Frame.class);
if (optionalFrameType.isPresent()) {
HostedType frameType = (HostedType) optionalFrameType.get();
Set<HostedType> implementations = new HashSet<>();
collectImplementations(frameType, implementations);
if (implementations.size() > 1) {
throw UserError.abort("More than one implementation of %s found. For performance reasons, Truffle languages must not provide new implementations, " +
"and instead only use the single implementation provided by the Truffle runtime. To disable this check, add %s to the native-image command line. Found classes: %s",
Frame.class.getTypeName(), SubstrateOptionsParser.commandArgument(Options.TruffleCheckFrameImplementation, "-"),
implementations.stream().map(m -> m.toJavaName(true)).collect(Collectors.joining(", ")));
} else {
assert implementations.size() == 0 || implementations.iterator().next() == frameType.getSingleImplementor();
}
}
}
}
private static void collectImplementations(HostedType type, Set<HostedType> implementations) {
for (HostedType subType : type.getSubTypes()) {
if (!subType.isAbstract()) {
implementations.add(subType);
}
collectImplementations(subType, implementations);
}
}
}
@TargetClass(className = "org.graalvm.compiler.truffle.runtime.OptimizedCallTarget", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_org_graalvm_compiler_truffle_runtime_OptimizedCallTarget {
@Alias @RecomputeFieldValue(kind = Kind.Reset)
boolean compilationFailed;
@Alias @RecomputeFieldValue(kind = Kind.Reset)
long initializedTimestamp;
}
@TargetClass(className = "com.oracle.truffle.polyglot.PolyglotContextImpl", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_polyglot_PolyglotContextImpl {
@Alias @RecomputeFieldValue(kind = Kind.NewInstance, declClassName = "com.oracle.truffle.polyglot.PolyglotContextImpl$SingleContextState", isFinal = true)
static Target_com_oracle_truffle_polyglot_PolyglotContextImpl_SingleContextState singleContextState;
}
@TargetClass(className = "com.oracle.truffle.polyglot.PolyglotContextThreadLocal", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_polyglot_PolyglotContextThreadLocal {
@Alias @RecomputeFieldValue(kind = Kind.Reset)
Thread activeSingleThread;
}
@TargetClass(className = "com.oracle.truffle.polyglot.PolyglotContextImpl$SingleContextState", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_polyglot_PolyglotContextImpl_SingleContextState {
}
@Delete
@TargetClass(className = "java.lang.ProcessBuilder", onlyWith = {TruffleFeature.IsEnabled.class, TruffleFeature.IsCreateProcessDisabled.class})
final class Target_java_lang_ProcessBuilder {
}
@TargetClass(className = "org.graalvm.compiler.code.ObjdumpDisassemblerProvider", onlyWith = {TruffleFeature.IsEnabled.class, TruffleFeature.IsCreateProcessDisabled.class})
final class Target_org_graalvm_compiler_code_ObjdumpDisassemblerProvider {
@Substitute
@SuppressWarnings("unused")
static Process createProcess(String[] cmd) {
return null;
}
}
@TargetClass(className = "com.oracle.truffle.polyglot.LanguageCache", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_polyglot_LanguageCache {
@Alias @RecomputeFieldValue(kind = Kind.Reset)
private String languageHome;
}
@TargetClass(className = "com.oracle.truffle.object.CoreLocations$DynamicObjectFieldLocation", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_object_CoreLocations_DynamicObjectFieldLocation {
@Alias @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset)
private long offset;
}
@TargetClass(className = "com.oracle.truffle.object.CoreLocations$DynamicLongFieldLocation", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_object_CoreLocations_DynamicLongFieldLocation {
@Alias @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset)
private long offset;
}