backendProvider = TruffleRuntimeCompilationSupport.getRuntimeBackendProvider();
- ClassInitializationSupport classInitializationSupport = config.getHostVM().getClassInitializationSupport();
- Providers originalProviders = GraalAccess.getOriginalProviders();
- SubstratePlatformConfigurationProvider platformConfig = new SubstratePlatformConfigurationProvider(ImageSingletons.lookup(BarrierSetProvider.class).createBarrierSet(config.getMetaAccess()));
- RuntimeConfiguration runtimeConfig = ImageSingletons.lookup(SubstrateGraalCompilerSetup.class)
- .createRuntimeConfigurationBuilder(RuntimeOptionValues.singleton(), config.getHostVM(), config.getUniverse(), config.getMetaAccess(),
- backendProvider, classInitializationSupport, originalProviders.getLoopsDataProvider(), platformConfig, config.getBigBang().getSnippetReflectionProvider())
- .build();
-
- Providers runtimeProviders = runtimeConfig.getProviders();
- hostedProviders = new HostedProviders(runtimeProviders.getMetaAccess(), runtimeProviders.getCodeCache(), runtimeProviders.getConstantReflection(), runtimeProviders.getConstantFieldProvider(),
- runtimeProviders.getForeignCalls(), runtimeProviders.getLowerer(), runtimeProviders.getReplacements(), runtimeProviders.getStampProvider(),
- runtimeConfig.getSnippetReflection(), runtimeProviders.getWordTypes(), runtimeProviders.getPlatformConfigurationProvider(), new GraphPrepareMetaAccessExtensionProvider(),
- runtimeProviders.getLoopsDataProvider());
-
- FeatureHandler featureHandler = config.getFeatureHandler();
- final boolean supportsStubBasedPlugins = !SubstrateOptions.useLLVMBackend();
-
- NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, runtimeConfig, hostedProviders, config.getMetaAccess(), config.getUniverse(), null, config.getNativeLibraries(),
- config.getImageClassLoader(), ParsingReason.JITCompilation, ((Inflation) config.getBigBang()).getAnnotationSubstitutionProcessor(),
- new SubstrateClassInitializationPlugin(config.getHostVM()), ConfigurationValues.getTarget(), supportsStubBasedPlugins);
-
- NativeImageGenerator.registerReplacements(DebugContext.forCurrentThread(), featureHandler, runtimeConfig, runtimeConfig.getProviders(), false, true,
- new RuntimeCompilationGraphEncoder(ConfigurationValues.getTarget().arch, config.getUniverse().getHeapScanner()));
-
- featureHandler.forEachGraalFeature(feature -> feature.registerCodeObserver(runtimeConfig));
- Suites suites = NativeImageGenerator.createSuites(featureHandler, runtimeConfig, runtimeConfig.getSnippetReflection(), false);
- LIRSuites lirSuites = NativeImageGenerator.createLIRSuites(featureHandler, runtimeConfig.getProviders(), false);
- Suites firstTierSuites = NativeImageGenerator.createFirstTierSuites(featureHandler, runtimeConfig, runtimeConfig.getSnippetReflection(), false);
- LIRSuites firstTierLirSuites = NativeImageGenerator.createFirstTierLIRSuites(featureHandler, runtimeConfig.getProviders(), false);
-
- TruffleRuntimeCompilationSupport.setRuntimeConfig(runtimeConfig, suites, lirSuites, firstTierSuites, firstTierLirSuites);
- }
-
- /**
- * A graph encoder that unwraps the {@link ImageHeapConstant} objects. This is used both after
- * analysis and after compilation. The corresponding graph decoder used during AOT compilation,
- * {@link RuntimeCompilationGraphDecoder}, looks-up the constant in the shadow heap and re-wraps
- * it.
- *
- * The reason why we need to unwrap the {@link ImageHeapConstant}s after analysis, and not only
- * when we finally encode the graphs for run time compilation, is because the values in
- * {@link GraphEncoder#objectsArray} are captured in GraalSupport#graphObjects and
- * SubstrateReplacements#snippetObjects which are then scanned.
- */
- public static class RuntimeCompilationGraphEncoder extends GraphEncoder {
-
- private final ImageHeapScanner heapScanner;
- /**
- * Cache already converted location identity objects to avoid creating multiple new
- * instances for the same underlying location identity.
- */
- private final Map locationIdentityCache;
-
- public RuntimeCompilationGraphEncoder(Architecture architecture, ImageHeapScanner heapScanner) {
- super(architecture);
- this.heapScanner = heapScanner;
- this.locationIdentityCache = new ConcurrentHashMap<>();
- }
-
- @Override
- protected void addObject(Object object) {
- super.addObject(unwrap(object));
- }
-
- @Override
- protected void writeObjectId(Object object) {
- super.writeObjectId(unwrap(object));
- }
-
- @Override
- protected GraphDecoder graphDecoderForVerification(StructuredGraph decodedGraph) {
- return new RuntimeCompilationGraphDecoder(architecture, decodedGraph, heapScanner);
- }
-
- private Object unwrap(Object object) {
- if (object instanceof ImageHeapConstant ihc) {
- VMError.guarantee(ihc.getHostedObject() != null);
- return ihc.getHostedObject();
- } else if (object instanceof ObjectLocationIdentity oli && oli.getObject() instanceof ImageHeapConstant heapConstant) {
- return locationIdentityCache.computeIfAbsent(heapConstant, (hc) -> ObjectLocationIdentity.create(hc.getHostedObject()));
- }
- return object;
- }
- }
-
- static class RuntimeCompilationGraphDecoder extends GraphDecoder {
-
- private final ImageHeapScanner heapScanner;
- /**
- * Cache already converted location identity objects to avoid creating multiple new
- * instances for the same underlying location identity.
- */
- private final Map locationIdentityCache;
-
- RuntimeCompilationGraphDecoder(Architecture architecture, StructuredGraph graph, ImageHeapScanner heapScanner) {
- super(architecture, graph);
- this.heapScanner = heapScanner;
- this.locationIdentityCache = new ConcurrentHashMap<>();
- }
-
- @Override
- protected Object readObject(MethodScope methodScope) {
- Object object = super.readObject(methodScope);
- if (object instanceof JavaConstant constant) {
- return heapScanner.getImageHeapConstant(constant);
- } else if (object instanceof ObjectLocationIdentity oli) {
- return locationIdentityCache.computeIfAbsent(oli.getObject(), (obj) -> ObjectLocationIdentity.create(heapScanner.getImageHeapConstant(obj)));
- }
- return object;
- }
- }
-
- protected final void beforeAnalysisHelper(BeforeAnalysisAccess c) {
-
- BeforeAnalysisAccessImpl config = (BeforeAnalysisAccessImpl) c;
- installRuntimeConfig(config);
-
- SubstrateGraalRuntime graalRuntime = new SubstrateGraalRuntime();
- objectReplacer.setGraalRuntime(graalRuntime);
- objectReplacer.setAnalysisAccess(config);
- ImageSingletons.add(GraalRuntime.class, graalRuntime);
- RuntimeSupport.getRuntimeSupport().addShutdownHook(new GraalSupport.GraalShutdownHook());
-
- /* Initialize configuration with reasonable default values. */
- graphBuilderConfig = GraphBuilderConfiguration.getDefault(hostedProviders.getGraphBuilderPlugins()).withBytecodeExceptionMode(BytecodeExceptionMode.ExplicitOnly);
- runtimeCompilationCandidatePredicate = RuntimeCompilationFeature::defaultAllowRuntimeCompilation;
- optimisticOpts = OptimisticOptimizations.ALL.remove(OptimisticOptimizations.Optimization.UseLoopLimitChecks);
- graphEncoder = new RuntimeCompilationGraphEncoder(ConfigurationValues.getTarget().arch, config.getUniverse().getHeapScanner());
-
- /*
- * Ensure all snippet types are registered as used.
- */
- SubstrateReplacements replacements = (SubstrateReplacements) TruffleRuntimeCompilationSupport.getRuntimeConfig().getProviders().getReplacements();
- for (NodeClass> nodeClass : replacements.getSnippetNodeClasses()) {
- config.getMetaAccess().lookupJavaType(nodeClass.getClazz()).registerAsAllocated("All " + NodeClass.class.getName() + " classes are marked as instantiated eagerly.");
- }
- /*
- * Ensure runtime snippet graphs are analyzed.
- */
- NativeImageGenerator.performSnippetGraphAnalysis(config.getBigBang(), replacements, config.getBigBang().getOptions());
-
- /*
- * Ensure that all snippet methods have their SubstrateMethod object created by the object
- * replacer, to avoid corner cases later when writing the native image.
- */
- for (ResolvedJavaMethod method : replacements.getSnippetMethods()) {
- objectReplacer.apply(method);
- }
- }
-
- @SuppressWarnings("unused")
- private static boolean defaultAllowRuntimeCompilation(ResolvedJavaMethod method) {
- return false;
- }
-
- public void initializeRuntimeCompilationForTesting(FeatureImpl.BeforeAnalysisAccessImpl config, RuntimeCompilationCandidatePredicate newRuntimeCompilationCandidatePredicate) {
- initializeRuntimeCompilationConfiguration(hostedProviders, graphBuilderConfig, newRuntimeCompilationCandidatePredicate, deoptimizeOnExceptionPredicate);
- initializeRuntimeCompilationForTesting(config);
- }
-
- public void initializeRuntimeCompilationForTesting(BeforeAnalysisAccessImpl config) {
- initializeAnalysisProviders(config.getBigBang(), provider -> provider);
- }
-
- public void initializeRuntimeCompilationConfiguration(HostedProviders newHostedProviders, GraphBuilderConfiguration newGraphBuilderConfig,
- RuntimeCompilationCandidatePredicate newRuntimeCompilationCandidatePredicate,
- Predicate newDeoptimizeOnExceptionPredicate) {
- guarantee(initialized == false, "runtime compilation configuration already initialized");
- initialized = true;
-
- hostedProviders = newHostedProviders;
- graphBuilderConfig = newGraphBuilderConfig.withNodeSourcePosition(true);
- assert !runtimeCompilationCandidatePredicateUpdated : "Updated compilation predicate multiple times";
- runtimeCompilationCandidatePredicate = newRuntimeCompilationCandidatePredicate;
- runtimeCompilationCandidatePredicateUpdated = true;
- deoptimizeOnExceptionPredicate = newDeoptimizeOnExceptionPredicate;
- }
-
- public SubstrateMethod requireFrameInformationForMethod(ResolvedJavaMethod method, BeforeAnalysisAccessImpl config, boolean registerAsRoot) {
- AnalysisMethod aMethod = (AnalysisMethod) method;
- SubstrateMethod sMethod = objectReplacer.createMethod(aMethod);
-
- requireFrameInformationForMethodHelper(aMethod, config, registerAsRoot);
-
- return sMethod;
- }
-
- protected abstract void requireFrameInformationForMethodHelper(AnalysisMethod aMethod, BeforeAnalysisAccessImpl config, boolean registerAsRoot);
-
- public SubstrateMethod prepareMethodForRuntimeCompilation(Executable method, BeforeAnalysisAccessImpl config) {
- return prepareMethodForRuntimeCompilation(config.getMetaAccess().lookupJavaMethod(method), config);
- }
-
- public abstract void initializeAnalysisProviders(BigBang bb, Function generator);
-
- public abstract void registerAllowInliningPredicate(AllowInliningPredicate predicate);
-
- public abstract SubstrateMethod prepareMethodForRuntimeCompilation(ResolvedJavaMethod method, BeforeAnalysisAccessImpl config);
-
- protected final void afterAnalysisHelper() {
- ProgressReporter.singleton().setNumRuntimeCompiledMethods(getRuntimeCompiledMethods().size());
- }
-
- /**
- * Checks if any illegal nodes are present within the graph. Runtime Compiled methods should
- * never have explicit BytecodeExceptions; instead they should have deoptimizations.
- */
- protected static boolean verifyNodes(StructuredGraph graph) {
- for (var node : graph.getNodes()) {
- boolean invalidNodeKind = node instanceof BytecodeExceptionNode || node instanceof ThrowBytecodeExceptionNode;
- assert !invalidNodeKind : "illegal node in graph: " + node + " method: " + graph.method();
- }
- return true;
- }
-
- protected final void beforeCompilationHelper() {
- if (Options.PrintRuntimeCompileMethods.getValue()) {
- System.out.println("****Start Print Runtime Compile Methods***");
- getRuntimeCompiledMethods().stream().map(m -> m.getMethod().format("%H.%n(%p)")).sorted().collect(Collectors.toList()).forEach(System.out::println);
- System.out.println("****End Print Runtime Compile Methods***");
- }
-
- if (Options.PrintRuntimeCompilationCallTree.getValue()) {
- System.out.println("****Start Print Runtime Compile Call Tree***");
- printCallTree();
- System.out.println("****End Print Runtime Compile Call Tree***");
- }
-
- int maxMethods = 0;
- for (String value : Options.MaxRuntimeCompileMethods.getValue().values()) {
- String numberStr = null;
- try {
- /* Strip optional comment string from MaxRuntimeCompileMethods value */
- numberStr = value.split("#")[0];
- maxMethods += Integer.parseInt(numberStr);
- } catch (NumberFormatException ex) {
- throw UserError.abort("Invalid value for option 'MaxRuntimeCompileMethods': '%s' is not a valid number", numberStr);
- }
- }
- if (Options.EnforceMaxRuntimeCompileMethods.getValue() && maxMethods != 0 && getRuntimeCompiledMethods().size() > maxMethods) {
- printDeepestLevelPath();
- throw VMError.shouldNotReachHere("Number of methods for runtime compilation exceeds the allowed limit: " + getRuntimeCompiledMethods().size() + " > " + maxMethods);
- }
- }
-
- private void printDeepestLevelPath() {
- AbstractCallTreeNode maxLevelCallTreeNode = null;
- for (var method : getRuntimeCompiledMethods()) {
- var callTreeNode = getCallTreeNode(method);
- if (maxLevelCallTreeNode == null || maxLevelCallTreeNode.getLevel() < callTreeNode.getLevel()) {
- maxLevelCallTreeNode = callTreeNode;
- }
- }
-
- System.out.println(String.format("Deepest level call tree path (%d calls):", maxLevelCallTreeNode.getLevel()));
- AbstractCallTreeNode node = maxLevelCallTreeNode;
- while (node != null) {
- AnalysisMethod implementationMethod = node.getImplementationMethod();
- AnalysisMethod targetMethod = node.getTargetMethod();
-
- System.out.format("%5d ; %s ; %s", node.getNodeCount(), node.getPosition(), implementationMethod == null ? ""
- : implementationMethod.format("%H.%n(%p)"));
- if (targetMethod != null && !targetMethod.equals(implementationMethod)) {
- System.out.print(" ; " + targetMethod.format("%H.%n(%p)"));
- }
- System.out.println();
- node = node.getParent();
- }
- }
-
- private void printCallTree() {
- System.out.println("depth;method;Graal nodes;invoked from source;full method name;full name of invoked virtual method");
- for (var method : getRuntimeCompiledMethods()) {
- var node = getCallTreeNode(method);
- if (node.getLevel() == 0) {
- printCallTreeNode(node);
- }
- }
- }
-
- private void printCallTreeNode(AbstractCallTreeNode node) {
- AnalysisMethod implementationMethod = node.getImplementationMethod();
- AnalysisMethod targetMethod = node.getTargetMethod();
-
- StringBuilder indent = new StringBuilder();
- for (int i = 0; i < node.getLevel(); i++) {
- indent.append(" ");
- }
- if (implementationMethod != null) {
- indent.append(implementationMethod.format("%h.%n"));
- }
-
- System.out.format("%4d ; %-80s ;%5d ; %s ; %s", node.getLevel(), indent, node.getNodeCount(), node.getPosition(),
- implementationMethod == null ? "" : implementationMethod.format("%H.%n(%p)"));
- if (targetMethod != null && !targetMethod.equals(implementationMethod)) {
- System.out.print(" ; " + targetMethod.format("%H.%n(%p)"));
- }
- System.out.println();
-
- for (AbstractCallTreeNode child : node.getChildren()) {
- printCallTreeNode(child);
- }
- }
-
- protected final void afterCompilationHelper(AfterCompilationAccess a) {
- CompilationAccessImpl config = (CompilationAccessImpl) a;
-
- HostedMetaAccess hMetaAccess = config.getMetaAccess();
- HostedUniverse hUniverse = hMetaAccess.getUniverse();
- objectReplacer.updateSubstrateDataAfterCompilation(hUniverse, config.getProviders());
- }
-
- protected final void beforeHeapLayoutHelper(BeforeHeapLayoutAccess a) {
- objectReplacer.registerImmutableObjects(a);
- TruffleRuntimeCompilationSupport.registerImmutableObjects(a);
- ((SubstrateReplacements) TruffleRuntimeCompilationSupport.getRuntimeConfig().getProviders().getReplacements()).registerImmutableObjects(a);
- }
-
- protected final void afterHeapLayoutHelper(AfterHeapLayoutAccess a) {
- AfterHeapLayoutAccessImpl config = (AfterHeapLayoutAccessImpl) a;
- HostedMetaAccess hMetaAccess = config.getMetaAccess();
- HostedUniverse hUniverse = hMetaAccess.getUniverse();
- objectReplacer.updateSubstrateDataAfterHeapLayout(hUniverse);
- }
-}
-
-/**
- * Same behavior as {@link SubstrateMetaAccessExtensionProvider}, but operating on
- * {@link AnalysisType} instead of {@link SharedType} since parsing of graphs for runtime
- * compilation happens in the Analysis universe.
- */
-class GraphPrepareMetaAccessExtensionProvider implements MetaAccessExtensionProvider {
-
- @Override
- public JavaKind getStorageKind(JavaType type) {
- return ((AnalysisType) type).getStorageKind();
- }
-
- @Override
- public boolean canConstantFoldDynamicAllocation(ResolvedJavaType type) {
- assert type instanceof AnalysisType : "AnalysisType is required; AnalysisType lazily creates array types of any depth, so type cannot be null";
- return ((AnalysisType) type).isInstantiated();
- }
-
- @Override
- public boolean isGuaranteedSafepoint(ResolvedJavaMethod method, boolean isDirect) {
- throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport
- }
-
- @Override
- public boolean canVirtualize(ResolvedJavaType instanceType) {
- return true;
- }
-}
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/CallTreeInfo.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/CallTreeInfo.java
new file mode 100644
index 000000000000..0ed531bea9bb
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/CallTreeInfo.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2023, 2023, 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.graal.hosted.runtimecompilation;
+
+import static com.oracle.svm.common.meta.MultiMethod.ORIGINAL_METHOD;
+import static com.oracle.svm.hosted.code.SubstrateCompilationDirectives.RUNTIME_COMPILED_METHOD;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.oracle.graal.pointsto.meta.AnalysisMethod;
+import com.oracle.graal.pointsto.meta.AnalysisUniverse;
+import com.oracle.graal.pointsto.meta.InvokeInfo;
+import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.hosted.code.SubstrateCompilationDirectives;
+
+import jdk.vm.ci.code.BytecodeFrame;
+import jdk.vm.ci.code.BytecodePosition;
+
+public final class CallTreeInfo {
+ private final Map runtimeCompilations;
+ private Map runtimeCandidateMap;
+ private Map analysisMethodMap;
+ private boolean callTreeInitialized = false;
+ private boolean callTraceInitialized = false;
+
+ private CallTreeInfo(Map runtimeCompilations) {
+ this.runtimeCompilations = runtimeCompilations;
+ }
+
+ public Collection runtimeCompilations() {
+ return runtimeCompilations.values();
+ }
+
+ public static CallTreeInfo create(AnalysisUniverse aUniverse, Map invalidForRuntimeCompilation) {
+ Map runtimeCompilations = new HashMap<>();
+ for (var method : aUniverse.getMethods()) {
+ var rMethod = method.getMultiMethod(RUNTIME_COMPILED_METHOD);
+ if (rMethod != null && rMethod.isReachable() && !invalidForRuntimeCompilation.containsKey(rMethod)) {
+ var origInlinedMethods = rMethod.getAnalyzedGraph().getInlinedMethods().stream().map(inlinedMethod -> {
+ AnalysisMethod orig = ((AnalysisMethod) inlinedMethod).getMultiMethod(ORIGINAL_METHOD);
+ assert orig != null;
+ return orig;
+ }).collect(Collectors.toUnmodifiableSet());
+ var previous = runtimeCompilations.put(rMethod, new RuntimeCompiledMethod(rMethod, origInlinedMethods));
+ assert previous == null : previous;
+ }
+ }
+
+ return new CallTreeInfo(runtimeCompilations);
+ }
+
+ private void initializeCallTraceInfo() {
+ if (callTraceInitialized) {
+ return;
+ }
+
+ callTraceInitialized = true;
+ analysisMethodMap = new HashMap<>();
+ runtimeCandidateMap = new HashMap<>();
+
+ for (var runtimeCompilation : runtimeCompilations()) {
+ AnalysisMethod method = runtimeCompilation.runtimeMethod;
+ MethodNode callerMethodNode = analysisMethodMap.computeIfAbsent(method, MethodNode::new);
+
+ for (InvokeInfo invokeInfo : method.getInvokes()) {
+ AnalysisMethod invokeTarget = invokeInfo.getTargetMethod();
+ boolean deoptInvokeTypeFlow = invokeInfo.isDeoptInvokeTypeFlow();
+ if (deoptInvokeTypeFlow) {
+ assert SubstrateCompilationDirectives.isRuntimeCompiledMethod(invokeTarget);
+ invokeTarget = invokeTarget.getMultiMethod(ORIGINAL_METHOD);
+ }
+ assert invokeTarget.isOriginalMethod();
+ for (AnalysisMethod callee : invokeInfo.getAllCallees()) {
+ /*
+ * Special handling is needed for deoptInvokeTypeFlows because they only have
+ * the deopt method variant as a callee.
+ */
+ if (deoptInvokeTypeFlow || SubstrateCompilationDirectives.isRuntimeCompiledMethod(callee)) {
+ MethodNode calleeMethodNode = analysisMethodMap.computeIfAbsent(callee, MethodNode::new);
+ InvokeNode invoke = new InvokeNode(callerMethodNode, invokeInfo.getPosition());
+ calleeMethodNode.addCaller(invoke);
+
+ var origCallee = callee.getMultiMethod(ORIGINAL_METHOD);
+ assert origCallee != null;
+ runtimeCandidateMap.putIfAbsent(new RuntimeCompilationCandidate(origCallee, invokeTarget), invoke);
+ } else if (callee.isOriginalMethod() && callee.getMultiMethod(RUNTIME_COMPILED_METHOD) == null) {
+ /*
+ * Recording that this call was reachable, but not converted to a runtime
+ * compiled method.
+ */
+ runtimeCandidateMap.computeIfAbsent(new RuntimeCompilationCandidate(callee, invokeTarget),
+ (candidate) -> {
+ return new InvokeNode(callerMethodNode, invokeInfo.getPosition());
+ });
+ }
+ }
+ }
+ }
+ }
+
+ public void initializeCallTreeInfo(Set registeredRoots) {
+ if (callTreeInitialized) {
+ return;
+ }
+
+ initializeCallTraceInfo();
+ callTreeInitialized = true;
+
+ // ensure invokeInfo calculated
+
+ Queue worklist = new LinkedList<>();
+ /*
+ * First initialize all nodes with no callers.
+ */
+ for (var methodNode : analysisMethodMap.values()) {
+ if (methodNode.getCallers().isEmpty() || registeredRoots.contains(methodNode.method.getMultiMethod(ORIGINAL_METHOD))) {
+ worklist.add(methodNode);
+ methodNode.trace = new TraceInfo(0, new BytecodePosition(null, methodNode.method, BytecodeFrame.UNKNOWN_BCI), null);
+ }
+ }
+
+ /* Walk through to find a reachable path for all nodes */
+ while (!worklist.isEmpty()) {
+ MethodNode callerMethodNode = worklist.remove();
+ TraceInfo callerTrace = callerMethodNode.trace;
+ VMError.guarantee(callerTrace != null);
+
+ for (InvokeInfo invokeInfo : callerMethodNode.method.getInvokes()) {
+ boolean deoptInvokeTypeFlow = invokeInfo.isDeoptInvokeTypeFlow();
+ if (deoptInvokeTypeFlow) {
+ // we do not need to trace deoptInvokes
+ continue;
+ }
+ InvokeNode callerInvokeNode = null;
+ for (AnalysisMethod callee : invokeInfo.getAllCallees()) {
+ if (SubstrateCompilationDirectives.isRuntimeCompiledMethod(callee)) {
+ MethodNode calleeMethodNode = analysisMethodMap.get(callee);
+ if (calleeMethodNode.trace == null) {
+ /*
+ * If this was the first time this node was reached, then add to
+ * worklist.
+ */
+ if (callerInvokeNode == null) {
+ callerInvokeNode = new InvokeNode(callerMethodNode, invokeInfo.getPosition());
+ }
+ worklist.add(calleeMethodNode);
+ calleeMethodNode.trace = new TraceInfo(callerTrace.level + 1, invokeInfo.getPosition(), callerInvokeNode);
+ callerTrace.addTraceTarget(calleeMethodNode);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static final String[] UNKNOWN_TRACE = new String[]{"Unknown"};
+ private static final String[] EMPTY_STRING = new String[0];
+
+ public static String[] getCallTrace(CallTreeInfo callTreeInfo, AnalysisMethod method) {
+ callTreeInfo.initializeCallTraceInfo();
+ MethodNode methodNode = callTreeInfo.analysisMethodMap.get(method);
+ if (methodNode == null) {
+ return UNKNOWN_TRACE;
+ }
+
+ ArrayList trace = new ArrayList<>();
+ findCallTraceHelper(trace, methodNode);
+ return trace.toArray(EMPTY_STRING);
+ }
+
+ public static String[] getCallTrace(CallTreeInfo callTreeInfo, RuntimeCompilationCandidate candidate) {
+ callTreeInfo.initializeCallTraceInfo();
+ InvokeNode invokeNode = callTreeInfo.runtimeCandidateMap.get(candidate);
+ if (invokeNode == null) {
+ return UNKNOWN_TRACE;
+ }
+
+ ArrayList trace = new ArrayList<>();
+ findCallTraceHelper(trace, invokeNode.method);
+ return trace.toArray(EMPTY_STRING);
+ }
+
+ private static void findCallTraceHelper(ArrayList trace, MethodNode first) {
+ Set covered = new HashSet<>();
+ MethodNode current = first;
+ covered.add(current);
+
+ while (current != null) {
+ // find parent
+ MethodNode parent = null;
+ for (InvokeNode caller : current.getCallers()) {
+ if (covered.add(caller.method)) {
+ parent = caller.method;
+ trace.add(caller.position.toString());
+ break;
+ }
+ }
+ current = parent;
+ }
+ }
+
+ public static void printCallTree(CallTreeInfo info, Set registeredRuntimeCompilations) {
+ info.initializeCallTreeInfo(registeredRuntimeCompilations);
+
+ System.out.println("depth;method;invoke position");
+ for (MethodNode methodNode : info.analysisMethodMap.values()) {
+ if (methodNode.trace != null && methodNode.trace.level == 0) {
+ printCallTreeNode(methodNode);
+ }
+ }
+ }
+
+ private static void printCallTreeNode(MethodNode node) {
+ TraceInfo trace = node.trace;
+ StringBuilder indent = new StringBuilder();
+ indent.append(" ".repeat(Math.max(0, trace.level)));
+ indent.append(node.method.format("%H.%n"));
+ System.out.format("%4d ; %-80s ; %s%n", trace.level, indent, trace.position);
+ for (MethodNode child : trace.getTraceTargets()) {
+ printCallTreeNode(child);
+ }
+ }
+
+ public static void printDeepestPath(CallTreeInfo info, Set registeredRuntimeCompilations) {
+ info.initializeCallTreeInfo(registeredRuntimeCompilations);
+
+ Optional deepestNode = info.analysisMethodMap.values().stream().max(Comparator.comparingInt(t -> t.trace == null ? -1 : t.trace.level));
+
+ if (deepestNode.isEmpty() || deepestNode.get().trace == null) {
+ System.out.println("Could not find a trace");
+ return;
+ }
+
+ MethodNode node = deepestNode.get();
+ System.out.printf("Deepest level call tree path (%s calls):%n", node.trace.level);
+ System.out.println("depth;method;invoke position");
+ do {
+ TraceInfo trace = node.trace;
+ StringBuilder indent = new StringBuilder();
+ indent.append(" ".repeat(Math.max(0, trace.level)));
+ indent.append(node.method.format("%H.%n"));
+ System.out.format("%4d ; %-80s ; %s%n", trace.level, indent, trace.position);
+ InvokeNode call = trace.invokeParent;
+ node = call == null ? null : call.method;
+ } while (node != null);
+ }
+}
+
+class MethodNode {
+ public AnalysisMethod method;
+ public List callers;
+ public TraceInfo trace;
+
+ MethodNode(AnalysisMethod method) {
+ this.method = method;
+ this.callers = null;
+
+ }
+
+ List getCallers() {
+ return callers == null ? List.of() : callers;
+ }
+
+ void addCaller(InvokeNode invoke) {
+ if (callers == null) {
+ callers = new ArrayList<>();
+ }
+ callers.add(invoke);
+ }
+}
+
+class InvokeNode {
+ final MethodNode method;
+ final BytecodePosition position;
+
+ InvokeNode(MethodNode method, BytecodePosition position) {
+ this.method = method;
+ this.position = position;
+ }
+}
+
+class TraceInfo {
+ final int level;
+ final BytecodePosition position;
+ final InvokeNode invokeParent;
+ List traceTargets;
+
+ TraceInfo(int level, BytecodePosition position, InvokeNode invokeParent) {
+ this.level = level;
+ this.position = position;
+ this.invokeParent = invokeParent;
+ }
+
+ List getTraceTargets() {
+ return traceTargets == null ? List.of() : traceTargets;
+ }
+
+ void addTraceTarget(MethodNode node) {
+ if (traceTargets == null) {
+ traceTargets = new ArrayList<>();
+ }
+ traceTargets.add(node);
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/GraalGraphObjectReplacer.java
similarity index 99%
rename from substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java
rename to substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/GraalGraphObjectReplacer.java
index cc3ed6852370..de2e922f1857 100644
--- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/GraalGraphObjectReplacer.java
@@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package com.oracle.svm.graal.hosted;
+package com.oracle.svm.graal.hosted.runtimecompilation;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationCandidate.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationCandidate.java
new file mode 100644
index 000000000000..361cd8532c46
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationCandidate.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023, 2023, 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.graal.hosted.runtimecompilation;
+
+import java.util.Objects;
+
+import com.oracle.graal.pointsto.meta.AnalysisMethod;
+
+public final class RuntimeCompilationCandidate {
+ AnalysisMethod implementationMethod;
+ AnalysisMethod targetMethod;
+
+ RuntimeCompilationCandidate(AnalysisMethod implementationMethod, AnalysisMethod targetMethod) {
+ this.implementationMethod = implementationMethod;
+ this.targetMethod = targetMethod;
+ }
+
+ public AnalysisMethod getImplementationMethod() {
+ return implementationMethod;
+ }
+
+ public AnalysisMethod getTargetMethod() {
+ return targetMethod;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RuntimeCompilationCandidate that = (RuntimeCompilationCandidate) o;
+ return implementationMethod.equals(that.implementationMethod) && targetMethod.equals(that.targetMethod);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(implementationMethod, targetMethod);
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/ParseOnceRuntimeCompilationFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java
similarity index 52%
rename from substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/ParseOnceRuntimeCompilationFeature.java
rename to substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java
index a77c64d41a88..2ed14e40d39d 100644
--- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/ParseOnceRuntimeCompilationFeature.java
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2022, 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
@@ -22,31 +22,29 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package com.oracle.svm.graal.hosted;
+package com.oracle.svm.graal.hosted.runtimecompilation;
import static com.oracle.svm.common.meta.MultiMethod.DEOPT_TARGET_METHOD;
import static com.oracle.svm.common.meta.MultiMethod.ORIGINAL_METHOD;
+import static com.oracle.svm.core.util.VMError.guarantee;
import static com.oracle.svm.hosted.code.SubstrateCompilationDirectives.RUNTIME_COMPILED_METHOD;
import static com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyUtils.Options.InlineBeforeAnalysisAllowedDepth;
import static jdk.graal.compiler.java.BytecodeParserOptions.InlineDuringParsingMaxDepth;
-import java.util.ArrayList;
+import java.lang.reflect.Executable;
+import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
+import java.util.stream.Stream;
-import org.graalvm.collections.EconomicMap;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
@@ -55,330 +53,395 @@
import com.oracle.graal.pointsto.api.HostVM;
import com.oracle.graal.pointsto.flow.InvokeTypeFlow;
import com.oracle.graal.pointsto.flow.MethodFlowsGraph;
-import com.oracle.graal.pointsto.heap.ImageHeapScanner;
import com.oracle.graal.pointsto.infrastructure.GraphProvider;
-import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
+import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.graal.pointsto.meta.HostedProviders;
-import com.oracle.graal.pointsto.meta.InvokeInfo;
import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod;
import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisPolicy;
-import com.oracle.graal.pointsto.util.CompletionExecutor;
+import com.oracle.graal.pointsto.util.AnalysisError;
+import com.oracle.graal.pointsto.util.GraalAccess;
+import com.oracle.graal.pointsto.util.ParallelExecutionException;
import com.oracle.svm.common.meta.MultiMethod;
+import com.oracle.svm.core.FrameAccess;
import com.oracle.svm.core.ParsingReason;
+import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.config.ConfigurationValues;
-import com.oracle.svm.core.graal.nodes.DeoptEntryNode;
-import com.oracle.svm.core.graal.nodes.DeoptEntrySupport;
-import com.oracle.svm.core.graal.nodes.DeoptProxyAnchorNode;
+import com.oracle.svm.core.graal.RuntimeCompilationCanaryFeature;
+import com.oracle.svm.core.graal.code.SubstrateBackend;
+import com.oracle.svm.core.graal.code.SubstrateMetaAccessExtensionProvider;
+import com.oracle.svm.core.graal.code.SubstratePlatformConfigurationProvider;
+import com.oracle.svm.core.graal.meta.RuntimeConfiguration;
+import com.oracle.svm.core.graal.meta.SubstrateReplacements;
import com.oracle.svm.core.graal.nodes.InlinedInvokeArgumentsNode;
+import com.oracle.svm.core.graal.word.SubstrateWordTypes;
+import com.oracle.svm.core.heap.BarrierSetProvider;
+import com.oracle.svm.core.jdk.RuntimeSupport;
+import com.oracle.svm.core.meta.SharedType;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.HostedOptionValues;
+import com.oracle.svm.core.option.LocatableMultiOptionValue;
+import com.oracle.svm.core.option.RuntimeOptionValues;
+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.SubstrateGraalRuntime;
import com.oracle.svm.graal.TruffleRuntimeCompilationSupport;
+import com.oracle.svm.graal.hosted.DeoptimizationFeature;
+import com.oracle.svm.graal.hosted.FieldsOffsetsFeature;
+import com.oracle.svm.graal.hosted.GraalCompilerFeature;
+import com.oracle.svm.graal.meta.SubstrateField;
import com.oracle.svm.graal.meta.SubstrateMethod;
+import com.oracle.svm.graal.meta.SubstrateType;
+import com.oracle.svm.graal.meta.SubstrateUniverseFactory;
+import com.oracle.svm.hosted.FeatureHandler;
import com.oracle.svm.hosted.FeatureImpl;
-import com.oracle.svm.hosted.HeapBreakdownProvider;
-import com.oracle.svm.hosted.RuntimeCompilationSupport;
+import com.oracle.svm.hosted.FeatureImpl.AfterHeapLayoutAccessImpl;
+import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
+import com.oracle.svm.hosted.FeatureImpl.CompilationAccessImpl;
+import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl;
+import com.oracle.svm.hosted.NativeImageGenerator;
+import com.oracle.svm.hosted.ProgressReporter;
+import com.oracle.svm.hosted.RuntimeCompilationCallbacks;
import com.oracle.svm.hosted.SVMHost;
-import com.oracle.svm.hosted.ameta.AnalysisConstantFieldProvider;
-import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider;
+import com.oracle.svm.hosted.analysis.Inflation;
import com.oracle.svm.hosted.analysis.SVMParsingSupport;
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
import com.oracle.svm.hosted.code.CompileQueue;
import com.oracle.svm.hosted.code.DeoptimizationUtils;
import com.oracle.svm.hosted.code.SubstrateCompilationDirectives;
-import com.oracle.svm.hosted.meta.HostedConstantFieldProvider;
-import com.oracle.svm.hosted.meta.HostedField;
+import com.oracle.svm.hosted.meta.HostedMetaAccess;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.meta.HostedUniverse;
-import com.oracle.svm.hosted.nodes.DeoptProxyNode;
-import com.oracle.svm.hosted.phases.AnalysisGraphBuilderPhase;
import com.oracle.svm.hosted.phases.ConstantFoldLoadFieldPlugin;
import com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyUtils;
-import com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyUtils.AccumulativeInlineScope;
-import com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyUtils.AlwaysInlineScope;
+import com.oracle.svm.hosted.phases.SubstrateClassInitializationPlugin;
+import jdk.graal.compiler.api.runtime.GraalRuntime;
import jdk.graal.compiler.core.common.PermanentBailoutException;
import jdk.graal.compiler.core.common.spi.ConstantFieldProvider;
+import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider;
import jdk.graal.compiler.debug.DebugContext;
-import jdk.graal.compiler.debug.DebugHandlersFactory;
import jdk.graal.compiler.debug.Indent;
import jdk.graal.compiler.graph.NodeClass;
import jdk.graal.compiler.graph.NodeSourcePosition;
-import jdk.graal.compiler.java.BytecodeParser;
-import jdk.graal.compiler.java.GraphBuilderPhase;
+import jdk.graal.compiler.lir.phases.LIRSuites;
import jdk.graal.compiler.loop.phases.ConvertDeoptimizeToGuardPhase;
-import jdk.graal.compiler.nodes.CallTargetNode;
import jdk.graal.compiler.nodes.FixedWithNextNode;
-import jdk.graal.compiler.nodes.FrameState;
-import jdk.graal.compiler.nodes.StateSplit;
+import jdk.graal.compiler.nodes.GraphEncoder;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
+import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.BytecodeExceptionMode;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import jdk.graal.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
-import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext;
import jdk.graal.compiler.nodes.graphbuilderconf.NodePlugin;
-import jdk.graal.compiler.nodes.java.ExceptionObjectNode;
import jdk.graal.compiler.options.Option;
-import jdk.graal.compiler.options.OptionValues;
+import jdk.graal.compiler.options.OptionStability;
import jdk.graal.compiler.phases.OptimisticOptimizations;
-import jdk.graal.compiler.phases.Phase;
-import jdk.graal.compiler.phases.PhaseSuite;
import jdk.graal.compiler.phases.common.CanonicalizerPhase;
-import jdk.graal.compiler.phases.common.DominatorBasedGlobalValueNumberingPhase;
-import jdk.graal.compiler.phases.common.IterativeConditionalEliminationPhase;
-import jdk.graal.compiler.phases.tiers.HighTierContext;
+import jdk.graal.compiler.phases.tiers.Suites;
import jdk.graal.compiler.phases.util.Providers;
-import jdk.graal.compiler.printer.GraalDebugHandlersFactory;
import jdk.graal.compiler.truffle.phases.DeoptimizeOnExceptionPhase;
-import jdk.vm.ci.code.BytecodeFrame;
-import jdk.vm.ci.code.BytecodePosition;
-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.JavaType;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
-public class ParseOnceRuntimeCompilationFeature extends RuntimeCompilationFeature implements Feature, RuntimeCompilationSupport {
+/**
+ * The main handler for running the Graal compiler in the Substrate VM at run time. This feature
+ * (and features it depends on like {@link FieldsOffsetsFeature}) encodes Graal graphs for runtime
+ * compilation, ensures that all required {@link SubstrateType}, {@link SubstrateMethod},
+ * {@link SubstrateField} objects are created by {@link GraalGraphObjectReplacer} and added to the
+ * image. Data that is prepared during image generation and used at run time is stored in
+ * {@link TruffleRuntimeCompilationSupport}.
+ */
+public final class RuntimeCompilationFeature implements Feature, RuntimeCompilationCallbacks {
public static class Options {
- @Option(help = "Remove Deopt(Entries,Anchors,Proxies) determined to be unneeded after the runtime compiled graphs have been finalized.")//
- public static final HostedOptionKey RemoveUnneededDeoptSupport = new HostedOptionKey<>(true);
+ @Option(help = "Print methods available for runtime compilation")//
+ public static final HostedOptionKey PrintRuntimeCompileMethods = new HostedOptionKey<>(false);
+
+ @Option(help = "Print call tree of methods reachable for runtime compilation")//
+ public static final HostedOptionKey PrintRuntimeCompilationCallTree = new HostedOptionKey<>(false);
+
+ @Option(help = "Maximum number of methods allowed for runtime compilation.", stability = OptionStability.STABLE)//
+ public static final HostedOptionKey MaxRuntimeCompileMethods = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.build());
+
+ @Option(help = "Enforce checking of maximum number of methods allowed for runtime compilation. Useful for checking in the gate that the number of methods does not go up without a good reason.")//
+ public static final HostedOptionKey EnforceMaxRuntimeCompileMethods = new HostedOptionKey<>(false);
@Option(help = "Perform InlineBeforeAnalysis on runtime compiled methods")//
public static final HostedOptionKey RuntimeCompilationInlineBeforeAnalysis = new HostedOptionKey<>(true);
}
- public static final class CallTreeNode extends AbstractCallTreeNode {
- final BytecodePosition position;
- final boolean inlined;
-
- CallTreeNode(AnalysisMethod implementationMethod, AnalysisMethod targetMethod, CallTreeNode parent, BytecodePosition position, boolean inlined) {
- super(parent, targetMethod, implementationMethod);
- this.position = position;
- this.inlined = inlined;
+ public static final class IsEnabled implements BooleanSupplier {
+ @Override
+ public boolean getAsBoolean() {
+ return ImageSingletons.contains(RuntimeCompilationFeature.class);
}
+ }
- @Override
- public String getPosition() {
- String message = "";
- if (inlined) {
- message += "(inlined: some parent frames may be missing) ";
+ public static RuntimeCompilationFeature singleton() {
+ return ImageSingletons.lookup(RuntimeCompilationFeature.class);
+ }
- }
- if (getParent() == null) {
- message += "[root] ";
- }
- return message + position;
- }
+ public interface RuntimeCompilationCandidatePredicate {
+ boolean allowRuntimeCompilation(ResolvedJavaMethod method);
+ }
- /**
- * It is not worthwhile to decode the graph to get the node count.
- */
- @Override
- public int getNodeCount() {
- return -1;
+ public interface AllowInliningPredicate {
+ enum InlineDecision {
+ INLINE,
+ INLINING_DISALLOWED,
+ NO_DECISION
}
+ InlineDecision allowInlining(GraphBuilderContext builder, ResolvedJavaMethod target);
}
- static class RuntimeCompilationCandidateImpl implements RuntimeCompilationCandidate {
- AnalysisMethod implementationMethod;
- AnalysisMethod targetMethod;
+ private GraalGraphObjectReplacer objectReplacer;
+ private HostedProviders hostedProviders;
+ private GraphEncoder graphEncoder;
- RuntimeCompilationCandidateImpl(AnalysisMethod implementationMethod, AnalysisMethod targetMethod) {
- this.implementationMethod = implementationMethod;
- this.targetMethod = targetMethod;
- }
+ private boolean initialized;
+ private GraphBuilderConfiguration graphBuilderConfig;
+ private OptimisticOptimizations optimisticOpts;
+ private RuntimeCompilationCandidatePredicate runtimeCompilationCandidatePredicate;
+ private boolean runtimeCompilationCandidatePredicateUpdated = false;
+ private Predicate deoptimizeOnExceptionPredicate;
- @Override
- public AnalysisMethod getImplementationMethod() {
- return implementationMethod;
- }
+ private SubstrateUniverseFactory universeFactory = new SubstrateUniverseFactory();
- @Override
- public AnalysisMethod getTargetMethod() {
- return targetMethod;
- }
+ private final Set registeredRuntimeCompilations = ConcurrentHashMap.newKeySet();
+ private final Set substrateAnalysisMethods = ConcurrentHashMap.newKeySet();
+ private final Map invalidForRuntimeCompilation = new ConcurrentHashMap<>();
+ private final Set runtimeCompilationCandidates = ConcurrentHashMap.newKeySet();
+ private CallTreeInfo callTreeMetadata = null;
+ private HostedProviders analysisProviders = null;
+ private AllowInliningPredicate allowInliningPredicate = (builder, target) -> AllowInliningPredicate.InlineDecision.INLINING_DISALLOWED;
+ private boolean allowInliningPredicateUpdated = false;
+ private Function constantFieldProviderWrapper = Function.identity();
+ private Consumer blocklistChecker = (ignore) -> {
+ };
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- RuntimeCompilationCandidateImpl that = (RuntimeCompilationCandidateImpl) o;
- return implementationMethod.equals(that.implementationMethod) && targetMethod.equals(that.targetMethod);
- }
+ public HostedProviders getHostedProviders() {
+ return hostedProviders;
+ }
- @Override
- public int hashCode() {
- return Objects.hash(implementationMethod, targetMethod);
- }
+ public GraalGraphObjectReplacer getObjectReplacer() {
+ return objectReplacer;
}
- static final class RuntimeCompiledMethodImpl implements RuntimeCompiledMethod {
- final AnalysisMethod method;
- final Collection inlinedMethods;
+ public void setUniverseFactory(SubstrateUniverseFactory universeFactory) {
+ this.universeFactory = universeFactory;
+ }
- private RuntimeCompiledMethodImpl(AnalysisMethod method, Collection inlinedMethods) {
- this.method = method;
- this.inlinedMethods = inlinedMethods;
- }
+ @SuppressWarnings("unused")
+ private static boolean defaultAllowRuntimeCompilation(ResolvedJavaMethod method) {
+ return false;
+ }
- @Override
- public AnalysisMethod getMethod() {
- return method;
- }
+ public void initializeRuntimeCompilationForTesting(FeatureImpl.BeforeAnalysisAccessImpl config, RuntimeCompilationCandidatePredicate newRuntimeCompilationCandidatePredicate) {
+ initializeRuntimeCompilationConfiguration(hostedProviders, graphBuilderConfig, newRuntimeCompilationCandidatePredicate, deoptimizeOnExceptionPredicate, blocklistChecker);
+ initializeRuntimeCompilationForTesting(config);
+ }
- @Override
- public Collection getInlinedMethods() {
- return inlinedMethods;
- }
+ public void initializeRuntimeCompilationForTesting(BeforeAnalysisAccessImpl config) {
+ initializeAnalysisProviders(config.getBigBang(), provider -> provider);
+ }
- @Override
- public Collection getInvokeTargets() {
- List targets = new ArrayList<>();
- for (var invoke : method.getInvokes()) {
- targets.add(invoke.getTargetMethod());
- }
- return targets;
- }
+ public void initializeRuntimeCompilationConfiguration(HostedProviders newHostedProviders, GraphBuilderConfiguration newGraphBuilderConfig,
+ RuntimeCompilationCandidatePredicate newRuntimeCompilationCandidatePredicate,
+ Predicate newDeoptimizeOnExceptionPredicate, Consumer newBlocklistChecker) {
+ guarantee(initialized == false, "runtime compilation configuration already initialized");
+ initialized = true;
+
+ hostedProviders = newHostedProviders;
+ graphBuilderConfig = newGraphBuilderConfig.withNodeSourcePosition(true);
+ assert !runtimeCompilationCandidatePredicateUpdated : "Updated compilation predicate multiple times";
+ runtimeCompilationCandidatePredicate = newRuntimeCompilationCandidatePredicate;
+ runtimeCompilationCandidatePredicateUpdated = true;
+ deoptimizeOnExceptionPredicate = newDeoptimizeOnExceptionPredicate;
+ blocklistChecker = newBlocklistChecker;
+ }
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- RuntimeCompiledMethodImpl that = (RuntimeCompiledMethodImpl) o;
- return method.equals(that.method);
- }
+ public SubstrateMethod requireFrameInformationForMethod(ResolvedJavaMethod method, BeforeAnalysisAccessImpl config, boolean registerAsRoot) {
+ AnalysisMethod aMethod = (AnalysisMethod) method;
+ SubstrateMethod sMethod = objectReplacer.createMethod(aMethod);
- @Override
- public int hashCode() {
- return Objects.hash(method);
+ assert aMethod.isOriginalMethod();
+ AnalysisMethod deoptTarget = aMethod.getOrCreateMultiMethod(DEOPT_TARGET_METHOD);
+ SubstrateCompilationDirectives.singleton().registerFrameInformationRequired(aMethod, deoptTarget);
+ if (registerAsRoot) {
+ config.registerAsRoot(aMethod, true, "Frame information required, registered in " + RuntimeCompilationFeature.class, DEOPT_TARGET_METHOD);
}
- }
- private static final class RuntimeGraphBuilderPhase extends AnalysisGraphBuilderPhase {
+ return sMethod;
+ }
- private RuntimeGraphBuilderPhase(Providers providers,
- GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, SVMHost hostVM) {
- super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, hostVM);
- }
+ public SubstrateMethod prepareMethodForRuntimeCompilation(Executable method, BeforeAnalysisAccessImpl config) {
+ return prepareMethodForRuntimeCompilation(config.getMetaAccess().lookupJavaMethod(method), config);
+ }
- static RuntimeGraphBuilderPhase createRuntimeGraphBuilderPhase(BigBang bb, Providers providers,
- GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts) {
+ public void registerAllowInliningPredicate(AllowInliningPredicate predicate) {
+ assert !allowInliningPredicateUpdated;
+ allowInliningPredicate = predicate;
+ allowInliningPredicateUpdated = true;
+ }
- // Adjust graphbuilderconfig to match analysis phase
- var newGraphBuilderConfig = graphBuilderConfig
- .withEagerResolving(true)
- .withUnresolvedIsError(false);
- return new RuntimeGraphBuilderPhase(providers, newGraphBuilderConfig, optimisticOpts, null, (SVMHost) bb.getHostVM());
- }
+ public void initializeAnalysisProviders(BigBang bb, Function generator) {
+ HostedProviders defaultProviders = bb.getProviders(ORIGINAL_METHOD);
+ HostedProviders customHostedProviders = defaultProviders.copyWith(generator.apply(defaultProviders.getConstantFieldProvider()));
+ constantFieldProviderWrapper = generator;
+ customHostedProviders.setGraphBuilderPlugins(hostedProviders.getGraphBuilderPlugins());
+ analysisProviders = customHostedProviders;
+ }
- @Override
- protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
- return new RuntimeBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext, hostVM);
- }
+ public CallTreeInfo getCallTreeInfo() {
+ VMError.guarantee(callTreeMetadata != null);
+ return callTreeMetadata;
}
- private static final class RuntimeBytecodeParser extends AnalysisGraphBuilderPhase.AnalysisBytecodeParser {
+ public Collection getAllRuntimeCompilationCandidates() {
+ return runtimeCompilationCandidates;
+ }
- RuntimeBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI,
- IntrinsicContext intrinsicContext, SVMHost svmHost) {
- super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, svmHost, false);
- }
+ public SubstrateMethod prepareMethodForRuntimeCompilation(ResolvedJavaMethod method, FeatureImpl.BeforeAnalysisAccessImpl config) {
+ AnalysisMethod aMethod = (AnalysisMethod) method;
+ assert aMethod.isOriginalMethod();
- @Override
- protected boolean tryInvocationPlugin(CallTargetNode.InvokeKind invokeKind, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType) {
- boolean result = super.tryInvocationPlugin(invokeKind, args, targetMethod, resultType);
- if (result) {
- SubstrateCompilationDirectives.singleton().registerAsDeoptInlininingExclude(targetMethod);
- }
- return result;
- }
+ SubstrateMethod sMethod = objectReplacer.createMethod(aMethod);
+ substrateAnalysisMethods.add(sMethod);
- @Override
- protected boolean shouldVerifyFrameStates() {
+ if (registeredRuntimeCompilations.add(aMethod)) {
+ aMethod.getOrCreateMultiMethod(RUNTIME_COMPILED_METHOD);
/*
- * (GR-46115) Ideally we should verify frame states in methods registered for runtime
- * compilations, as well as any other methods that can deoptimize. Because runtime
- * compiled methods can pull in almost arbitrary code, this means most frame states
- * should be verified. We currently use illegal states as placeholders in many places,
- * so this cannot be enabled at the moment.
+ * For static methods it is important to also register the deopt targets to ensure the
+ * method will be linked appropriately. However, we do not need to make the entire flow
+ * until we see what FrameStates exist.
*/
- return false;
+ var deoptMethod = aMethod.getOrCreateMultiMethod(DEOPT_TARGET_METHOD, (newMethod) -> ((PointsToAnalysisMethod) newMethod).getTypeFlow().setAsStubFlow());
+ SubstrateCompilationDirectives.singleton().registerDeoptTarget(deoptMethod);
+ config.registerAsRoot(aMethod, true, "Runtime compilation, registered in " + RuntimeCompilationFeature.class, RUNTIME_COMPILED_METHOD, DEOPT_TARGET_METHOD);
}
- }
- private final Set registeredRuntimeCompilations = ConcurrentHashMap.newKeySet();
- private final Set substrateAnalysisMethods = ConcurrentHashMap.newKeySet();
- private final Map invalidForRuntimeCompilation = new ConcurrentHashMap<>();
- private final Set runtimeCompilationCandidates = ConcurrentHashMap.newKeySet();
- private Set runtimeCompilations = null;
- private Map runtimeCandidateCallTree = null;
- private Map runtimeCompiledMethodCallTree = null;
- private HostedProviders analysisProviders = null;
- private ImageHeapScanner heapScanner;
- private HostedProviders runtimeCompilationProviders = null;
- private AllowInliningPredicate allowInliningPredicate = (builder, target) -> AllowInliningPredicate.InlineDecision.INLINING_DISALLOWED;
- private boolean allowInliningPredicateUpdated = false;
- private Function constantFieldProviderWrapper = Function.identity();
+ return sMethod;
+ }
@Override
public List> getRequiredFeatures() {
- return RuntimeCompilationFeature.getRequiredFeaturesHelper();
+ return List.of(RuntimeCompilationCanaryFeature.class, DeoptimizationFeature.class, GraalCompilerFeature.class);
}
@Override
- public void afterRegistration(AfterRegistrationAccess access) {
+ public void afterRegistration(Feature.AfterRegistrationAccess access) {
ImageSingletons.add(SVMParsingSupport.class, new RuntimeCompilationParsingSupport());
ImageSingletons.add(HostVM.MultiMethodAnalysisPolicy.class, new RuntimeCompilationAnalysisPolicy());
- ImageSingletons.add(RuntimeCompilationFeature.class, this);
- ImageSingletons.add(RuntimeCompilationSupport.class, this);
+ ImageSingletons.add(RuntimeCompilationCallbacks.class, this);
}
@Override
public void duringSetup(DuringSetupAccess c) {
- duringSetupHelper(c);
- }
+ if (SubstrateOptions.useLLVMBackend()) {
+ throw UserError.abort("Runtime compilation is currently unimplemented on the LLVM backend (GR-43073).");
+ }
+ ImageSingletons.add(TruffleRuntimeCompilationSupport.class, new TruffleRuntimeCompilationSupport());
+ if (!ImageSingletons.contains(SubstrateGraalCompilerSetup.class)) {
+ ImageSingletons.add(SubstrateGraalCompilerSetup.class, new SubstrateGraalCompilerSetup());
+ }
- @Override
- public void beforeAnalysis(BeforeAnalysisAccess c) {
- beforeAnalysisHelper(c);
+ DuringSetupAccessImpl config = (DuringSetupAccessImpl) c;
+ AnalysisMetaAccess aMetaAccess = config.getMetaAccess();
+ SubstrateWordTypes wordTypes = new SubstrateWordTypes(aMetaAccess, FrameAccess.getWordKind());
+ SubstrateProviders substrateProviders = ImageSingletons.lookup(SubstrateGraalCompilerSetup.class).getSubstrateProviders(aMetaAccess, wordTypes);
+ objectReplacer = new GraalGraphObjectReplacer(config.getUniverse(), substrateProviders, universeFactory);
+ config.registerObjectReplacer(objectReplacer);
}
- @Override
- public void registerAllowInliningPredicate(AllowInliningPredicate predicate) {
- assert !allowInliningPredicateUpdated;
- allowInliningPredicate = predicate;
- allowInliningPredicateUpdated = true;
+ private void installRuntimeConfig(BeforeAnalysisAccessImpl config) {
+ Function backendProvider = TruffleRuntimeCompilationSupport.getRuntimeBackendProvider();
+ ClassInitializationSupport classInitializationSupport = config.getHostVM().getClassInitializationSupport();
+ Providers originalProviders = GraalAccess.getOriginalProviders();
+ SubstratePlatformConfigurationProvider platformConfig = new SubstratePlatformConfigurationProvider(
+ ImageSingletons.lookup(BarrierSetProvider.class).createBarrierSet(config.getMetaAccess()));
+ RuntimeConfiguration runtimeConfig = ImageSingletons.lookup(SubstrateGraalCompilerSetup.class)
+ .createRuntimeConfigurationBuilder(RuntimeOptionValues.singleton(), config.getHostVM(), config.getUniverse(), config.getMetaAccess(),
+ backendProvider, classInitializationSupport, originalProviders.getLoopsDataProvider(), platformConfig,
+ config.getBigBang().getSnippetReflectionProvider())
+ .build();
+
+ Providers runtimeProviders = runtimeConfig.getProviders();
+ hostedProviders = new HostedProviders(runtimeProviders.getMetaAccess(), runtimeProviders.getCodeCache(), runtimeProviders.getConstantReflection(),
+ runtimeProviders.getConstantFieldProvider(),
+ runtimeProviders.getForeignCalls(), runtimeProviders.getLowerer(), runtimeProviders.getReplacements(), runtimeProviders.getStampProvider(),
+ runtimeConfig.getSnippetReflection(), runtimeProviders.getWordTypes(), runtimeProviders.getPlatformConfigurationProvider(), new GraphPrepareMetaAccessExtensionProvider(),
+ runtimeProviders.getLoopsDataProvider());
+
+ FeatureHandler featureHandler = config.getFeatureHandler();
+ final boolean supportsStubBasedPlugins = !SubstrateOptions.useLLVMBackend();
+
+ NativeImageGenerator.registerGraphBuilderPlugins(featureHandler, runtimeConfig, hostedProviders, config.getMetaAccess(), config.getUniverse(), null, config.getNativeLibraries(),
+ config.getImageClassLoader(), ParsingReason.JITCompilation, ((Inflation) config.getBigBang()).getAnnotationSubstitutionProcessor(),
+ new SubstrateClassInitializationPlugin(config.getHostVM()), ConfigurationValues.getTarget(), supportsStubBasedPlugins);
+
+ NativeImageGenerator.registerReplacements(DebugContext.forCurrentThread(), featureHandler, runtimeConfig, runtimeConfig.getProviders(), false, true,
+ new RuntimeCompiledMethodSupport.RuntimeCompilationGraphEncoder(ConfigurationValues.getTarget().arch, config.getUniverse().getHeapScanner()));
+
+ featureHandler.forEachGraalFeature(feature -> feature.registerCodeObserver(runtimeConfig));
+ Suites suites = NativeImageGenerator.createSuites(featureHandler, runtimeConfig, runtimeConfig.getSnippetReflection(), false);
+ LIRSuites lirSuites = NativeImageGenerator.createLIRSuites(featureHandler, runtimeConfig.getProviders(), false);
+ Suites firstTierSuites = NativeImageGenerator.createFirstTierSuites(featureHandler, runtimeConfig, runtimeConfig.getSnippetReflection(), false);
+ LIRSuites firstTierLirSuites = NativeImageGenerator.createFirstTierLIRSuites(featureHandler, runtimeConfig.getProviders(), false);
+
+ TruffleRuntimeCompilationSupport.setRuntimeConfig(runtimeConfig, suites, lirSuites, firstTierSuites, firstTierLirSuites);
}
@Override
- public void initializeAnalysisProviders(BigBang bb, Function generator) {
- HostedProviders defaultProviders = bb.getProviders(ORIGINAL_METHOD);
- HostedProviders customHostedProviders = defaultProviders.copyWith(generator.apply(defaultProviders.getConstantFieldProvider()));
- constantFieldProviderWrapper = generator;
- customHostedProviders.setGraphBuilderPlugins(hostedProviders.getGraphBuilderPlugins());
- analysisProviders = customHostedProviders;
- heapScanner = bb.getUniverse().getHeapScanner();
+ public void beforeAnalysis(BeforeAnalysisAccess c) {
+
+ BeforeAnalysisAccessImpl config = (BeforeAnalysisAccessImpl) c;
+ installRuntimeConfig(config);
+
+ SubstrateGraalRuntime graalRuntime = new SubstrateGraalRuntime();
+ objectReplacer.setGraalRuntime(graalRuntime);
+ objectReplacer.setAnalysisAccess(config);
+ ImageSingletons.add(GraalRuntime.class, graalRuntime);
+ RuntimeSupport.getRuntimeSupport().addShutdownHook(new GraalSupport.GraalShutdownHook());
+
+ /* Initialize configuration with reasonable default values. */
+ graphBuilderConfig = GraphBuilderConfiguration.getDefault(hostedProviders.getGraphBuilderPlugins()).withBytecodeExceptionMode(BytecodeExceptionMode.ExplicitOnly);
+ runtimeCompilationCandidatePredicate = RuntimeCompilationFeature::defaultAllowRuntimeCompilation;
+ optimisticOpts = OptimisticOptimizations.ALL.remove(OptimisticOptimizations.Optimization.UseLoopLimitChecks);
+ graphEncoder = new RuntimeCompiledMethodSupport.RuntimeCompilationGraphEncoder(ConfigurationValues.getTarget().arch, config.getUniverse().getHeapScanner());
+
+ /*
+ * Ensure all snippet types are registered as used.
+ */
+ SubstrateReplacements replacements = (SubstrateReplacements) TruffleRuntimeCompilationSupport.getRuntimeConfig().getProviders().getReplacements();
+ for (NodeClass> nodeClass : replacements.getSnippetNodeClasses()) {
+ config.getMetaAccess().lookupJavaType(nodeClass.getClazz()).registerAsAllocated("All " + NodeClass.class.getName() + " classes are marked as instantiated eagerly.");
+ }
+ /*
+ * Ensure runtime snippet graphs are analyzed.
+ */
+ NativeImageGenerator.performSnippetGraphAnalysis(config.getBigBang(), replacements, config.getBigBang().getOptions());
+
+ /*
+ * Ensure that all snippet methods have their SubstrateMethod object created by the object
+ * replacer, to avoid corner cases later when writing the native image.
+ */
+ for (ResolvedJavaMethod method : replacements.getSnippetMethods()) {
+ objectReplacer.apply(method);
+ }
}
boolean newRuntimeMethodsSeen = false;
@Override
- public void duringAnalysis(DuringAnalysisAccess c) {
+ public void duringAnalysis(Feature.DuringAnalysisAccess c) {
/*
* Note this will be removed once graphEncoder and the graal graph object replacer are
* thread friendly.
@@ -403,375 +466,129 @@ public void duringAnalysis(DuringAnalysisAccess c) {
}
}
- @Override
- public void afterAnalysis(AfterAnalysisAccess access) {
- /*
- * At this point need to determine which methods are actually valid for runtime compilation
- * and calculate their reachability info.
- */
- buildCallTrees();
-
- runtimeCompilations = new HashSet<>();
- FeatureImpl.AfterAnalysisAccessImpl impl = (FeatureImpl.AfterAnalysisAccessImpl) access;
- for (var method : impl.getUniverse().getMethods()) {
- var rMethod = method.getMultiMethod(RUNTIME_COMPILED_METHOD);
- if (rMethod != null && rMethod.isReachable() && !invalidForRuntimeCompilation.containsKey(rMethod)) {
- var runtimeInlinedMethods = rMethod.getAnalyzedGraph().getInlinedMethods();
- var inlinedMethods = runtimeInlinedMethods.stream().map(inlinedMethod -> {
- ResolvedJavaMethod orig = ((AnalysisMethod) inlinedMethod).getMultiMethod(ORIGINAL_METHOD);
- assert orig != null;
- return orig;
- }).collect(Collectors.toUnmodifiableSet());
- boolean added = runtimeCompilations.add(new RuntimeCompiledMethodImpl(method, inlinedMethods));
- assert added;
- assert runtimeCompiledMethodCallTree.containsKey(method);
+ private void checkMaxRuntimeCompiledMethods(CallTreeInfo callTreeInfo) {
+ int maxMethods = 0;
+ for (String value : Options.MaxRuntimeCompileMethods.getValue().values()) {
+ String numberStr = null;
+ try {
+ /* Strip optional comment string from MaxRuntimeCompileMethods value */
+ numberStr = value.split("#")[0];
+ maxMethods += Integer.parseInt(numberStr);
+ } catch (NumberFormatException ex) {
+ throw UserError.abort("Invalid value for option 'MaxRuntimeCompileMethods': '%s' is not a valid number", numberStr);
}
}
- // call super after
- afterAnalysisHelper();
-
- // after analysis has completed we must ensure no new SubstrateTypes are introduced
- objectReplacer.forbidNewTypes();
- }
-
- @Override
- protected AbstractCallTreeNode getCallTreeNode(RuntimeCompilationCandidate candidate) {
- var result = runtimeCandidateCallTree.get(candidate);
- assert result != null;
- return result;
- }
-
- @Override
- protected AbstractCallTreeNode getCallTreeNode(RuntimeCompiledMethod method) {
- return getCallTreeNode(method.getMethod());
- }
-
- @Override
- protected AbstractCallTreeNode getCallTreeNode(ResolvedJavaMethod method) {
- ResolvedJavaMethod origMethod = method instanceof MultiMethod m ? (ResolvedJavaMethod) m.getMultiMethod(ORIGINAL_METHOD) : method;
- var result = runtimeCompiledMethodCallTree.get(origMethod);
- assert result != null;
- return result;
- }
-
- @Override
- public Collection getRuntimeCompiledMethods() {
- return runtimeCompilations;
- }
-
- @Override
- public Collection getAllRuntimeCompilationCandidates() {
- return runtimeCompilationCandidates;
- }
-
- private void buildCallTrees() {
- /*
- * While it is possible to dynamically calculate call traces by enabling
- * PointstoOptions#TraceAccessChain, creating call trees post-analysis for runtime compiled
- * methods allows use to not have this overhead during analysis and also to determine the
- * access chains for multiple call sites with the same destination.
- *
- * This is useful to create more stringent blocklist checks.
- */
- assert runtimeCandidateCallTree == null && runtimeCompiledMethodCallTree == null;
- runtimeCandidateCallTree = new HashMap<>();
- runtimeCompiledMethodCallTree = new HashMap<>();
-
- Queue worklist = new LinkedList<>();
-
- /* First initialize with registered runtime compilations */
- for (AnalysisMethod root : registeredRuntimeCompilations) {
- var runtimeRoot = root.getMultiMethod(RUNTIME_COMPILED_METHOD);
- if (runtimeRoot != null) {
- runtimeCandidateCallTree.computeIfAbsent(new RuntimeCompilationCandidateImpl(root, root), (candidate) -> {
- var result = new CallTreeNode(root, root, null, new BytecodePosition(null, root, BytecodeFrame.UNKNOWN_BCI), false);
- worklist.add(result);
- return result;
- });
- }
- }
-
- /*
- * Find all runtime compiled methods reachable from registered runtime compilations.
- *
- * Note within the maps we store the original methods, not the runtime methods.
- */
- while (!worklist.isEmpty()) {
- CallTreeNode caller = worklist.remove();
- caller.linkAsChild();
-
- /*
- * We only need to record one trace for methods
- */
- var method = caller.getImplementationMethod();
- if (runtimeCompiledMethodCallTree.containsKey(method)) {
- // This method has already been processed
- continue;
- } else {
- runtimeCompiledMethodCallTree.put(method, caller);
- }
- AnalysisMethod runtimeMethod = method.getMultiMethod(RUNTIME_COMPILED_METHOD);
- assert runtimeMethod != null;
-
- for (InvokeInfo invokeInfo : runtimeMethod.getInvokes()) {
- AnalysisMethod invokeTarget = invokeInfo.getTargetMethod();
- boolean deoptInvokeTypeFlow = invokeInfo.isDeoptInvokeTypeFlow();
- if (deoptInvokeTypeFlow) {
- assert SubstrateCompilationDirectives.isRuntimeCompiledMethod(invokeTarget);
- invokeTarget = invokeTarget.getMultiMethod(ORIGINAL_METHOD);
- }
- AnalysisMethod target = invokeTarget;
- assert target.isOriginalMethod();
- for (AnalysisMethod implementation : invokeInfo.getAllCallees()) {
- if (deoptInvokeTypeFlow || SubstrateCompilationDirectives.isRuntimeCompiledMethod(implementation)) {
- var origImpl = implementation.getMultiMethod(ORIGINAL_METHOD);
- assert origImpl != null;
- runtimeCandidateCallTree.computeIfAbsent(new RuntimeCompilationCandidateImpl(origImpl, target), (candidate) -> {
- var result = new CallTreeNode(origImpl, target, caller, invokeInfo.getPosition(), deoptInvokeTypeFlow);
- worklist.add(result);
- return result;
- });
- } else if (implementation.isOriginalMethod() && implementation.getMultiMethod(RUNTIME_COMPILED_METHOD) == null) {
- /*
- * Recording that this call was reachable, but not converted to a runtime
- * compiled method.
- */
- runtimeCandidateCallTree.computeIfAbsent(new RuntimeCompilationCandidateImpl(implementation, target),
- (candidate) -> {
- var result = new CallTreeNode(implementation, target, caller, invokeInfo.getPosition(), false);
- result.linkAsChild();
- return result;
- });
- }
- }
- }
- }
- }
-
- public Set parsedRuntimeMethods = ConcurrentHashMap.newKeySet();
- public AtomicLong totalParsedRuntimeMethods = new AtomicLong();
- public Set parsedDeoptMethods = ConcurrentHashMap.newKeySet();
- public AtomicLong totalParsedDeoptMethods = new AtomicLong();
-
- private class RuntimeCompileTask implements CompletionExecutor.DebugContextRunnable {
- final HostedMethod method;
-
- RuntimeCompileTask(HostedMethod method) {
- this.method = method;
- }
-
- @Override
- public DebugContext getDebug(OptionValues options, List factories) {
- return new DebugContext.Builder(options, factories).description(getDescription()).build();
- }
-
- @Override
- public void run(DebugContext debug) {
- compileRuntimeCompiledMethod(debug, method);
+ if (Options.EnforceMaxRuntimeCompileMethods.getValue() && maxMethods != 0 && callTreeInfo.runtimeCompilations().size() > maxMethods) {
+ CallTreeInfo.printDeepestPath(callTreeInfo, registeredRuntimeCompilations);
+ throw VMError.shouldNotReachHere("Number of methods for runtime compilation exceeds the allowed limit: " + callTreeInfo.runtimeCompilations().size() + " > " + maxMethods);
}
}
- private final Map runtimeGraphs = new ConcurrentHashMap<>();
-
- @SuppressWarnings("try")
- private void compileRuntimeCompiledMethod(DebugContext debug, HostedMethod method) {
- assert method.getMultiMethodKey() == RUNTIME_COMPILED_METHOD;
-
- AnalysisMethod aMethod = method.getWrapped();
- StructuredGraph graph = aMethod.decodeAnalyzedGraph(debug, null, false,
- (arch, analyzedGraph) -> new RuntimeCompilationGraphDecoder(arch, analyzedGraph, heapScanner));
- if (graph == null) {
- throw VMError.shouldNotReachHere("Method not parsed during static analysis: " + aMethod.format("%r %H.%n(%p)"));
- }
- /*
- * The graph in the analysis universe is no longer necessary once it is transplanted into
- * the hosted universe.
- */
- aMethod.setAnalyzedGraph(null);
-
- try (DebugContext.Scope s = debug.scope("RuntimeOptimize", graph, method, this)) {
- CanonicalizerPhase canonicalizer = CanonicalizerPhase.create();
- canonicalizer.apply(graph, runtimeCompilationProviders);
-
- new DominatorBasedGlobalValueNumberingPhase(canonicalizer).apply(graph, runtimeCompilationProviders);
-
- new IterativeConditionalEliminationPhase(canonicalizer, true).apply(graph, runtimeCompilationProviders);
-
- /*
- * ConvertDeoptimizeToGuardPhase was already executed after parsing, but optimizations
- * applied in between can provide new potential.
- */
- new ConvertDeoptimizeToGuardPhase(canonicalizer).apply(graph, runtimeCompilationProviders);
+ @Override
+ public void afterAnalysis(Feature.AfterAnalysisAccess access) {
+ VMError.guarantee(callTreeMetadata == null);
+ callTreeMetadata = CallTreeInfo.create(((FeatureImpl.AfterAnalysisAccessImpl) access).getUniverse(), invalidForRuntimeCompilation);
- /*
- * More optimizations can be added here.
- */
- } catch (Throwable e) {
- throw debug.handle(e);
- }
+ ProgressReporter.singleton().setNumRuntimeCompiledMethods(callTreeMetadata.runtimeCompilations().size());
+ // after analysis has completed we must ensure no new SubstrateTypes are introduced
+ objectReplacer.forbidNewTypes();
- /*
- * Registering all deopt entries seen within the optimized graph. This should be strictly a
- * subset of the deopt entrypoints seen during evaluation.
- */
- AnalysisMethod origMethod = method.getMultiMethod(ORIGINAL_METHOD).getWrapped();
- DeoptimizationUtils.registerDeoptEntries(graph, registeredRuntimeCompilations.contains(origMethod),
- (deoptEntryMethod -> {
- PointsToAnalysisMethod deoptMethod = (PointsToAnalysisMethod) ((PointsToAnalysisMethod) deoptEntryMethod).getMultiMethod(DEOPT_TARGET_METHOD);
- VMError.guarantee(deoptMethod != null, "New deopt target method seen: %s", deoptEntryMethod);
- return deoptMethod;
- }));
-
- assert RuntimeCompilationFeature.verifyNodes(graph);
- var previous = runtimeGraphs.put(method, graph);
- assert previous == null;
-
- // graph encoder is not currently threadsafe
- synchronized (this) {
- graphEncoder.prepare(graph);
+ VMError.guarantee(callTreeMetadata != null);
+ if (Options.PrintRuntimeCompileMethods.getValue()) {
+ System.out.println("****Start Print Runtime Compile Methods***");
+ callTreeMetadata.runtimeCompilations().stream().map(m -> m.getRuntimeMethod().format("%H.%n(%p)")).sorted().toList().forEach(System.out::println);
+ System.out.println("****End Print Runtime Compile Methods***");
}
- }
- @SuppressWarnings("try")
- private void encodeRuntimeCompiledMethods() {
- graphEncoder.finishPrepare();
-
- // at this point no new deoptimization entrypoints can be registered.
- SubstrateCompilationDirectives.singleton().sealDeoptimizationInfo();
-
- for (var runtimeInfo : runtimeGraphs.entrySet()) {
- var graph = runtimeInfo.getValue();
- var method = runtimeInfo.getKey();
- DebugContext debug = new DebugContext.Builder(graph.getOptions(), new GraalDebugHandlersFactory(runtimeCompilationProviders.getSnippetReflection())).build();
- graph.resetDebug(debug);
- try (DebugContext.Scope s = debug.scope("Graph Encoding", graph);
- DebugContext.Activation a = debug.activate()) {
- long startOffset = graphEncoder.encode(graph);
- objectReplacer.createMethod(method).setEncodedGraphStartOffset(startOffset);
- } catch (Throwable ex) {
- debug.handle(ex);
- }
+ if (Options.PrintRuntimeCompilationCallTree.getValue()) {
+ System.out.println("****Start Print Runtime Compile Call Tree***");
+ CallTreeInfo.printCallTree(callTreeMetadata, registeredRuntimeCompilations);
+ System.out.println("****End Print Runtime Compile Call Tree***");
}
- HeapBreakdownProvider.singleton().setGraphEncodingByteLength(graphEncoder.getEncoding().length);
- TruffleRuntimeCompilationSupport.setGraphEncoding(null, graphEncoder.getEncoding(), graphEncoder.getObjects(), graphEncoder.getNodeClasses());
-
- objectReplacer.setMethodsImplementations();
-
- /* All the temporary data structures used during encoding are no longer necessary. */
- graphEncoder = null;
- }
-
- @Override
- public void beforeCompilation(BeforeCompilationAccess c) {
- beforeCompilationHelper();
+ checkMaxRuntimeCompiledMethods(callTreeMetadata);
}
@Override
public void onCompileQueueCreation(BigBang bb, HostedUniverse hUniverse, CompileQueue compileQueue) {
- /*
- * Start fresh with a new GraphEncoder, since we are going to optimize all graphs now that
- * the static analysis results are available.
- */
- graphEncoder = new RuntimeCompilationGraphEncoder(ConfigurationValues.getTarget().arch, bb.getUniverse().getHeapScanner());
- assert runtimeCompilationProviders == null;
- runtimeCompilationProviders = hostedProviders
- .copyWith(constantFieldProviderWrapper.apply(new RuntimeCompilationFieldProvider(hostedProviders.getMetaAccess(), hUniverse)))
- .copyWith(new RuntimeCompilationReflectionProvider(bb, hUniverse.hostVM().getClassInitializationSupport()));
-
- SubstrateCompilationDirectives.singleton().resetDeoptEntries();
- /*
- * Customize runtime compile methods for compiling them into substrate graphs.
- */
- CompletionExecutor executor = compileQueue.getExecutor();
- try {
- compileQueue.runOnExecutor(() -> {
- hUniverse.getMethods().stream().map(method -> method.getMultiMethod(RUNTIME_COMPILED_METHOD)).filter(method -> {
- if (method != null) {
- AnalysisMethod aMethod = method.getWrapped();
- return aMethod.isImplementationInvoked() && !invalidForRuntimeCompilation.containsKey(aMethod);
- }
- return false;
- }).forEach(method -> {
- executor.execute(new RuntimeCompileTask(method));
- });
- });
- } catch (InterruptedException exception) {
- VMError.shouldNotReachHere(exception);
- }
- encodeRuntimeCompiledMethods();
-
- /*
- * For Deoptimization Targets add a custom phase which removes all deoptimization
- * entrypoints which are deemed no longer necessary.
- */
- CompileQueue.ParseHooks deoptParseHooks = new CompileQueue.ParseHooks(compileQueue) {
- @Override
- protected PhaseSuite getAfterParseSuite() {
- PhaseSuite suite = super.getAfterParseSuite();
- if (Options.RemoveUnneededDeoptSupport.getValue()) {
- suite.prependPhase(new RemoveUnneededDeoptSupport());
- }
-
- return suite;
- }
- };
-
- hUniverse.getMethods().stream().map(method -> method.getMultiMethod(DEOPT_TARGET_METHOD)).filter(method -> {
+ graphEncoder = null;
+ Stream methodsToCompile = hUniverse.getMethods().stream().map(method -> method.getMultiMethod(RUNTIME_COMPILED_METHOD)).filter(method -> {
if (method != null) {
- return compileQueue.isRegisteredDeoptTarget(method);
+ AnalysisMethod aMethod = method.getWrapped();
+ return aMethod.isImplementationInvoked() && !invalidForRuntimeCompilation.containsKey(aMethod);
}
return false;
- }).forEach(method -> method.compilationInfo.setCustomParseHooks(deoptParseHooks));
-
+ });
+ RuntimeCompiledMethodSupport.onCompileQueueCreation(bb, hUniverse, compileQueue, hostedProviders, constantFieldProviderWrapper, objectReplacer,
+ registeredRuntimeCompilations, methodsToCompile);
}
@Override
public void afterCompilation(AfterCompilationAccess a) {
- super.afterCompilationHelper(a);
+ CompilationAccessImpl config = (CompilationAccessImpl) a;
+
+ HostedMetaAccess hMetaAccess = config.getMetaAccess();
+ HostedUniverse hUniverse = hMetaAccess.getUniverse();
+ objectReplacer.updateSubstrateDataAfterCompilation(hUniverse, config.getProviders());
}
@Override
public void beforeHeapLayout(BeforeHeapLayoutAccess a) {
- super.beforeHeapLayoutHelper(a);
+ objectReplacer.registerImmutableObjects(a);
+ TruffleRuntimeCompilationSupport.registerImmutableObjects(a);
+ ((SubstrateReplacements) TruffleRuntimeCompilationSupport.getRuntimeConfig().getProviders().getReplacements()).registerImmutableObjects(a);
}
@Override
public void afterHeapLayout(AfterHeapLayoutAccess a) {
- afterHeapLayoutHelper(a);
+ AfterHeapLayoutAccessImpl config = (AfterHeapLayoutAccessImpl) a;
+ HostedMetaAccess hMetaAccess = config.getMetaAccess();
+ HostedUniverse hUniverse = hMetaAccess.getUniverse();
+ objectReplacer.updateSubstrateDataAfterHeapLayout(hUniverse);
}
+ /**
+ * When a failure occurs during parsing, it is likely due to a missing truffle boundary. Check
+ * to see if anything on the blocklist has been reached and/or the number of runtime compiled
+ * methods exceeds the maximum allowed.
+ */
@Override
- public SubstrateMethod prepareMethodForRuntimeCompilation(ResolvedJavaMethod method, FeatureImpl.BeforeAnalysisAccessImpl config) {
- AnalysisMethod aMethod = (AnalysisMethod) method;
- assert aMethod.isOriginalMethod();
-
- SubstrateMethod sMethod = objectReplacer.createMethod(aMethod);
- substrateAnalysisMethods.add(sMethod);
-
- if (registeredRuntimeCompilations.add(aMethod)) {
- aMethod.getOrCreateMultiMethod(RUNTIME_COMPILED_METHOD);
- /*
- * For static methods it is important to also register the deopt targets to ensure the
- * method will be linked appropriately. However, we do not need to make the entire flow
- * until we see what FrameStates exist.
- */
- var deoptMethod = aMethod.getOrCreateMultiMethod(DEOPT_TARGET_METHOD, (newMethod) -> ((PointsToAnalysisMethod) newMethod).getTypeFlow().setAsStubFlow());
- SubstrateCompilationDirectives.singleton().registerDeoptTarget(deoptMethod);
- config.registerAsRoot(aMethod, true, "Runtime compilation, registered in " + ParseOnceRuntimeCompilationFeature.class, RUNTIME_COMPILED_METHOD, DEOPT_TARGET_METHOD);
+ public void reportAnalysisError(AnalysisUniverse aUniverse, Throwable t) {
+ var treeInfo = CallTreeInfo.create(aUniverse, invalidForRuntimeCompilation);
+
+ blocklistChecker.accept(treeInfo);
+
+ checkMaxRuntimeCompiledMethods(treeInfo);
+
+ boolean foundError = false;
+ if (t instanceof ParallelExecutionException exception) {
+ for (var e : exception.getExceptions()) {
+ if (e instanceof AnalysisError.ParsingError parsingError) {
+ AnalysisMethod errorMethod = parsingError.getMethod();
+ if (errorMethod.isDeoptTarget() || SubstrateCompilationDirectives.isRuntimeCompiledMethod(errorMethod)) {
+ foundError = true;
+ AnalysisMethod failingRuntimeMethod = null;
+ if (SubstrateCompilationDirectives.isRuntimeCompiledMethod(errorMethod)) {
+ failingRuntimeMethod = errorMethod;
+ } else if (errorMethod.isDeoptTarget()) {
+ failingRuntimeMethod = errorMethod.getMultiMethod(RUNTIME_COMPILED_METHOD);
+ }
+ System.out.println("Parsing failed on a special method version: " + errorMethod.format("%H.%n"));
+ System.out.println("Method reachability trace");
+ if (failingRuntimeMethod != null) {
+ Arrays.stream(CallTreeInfo.getCallTrace(treeInfo, failingRuntimeMethod)).forEach(System.out::println);
+ } else {
+ System.out.println("trace unavailable");
+ }
+ System.out.println("error: " + e.getMessage());
+ }
+ }
+ }
}
- return sMethod;
- }
-
- @Override
- protected void requireFrameInformationForMethodHelper(AnalysisMethod aMethod, FeatureImpl.BeforeAnalysisAccessImpl config, boolean registerAsRoot) {
- assert aMethod.isOriginalMethod();
- AnalysisMethod deoptTarget = aMethod.getOrCreateMultiMethod(DEOPT_TARGET_METHOD);
- SubstrateCompilationDirectives.singleton().registerFrameInformationRequired(aMethod, deoptTarget);
- if (registerAsRoot) {
- config.registerAsRoot(aMethod, true, "Frame information required, registered in " + ParseOnceRuntimeCompilationFeature.class, DEOPT_TARGET_METHOD);
+ if (foundError) {
+ throw VMError.shouldNotReachHere("Analysis failed while parsing deopt and/or runtime methods");
}
}
@@ -845,7 +662,7 @@ private Object parseRuntimeCompiledMethod(BigBang bb, DebugContext debug, Analys
if (parsed) {
// enable this logging to get log output in compilation passes
try (Indent indent2 = debug.logAndIndent("parse graph phases")) {
- RuntimeGraphBuilderPhase.createRuntimeGraphBuilderPhase(bb, analysisProviders, graphBuilderConfig, optimisticOpts).apply(graph);
+ RuntimeCompiledMethodSupport.RuntimeGraphBuilderPhase.createRuntimeGraphBuilderPhase(bb, analysisProviders, graphBuilderConfig, optimisticOpts).apply(graph);
} catch (PermanentBailoutException ex) {
bb.getUnsupportedFeatures().addMessage(method.format("%H.%n(%p)"), method, ex.getLocalizedMessage(), null, ex);
recordFailed(method);
@@ -893,8 +710,6 @@ public boolean validateGraph(PointsToAnalysis bb, StructuredGraph graph) {
}
}
if (multiMethodKey == RUNTIME_COMPILED_METHOD) {
- parsedRuntimeMethods.add(aMethod);
- totalParsedRuntimeMethods.incrementAndGet();
/*
* Register all FrameStates as DeoptEntries.
*/
@@ -924,10 +739,7 @@ public boolean validateGraph(PointsToAnalysis bb, StructuredGraph graph) {
substrateAnalysisMethods.add(sMethod);
graphEncoder.prepare(graph);
}
- assert RuntimeCompilationFeature.verifyNodes(graph);
- } else if (multiMethodKey == DEOPT_TARGET_METHOD) {
- parsedDeoptMethods.add(aMethod);
- totalParsedDeoptMethods.incrementAndGet();
+ assert RuntimeCompiledMethodSupport.verifyNodes(graph);
}
return true;
@@ -935,7 +747,7 @@ public boolean validateGraph(PointsToAnalysis bb, StructuredGraph graph) {
@Override
public void initializeInlineBeforeAnalysisPolicy(SVMHost svmHost, InlineBeforeAnalysisPolicyUtils inliningUtils) {
- if (Options.RuntimeCompilationInlineBeforeAnalysis.getValue()) {
+ if (RuntimeCompilationFeature.Options.RuntimeCompilationInlineBeforeAnalysis.getValue()) {
assert runtimeInlineBeforeAnalysisPolicy == null;
runtimeInlineBeforeAnalysisPolicy = new RuntimeCompilationInlineBeforeAnalysisPolicy(svmHost, inliningUtils);
}
@@ -948,7 +760,7 @@ public InlineBeforeAnalysisPolicy inlineBeforeAnalysisPolicy(MultiMethod.MultiMe
} else if (multiMethodKey == DEOPT_TARGET_METHOD) {
return InlineBeforeAnalysisPolicy.NO_INLINING;
} else if (multiMethodKey == RUNTIME_COMPILED_METHOD) {
- if (Options.RuntimeCompilationInlineBeforeAnalysis.getValue()) {
+ if (RuntimeCompilationFeature.Options.RuntimeCompilationInlineBeforeAnalysis.getValue()) {
assert runtimeInlineBeforeAnalysisPolicy != null;
return runtimeInlineBeforeAnalysisPolicy;
}
@@ -1061,7 +873,7 @@ protected AbstractPolicyScope createRootScope() {
@Override
protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMetaAccess metaAccess,
AnalysisMethod method, boolean[] constArgsWithReceiver, boolean intrinsifiedMethodHandle) {
- if (outer instanceof AccumulativeInlineScope accOuter) {
+ if (outer instanceof InlineBeforeAnalysisPolicyUtils.AccumulativeInlineScope accOuter) {
/*
* once the accumulative policy is activated, then we cannot return to the trivial
* policy
@@ -1069,13 +881,13 @@ protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, Analysi
return inliningUtils.createAccumulativeInlineScope(accOuter, metaAccess, method, constArgsWithReceiver, intrinsifiedMethodHandle);
}
- assert outer == null || outer instanceof AlwaysInlineScope : "unexpected outer scope: " + outer;
+ assert outer == null || outer instanceof InlineBeforeAnalysisPolicyUtils.AlwaysInlineScope : "unexpected outer scope: " + outer;
// check if trivial is possible
boolean trivialInlineAllowed = hostVM.isAnalysisTrivialMethod(method);
int inliningDepth = outer == null ? 1 : outer.inliningDepth + 1;
if (trivialInlineAllowed && inliningDepth <= trivialAllowingInliningDepth) {
- return new AlwaysInlineScope(inliningDepth);
+ return new InlineBeforeAnalysisPolicyUtils.AlwaysInlineScope(inliningDepth);
} else {
// start with a new accumulative inline scope
return inliningUtils.createAccumulativeInlineScope(null, metaAccess, method, constArgsWithReceiver, intrinsifiedMethodHandle);
@@ -1096,7 +908,7 @@ public Collection determineCallees(BigBang bb, T i
assert SubstrateCompilationDirectives.isRuntimeCompiledMethod(implementation);
var originalTarget = implementation.getMultiMethod(ORIGINAL_METHOD);
assert originalTarget != null;
- runtimeCompilationCandidates.add(new RuntimeCompilationCandidateImpl(originalTarget, originalTarget));
+ runtimeCompilationCandidates.add(new RuntimeCompilationCandidate(originalTarget, originalTarget));
return List.of(getStubDeoptVersion(implementation));
}
assert implementation.isOriginalMethod() && target.isOriginalMethod();
@@ -1129,7 +941,7 @@ public Collection determineCallees(BigBang bb, T i
if (callerMultiMethodKey == RUNTIME_COMPILED_METHOD) {
// recording compilation candidate
- runtimeCompilationCandidates.add(new RuntimeCompilationCandidateImpl(implementation, target));
+ runtimeCompilationCandidates.add(new RuntimeCompilationCandidate(implementation, target));
/*
* The runtime method can call all three types: original (if it is not partial
* evaluated), runtime (if it is partial evaluated), and deoptimized (if the
@@ -1224,7 +1036,9 @@ public boolean performParameterLinking(MultiMethod.MultiMethodKey callerMultiMet
@Override
public boolean performReturnLinking(MultiMethod.MultiMethodKey callerMultiMethodKey, MultiMethod.MultiMethodKey calleeMultiMethodKey) {
if (callerMultiMethodKey == RUNTIME_COMPILED_METHOD) {
- /* A runtime method can be returned to from either a runtime or original method. */
+ /*
+ * A runtime method can be returned to from either a runtime or original method.
+ */
return calleeMultiMethodKey == RUNTIME_COMPILED_METHOD || calleeMultiMethodKey == ORIGINAL_METHOD;
} else if (callerMultiMethodKey == DEOPT_TARGET_METHOD) {
/* A deopt method can be returned to from all three. */
@@ -1266,143 +1080,33 @@ public boolean unknownReturnValue(BigBang bb, MultiMethod.MultiMethodKey callerM
return false;
}
}
+}
- /**
- * Since we perform runtime compilation after universe creation, we can leverage components of
- * the hosted universe provider for identifying final fields.
- */
- private static class RuntimeCompilationFieldProvider extends AnalysisConstantFieldProvider {
- final HostedUniverse hUniverse;
-
- RuntimeCompilationFieldProvider(MetaAccessProvider metaAccess, HostedUniverse hUniverse) {
- super(metaAccess, hUniverse.hostVM());
- this.hUniverse = hUniverse;
- }
+/**
+ * Same behavior as {@link SubstrateMetaAccessExtensionProvider}, but operating on
+ * {@link AnalysisType} instead of {@link SharedType} since parsing of graphs for runtime
+ * compilation happens in the Analysis universe.
+ */
+class GraphPrepareMetaAccessExtensionProvider implements MetaAccessExtensionProvider {
- @Override
- public boolean isFinalField(ResolvedJavaField f, ConstantFieldTool> tool) {
- HostedField hField = hUniverse.lookup(f);
- if (HostedConstantFieldProvider.isFinalField(hField)) {
- return true;
- }
- return super.isFinalField(f, tool);
- }
+ @Override
+ public JavaKind getStorageKind(JavaType type) {
+ return ((AnalysisType) type).getStorageKind();
}
- private static class RuntimeCompilationReflectionProvider extends AnalysisConstantReflectionProvider {
-
- RuntimeCompilationReflectionProvider(BigBang bb, ClassInitializationSupport classInitializationSupport) {
- super(bb.getUniverse(), bb.getMetaAccess(), classInitializationSupport);
- }
-
- @Override
- public JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receiver) {
- /*
- * We cannot fold simulated values during initial before-analysis graph creation;
- * however, this runs after analysis has completed.
- */
- return readValue(metaAccess, (AnalysisField) field, receiver, true);
- }
+ @Override
+ public boolean canConstantFoldDynamicAllocation(ResolvedJavaType type) {
+ assert type instanceof AnalysisType : "AnalysisType is required; AnalysisType lazily creates array types of any depth, so type cannot be null";
+ return ((AnalysisType) type).isInstantiated();
}
- /**
- * Removes {@link DeoptEntryNode}s, {@link DeoptProxyAnchorNode}s, and {@link DeoptProxyNode}s
- * which are determined to be unnecessary after the runtime compilation methods are optimized.
- */
- static class RemoveUnneededDeoptSupport extends Phase {
- enum RemovalDecision {
- KEEP,
- PROXIFY,
- REMOVE
- }
-
- @Override
- protected void run(StructuredGraph graph) {
- EconomicMap decisionCache = EconomicMap.create();
-
- // First go through and delete all unneeded proxies
- for (DeoptProxyNode proxyNode : graph.getNodes(DeoptProxyNode.TYPE).snapshot()) {
- ValueNode proxyPoint = proxyNode.getProxyPoint();
- if (proxyPoint instanceof StateSplit) {
- if (getDecision((StateSplit) proxyPoint, decisionCache) == RemovalDecision.REMOVE) {
- proxyNode.replaceAtAllUsages(proxyNode.getOriginalNode(), true);
- proxyNode.safeDelete();
- }
- }
- }
-
- // Next, remove all unneeded DeoptEntryNodes
- for (DeoptEntryNode deoptEntry : graph.getNodes().filter(DeoptEntryNode.class).snapshot()) {
- switch (getDecision(deoptEntry, decisionCache)) {
- case REMOVE -> {
- deoptEntry.killExceptionEdge();
- graph.removeSplit(deoptEntry, deoptEntry.getPrimarySuccessor());
- }
- case PROXIFY -> {
- deoptEntry.killExceptionEdge();
- DeoptProxyAnchorNode newAnchor = graph.add(new DeoptProxyAnchorNode(deoptEntry.getProxifiedInvokeBci()));
- newAnchor.setStateAfter(deoptEntry.stateAfter());
- graph.replaceSplitWithFixed(deoptEntry, newAnchor, deoptEntry.getPrimarySuccessor());
- }
- }
- }
-
- // Finally, remove all unneeded DeoptProxyAnchorNodes
- for (DeoptProxyAnchorNode proxyAnchor : graph.getNodes().filter(DeoptProxyAnchorNode.class).snapshot()) {
- if (getDecision(proxyAnchor, decisionCache) == RemovalDecision.REMOVE) {
- graph.removeFixed(proxyAnchor);
- }
- }
- }
-
- RemovalDecision getDecision(StateSplit node, EconomicMap decisionCache) {
- RemovalDecision cached = decisionCache.get(node);
- if (cached != null) {
- return cached;
- }
-
- DeoptEntrySupport proxyNode;
- if (node instanceof ExceptionObjectNode exceptionObject) {
- /*
- * For the exception edge of a DeoptEntryNode, we insert the proxies on the
- * exception object.
- */
- proxyNode = (DeoptEntrySupport) exceptionObject.predecessor();
- } else {
- proxyNode = (DeoptEntrySupport) node;
- }
-
- RemovalDecision decision = RemovalDecision.REMOVE;
- var directive = SubstrateCompilationDirectives.singleton();
- FrameState state = proxyNode.stateAfter();
- HostedMethod method = (HostedMethod) state.getMethod();
- if (proxyNode instanceof DeoptEntryNode) {
- if (directive.isDeoptEntry(method, state.bci, state.getStackState())) {
- // must keep all deopt entries which are still guarding nodes
- decision = RemovalDecision.KEEP;
- }
- }
-
- if (decision == RemovalDecision.REMOVE) {
- // now check for any implicit deopt entry being protected against
- int proxifiedInvokeBci = proxyNode.getProxifiedInvokeBci();
- if (proxifiedInvokeBci != BytecodeFrame.UNKNOWN_BCI && directive.isDeoptEntry(method, proxifiedInvokeBci, FrameState.StackState.AfterPop)) {
- // must keep still keep a proxy for nodes which are "proxifying" an invoke
- decision = proxyNode instanceof DeoptEntryNode ? RemovalDecision.PROXIFY : RemovalDecision.KEEP;
- }
- }
-
- // cache the decision
- decisionCache.put(node, decision);
- if (proxyNode != node) {
- decisionCache.put(proxyNode, decision);
- }
- return decision;
- }
+ @Override
+ public boolean isGuaranteedSafepoint(ResolvedJavaMethod method, boolean isDirect) {
+ throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport
+ }
- @Override
- public CharSequence getName() {
- return "RemoveUnneededDeoptSupport";
- }
+ @Override
+ public boolean canVirtualize(ResolvedJavaType instanceType) {
+ return true;
}
}
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethod.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethod.java
new file mode 100644
index 000000000000..427c9641640c
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethod.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023, 2023, 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.graal.hosted.runtimecompilation;
+
+import static com.oracle.svm.common.meta.MultiMethod.ORIGINAL_METHOD;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.oracle.graal.pointsto.meta.AnalysisMethod;
+import com.oracle.svm.common.meta.MultiMethod;
+import com.oracle.svm.hosted.code.SubstrateCompilationDirectives;
+
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+public class RuntimeCompiledMethod {
+ final AnalysisMethod runtimeMethod;
+ final AnalysisMethod originalMethod;
+
+ /**
+ * Collection of all methods inlined into this method. All methods contained here are of the
+ * {@link MultiMethod#ORIGINAL_METHOD} type.
+ */
+ final Collection inlinedMethods;
+
+ RuntimeCompiledMethod(AnalysisMethod runtimeMethod, Collection inlinedMethods) {
+ this.runtimeMethod = runtimeMethod;
+ assert SubstrateCompilationDirectives.isRuntimeCompiledMethod(runtimeMethod) : runtimeMethod;
+ this.originalMethod = runtimeMethod.getMultiMethod(ORIGINAL_METHOD);
+ assert originalMethod != null;
+ this.inlinedMethods = inlinedMethods;
+ }
+
+ public AnalysisMethod getRuntimeMethod() {
+ return runtimeMethod;
+ }
+
+ public AnalysisMethod getOriginalMethod() {
+ return originalMethod;
+ }
+
+ public Collection getInlinedMethods() {
+ return inlinedMethods;
+ }
+
+ public Collection getInvokeTargets() {
+ List targets = new ArrayList<>();
+ for (var invoke : runtimeMethod.getInvokes()) {
+ if (invoke.isDeoptInvokeTypeFlow()) {
+ // deopt invoke type flows are not real targets
+ continue;
+ }
+ targets.add(invoke.getTargetMethod());
+ }
+ return targets;
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java
new file mode 100644
index 000000000000..954cdc5616b8
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 2023, 2023, 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.graal.hosted.runtimecompilation;
+
+import static com.oracle.svm.common.meta.MultiMethod.DEOPT_TARGET_METHOD;
+import static com.oracle.svm.common.meta.MultiMethod.ORIGINAL_METHOD;
+import static com.oracle.svm.hosted.code.SubstrateCompilationDirectives.RUNTIME_COMPILED_METHOD;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import org.graalvm.collections.EconomicMap;
+import org.graalvm.word.LocationIdentity;
+
+import com.oracle.graal.pointsto.BigBang;
+import com.oracle.graal.pointsto.heap.ImageHeapConstant;
+import com.oracle.graal.pointsto.heap.ImageHeapScanner;
+import com.oracle.graal.pointsto.meta.AnalysisField;
+import com.oracle.graal.pointsto.meta.AnalysisMethod;
+import com.oracle.graal.pointsto.meta.HostedProviders;
+import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod;
+import com.oracle.graal.pointsto.util.CompletionExecutor;
+import com.oracle.svm.core.config.ConfigurationValues;
+import com.oracle.svm.core.graal.nodes.DeoptEntryNode;
+import com.oracle.svm.core.graal.nodes.DeoptEntrySupport;
+import com.oracle.svm.core.graal.nodes.DeoptProxyAnchorNode;
+import com.oracle.svm.core.graal.nodes.ThrowBytecodeExceptionNode;
+import com.oracle.svm.core.option.HostedOptionKey;
+import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.hosted.HeapBreakdownProvider;
+import com.oracle.svm.hosted.SVMHost;
+import com.oracle.svm.hosted.ameta.AnalysisConstantFieldProvider;
+import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider;
+import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
+import com.oracle.svm.hosted.code.CompileQueue;
+import com.oracle.svm.hosted.code.DeoptimizationUtils;
+import com.oracle.svm.hosted.code.SubstrateCompilationDirectives;
+import com.oracle.svm.hosted.meta.HostedConstantFieldProvider;
+import com.oracle.svm.hosted.meta.HostedField;
+import com.oracle.svm.hosted.meta.HostedMethod;
+import com.oracle.svm.hosted.meta.HostedUniverse;
+import com.oracle.svm.hosted.nodes.DeoptProxyNode;
+import com.oracle.svm.hosted.phases.AnalysisGraphBuilderPhase;
+
+import jdk.graal.compiler.core.common.spi.ConstantFieldProvider;
+import jdk.graal.compiler.debug.DebugContext;
+import jdk.graal.compiler.debug.DebugHandlersFactory;
+import jdk.graal.compiler.java.BytecodeParser;
+import jdk.graal.compiler.java.GraphBuilderPhase;
+import jdk.graal.compiler.loop.phases.ConvertDeoptimizeToGuardPhase;
+import jdk.graal.compiler.nodes.CallTargetNode;
+import jdk.graal.compiler.nodes.FrameState;
+import jdk.graal.compiler.nodes.GraphDecoder;
+import jdk.graal.compiler.nodes.GraphEncoder;
+import jdk.graal.compiler.nodes.StateSplit;
+import jdk.graal.compiler.nodes.StructuredGraph;
+import jdk.graal.compiler.nodes.ValueNode;
+import jdk.graal.compiler.nodes.extended.BytecodeExceptionNode;
+import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
+import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext;
+import jdk.graal.compiler.nodes.java.ExceptionObjectNode;
+import jdk.graal.compiler.options.Option;
+import jdk.graal.compiler.options.OptionValues;
+import jdk.graal.compiler.phases.OptimisticOptimizations;
+import jdk.graal.compiler.phases.Phase;
+import jdk.graal.compiler.phases.PhaseSuite;
+import jdk.graal.compiler.phases.common.CanonicalizerPhase;
+import jdk.graal.compiler.phases.common.DominatorBasedGlobalValueNumberingPhase;
+import jdk.graal.compiler.phases.common.IterativeConditionalEliminationPhase;
+import jdk.graal.compiler.phases.tiers.HighTierContext;
+import jdk.graal.compiler.phases.util.Providers;
+import jdk.graal.compiler.printer.GraalDebugHandlersFactory;
+import jdk.graal.compiler.truffle.nodes.ObjectLocationIdentity;
+import jdk.vm.ci.code.Architecture;
+import jdk.vm.ci.code.BytecodeFrame;
+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;
+
+/**
+ * This class infrastructure needed for creating the RuntimeCompiledMethods variants.
+ */
+public class RuntimeCompiledMethodSupport {
+
+ public static class Options {
+ @Option(help = "Remove Deopt(Entries,Anchors,Proxies) determined to be unneeded after the runtime compiled graphs have been finalized.")//
+ public static final HostedOptionKey RemoveUnneededDeoptSupport = new HostedOptionKey<>(true);
+ }
+
+ private record CompilationState(
+ GraalGraphObjectReplacer objectReplacer,
+ GraphEncoder graphEncoder,
+ HostedProviders runtimeCompilationProviders,
+ ImageHeapScanner heapScanner,
+ Map runtimeGraphs,
+ Set registeredRuntimeCompilations) {
+ }
+
+ public static void onCompileQueueCreation(BigBang bb, HostedUniverse hUniverse, CompileQueue compileQueue, HostedProviders hostedProviders,
+ Function constantFieldProviderWrapper,
+ GraalGraphObjectReplacer objectReplacer, Set registeredRuntimeCompilations, Stream methodsToCompile) {
+
+ ImageHeapScanner imageScanner = bb.getUniverse().getHeapScanner();
+
+ GraphEncoder graphEncoder = new RuntimeCompiledMethodSupport.RuntimeCompilationGraphEncoder(ConfigurationValues.getTarget().arch, imageScanner);
+ HostedProviders runtimeCompilationProviders = hostedProviders
+ .copyWith(constantFieldProviderWrapper.apply(new RuntimeCompilationFieldProvider(hostedProviders.getMetaAccess(), hUniverse)))
+ .copyWith(new RuntimeCompilationReflectionProvider(bb, hUniverse.hostVM().getClassInitializationSupport()));
+
+ SubstrateCompilationDirectives.singleton().resetDeoptEntries();
+
+ CompilationState compilationState = new CompilationState(objectReplacer, graphEncoder, runtimeCompilationProviders, imageScanner, new ConcurrentHashMap<>(),
+ registeredRuntimeCompilations);
+
+ /*
+ * Customize runtime compile methods for compiling them into substrate graphs.
+ */
+ CompletionExecutor executor = compileQueue.getExecutor();
+ try {
+ compileQueue.runOnExecutor(() -> {
+ methodsToCompile.forEach(method -> {
+ executor.execute(new RuntimeCompileTask(method, compilationState));
+ });
+ });
+ } catch (InterruptedException exception) {
+ VMError.shouldNotReachHere(exception);
+ }
+
+ encodeRuntimeCompiledMethods(compilationState);
+
+ /*
+ * For Deoptimization Targets add a custom phase which removes all deoptimization
+ * entrypoints which are deemed no longer necessary.
+ */
+ CompileQueue.ParseHooks deoptParseHooks = new CompileQueue.ParseHooks(compileQueue) {
+ @Override
+ protected PhaseSuite getAfterParseSuite() {
+ PhaseSuite suite = super.getAfterParseSuite();
+ if (Options.RemoveUnneededDeoptSupport.getValue()) {
+ suite.prependPhase(new RemoveUnneededDeoptSupport());
+ }
+
+ return suite;
+ }
+ };
+
+ hUniverse.getMethods().stream().map(method -> method.getMultiMethod(DEOPT_TARGET_METHOD)).filter(method -> {
+ if (method != null) {
+ return compileQueue.isRegisteredDeoptTarget(method);
+ }
+ return false;
+ }).forEach(method -> method.compilationInfo.setCustomParseHooks(deoptParseHooks));
+
+ }
+
+ private static class RuntimeCompileTask implements CompletionExecutor.DebugContextRunnable {
+ final HostedMethod method;
+ final CompilationState compilationState;
+
+ RuntimeCompileTask(HostedMethod method, CompilationState compilationState) {
+ this.method = method;
+ this.compilationState = compilationState;
+ }
+
+ @Override
+ public DebugContext getDebug(OptionValues options, List factories) {
+ return new DebugContext.Builder(options, factories).description(getDescription()).build();
+ }
+
+ @Override
+ public void run(DebugContext debug) {
+ compileRuntimeCompiledMethod(debug, method);
+ }
+
+ @SuppressWarnings("try")
+ private void compileRuntimeCompiledMethod(DebugContext debug, HostedMethod method) {
+ assert method.getMultiMethodKey() == RUNTIME_COMPILED_METHOD;
+
+ AnalysisMethod aMethod = method.getWrapped();
+ StructuredGraph graph = aMethod.decodeAnalyzedGraph(debug, null, false,
+ (arch, analyzedGraph) -> new RuntimeCompilationGraphDecoder(arch, analyzedGraph, compilationState.heapScanner));
+ if (graph == null) {
+ throw VMError.shouldNotReachHere("Method not parsed during static analysis: " + aMethod.format("%r %H.%n(%p)"));
+ }
+ /*
+ * The graph in the analysis universe is no longer necessary once it is transplanted
+ * into the hosted universe.
+ */
+ aMethod.setAnalyzedGraph(null);
+
+ try (DebugContext.Scope s = debug.scope("RuntimeOptimize", graph, method, this)) {
+ CanonicalizerPhase canonicalizer = CanonicalizerPhase.create();
+ canonicalizer.apply(graph, compilationState.runtimeCompilationProviders);
+
+ new DominatorBasedGlobalValueNumberingPhase(canonicalizer).apply(graph, compilationState.runtimeCompilationProviders);
+
+ new IterativeConditionalEliminationPhase(canonicalizer, true).apply(graph, compilationState.runtimeCompilationProviders);
+
+ /*
+ * ConvertDeoptimizeToGuardPhase was already executed after parsing, but
+ * optimizations applied in between can provide new potential.
+ */
+ new ConvertDeoptimizeToGuardPhase(canonicalizer).apply(graph, compilationState.runtimeCompilationProviders);
+
+ /*
+ * More optimizations can be added here.
+ */
+ } catch (Throwable e) {
+ throw debug.handle(e);
+ }
+
+ /*
+ * Registering all deopt entries seen within the optimized graph. This should be
+ * strictly a subset of the deopt entrypoints seen during evaluation.
+ */
+ AnalysisMethod origMethod = method.getMultiMethod(ORIGINAL_METHOD).getWrapped();
+ DeoptimizationUtils.registerDeoptEntries(graph, compilationState.registeredRuntimeCompilations.contains(origMethod),
+ (deoptEntryMethod -> {
+ PointsToAnalysisMethod deoptMethod = (PointsToAnalysisMethod) ((PointsToAnalysisMethod) deoptEntryMethod).getMultiMethod(DEOPT_TARGET_METHOD);
+ VMError.guarantee(deoptMethod != null, "New deopt target method seen: %s", deoptEntryMethod);
+ return deoptMethod;
+ }));
+
+ assert verifyNodes(graph);
+ var previous = compilationState.runtimeGraphs.put(method, graph);
+ assert previous == null;
+
+ // graph encoder is not currently threadsafe
+ synchronized (compilationState.graphEncoder) {
+ compilationState.graphEncoder.prepare(graph);
+ }
+ }
+ }
+
+ /**
+ * Checks if any illegal nodes are present within the graph. Runtime Compiled methods should
+ * never have explicit BytecodeExceptions; instead they should have deoptimizations.
+ */
+ static boolean verifyNodes(StructuredGraph graph) {
+ for (var node : graph.getNodes()) {
+ boolean invalidNodeKind = node instanceof BytecodeExceptionNode || node instanceof ThrowBytecodeExceptionNode;
+ assert !invalidNodeKind : "illegal node in graph: " + node + " method: " + graph.method();
+ }
+ return true;
+ }
+
+ @SuppressWarnings("try")
+ private static void encodeRuntimeCompiledMethods(CompilationState compilationState) {
+ compilationState.graphEncoder.finishPrepare();
+
+ // at this point no new deoptimization entrypoints can be registered.
+ SubstrateCompilationDirectives.singleton().sealDeoptimizationInfo();
+
+ for (var runtimeInfo : compilationState.runtimeGraphs.entrySet()) {
+ var graph = runtimeInfo.getValue();
+ var method = runtimeInfo.getKey();
+ DebugContext debug = new DebugContext.Builder(graph.getOptions(), new GraalDebugHandlersFactory(compilationState.runtimeCompilationProviders.getSnippetReflection())).build();
+ graph.resetDebug(debug);
+ try (DebugContext.Scope s = debug.scope("Graph Encoding", graph);
+ DebugContext.Activation a = debug.activate()) {
+ long startOffset = compilationState.graphEncoder.encode(graph);
+ compilationState.objectReplacer.createMethod(method).setEncodedGraphStartOffset(startOffset);
+ } catch (Throwable ex) {
+ debug.handle(ex);
+ }
+ }
+
+ HeapBreakdownProvider.singleton().setGraphEncodingByteLength(compilationState.graphEncoder.getEncoding().length);
+ com.oracle.svm.graal.TruffleRuntimeCompilationSupport.setGraphEncoding(null, compilationState.graphEncoder.getEncoding(), compilationState.graphEncoder.getObjects(),
+ compilationState.graphEncoder.getNodeClasses());
+
+ compilationState.objectReplacer.setMethodsImplementations();
+ }
+
+ /**
+ * Since we perform runtime compilation after universe creation, we can leverage components of
+ * the hosted universe provider for identifying final fields.
+ */
+ static class RuntimeCompilationFieldProvider extends AnalysisConstantFieldProvider {
+ final HostedUniverse hUniverse;
+
+ RuntimeCompilationFieldProvider(MetaAccessProvider metaAccess, HostedUniverse hUniverse) {
+ super(metaAccess, hUniverse.hostVM());
+ this.hUniverse = hUniverse;
+ }
+
+ @Override
+ public boolean isFinalField(ResolvedJavaField f, ConstantFieldTool> tool) {
+ HostedField hField = hUniverse.lookup(f);
+ if (HostedConstantFieldProvider.isFinalField(hField)) {
+ return true;
+ }
+ return super.isFinalField(f, tool);
+ }
+ }
+
+ static class RuntimeCompilationReflectionProvider extends AnalysisConstantReflectionProvider {
+
+ RuntimeCompilationReflectionProvider(BigBang bb, ClassInitializationSupport classInitializationSupport) {
+ super(bb.getUniverse(), bb.getMetaAccess(), classInitializationSupport);
+ }
+
+ @Override
+ public JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receiver) {
+ /*
+ * We cannot fold simulated values during initial before-analysis graph creation;
+ * however, this runs after analysis has completed.
+ */
+ return readValue(metaAccess, (AnalysisField) field, receiver, true);
+ }
+ }
+
+ /**
+ * A graph encoder that unwraps the {@link ImageHeapConstant} objects. This is used both after
+ * analysis and after compilation. The corresponding graph decoder used during AOT compilation,
+ * {@link RuntimeCompilationGraphDecoder}, looks-up the constant in the shadow heap and re-wraps
+ * it.
+ *
+ * The reason why we need to unwrap the {@link ImageHeapConstant}s after analysis, and not only
+ * when we finally encode the graphs for run time compilation, is because the values in
+ * {@link GraphEncoder#objectsArray} are captured in GraalSupport#graphObjects and
+ * SubstrateReplacements#snippetObjects which are then scanned.
+ */
+ public static class RuntimeCompilationGraphEncoder extends GraphEncoder {
+
+ private final ImageHeapScanner heapScanner;
+ /**
+ * Cache already converted location identity objects to avoid creating multiple new
+ * instances for the same underlying location identity.
+ */
+ private final Map locationIdentityCache;
+
+ public RuntimeCompilationGraphEncoder(Architecture architecture, ImageHeapScanner heapScanner) {
+ super(architecture);
+ this.heapScanner = heapScanner;
+ this.locationIdentityCache = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ protected void addObject(Object object) {
+ super.addObject(unwrap(object));
+ }
+
+ @Override
+ protected void writeObjectId(Object object) {
+ super.writeObjectId(unwrap(object));
+ }
+
+ @Override
+ protected GraphDecoder graphDecoderForVerification(StructuredGraph decodedGraph) {
+ return new RuntimeCompilationGraphDecoder(architecture, decodedGraph, heapScanner);
+ }
+
+ private Object unwrap(Object object) {
+ if (object instanceof ImageHeapConstant ihc) {
+ VMError.guarantee(ihc.getHostedObject() != null);
+ return ihc.getHostedObject();
+ } else if (object instanceof ObjectLocationIdentity oli && oli.getObject() instanceof ImageHeapConstant heapConstant) {
+ return locationIdentityCache.computeIfAbsent(heapConstant, (hc) -> ObjectLocationIdentity.create(hc.getHostedObject()));
+ }
+ return object;
+ }
+ }
+
+ static class RuntimeCompilationGraphDecoder extends GraphDecoder {
+
+ private final ImageHeapScanner heapScanner;
+ /**
+ * Cache already converted location identity objects to avoid creating multiple new
+ * instances for the same underlying location identity.
+ */
+ private final Map locationIdentityCache;
+
+ RuntimeCompilationGraphDecoder(Architecture architecture, StructuredGraph graph, ImageHeapScanner heapScanner) {
+ super(architecture, graph);
+ this.heapScanner = heapScanner;
+ this.locationIdentityCache = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ protected Object readObject(MethodScope methodScope) {
+ Object object = super.readObject(methodScope);
+ if (object instanceof JavaConstant constant) {
+ return heapScanner.getImageHeapConstant(constant);
+ } else if (object instanceof ObjectLocationIdentity oli) {
+ return locationIdentityCache.computeIfAbsent(oli.getObject(), (obj) -> ObjectLocationIdentity.create(heapScanner.getImageHeapConstant(obj)));
+ }
+ return object;
+ }
+ }
+
+ static final class RuntimeGraphBuilderPhase extends AnalysisGraphBuilderPhase {
+
+ private RuntimeGraphBuilderPhase(Providers providers,
+ GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, SVMHost hostVM) {
+ super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, hostVM);
+ }
+
+ static RuntimeGraphBuilderPhase createRuntimeGraphBuilderPhase(BigBang bb, Providers providers,
+ GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts) {
+
+ // Adjust graphbuilderconfig to match analysis phase
+ var newGraphBuilderConfig = graphBuilderConfig.withEagerResolving(true).withUnresolvedIsError(false);
+ return new RuntimeGraphBuilderPhase(providers, newGraphBuilderConfig, optimisticOpts, null, (SVMHost) bb.getHostVM());
+ }
+
+ @Override
+ protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
+ return new RuntimeBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext, hostVM);
+ }
+ }
+
+ private static final class RuntimeBytecodeParser extends AnalysisGraphBuilderPhase.AnalysisBytecodeParser {
+
+ RuntimeBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI,
+ IntrinsicContext intrinsicContext, SVMHost svmHost) {
+ super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, svmHost, false);
+ }
+
+ @Override
+ protected boolean tryInvocationPlugin(CallTargetNode.InvokeKind invokeKind, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType) {
+ boolean result = super.tryInvocationPlugin(invokeKind, args, targetMethod, resultType);
+ if (result) {
+ SubstrateCompilationDirectives.singleton().registerAsDeoptInlininingExclude(targetMethod);
+ }
+ return result;
+ }
+
+ @Override
+ protected boolean shouldVerifyFrameStates() {
+ /*
+ * (GR-46115) Ideally we should verify frame states in methods registered for runtime
+ * compilations, as well as any other methods that can deoptimize. Because runtime
+ * compiled methods can pull in almost arbitrary code, this means most frame states
+ * should be verified. We currently use illegal states as placeholders in many places,
+ * so this cannot be enabled at the moment.
+ */
+ return false;
+ }
+ }
+
+ /**
+ * Removes {@link DeoptEntryNode}s, {@link DeoptProxyAnchorNode}s, and {@link DeoptProxyNode}s
+ * which are determined to be unnecessary after the runtime compilation methods are optimized.
+ */
+ private static class RemoveUnneededDeoptSupport extends Phase {
+ enum RemovalDecision {
+ KEEP,
+ PROXIFY,
+ REMOVE
+ }
+
+ @Override
+ protected void run(StructuredGraph graph) {
+ EconomicMap decisionCache = EconomicMap.create();
+
+ // First go through and delete all unneeded proxies
+ for (DeoptProxyNode proxyNode : graph.getNodes(DeoptProxyNode.TYPE).snapshot()) {
+ ValueNode proxyPoint = proxyNode.getProxyPoint();
+ if (proxyPoint instanceof StateSplit) {
+ if (getDecision((StateSplit) proxyPoint, decisionCache) == RemovalDecision.REMOVE) {
+ proxyNode.replaceAtAllUsages(proxyNode.getOriginalNode(), true);
+ proxyNode.safeDelete();
+ }
+ }
+ }
+
+ // Next, remove all unneeded DeoptEntryNodes
+ for (DeoptEntryNode deoptEntry : graph.getNodes().filter(DeoptEntryNode.class).snapshot()) {
+ switch (getDecision(deoptEntry, decisionCache)) {
+ case REMOVE -> {
+ deoptEntry.killExceptionEdge();
+ graph.removeSplit(deoptEntry, deoptEntry.getPrimarySuccessor());
+ }
+ case PROXIFY -> {
+ deoptEntry.killExceptionEdge();
+ DeoptProxyAnchorNode newAnchor = graph.add(new DeoptProxyAnchorNode(deoptEntry.getProxifiedInvokeBci()));
+ newAnchor.setStateAfter(deoptEntry.stateAfter());
+ graph.replaceSplitWithFixed(deoptEntry, newAnchor, deoptEntry.getPrimarySuccessor());
+ }
+ }
+ }
+
+ // Finally, remove all unneeded DeoptProxyAnchorNodes
+ for (DeoptProxyAnchorNode proxyAnchor : graph.getNodes().filter(DeoptProxyAnchorNode.class).snapshot()) {
+ if (getDecision(proxyAnchor, decisionCache) == RemovalDecision.REMOVE) {
+ graph.removeFixed(proxyAnchor);
+ }
+ }
+ }
+
+ RemovalDecision getDecision(StateSplit node, EconomicMap decisionCache) {
+ RemovalDecision cached = decisionCache.get(node);
+ if (cached != null) {
+ return cached;
+ }
+
+ DeoptEntrySupport proxyNode;
+ if (node instanceof ExceptionObjectNode exceptionObject) {
+ /*
+ * For the exception edge of a DeoptEntryNode, we insert the proxies on the
+ * exception object.
+ */
+ proxyNode = (DeoptEntrySupport) exceptionObject.predecessor();
+ } else {
+ proxyNode = (DeoptEntrySupport) node;
+ }
+
+ RemovalDecision decision = RemovalDecision.REMOVE;
+ var directive = SubstrateCompilationDirectives.singleton();
+ FrameState state = proxyNode.stateAfter();
+ HostedMethod method = (HostedMethod) state.getMethod();
+ if (proxyNode instanceof DeoptEntryNode) {
+ if (directive.isDeoptEntry(method, state.bci, state.getStackState())) {
+ // must keep all deopt entries which are still guarding nodes
+ decision = RemovalDecision.KEEP;
+ }
+ }
+
+ if (decision == RemovalDecision.REMOVE) {
+ // now check for any implicit deopt entry being protected against
+ int proxifiedInvokeBci = proxyNode.getProxifiedInvokeBci();
+ if (proxifiedInvokeBci != BytecodeFrame.UNKNOWN_BCI && directive.isDeoptEntry(method, proxifiedInvokeBci, FrameState.StackState.AfterPop)) {
+ // must keep still keep a proxy for nodes which are "proxifying" an invoke
+ decision = proxyNode instanceof DeoptEntryNode ? RemovalDecision.PROXIFY : RemovalDecision.KEEP;
+ }
+ }
+
+ // cache the decision
+ decisionCache.put(node, decision);
+ if (proxyNode != node) {
+ decisionCache.put(proxyNode, decision);
+ }
+ return decision;
+ }
+
+ @Override
+ public CharSequence getName() {
+ return "RemoveUnneededDeoptSupport";
+ }
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateGraalCompilerSetup.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/SubstrateGraalCompilerSetup.java
similarity index 98%
rename from substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateGraalCompilerSetup.java
rename to substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/SubstrateGraalCompilerSetup.java
index 43b70d36c3d8..8124c7ddc8da 100644
--- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateGraalCompilerSetup.java
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/SubstrateGraalCompilerSetup.java
@@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package com.oracle.svm.graal.hosted;
+package com.oracle.svm.graal.hosted.runtimecompilation;
import java.util.function.Function;
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateProviders.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/SubstrateProviders.java
similarity index 98%
rename from substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateProviders.java
rename to substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/SubstrateProviders.java
index c05496a5cd8a..cd135cee5ec0 100644
--- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/SubstrateProviders.java
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/SubstrateProviders.java
@@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package com.oracle.svm.graal.hosted;
+package com.oracle.svm.graal.hosted.runtimecompilation;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.svm.core.graal.meta.SubstrateSnippetReflectionProvider;
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolateAwareProviders.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolateAwareProviders.java
index c0d50ef2ffea..88c434d54775 100644
--- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolateAwareProviders.java
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolateAwareProviders.java
@@ -25,7 +25,7 @@
package com.oracle.svm.graal.isolated;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
-import com.oracle.svm.graal.hosted.SubstrateProviders;
+import com.oracle.svm.graal.hosted.runtimecompilation.SubstrateProviders;
import com.oracle.svm.graal.meta.SubstrateConstantFieldProvider;
public final class IsolateAwareProviders extends SubstrateProviders {
diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java
index b6fb0a8b5d25..4c43000bf0dc 100644
--- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java
+++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java
@@ -63,7 +63,7 @@
import com.oracle.svm.graal.TruffleRuntimeCompilationSupport;
import com.oracle.svm.graal.hosted.FieldsOffsetsFeature;
import com.oracle.svm.graal.hosted.GraalCompilerFeature;
-import com.oracle.svm.graal.hosted.RuntimeCompilationFeature;
+import com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompilationFeature;
import com.oracle.svm.graal.meta.SubstrateMethod;
import com.oracle.svm.util.ReflectionUtil;
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
index fbce62ddb497..4b2b2f91a1af 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
@@ -648,8 +648,8 @@ protected void doRun(Map entryPoints, JavaMainSupport j
CompileQueue compileQueue;
try (StopTimer t = TimerCollection.createTimerAndStart(TimerCollection.Registry.COMPILE_TOTAL)) {
compileQueue = HostedConfiguration.instance().createCompileQueue(debug, featureHandler, hUniverse, runtimeConfiguration, DeoptTester.enabled(), bb.getSnippetReflectionProvider());
- if (ImageSingletons.contains(RuntimeCompilationSupport.class)) {
- ImageSingletons.lookup(RuntimeCompilationSupport.class).onCompileQueueCreation(bb, hUniverse, compileQueue);
+ if (ImageSingletons.contains(RuntimeCompilationCallbacks.class)) {
+ ImageSingletons.lookup(RuntimeCompilationCallbacks.class).onCompileQueueCreation(bb, hUniverse, compileQueue);
}
compileQueue.finish(debug);
BuildPhaseProvider.markCompileQueueFinished();
@@ -799,8 +799,16 @@ protected boolean runPointsToAnalysis(String imageName, OptionValues options, De
}
return !config.getAndResetRequireAnalysisIteration();
});
- } catch (AnalysisError e) {
- throw UserError.abort(e, "Analysis step failed. Reason: %s.", e.getMessage());
+ } catch (Throwable t) {
+ if (ImageSingletons.contains(RuntimeCompilationCallbacks.class)) {
+ ImageSingletons.lookup(RuntimeCompilationCallbacks.class).reportAnalysisError(aUniverse, t);
+ }
+
+ if (t instanceof AnalysisError e) {
+ throw UserError.abort(e, "Analysis step failed. Reason: %s.", e.getMessage());
+ } else {
+ throw t;
+ }
}
assert verifyAssignableTypes();
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RuntimeCompilationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RuntimeCompilationCallbacks.java
similarity index 89%
rename from substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RuntimeCompilationSupport.java
rename to substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RuntimeCompilationCallbacks.java
index a6530ef70880..d1a6414c64fc 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RuntimeCompilationSupport.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/RuntimeCompilationCallbacks.java
@@ -25,9 +25,12 @@
package com.oracle.svm.hosted;
import com.oracle.graal.pointsto.BigBang;
+import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.svm.hosted.code.CompileQueue;
import com.oracle.svm.hosted.meta.HostedUniverse;
-public interface RuntimeCompilationSupport {
+public interface RuntimeCompilationCallbacks {
void onCompileQueueCreation(BigBang bb, HostedUniverse hUniverse, CompileQueue compileQueue);
+
+ void reportAnalysisError(AnalysisUniverse aUniverse, Throwable error);
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/SVMParsingSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/SVMParsingSupport.java
index ae0911f99a81..ee2b1cc5d8dc 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/SVMParsingSupport.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/SVMParsingSupport.java
@@ -26,10 +26,6 @@
import java.util.function.Function;
-import jdk.graal.compiler.debug.DebugContext;
-import jdk.graal.compiler.nodes.StructuredGraph;
-import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
-
import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.PointsToAnalysis;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
@@ -40,6 +36,9 @@
import com.oracle.svm.hosted.SVMHost;
import com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyUtils;
+import jdk.graal.compiler.debug.DebugContext;
+import jdk.graal.compiler.nodes.StructuredGraph;
+import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import jdk.vm.ci.meta.ResolvedJavaType;
/**
diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java
index a91e7eef28d0..1c430527e219 100644
--- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java
+++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java
@@ -102,9 +102,9 @@
import com.oracle.svm.core.reflect.target.ReflectionSubstitutionSupport;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
-import com.oracle.svm.graal.hosted.GraalGraphObjectReplacer;
-import com.oracle.svm.graal.hosted.SubstrateGraalCompilerSetup;
-import com.oracle.svm.graal.hosted.SubstrateProviders;
+import com.oracle.svm.graal.hosted.runtimecompilation.GraalGraphObjectReplacer;
+import com.oracle.svm.graal.hosted.runtimecompilation.SubstrateGraalCompilerSetup;
+import com.oracle.svm.graal.hosted.runtimecompilation.SubstrateProviders;
import com.oracle.svm.graal.meta.SubstrateUniverseFactory;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java
index e3b26b3e93c3..27f42f9680c7 100644
--- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java
+++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java
@@ -25,9 +25,9 @@
package com.oracle.svm.truffle;
-import static com.oracle.svm.graal.hosted.RuntimeCompilationFeature.AllowInliningPredicate.InlineDecision.INLINE;
-import static com.oracle.svm.graal.hosted.RuntimeCompilationFeature.AllowInliningPredicate.InlineDecision.INLINING_DISALLOWED;
-import static com.oracle.svm.graal.hosted.RuntimeCompilationFeature.AllowInliningPredicate.InlineDecision.NO_DECISION;
+import static com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompilationFeature.AllowInliningPredicate.InlineDecision.INLINE;
+import static com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompilationFeature.AllowInliningPredicate.InlineDecision.INLINING_DISALLOWED;
+import static com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompilationFeature.AllowInliningPredicate.InlineDecision.NO_DECISION;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
@@ -124,9 +124,10 @@
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.RuntimeCompilationFeature;
-import com.oracle.svm.graal.hosted.RuntimeCompilationFeature.RuntimeCompilationCandidate;
-import com.oracle.svm.graal.hosted.RuntimeCompilationFeature.RuntimeCompiledMethod;
+import com.oracle.svm.graal.hosted.runtimecompilation.CallTreeInfo;
+import com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompilationCandidate;
+import com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompilationFeature;
+import com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompiledMethod;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
@@ -248,7 +249,7 @@ public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues o
@Override
public List> getRequiredFeatures() {
- return List.of(RuntimeCompilationFeature.getRuntimeCompilationFeature(), TruffleBaseFeature.class);
+ return List.of(RuntimeCompilationFeature.class, TruffleBaseFeature.class);
}
/*
@@ -389,7 +390,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
runtimeCompilationFeature.getHostedProviders().getLoopsDataProvider());
newHostedProviders.setGraphBuilderPlugins(graphBuilderConfig.getPlugins());
- runtimeCompilationFeature.initializeRuntimeCompilationConfiguration(newHostedProviders, graphBuilderConfig, this::allowRuntimeCompilation, this::deoptimizeOnException);
+ runtimeCompilationFeature.initializeRuntimeCompilationConfiguration(newHostedProviders, graphBuilderConfig, this::allowRuntimeCompilation, this::deoptimizeOnException, this::checkBlockList);
for (ResolvedJavaMethod method : partialEvaluator.getCompilationRootMethods()) {
runtimeCompilationFeature.prepareMethodForRuntimeCompilation(method, config);
}
@@ -780,10 +781,11 @@ private void warnAllMethods(MetaAccessProvider metaAccess, Class> clazz) {
}
}
- @Override
- public void beforeCompilation(BeforeCompilationAccess config) {
- FeatureImpl.BeforeCompilationAccessImpl access = (FeatureImpl.BeforeCompilationAccessImpl) config;
+ private record BlocklistViolationInfo(RuntimeCompilationCandidate candidate, String[] callTrace) {
+ };
+ private void checkBlockList(CallTreeInfo treeInfo) {
+ RuntimeCompilationFeature runtimeCompilation = RuntimeCompilationFeature.singleton();
boolean failBlockListViolations;
if (Options.TruffleCheckBlackListedMethods.hasBeenSet()) {
failBlockListViolations = Options.TruffleCheckBlackListedMethods.getValue();
@@ -792,8 +794,8 @@ public void beforeCompilation(BeforeCompilationAccess config) {
}
boolean printBlockListViolations = RuntimeCompilationFeature.Options.PrintRuntimeCompileMethods.getValue() || failBlockListViolations;
if (printBlockListViolations) {
- Set blocklistViolations = new TreeSet<>(RuntimeCompilationFeature.singleton().getRuntimeCompilationComparator());
- for (RuntimeCompilationCandidate candidate : RuntimeCompilationFeature.singleton().getAllRuntimeCompilationCandidates()) {
+ Set blocklistViolations = new TreeSet<>((o1, o2) -> Arrays.compare(o1.callTrace(), o2.callTrace()));
+ for (RuntimeCompilationCandidate candidate : runtimeCompilation.getAllRuntimeCompilationCandidates()) {
// Determine blocklist violations
if (!runtimeCompilationForbidden(candidate.getImplementationMethod())) {
@@ -802,7 +804,8 @@ public void beforeCompilation(BeforeCompilationAccess config) {
tempTargetAllowlistMethods.contains(candidate.getTargetMethod()) &&
!isBlocklisted(candidate.getImplementationMethod());
if (!tempAllow) {
- blocklistViolations.add(candidate);
+ BlocklistViolationInfo violation = new BlocklistViolationInfo(candidate, CallTreeInfo.getCallTrace(treeInfo, candidate));
+ blocklistViolations.add(violation);
}
}
}
@@ -811,11 +814,13 @@ public void beforeCompilation(BeforeCompilationAccess config) {
System.out.println();
System.out.println("=== Found " + blocklistViolations.size() + " compilation blocklist violations ===");
System.out.println();
- for (RuntimeCompilationCandidate violation : blocklistViolations) {
+ for (BlocklistViolationInfo violation : blocklistViolations) {
System.out.println("Blocklisted method");
- System.out.format(" %s (target: %s)%n", violation.getImplementationMethod().format("%H.%n(%p)"), violation.getTargetMethod().format("%H.%n(%p)"));
+ System.out.format(" %s (target: %s)%n", violation.candidate.getImplementationMethod().format("%H.%n(%p)"), violation.candidate.getTargetMethod().format("%H.%n(%p)"));
System.out.println("trace:");
- RuntimeCompilationFeature.singleton().getCallTrace(violation).forEach(item -> System.out.println(" " + item));
+ for (String item : violation.callTrace()) {
+ System.out.println(" " + item);
+ }
}
if (failBlockListViolations) {
throw VMError.shouldNotReachHere("Blocklisted methods are reachable for runtime compilation");
@@ -824,7 +829,7 @@ public void beforeCompilation(BeforeCompilationAccess config) {
}
Set warnViolations = new HashSet<>();
- for (RuntimeCompilationCandidate node : RuntimeCompilationFeature.singleton().getAllRuntimeCompilationCandidates()) {
+ for (RuntimeCompilationCandidate node : runtimeCompilation.getAllRuntimeCompilationCandidates()) {
var method = node.getImplementationMethod();
if (warnMethods.contains(method)) {
warnViolations.add(node);
@@ -841,7 +846,9 @@ public void beforeCompilation(BeforeCompilationAccess config) {
for (RuntimeCompilationCandidate violation : warnViolations) {
System.out.println("Suspicious method: " + violation.getImplementationMethod().format("%H.%n(%p)"));
System.out.println("trace:");
- RuntimeCompilationFeature.singleton().getCallTrace(violation).forEach(item -> System.out.println(" " + item));
+ for (String item : CallTreeInfo.getCallTrace(treeInfo, violation)) {
+ System.out.println(" " + item);
+ }
}
}
@@ -850,10 +857,16 @@ public void beforeCompilation(BeforeCompilationAccess config) {
for (Pair violation : neverPartOfCompilationViolations) {
System.out.println("called from");
System.out.println("(inlined call path): " + violation.getRight());
- RuntimeCompilationFeature.singleton().getCallTrace(violation.getLeft()).forEach(item -> System.out.println(" " + item));
+ for (String item : CallTreeInfo.getCallTrace(treeInfo, (AnalysisMethod) violation.getLeft())) {
+ System.out.println(" " + item);
+ }
}
throw VMError.shouldNotReachHere("CompilerAsserts.neverPartOfCompilation reachable for runtime compilation");
}
+ }
+
+ @Override
+ public void beforeCompilation(BeforeCompilationAccess config) {
if (Options.TruffleCheckFrameImplementation.getValue()) {
/*
@@ -865,7 +878,7 @@ public void beforeCompilation(BeforeCompilationAccess config) {
* 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);
+ Optional extends ResolvedJavaType> optionalFrameType = ((FeatureImpl.BeforeCompilationAccessImpl) config).getMetaAccess().optionalLookupJavaType(Frame.class);
if (optionalFrameType.isPresent()) {
HostedType frameType = (HostedType) optionalFrameType.get();
Set implementations = new HashSet<>();
@@ -919,8 +932,12 @@ private static void collectImplementations(HostedType type, Set impl
@Override
public void afterAnalysis(AfterAnalysisAccess access) {
+ CallTreeInfo treeInfo = RuntimeCompilationFeature.singleton().getCallTreeInfo();
+
+ checkBlockList(treeInfo);
+
if (Options.PrintStaticTruffleBoundaries.getValue()) {
- printStaticTruffleBoundaries();
+ printStaticTruffleBoundaries(treeInfo);
}
SubstrateTruffleRuntime truffleRuntime = (SubstrateTruffleRuntime) Truffle.getRuntime();
@@ -931,9 +948,9 @@ public void afterAnalysis(AfterAnalysisAccess access) {
runtimeCompiledMethods.addAll(Arrays.asList(config.getMetaAccess().lookupJavaType(CompilerDirectives.class).getDeclaredMethods(false)));
runtimeCompiledMethods.addAll(Arrays.asList(config.getMetaAccess().lookupJavaType(CompilerAsserts.class).getDeclaredMethods(false)));
- for (RuntimeCompiledMethod runtimeCompiledMethod : RuntimeCompilationFeature.singleton().getRuntimeCompiledMethods()) {
+ for (RuntimeCompiledMethod runtimeCompiledMethod : treeInfo.runtimeCompilations()) {
- runtimeCompiledMethods.add(runtimeCompiledMethod.getMethod());
+ runtimeCompiledMethods.add(runtimeCompiledMethod.getOriginalMethod());
/*
* The list of runtime compiled methods is not containing all methods that are always
@@ -955,11 +972,11 @@ public void afterAnalysis(AfterAnalysisAccess access) {
}
}
- private static void printStaticTruffleBoundaries() {
+ private static void printStaticTruffleBoundaries(CallTreeInfo treeInfo) {
HashSet foundBoundaries = new HashSet<>();
int callSiteCount = 0;
int calleeCount = 0;
- for (RuntimeCompiledMethod runtimeCompiledMethod : RuntimeCompilationFeature.singleton().getRuntimeCompiledMethods()) {
+ for (RuntimeCompiledMethod runtimeCompiledMethod : treeInfo.runtimeCompilations()) {
for (ResolvedJavaMethod targetMethod : runtimeCompiledMethod.getInvokeTargets()) {
TruffleBoundary truffleBoundary = targetMethod.getAnnotation(TruffleBoundary.class);
if (truffleBoundary != null) {
diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleSupport.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleSupport.java
index 3f37a934c36f..71542b803ef0 100644
--- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleSupport.java
+++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleSupport.java
@@ -28,14 +28,6 @@
import java.util.function.Consumer;
import java.util.function.Supplier;
-import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
-import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
-import jdk.graal.compiler.phases.util.Providers;
-import jdk.graal.compiler.truffle.EconomyPartialEvaluatorConfiguration;
-import jdk.graal.compiler.truffle.PartialEvaluatorConfiguration;
-import jdk.graal.compiler.truffle.TruffleCompilerConfiguration;
-import jdk.graal.compiler.truffle.TruffleCompilerImpl;
-import jdk.graal.compiler.truffle.TruffleTierConfiguration;
import org.graalvm.nativeimage.ImageSingletons;
import com.oracle.svm.core.SubstrateOptions;
@@ -43,7 +35,7 @@
import com.oracle.svm.core.graal.code.SubstrateBackend;
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.graal.TruffleRuntimeCompilationSupport;
-import com.oracle.svm.graal.hosted.RuntimeCompilationFeature;
+import com.oracle.svm.graal.hosted.runtimecompilation.RuntimeCompilationFeature;
import com.oracle.svm.truffle.api.SubstrateKnownTruffleTypes;
import com.oracle.svm.truffle.api.SubstrateOptimizedCallTarget;
import com.oracle.svm.truffle.api.SubstrateOptimizedCallTargetInstalledCode;
@@ -64,6 +56,14 @@
import com.oracle.truffle.runtime.OptimizedCallTarget;
import com.oracle.truffle.runtime.OptimizedDirectCallNode;
+import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
+import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
+import jdk.graal.compiler.phases.util.Providers;
+import jdk.graal.compiler.truffle.EconomyPartialEvaluatorConfiguration;
+import jdk.graal.compiler.truffle.PartialEvaluatorConfiguration;
+import jdk.graal.compiler.truffle.TruffleCompilerConfiguration;
+import jdk.graal.compiler.truffle.TruffleCompilerImpl;
+import jdk.graal.compiler.truffle.TruffleTierConfiguration;
import jdk.vm.ci.meta.JavaConstant;
public class TruffleSupport {