
com.oracle.svm.truffle.TruffleFeature Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of svm Show documentation
Show all versions of svm Show documentation
SubstrateVM image builder components
/*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.truffle;
//Checkstyle: allow reflection
import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsingMaxDepth;
import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createStandardInlineInfo;
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.nio.file.spi.FileTypeDetector;
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.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.Predicate;
import java.util.stream.Collectors;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.StructuredGraph;
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.options.OptionKey;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.compiler.truffle.compiler.PartialEvaluator;
import org.graalvm.compiler.truffle.compiler.SharedTruffleCompilerOptions;
import org.graalvm.compiler.truffle.compiler.nodes.asserts.NeverPartOfCompilationNode;
import org.graalvm.compiler.truffle.compiler.substitutions.KnownTruffleTypes;
import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget;
import org.graalvm.compiler.truffle.runtime.OptimizedCompilationProfile;
import org.graalvm.compiler.truffle.runtime.SharedTruffleRuntimeOptions;
import org.graalvm.compiler.truffle.runtime.TruffleCallBoundary;
import org.graalvm.nativeimage.Feature;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.RuntimeReflection;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.HostedProviders;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Delete;
import com.oracle.svm.core.annotate.NeverInline;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.deopt.Deoptimizer;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.jdk.FilesFeature;
import com.oracle.svm.core.jdk.FilesSupport;
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.stack.JavaStackWalker;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
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.AfterRegistrationAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl;
import com.oracle.svm.hosted.code.InliningUtilities;
import com.oracle.svm.hosted.meta.HostedType;
import com.oracle.svm.hosted.option.RuntimeOptionFeature;
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.SubstrateTruffleRuntime;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
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.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.profiles.Profile;
import jdk.vm.ci.code.Architecture;
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 = "Print a warning message and stack trace when CompilerAsserts.neverPartOfCompilation is reachable")//
public static final HostedOptionKey TruffleCheckNeverPartOfCompilation = new HostedOptionKey<>(false);
@Option(help = "Enforce that the Truffle runtime provides the only implementation of Frame")//
public static final HostedOptionKey TruffleCheckFrameImplementation = new HostedOptionKey<>(true);
@Option(help = "Inline trivial methods in Truffle graphs during native image generation")//
public static final HostedOptionKey TruffleInlineDuringParsing = new HostedOptionKey<>(true);
}
/**
* True in the first analysis run where contexts are pre-initialized.
*/
private boolean firstAnalysisRun;
public static final class IsEnabled implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return ImageSingletons.contains(TruffleFeature.class);
}
}
public static class Support {
public SubstrateOptimizedCallTarget createOptimizedCallTarget(OptimizedCallTarget sourceCallTarget, RootNode rootNode) {
return new SubstrateOptimizedCallTarget(sourceCallTarget, rootNode);
}
public SubstratePartialEvaluator createPartialEvaluator(Providers providers, GraphBuilderConfiguration configForRoot, SnippetReflectionProvider snippetReflection, Architecture architecture) {
return new SubstratePartialEvaluator(providers, configForRoot, snippetReflection, architecture);
}
@SuppressWarnings("unused")
public void registerInterpreterEntryMethodsAsCompiled(PartialEvaluator partialEvaluator, BeforeAnalysisAccess access) {
}
}
protected Support support;
private final Set blacklistMethods;
private final Set blacklistViolations;
private final Set warnMethods;
private final Set warnViolations;
private final Set 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> getRequiredFeatures() {
return Arrays.asList(GraalFeature.class, NodeClassFeature.class, FilesFeature.class);
}
private static void initializeTruffleReflectively(ClassLoader imageClassLoader) {
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());
invokeStaticMethod("com.oracle.truffle.api.impl.HomeFinder", "initializeNativeImageState", Collections.emptyList());
}
public static void removeTruffleLanguage(String mimeType) {
invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "removeLanguageFromNativeImage", Collections.singletonList(String.class), mimeType);
}
private static Collection> getLanguageClasses() {
return invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "getLanguageClasses", Collections.emptyList());
}
@SuppressWarnings("unchecked")
private static T invokeStaticMethod(String className, String methodName, Collection> parameterTypes, Object... args) {
try {
// Checkstyle: stop
Method method;
Class> clazz = Class.forName(className);
if (parameterTypes.size() > 0) {
method = clazz.getDeclaredMethod(methodName, parameterTypes.toArray(new Class>[parameterTypes.size()]));
} else {
method = clazz.getDeclaredMethod(methodName);
}
// Checkstyle: resume
method.setAccessible(true);
return (T) method.invoke(null, args);
} catch (Throwable e) {
throw VMError.shouldNotReachHere(e);
}
}
@Override
public void afterRegistration(AfterRegistrationAccess a) {
if (support == null) {
support = new Support();
}
TruffleRuntime runtime = Truffle.getRuntime();
UserError.guarantee(runtime != null, "TruffleRuntime not available via Truffle.getRuntime()");
UserError.guarantee(runtime instanceof SubstrateTruffleRuntime || runtime instanceof DefaultTruffleRuntime,
"Unsupported TruffleRuntime " + runtime.getClass().getName() + " (only SubstrateTruffleRuntime or DefaultTruffleRuntime allowed)");
if (useTruffleCompiler()) {
SubstrateTruffleRuntime truffleRuntime = (SubstrateTruffleRuntime) runtime;
truffleRuntime.resetHosted();
}
/* sun.nio.fs.GnomeFileTypeDetector is currently not supported (GR-4863) */
AfterRegistrationAccessImpl access = (AfterRegistrationAccessImpl) a;
access.findSubclasses(FileTypeDetector.class).stream().filter(detector -> !detector.getClass().getName().equals("sun.nio.fs.GnomeFileTypeDetector")).filter(
detector -> !Modifier.isAbstract(detector.getModifiers())).forEach(this::safeLoadFileDetector);
initializeTruffleReflectively(Thread.currentThread().getContextClassLoader());
}
private void safeLoadFileDetector(Class extends FileTypeDetector> detector) {
try {
ImageSingletons.lookup(FilesSupport.class).addFileTypeDetector(detector.getDeclaredConstructor().newInstance());
} catch (Exception ex) {
throw VMError.shouldNotReachHere(ex);
}
}
@Override
public void cleanup() {
// clean the cached call target nodes to prevent them from keeping application classes alive
TruffleRuntime runtime = Truffle.getRuntime();
if (runtime instanceof SubstrateTruffleRuntime) {
((SubstrateTruffleRuntime) runtime).resetNativeImageState();
} else if (!(runtime instanceof DefaultTruffleRuntime)) {
throw VMError.shouldNotReachHere("Only SubstrateTruffleRuntime and DefaultTruffleRuntime supported");
}
// clean up the language cache
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.interop.Message", "resetNativeImageState", Collections.emptyList());
invokeStaticMethod("com.oracle.truffle.api.impl.HomeFinder", "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) {
/*
* We need to constant-fold Profile.isProfilingEnabled already during static analysis, so
* that we get exact types for fields that store profiles.
*/
Registration r = new Registration(invocationPlugins, Profile.class);
r.register0("isProfilingEnabled", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(Truffle.getRuntime().isProfilingEnabled()));
return true;
}
});
}
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())) {
/* Ignore internal use from another method in CompilerAsserts class. */
} 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;
}
@SuppressWarnings("deprecation")
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
BeforeAnalysisAccessImpl config = (BeforeAnalysisAccessImpl) access;
getLanguageClasses().forEach(RuntimeReflection::registerForReflectiveInstantiation);
config.registerHierarchyForReflectiveInstantiation(TruffleInstrument.class);
config.registerHierarchyForReflectiveInstantiation(com.oracle.truffle.api.instrumentation.InstrumentableFactory.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);
registerTruffleOptions(config);
GraphBuilderConfiguration graphBuilderConfig = partialEvaluator.getConfigForParsing();
if (Options.TruffleInlineDuringParsing.getValue()) {
graphBuilderConfig.getPlugins().appendInlineInvokePlugin(
new TruffleParsingInlineInvokePlugin(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());
newHostedProviders.setGraphBuilderPlugins(graphBuilderConfig.getPlugins());
graalFeature.initializeRuntimeCompilationConfiguration(newHostedProviders, graphBuilderConfig, this::includeCallee, this::deoptimizeOnException);
for (ResolvedJavaMethod method : partialEvaluator.getCompilationRootMethods()) {
graalFeature.prepareMethodForRuntimeCompilation(method, config);
}
initializeMethodBlacklist(config.getMetaAccess());
/*
* Stack frames that are visited by Truffle-level stack walking must have full frame
* information available, otherwise SubstrateStackIntrospection cannot visit them.
*/
for (ResolvedJavaMethod method : truffleRuntime.getAnyFrameMethod()) {
graalFeature.requireFrameInformationForMethod(method);
/*
* To avoid corner case errors, we also force compilation of these methods. This
* only affects builds where no Truffle language is included, because any real
* language makes these methods reachable (and therefore compiled).
*/
config.registerAsCompiled((AnalysisMethod) method);
}
}
firstAnalysisRun = true;
}
/**
* The {@link SharedTruffleRuntimeOptions} are initialized by values assigned to
* {@link SharedTruffleCompilerOptions}. Fields of the latter must be registered as as accessed
* so that the {@link RuntimeOptionFeature} will pick them up to make the options settable at
* runtime.
*/
private static void registerTruffleOptions(BeforeAnalysisAccessImpl config) {
for (Field field : SharedTruffleCompilerOptions.class.getDeclaredFields()) {
if (OptionKey.class.isAssignableFrom(field.getType())) {
config.registerAsAccessed(field);
}
}
}
static class TruffleParsingInlineInvokePlugin implements InlineInvokePlugin {
private final Replacements replacements;
private final InvocationPlugins invocationPlugins;
private final PartialEvaluator partialEvaluator;
private final Predicate includeMethodPredicate;
TruffleParsingInlineInvokePlugin(Replacements replacements, InvocationPlugins invocationPlugins, PartialEvaluator partialEvaluator, Predicate includeMethodPredicate) {
this.replacements = replacements;
this.invocationPlugins = invocationPlugins;
this.partialEvaluator = partialEvaluator;
this.includeMethodPredicate = includeMethodPredicate;
}
@Override
public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments) {
if (original.getAnnotation(NeverInline.class) != null) {
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 (replacements.hasSubstitution(original, builder.bci())) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
}
for (ResolvedJavaMethod m : partialEvaluator.getNeverInlineMethods()) {
if (original.equals(m)) {
return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
}
}
StructuredGraph graph = ((AnalysisMethod) original).getTypeFlow().getGraph();
if (graph != null && original.getCode() != null && includeMethodPredicate.test(original) && InliningUtilities.isTrivialMethod(graph) &&
builder.getDepth() < InlineDuringParsingMaxDepth.getValue(HostedOptionValues.singleton())) {
return createStandardInlineInfo(original);
}
return null;
}
}
@Override
public void duringAnalysis(DuringAnalysisAccess access) {
if (firstAnalysisRun) {
firstAnalysisRun = false;
invokeStaticMethod("org.graalvm.polyglot.Engine$ImplHolder", "preInitializeEngine", Collections.emptyList());
access.requireAnalysisIteration();
}
}
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 boolean includeCallee(GraalFeature.CallTreeNode calleeNode, List implementationMethods) {
return includeCallee(calleeNode.getImplementationMethod(), calleeNode, implementationMethods);
}
private boolean includeCallee(ResolvedJavaMethod implementationMethod, GraalFeature.CallTreeNode calleeNode, List implementationMethods) {
if (implementationMethod.getAnnotation(CompilerDirectives.TruffleBoundary.class) != null) {
return false;
} else if (implementationMethod.getAnnotation(NeverInline.class) != null) {
/* Ensure that NeverInline methods are also never inlined during Truffle compilation. */
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 (method.isSynchronized() && method.getName().equals("fillInStackTrace")) {
/*
* We do not want anything related to Throwable.fillInStackTrace in the image. For
* simplicity, we just check the method name and not the declaring class too, but it is
* unlikely that some non-exception method is called "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.throwsControlFlowException() && 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, System.class, "getProperty", String.class);
blacklistAllMethods(metaAccess, AssertionError.class);
blacklistAllMethods(metaAccess, BigInteger.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);
/* Methods with synchronization are currently not supported as deoptimization targets. */
blacklistAllMethods(metaAccess, StringBuffer.class);
blacklistAllMethods(metaAccess, Vector.class);
blacklistAllMethods(metaAccess, Hashtable.class);
/*
* Core Substrate VM classes that very certainly should not be reachable for runtime
* compilation. Warn when they get reachable to detect explosion of reachable methods.
*/
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 warnAllMethods(MetaAccessProvider metaAccess, Class> clazz) {
for (Executable m : clazz.getDeclaredMethods()) {
/*
* Filter out methods that are, e.g., only present on a certain platform. We do not need
* all methods in the warning list, just enough to trigger the warnings. Accessors are
* generally allowed too.
*/
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().format("%H.%n(%p)").compareTo(n2.getTargetMethod().format("%H.%n(%p)"));
if (result == 0) {
result = n1.getSourceReference().compareTo(n2.getSourceReference());
}
return result;
}
@Override
public void beforeCompilation(BeforeCompilationAccess config) {
BeforeCompilationAccessImpl access = (BeforeCompilationAccessImpl) config;
if (GraalFeature.Options.PrintRuntimeCompileMethods.getValue() && 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 (warnViolations.size() > 0) {
/*
* It is enough to print one warning message with one stack trace. Take the shortest
* stack trace.
*/
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()) {
/*
* Check that only one Frame implementation is seen as instantiated by the static
* analysis. That allows de-virtualization of all calls to Frame methods in the
* interpreter.
*
* The DefaultTruffleRuntime uses multiple Frame implementations (DefaultVirtualFrame,
* DefaultMaterializedFrame, ReadOnlyFrame) to detect wrong usages of the Frame API, so
* we can only check when running with compilation enabled.
*/
Optional extends ResolvedJavaType> optionalFrameType = access.getMetaAccess().optionalLookupJavaType(Frame.class);
if (optionalFrameType.isPresent()) {
HostedType frameType = (HostedType) optionalFrameType.get();
Set implementations = new HashSet<>();
collectImplementations(frameType, implementations);
if (implementations.size() > 1) {
throw UserError.abort("More than one implementation of " + Frame.class.getTypeName() +
" 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 " + SubstrateOptionsParser.commandArgument(Options.TruffleCheckFrameImplementation, "-") + " to the native-image command line. " +
"Found classes: " + 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 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 {
/**
* Truffle code can run during image generation. Discard the profiling information collected and
* start with a fresh profile at run time.
*/
@Alias @RecomputeFieldValue(kind = Kind.Reset) //
OptimizedCompilationProfile compilationProfile;
}
// Checkstyle: stop
@TargetClass(className = "com.oracle.truffle.polyglot.PolyglotContextImpl", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_polyglot_PolyglotContextImpl {
/**
* Truffle code can run during image generation, i.e., one or many contexts can be used during
* image generation. Truffle optimizes the case where only one context is ever created, and also
* stores additional information regarding which thread or threads used the context. We need to
* start with a completely fresh specialization state. To simplify that, all static state that
* stores context information is abstracted in the SingleContextState class, and it is enough to
* recompute a single static field to a new SingleContextState instance.
*/
@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.PolyglotContextImpl$SingleContextState", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_polyglot_PolyglotContextImpl_SingleContextState {
}
/*
* Java interoperability cannot be supported on Substrate VM. Ensure that the nodes are not used by
* marking the node classes as deleted.
*/
@Delete
@TargetClass(className = "com.oracle.truffle.polyglot.HostFunction", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_api_interop_java_JavaFunctionObject {
}
@Delete
@TargetClass(className = "com.oracle.truffle.polyglot.HostInteropReflect", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_api_interop_java_JavaInteropReflect {
}
@Delete
@TargetClass(className = "com.oracle.truffle.polyglot.FunctionProxyHandler", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_api_interop_java_FunctionProxyHandler {
}
@Delete
@TargetClass(className = "com.oracle.truffle.polyglot.ObjectProxyHandler", onlyWith = TruffleFeature.IsEnabled.class)
final class Target_com_oracle_truffle_api_interop_java_ObjectProxyHandler {
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy