From 47b9c2a8f47abd7600d660effbdb73f47188e6e2 Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Wed, 21 Jun 2023 14:06:12 +0200 Subject: [PATCH 1/3] Add ParseOnceJIT support for Deopt Testing. --- substratevm/mx.substratevm/suite.py | 2 +- .../graal/pointsto/PointsToAnalysis.java | 58 ++-- .../graal/pointsto/ReachabilityAnalysis.java | 9 +- .../com/oracle/graal/pointsto/api/HostVM.java | 8 + .../pointsto/meta/PointsToAnalysisMethod.java | 5 +- .../pointsto/results/StrengthenGraphs.java | 6 +- .../ReachabilityAnalysisEngine.java | 8 +- .../core/graal/nodes/TestDeoptimizeNode.java | 9 +- .../hosted/ParseOnceDeoptTestFeature.java | 252 ++++++++++++++++++ .../ParseOnceRuntimeCompilationFeature.java | 139 ++++++---- .../hosted/RuntimeCompilationFeature.java | 11 +- .../com/oracle/svm/hosted/FeatureImpl.java | 9 +- .../src/com/oracle/svm/hosted/SVMHost.java | 10 + .../svm/hosted/SubstrateStrengthenGraphs.java | 7 +- .../SimulateClassInitializerSupport.java | 3 +- .../oracle/svm/hosted/code/CompileQueue.java | 51 ++-- .../svm/hosted/code/DeoptimizationUtils.java | 138 +++++++--- .../code/SubstrateCompilationDirectives.java | 31 ++- .../SubstrateGraphBuilderPlugins.java | 7 +- vm/mx.vm/mx_vm_gate.py | 1 + 20 files changed, 604 insertions(+), 160 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/ParseOnceDeoptTestFeature.java diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index e3525be1b791..691939d16211 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1646,7 +1646,7 @@ "name" : "org.graalvm.nativeimage.base", "exports" : [ "com.oracle.svm.util to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.librarysupport,org.graalvm.nativeimage.driver,org.graalvm.nativeimage.llvm,org.graalvm.nativeimage.agent.jvmtibase,org.graalvm.nativeimage.agent.tracing,org.graalvm.nativeimage.agent.diagnostics,org.graalvm.nativeimage.junitsupport,com.oracle.svm.svm_enterprise,com.oracle.svm_enterprise.ml_dataset,org.graalvm.extraimage.builder,com.oracle.svm.extraimage_enterprise,org.graalvm.extraimage.librarysupport", - "com.oracle.svm.common.meta to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder", + "com.oracle.svm.common.meta to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.llvm,org.graalvm.extraimage.builder", "com.oracle.svm.common.option to org.graalvm.nativeimage.pointsto,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.driver", ], } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java index 6a18732a5afb..30d8ade31df6 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java @@ -41,6 +41,7 @@ import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; +import java.util.function.Consumer; import java.util.stream.StreamSupport; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -79,6 +80,7 @@ import com.oracle.graal.pointsto.util.Timer; import com.oracle.graal.pointsto.util.Timer.StopTimer; import com.oracle.graal.pointsto.util.TimerCollection; +import com.oracle.svm.common.meta.MultiMethod; import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.ImageGeneratorThreadMarker; @@ -288,20 +290,20 @@ public Iterable getAllSynchronizedTypes() { } @Override - public AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial) { - return addRootMethod(metaAccess.lookupJavaMethod(method), invokeSpecial); + public AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots) { + return addRootMethod(metaAccess.lookupJavaMethod(method), invokeSpecial, otherRoots); } @Override @SuppressWarnings("try") - public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecial) { - assert aMethod.isOriginalMethod(); + public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots) { assert !universe.sealed() : "Cannot register root methods after analysis universe is sealed."; + AnalysisError.guarantee(aMethod.isOriginalMethod()); AnalysisType declaringClass = aMethod.getDeclaringClass(); boolean isStatic = aMethod.isStatic(); WrappedSignature signature = aMethod.getSignature(); int paramCount = signature.getParameterCount(!isStatic); - PointsToAnalysisMethod pointsToMethod = assertPointsToAnalysisMethod(aMethod); + PointsToAnalysisMethod originalPTAMethod = assertPointsToAnalysisMethod(aMethod); if (isStatic) { /* @@ -309,21 +311,29 @@ public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecia * and return the method flows graph. Then the method parameter type flows are * initialized with the corresponding parameter declared type. */ - postTask(() -> { - pointsToMethod.registerAsDirectRootMethod(); - pointsToMethod.registerAsImplementationInvoked("root method"); - MethodFlowsGraphInfo flowInfo = analysisPolicy.staticRootMethodGraph(this, pointsToMethod); - for (int idx = 0; idx < paramCount; idx++) { - AnalysisType declaredParamType = (AnalysisType) signature.getParameterType(idx, declaringClass); - FormalParamTypeFlow parameter = flowInfo.getParameter(idx); - if (declaredParamType.getJavaKind() == JavaKind.Object && parameter != null) { - TypeFlow initialParameterFlow = declaredParamType.getTypeFlow(this, true); - initialParameterFlow.addUse(this, parameter); + Consumer triggerStaticMethodFlow = (pointsToMethod) -> { + postTask(() -> { + pointsToMethod.registerAsDirectRootMethod(); + pointsToMethod.registerAsImplementationInvoked("root method"); + MethodFlowsGraphInfo flowInfo = analysisPolicy.staticRootMethodGraph(this, pointsToMethod); + for (int idx = 0; idx < paramCount; idx++) { + AnalysisType declaredParamType = (AnalysisType) signature.getParameterType(idx, declaringClass); + FormalParamTypeFlow parameter = flowInfo.getParameter(idx); + if (declaredParamType.getJavaKind() == JavaKind.Object && parameter != null) { + TypeFlow initialParameterFlow = declaredParamType.getTypeFlow(this, true); + initialParameterFlow.addUse(this, parameter); + } } - } - }); + }); + }; + triggerStaticMethodFlow.accept(originalPTAMethod); + for (MultiMethod.MultiMethodKey key : otherRoots) { + assert key != MultiMethod.ORIGINAL_METHOD; + PointsToAnalysisMethod ptaMethod = assertPointsToAnalysisMethod(originalPTAMethod.getMultiMethod(key)); + triggerStaticMethodFlow.accept(ptaMethod); + } } else { - if (invokeSpecial && pointsToMethod.isAbstract()) { + if (invokeSpecial && originalPTAMethod.isAbstract()) { throw AnalysisError.userError("Abstract methods cannot be registered as special invoke entry point."); } /* @@ -340,14 +350,20 @@ public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecia * corresponding declared type state. When a callee is resolved the method is parsed and * the actual parameter type state is propagated to the formal parameters. Then the * callee is linked and registered as implementation-invoked. + * + * Note for virtual and special methods no action is needed when there are otherRoots. + * This is due to two factors: First, the callee methods are only resolved once types + * flow into the context insensitive invoke typeflow. Second, otherRoots is only + * (currently) used for runtime compilation; in this use case, all necessary linking + * will be done during callee resolution. */ postTask(() -> { if (invokeSpecial) { - pointsToMethod.registerAsDirectRootMethod(); + originalPTAMethod.registerAsDirectRootMethod(); } else { - pointsToMethod.registerAsVirtualRootMethod(); + originalPTAMethod.registerAsVirtualRootMethod(); } - InvokeTypeFlow invoke = pointsToMethod.initAndGetContextInsensitiveInvoke(PointsToAnalysis.this, null, invokeSpecial, pointsToMethod.getMultiMethodKey()); + InvokeTypeFlow invoke = originalPTAMethod.initAndGetContextInsensitiveInvoke(PointsToAnalysis.this, null, invokeSpecial, MultiMethod.ORIGINAL_METHOD); /* * Initialize the type flow of the invoke's actual parameters with the corresponding * parameter declared type. Thus, when the invoke links callees it will propagate diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java index 088e681e0b06..da45a14d7bd8 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java @@ -31,6 +31,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.svm.common.meta.MultiMethod; import com.oracle.svm.util.UnsafePartitionKind; /** @@ -73,13 +74,15 @@ public interface ReachabilityAnalysis { * @param aMethod the method to register as root * @param invokeSpecial if true only the target method is analyzed, even if it has overrides, or * it is itself an override. If the method is static this flag is ignored. + * @param otherRoots other versions of this method to also register as roots. */ - AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecial); + AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots); /** - * @see ReachabilityAnalysis#addRootMethod(AnalysisMethod, boolean) + * @see ReachabilityAnalysis#addRootMethod(AnalysisMethod, boolean, + * MultiMethod.MultiMethodKey...) */ - AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial); + AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots); default void registerAsFrozenUnsafeAccessed(AnalysisField field) { field.setUnsafeFrozenTypeState(true); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index 1def9076770e..9fdc5f7e0fe2 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -388,6 +388,14 @@ public Function getStrengthenGraphsToTargetFunct return (t) -> t; } + public boolean allowConstantFolding(AnalysisMethod method) { + /* + * Currently constant folding is only enabled for original methods. More work is needed to + * support it within deoptimization targets and runtime-compiled methods. + */ + return method.isOriginalMethod(); + } + public FieldValueComputer createFieldValueComputer(@SuppressWarnings("unused") AnalysisField field) { return null; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java index 243d145804a3..b6abf954b306 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java @@ -242,8 +242,11 @@ public boolean isImplementationInvokable() { } else { /* * If only a stub is ever created for this method, then it will not be invoked. + * + * However, for deopt targets it is possible for a root to temporarily to be a stub + * before a full flow graph is created. */ - return !getTypeFlow().getMethodFlowsGraphInfo().isStub(); + return !getTypeFlow().getMethodFlowsGraphInfo().isStub() || (isDirectRootMethod() && isDeoptTarget()); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java index e03985e703d5..3d9ac4810bc3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java @@ -296,11 +296,7 @@ class StrengthenSimplifier implements CustomSimplification { nodeFlows.put(node, cursor.getValue()); } - /* - * Currently constant folding is only enabled for original methods. More work is needed - * to support it within deoptimization targets and runtime-compiled methods. - */ - this.allowConstantFolding = method.isOriginalMethod() && strengthenGraphWithConstants; + this.allowConstantFolding = strengthenGraphWithConstants && bb.getHostVM().allowConstantFolding(method); /* * In deoptimization target methods optimizing the return parameter can make new values diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java index b3dce66b97a4..7f8951416ce1 100644 --- a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java @@ -51,6 +51,7 @@ import com.oracle.graal.pointsto.util.CompletionExecutor; import com.oracle.graal.pointsto.util.Timer; import com.oracle.graal.pointsto.util.TimerCollection; +import com.oracle.svm.common.meta.MultiMethod; import jdk.vm.ci.code.BytecodePosition; import jdk.vm.ci.meta.ConstantReflectionProvider; @@ -106,8 +107,8 @@ public AnalysisType addRootClass(Class clazz, boolean addFields, boolean addA } @Override - public AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial) { - return addRootMethod(metaAccess.lookupJavaMethod(method), invokeSpecial); + public AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots) { + return addRootMethod(metaAccess.lookupJavaMethod(method), invokeSpecial, otherRoots); } @SuppressWarnings("try") @@ -145,7 +146,8 @@ public AnalysisType addRootField(Class clazz, String fieldName) { } @Override - public AnalysisMethod addRootMethod(AnalysisMethod m, boolean invokeSpecial) { + public AnalysisMethod addRootMethod(AnalysisMethod m, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots) { + assert otherRoots.length == 0; ReachabilityAnalysisMethod method = (ReachabilityAnalysisMethod) m; if (m.isStatic()) { postTask(() -> { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/TestDeoptimizeNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/TestDeoptimizeNode.java index d42bcb6644b7..149d16d11b9e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/TestDeoptimizeNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/TestDeoptimizeNode.java @@ -38,7 +38,6 @@ import com.oracle.svm.common.meta.MultiMethod; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.meta.SharedMethod; -import com.oracle.svm.core.util.VMError; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; @@ -61,7 +60,13 @@ public Node canonical(CanonicalizerTool tool) { ResolvedJavaMethod method = graph().method(); if (SubstrateOptions.parseOnce()) { - throw VMError.unimplemented("Deopt Testing does not yet work."); + if (MultiMethod.isDeoptTarget(method)) { + /* no-op for deoptimization target methods. */ + return null; + } else { + /* deoptimization for all other methods. */ + return new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter); + } } else { if (method instanceof SharedMethod) { if (MultiMethod.isDeoptTarget(method)) { diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/ParseOnceDeoptTestFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/ParseOnceDeoptTestFeature.java new file mode 100644 index 000000000000..fa31f7560f3e --- /dev/null +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/ParseOnceDeoptTestFeature.java @@ -0,0 +1,252 @@ +/* + * 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; + +import static com.oracle.svm.common.meta.MultiMethod.DEOPT_TARGET_METHOD; +import static com.oracle.svm.common.meta.MultiMethod.ORIGINAL_METHOD; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.PointsToAnalysis; +import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.flow.InvokeTypeFlow; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; +import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisPolicy; +import com.oracle.svm.common.meta.MultiMethod; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.graal.snippets.DeoptTester; +import com.oracle.svm.core.graal.stackvalue.StackValueNode; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.analysis.SVMParsingSupport; +import com.oracle.svm.hosted.code.DeoptimizationUtils; +import com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyUtils; + +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * For Deoptimization Testing we create two versions of candidate methods: + *
    + *
  1. The "original" method which can deoptimize
  2. + *
  3. A deoptimization target method to deoptimize to
  4. + *
+ * + * This is different than our runtime compilation support, as in that environment also a runtime + * compilation version for methods are created. In addition, in that environment "original" methods + * are not allowed to deoptimize. + */ +public class ParseOnceDeoptTestFeature implements InternalFeature { + @Override + public List> getRequiredFeatures() { + return List.of(DeoptimizationFeature.class); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(SVMParsingSupport.class, new DeoptTestingParsingSupport()); + ImageSingletons.add(HostVM.MultiMethodAnalysisPolicy.class, new DeoptTestingAnalysisPolicy()); + } + + private class DeoptTestingParsingSupport implements SVMParsingSupport { + + @Override + public Object parseGraph(BigBang bb, DebugContext debug, AnalysisMethod method) { + /* Regular parsing is always used. */ + return HostVM.PARSING_UNHANDLED; + } + + @Override + public GraphBuilderConfiguration updateGraphBuilderConfiguration(GraphBuilderConfiguration config, AnalysisMethod method) { + if (method.isDeoptTarget()) { + /* + * Local variables are never retained to help ensure the state of the deoptimization + * source will always be a superset of the deoptimization target. + */ + return config.withRetainLocalVariables(false); + } + return config; + } + + @Override + public boolean validateGraph(PointsToAnalysis bb, StructuredGraph graph) { + PointsToAnalysisMethod aMethod = (PointsToAnalysisMethod) graph.method(); + Supplier hasStackValues = () -> graph.getNodes(StackValueNode.TYPE).isNotEmpty(); + if (aMethod.isDeoptTarget()) { + /* + * Stack allocated memory is not seen by the deoptimization code, i.e., it is not + * copied in case of deoptimization. Also, pointers to it can be used for arbitrary + * address arithmetic, so we would not know how to update derived pointers into + * stack memory during deoptimization. Therefore, we cannot allow methods that + * allocate stack memory for runtime compilation. To remove this limitation, we + * would need to change how we handle stack allocated memory in Graal. + */ + return !hasStackValues.get(); + } else { + boolean canDeoptForTesting = aMethod.isOriginalMethod() && + DeoptimizationUtils.canDeoptForTesting(aMethod, DeoptTester.enabled(), hasStackValues); + if (canDeoptForTesting) { + DeoptimizationUtils.registerDeoptEntriesForDeoptTesting(bb, graph, aMethod); + } + } + + return true; + } + + @Override + public boolean allowAssumptions(AnalysisMethod method) { + /* Assumptions are not allowed it AOT compiled methods */ + return false; + } + + @Override + public HostedProviders getHostedProviders(MultiMethod.MultiMethodKey key) { + /* The buildtime providers are always used. */ + return null; + } + + @Override + public void initializeInlineBeforeAnalysisPolicy(SVMHost svmHost, InlineBeforeAnalysisPolicyUtils inliningUtils) { + /* We do not use a custom analysis policy for deopt testing. */ + } + + /** + * Currently we do not support inlining before analysis during deopt testing. More work is + * needed to support this. + */ + @Override + public InlineBeforeAnalysisPolicy inlineBeforeAnalysisPolicy(MultiMethod.MultiMethodKey multiMethodKey, InlineBeforeAnalysisPolicy defaultPolicy) { + if (multiMethodKey == ORIGINAL_METHOD) { + /* + * Since we can deopt from original methods, we do not inline here. Doing so would + * require us to track the flows into these inlined methods. + */ + return InlineBeforeAnalysisPolicy.NO_INLINING; + } else if (multiMethodKey == DEOPT_TARGET_METHOD) { + return InlineBeforeAnalysisPolicy.NO_INLINING; + } else { + throw VMError.shouldNotReachHere("Unexpected method key: %s", multiMethodKey); + } + } + + @Override + public Function getStrengthenGraphsToTargetFunction(MultiMethod.MultiMethodKey key) { + /* No customization is needed to deopt testing. */ + return null; + } + } + + private static class DeoptTestingAnalysisPolicy implements HostVM.MultiMethodAnalysisPolicy { + + @Override + public Collection determineCallees(BigBang bb, T implementation, T target, MultiMethod.MultiMethodKey callerMultiMethodKey, InvokeTypeFlow invokeFlow) { + if (callerMultiMethodKey == ORIGINAL_METHOD) { + if (DeoptimizationUtils.canDeoptForTesting(implementation, DeoptTester.enabled(), () -> false)) { + /* + * If the target is registered for deoptimization, then we must also make a + * deoptimized version. + */ + return List.of(implementation, getDeoptVersion(implementation)); + } else { + return List.of(implementation); + } + } else { + assert callerMultiMethodKey == DEOPT_TARGET_METHOD; + /* + * A deoptimization target will always call the original method. However, the return + * can also be from a deoptimized version when a deoptimization is triggered in an + * inlined callee. + */ + return List.of(implementation, getDeoptVersion(implementation)); + } + } + + @SuppressWarnings("unchecked") + protected T getDeoptVersion(T implementation) { + /* + * Flows for deopt versions are only created once a frame state for the method is seen + * within a runtime compiled method. + */ + return (T) implementation.getOrCreateMultiMethod(DEOPT_TARGET_METHOD, (newMethod) -> ((PointsToAnalysisMethod) newMethod).getTypeFlow().setAsStubFlow()); + } + + @Override + public boolean performParameterLinking(MultiMethod.MultiMethodKey callerMultiMethodKey, MultiMethod.MultiMethodKey calleeMultiMethodKey) { + if (callerMultiMethodKey == DEOPT_TARGET_METHOD) { + /* A deopt method can call the original version only. */ + return calleeMultiMethodKey == ORIGINAL_METHOD; + } else { + assert callerMultiMethodKey == ORIGINAL_METHOD; + /* An original method can call the deopt target as well. */ + return true; + } + } + + @Override + public boolean performReturnLinking(MultiMethod.MultiMethodKey callerMultiMethodKey, MultiMethod.MultiMethodKey calleeMultiMethodKey) { + if (callerMultiMethodKey == DEOPT_TARGET_METHOD) { + /* A deopt method can be returned to from the deopt target or an original method. */ + return true; + } else { + assert callerMultiMethodKey == ORIGINAL_METHOD; + /* + * An original method can be returned to from the deopt target or an original + * method. + */ + return true; + } + } + + @Override + public boolean canComputeReturnedParameterIndex(MultiMethod.MultiMethodKey multiMethodKey) { + /* + * Since Deopt Target Methods may have their flow created multiple times, this + * optimization is not allowed. + */ + return multiMethodKey != DEOPT_TARGET_METHOD; + } + + @Override + public boolean insertPlaceholderParamAndReturnFlows(MultiMethod.MultiMethodKey multiMethodKey) { + /* + * Since Deopt Target Methods may have their flow created multiple times, placeholder + * flows are needed. + */ + return multiMethodKey == DEOPT_TARGET_METHOD; + } + } +} 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/ParseOnceRuntimeCompilationFeature.java index 2632591a9001..2cb4b62da18f 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/ParseOnceRuntimeCompilationFeature.java @@ -43,6 +43,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; +import java.util.function.Supplier; import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.core.common.PermanentBailoutException; @@ -84,6 +85,7 @@ import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.flow.InvokeTypeFlow; import com.oracle.graal.pointsto.flow.MethodFlowsGraph; import com.oracle.graal.pointsto.infrastructure.GraphProvider; @@ -257,11 +259,19 @@ public int hashCode() { } } - public static class RuntimeGraphBuilderPhase extends AnalysisGraphBuilderPhase { + private static final class RuntimeGraphBuilderPhase extends AnalysisGraphBuilderPhase { - RuntimeGraphBuilderPhase(Providers providers, + private RuntimeGraphBuilderPhase(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes, SVMHost hostVM) { - super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes, hostVM); + super(providers, graphBuilderConfig.withEagerResolving(true), optimisticOpts, initialIntrinsicContext, wordTypes, hostVM); + } + + static RuntimeGraphBuilderPhase createRuntimeGraphBuilderPhase(BigBang bb, Providers providers, + GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts) { + + // Adjust graphbuilderconfig to match analysis phase + var newGraphBuilderConfig = graphBuilderConfig.withEagerResolving(true).withUnresolvedIsError(PointstoOptions.UnresolvedIsError.getValue(bb.getOptions())); + return new RuntimeGraphBuilderPhase(providers, newGraphBuilderConfig, optimisticOpts, null, providers.getWordTypes(), (SVMHost) bb.getHostVM()); } @Override @@ -270,7 +280,7 @@ protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodePar } } - public static class RuntimeBytecodeParser extends AnalysisGraphBuilderPhase.AnalysisBytecodeParser { + private static final class RuntimeBytecodeParser extends AnalysisGraphBuilderPhase.AnalysisBytecodeParser { RuntimeBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext, SVMHost svmHost) { @@ -307,7 +317,8 @@ protected boolean shouldVerifyFrameStates() { private Map runtimeCandidateCallTree = null; private Map runtimeCompiledMethodCallTree = null; private HostedProviders analysisProviders = null; - private AllowInliningPredicate allowInliningPredicate = null; + private AllowInliningPredicate allowInliningPredicate = (builder, target) -> AllowInliningPredicate.InlineDecision.INLINING_DISALLOWED; + private boolean allowInliningPredicateUpdated = false; @Override public List> getRequiredFeatures() { @@ -334,8 +345,9 @@ public void beforeAnalysis(BeforeAnalysisAccess c) { @Override public void registerAllowInliningPredicate(AllowInliningPredicate predicate) { - assert allowInliningPredicate == null; + assert !allowInliningPredicateUpdated; allowInliningPredicate = predicate; + allowInliningPredicateUpdated = true; } @Override @@ -399,8 +411,6 @@ public void afterAnalysis(AfterAnalysisAccess access) { // after analysis has completed we must ensure no new SubstrateTypes are introduced objectReplacer.forbidNewTypes(); - - System.out.println("Number of runtime compiled methods: " + getRuntimeCompiledMethods().size()); } @Override @@ -631,15 +641,7 @@ private void encodeRuntimeCompiledMethods() { @Override public void beforeCompilation(BeforeCompilationAccess c) { - System.out.println("Number of runtime compiled methods: " + getRuntimeCompiledMethods().size()); - beforeCompilationHelper(); - - System.out.println("Num runtime parsed methods " + parsedRuntimeMethods.size()); - System.out.println("Num deopt parsed methods " + parsedDeoptMethods.size()); - System.out.println("total count of runtime parsed methods " + totalParsedRuntimeMethods.get()); - System.out.println("total count of deopt parsed methods " + totalParsedDeoptMethods.get()); - } @Override @@ -721,7 +723,15 @@ public SubstrateMethod prepareMethodForRuntimeCompilation(ResolvedJavaMethod met substrateAnalysisMethods.add(sMethod); if (registeredRuntimeCompilations.add(aMethod)) { - config.registerAsRoot(aMethod, true); + 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_COMPILED_METHOD, DEOPT_TARGET_METHOD); } return sMethod; @@ -803,7 +813,9 @@ 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")) { - new RuntimeGraphBuilderPhase(analysisProviders, graphBuilderConfig, optimisticOpts, null, analysisProviders.getWordTypes(), (SVMHost) bb.getHostVM()).apply(graph); + 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); @@ -848,8 +860,13 @@ private void recordFailed(AnalysisMethod method) { public boolean validateGraph(PointsToAnalysis bb, StructuredGraph graph) { PointsToAnalysisMethod aMethod = (PointsToAnalysisMethod) graph.method(); MultiMethod.MultiMethodKey multiMethodKey = aMethod.getMultiMethodKey(); + Supplier hasStackValues = () -> graph.getNodes(StackValueNode.TYPE).isNotEmpty(); + if (aMethod.isOriginalMethod() && DeoptimizationUtils.canDeoptForTesting(aMethod, false, hasStackValues)) { + DeoptimizationUtils.registerDeoptEntriesForDeoptTesting(bb, graph, aMethod); + return true; + } if (multiMethodKey != ORIGINAL_METHOD) { - if (graph.getNodes(StackValueNode.TYPE).isNotEmpty()) { + if (hasStackValues.get()) { /* * Stack allocated memory is not seen by the deoptimization code, i.e., it is * not copied in case of deoptimization. Also, pointers to it can be used for @@ -1070,49 +1087,65 @@ public Collection determineCallees(BigBang bb, T i } assert implementation.isOriginalMethod() && target.isOriginalMethod(); - // recording compilation candidate - if (callerMultiMethodKey == RUNTIME_COMPILED_METHOD) { - runtimeCompilationCandidates.add(new RuntimeCompilationCandidateImpl(implementation, target)); - } - - boolean jitPossible = runtimeCompilationCandidatePredicate.allowRuntimeCompilation(implementation); - if (!jitPossible) { - assert !registeredRuntimeCompilations.contains(implementation) : "invalid method registered for runtime compilation"; - /* - * If this method cannot be jitted, then only the original implementation is needed. - */ - return List.of(implementation); - } - + boolean registeredRuntimeCompilation = registeredRuntimeCompilations.contains(implementation); if (callerMultiMethodKey == ORIGINAL_METHOD) { /* * Unless the method is a registered runtime compilation, it is not possible for an * original variant to call a runtime variant (and indirectly the deoptimiztation * variant). */ - if (registeredRuntimeCompilations.contains(implementation)) { + if (registeredRuntimeCompilation) { return List.of(implementation, getDeoptVersion(implementation), getRuntimeVersion(bb, implementation, true, invokeFlow)); + } else if (DeoptimizationUtils.canDeoptForTesting(implementation, false, () -> false)) { + /* + * If the target is registered for deoptimization, then we must also make a + * deoptimized version. + */ + return List.of(implementation, getDeoptVersion(implementation)); } else { return List.of(implementation); } - } else if (callerMultiMethodKey == RUNTIME_COMPILED_METHOD) { - /* - * 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 runtime - * deoptimizes). - */ - return List.of(implementation, getDeoptVersion(implementation), getRuntimeVersion(bb, implementation, true, invokeFlow)); } else { - assert callerMultiMethodKey == DEOPT_TARGET_METHOD; - /* - * A deoptimization target will always call the original method. However, the return - * can also be from a deoptimized version when a deoptimization is triggered in an - * inlined callee. In addition, because we want runtime information to flow into - * this method via the return, we also need to link against the runtime variant. We - * only register the runtime variant as a stub though because its flow only needs to - * be made upon it being reachable from a runtime compiled method's invoke. - */ - return List.of(implementation, getDeoptVersion(implementation), getRuntimeVersion(bb, implementation, false, invokeFlow)); + boolean runtimeCompilationCandidate = registeredRuntimeCompilation || runtimeCompilationCandidatePredicate.allowRuntimeCompilation(implementation); + + if (callerMultiMethodKey == RUNTIME_COMPILED_METHOD) { + // recording compilation candidate + runtimeCompilationCandidates.add(new RuntimeCompilationCandidateImpl(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 + * runtime deoptimizes). + */ + if (runtimeCompilationCandidate) { + return List.of(implementation, getDeoptVersion(implementation), getRuntimeVersion(bb, implementation, true, invokeFlow)); + } else { + /* + * If this method cannot be jitted, then only the original implementation is + * needed. + */ + return List.of(implementation); + } + } else { + assert callerMultiMethodKey == DEOPT_TARGET_METHOD; + /* + * A deoptimization target will always call the original method. However, the + * return can also be from a deoptimized version when a deoptimization is + * triggered in an inlined callee. In addition, because we want runtime + * information to flow into this method via the return, we also need to link + * against the runtime variant. We only register the runtime variant as a stub + * though because its flow only needs to be made upon it being reachable from a + * runtime compiled method's invoke. + */ + if (runtimeCompilationCandidate) { + return List.of(implementation, getDeoptVersion(implementation), getRuntimeVersion(bb, implementation, false, invokeFlow)); + } else { + /* + * If this method cannot be jitted, then only the original implementation is + * needed. + */ + return List.of(implementation); + } + } } } @@ -1171,6 +1204,10 @@ public boolean performReturnLinking(MultiMethod.MultiMethodKey callerMultiMethod @Override public boolean canComputeReturnedParameterIndex(MultiMethod.MultiMethodKey multiMethodKey) { + /* + * Since Deopt Target Methods may have their flow created multiple times, this + * optimization is not allowed. + */ return multiMethodKey != DEOPT_TARGET_METHOD; } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/RuntimeCompilationFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/RuntimeCompilationFeature.java index a497e7f1e302..c0f65e518f3c 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/RuntimeCompilationFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/RuntimeCompilationFeature.java @@ -99,6 +99,7 @@ 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.FeatureImpl.AfterHeapLayoutAccessImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.CompilationAccessImpl; @@ -350,6 +351,7 @@ private static List getCallTraceHelper(AbstractCallTreeNode node) { protected GraphBuilderConfiguration graphBuilderConfig; protected OptimisticOptimizations optimisticOpts; protected RuntimeCompilationCandidatePredicate runtimeCompilationCandidatePredicate; + private boolean runtimeCompilationCandidatePredicateUpdated = false; protected Predicate deoptimizeOnExceptionPredicate; private SubstrateUniverseFactory universeFactory = new SubstrateUniverseFactory(); @@ -491,8 +493,13 @@ private static boolean defaultAllowRuntimeCompilation(ResolvedJavaMethod method) return false; } - public void initializeRuntimeCompilationConfiguration(RuntimeCompilationCandidatePredicate newRuntimeCompilationCandidatePredicate) { + 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, @@ -503,7 +510,9 @@ public void initializeRuntimeCompilationConfiguration(HostedProviders newHostedP hostedProviders = newHostedProviders; graphBuilderConfig = newGraphBuilderConfig; + assert !runtimeCompilationCandidatePredicateUpdated : "Updated compilation predicate multiple times"; runtimeCompilationCandidatePredicate = newRuntimeCompilationCandidatePredicate; + runtimeCompilationCandidatePredicateUpdated = true; deoptimizeOnExceptionPredicate = newDeoptimizeOnExceptionPredicate; if (SubstrateOptions.IncludeNodeSourcePositions.getValue()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index 54dee934779e..f5c4ff8ecf01 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -65,6 +65,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.svm.common.meta.MultiMethod; import com.oracle.svm.core.LinkerInvocation; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.Delete; @@ -411,12 +412,12 @@ public void registerAsFrozenUnsafeAccessed(AnalysisField aField, Object reason) registerAsUnsafeAccessed(aField, reason); } - public void registerAsRoot(Executable method, boolean invokeSpecial) { - bb.addRootMethod(method, invokeSpecial); + public void registerAsRoot(Executable method, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots) { + bb.addRootMethod(method, invokeSpecial, otherRoots); } - public void registerAsRoot(AnalysisMethod aMethod, boolean invokeSpecial) { - bb.addRootMethod(aMethod, invokeSpecial); + public void registerAsRoot(AnalysisMethod aMethod, boolean invokeSpecial, MultiMethod.MultiMethodKey... otherRoots) { + bb.addRootMethod(aMethod, invokeSpecial, otherRoots); } public void registerUnsafeFieldsRecomputed(Class clazz) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 7ae5231b4005..f680ce1f3b45 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -942,6 +942,16 @@ public Function getStrengthenGraphsToTargetFunct return super.getStrengthenGraphsToTargetFunction(key); } + @Override + public boolean allowConstantFolding(AnalysisMethod method) { + /* + * Currently constant folding is only enabled for original methods which do not deoptimize. + * More work is needed to support it within deoptimization targets and runtime-compiled + * methods. + */ + return method.isOriginalMethod() && !SubstrateCompilationDirectives.singleton().isRegisteredForDeoptTesting(method); + } + @Override public FieldValueComputer createFieldValueComputer(AnalysisField field) { UnknownObjectField unknownObjectField = field.getAnnotation(UnknownObjectField.class); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java index fa56b353e89e..f79916bbcd42 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java @@ -96,12 +96,13 @@ protected FixedNode createUnreachable(StructuredGraph graph, CoreProviders provi * Uninterruptible methods might not be able to access the heap yet for the error message * constant, so we skip it for such methods too. * - * We also do not print out this message for runtime compiled methods because it would - * require us to preserve additional graph state. + * We also do not print out this message for runtime compiled methods and methods which can + * deopt for testing because it would require us to preserve additional graph state. */ boolean insertMessage = SubstrateUtil.assertionsEnabled() && !Uninterruptible.Utils.isUninterruptible(graph.method()) && - !SubstrateCompilationDirectives.isRuntimeCompiledMethod(graph.method()); + !SubstrateCompilationDirectives.isRuntimeCompiledMethod(graph.method()) && + !SubstrateCompilationDirectives.singleton().isRegisteredForDeoptTesting(graph.method()); if (insertMessage) { ConstantNode messageNode = ConstantNode.forConstant(providers.getConstantReflection().forString(message.get()), providers.getMetaAccess(), graph); ForeignCallNode foreignCallNode = graph.add(new ForeignCallNode(SnippetRuntime.UNSUPPORTED_FEATURE, messageNode)); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java index 345f54194859..e24cb407ed46 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java @@ -65,6 +65,7 @@ import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisGraphDecoder; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; +import com.oracle.svm.core.deopt.DeoptimizationSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; @@ -174,7 +175,7 @@ public class SimulateClassInitializerSupport { * Simulation of class initializer (like {@link InlineBeforeAnalysis}) requires ParseOnce, * because otherwise graphs parsed for static analysis omits exception edges. */ - protected final boolean enabled = ClassInitializationOptions.SimulateClassInitializer.getValue() && SubstrateOptions.parseOnce(); + protected final boolean enabled = ClassInitializationOptions.SimulateClassInitializer.getValue() && SubstrateOptions.parseOnce() && !DeoptimizationSupport.enabled(); /* Cached value of options to avoid frequent lookup of option values. */ protected final boolean collectAllReasons = ClassInitializationOptions.SimulateClassInitializerCollectAllReasons.getValue(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index 82b9a09f2054..2f8dbbd300fc 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -616,20 +616,31 @@ protected void parseAll() throws InterruptedException { private void parseAheadOfTimeCompiledMethods() { for (HostedMethod method : universe.getMethods()) { - /* - * Deoptimization target code for deoptimization testing: all methods that are not - * blacklisted are possible deoptimization targets. The methods are also flagged so that - * all possible deoptimization entry points are emitted. - */ - if (method.getWrapped().isImplementationInvoked() && DeoptimizationUtils.canDeoptForTesting(universe, method, deoptimizeAll)) { - method.compilationInfo.canDeoptForTesting = true; + if (parseOnce) { + if (SubstrateCompilationDirectives.singleton().isRegisteredForDeoptTesting(method)) { + method.compilationInfo.canDeoptForTesting = true; + assert SubstrateCompilationDirectives.singleton().isRegisteredDeoptTarget(method.getMultiMethod(DEOPT_TARGET_METHOD)); + } + } else { + /* + * Deoptimization target code for deoptimization testing: all methods that are not + * blacklisted are possible deoptimization targets. The methods are also flagged so + * that all possible deoptimization entry points are emitted. + */ + if (method.getWrapped().isImplementationInvoked() && DeoptimizationUtils.canDeoptForTesting(universe, method, deoptimizeAll)) { + method.compilationInfo.canDeoptForTesting = true; + } } for (MultiMethod multiMethod : method.getAllMultiMethods()) { - if (multiMethod.isDeoptTarget()) { - // deoptimization targets are parsed in a later phase + HostedMethod hMethod = (HostedMethod) multiMethod; + if (hMethod.isDeoptTarget() || SubstrateCompilationDirectives.isRuntimeCompiledMethod(hMethod)) { + /* + * Deoptimization targets are parsed in a later phase. + * + * Runtime compiled methods are compiled and encoded in a separate process. + */ continue; } - HostedMethod hMethod = (HostedMethod) multiMethod; if (hMethod.isEntryPoint() || SubstrateCompilationDirectives.singleton().isForcedCompilation(hMethod) || hMethod.wrapped.isDirectRootMethod() && hMethod.wrapped.isImplementationInvoked()) { ensureParsed(hMethod, null, new EntryPointReason()); @@ -668,12 +679,14 @@ private void parseDeoptimizationTargetMethods() { * Deoptimization target code for all methods that were manually marked as * deoptimization targets. */ - universe.getMethods().stream().map(method -> method.getMultiMethod(DEOPT_TARGET_METHOD)).filter(method -> { - if (method != null) { - return isRegisteredDeoptTarget(method); + universe.getMethods().stream().map(method -> method.getMultiMethod(DEOPT_TARGET_METHOD)).filter(deoptMethod -> { + if (deoptMethod != null) { + return isRegisteredDeoptTarget(deoptMethod); } return false; - }).forEach(method -> ensureParsed(method.getMultiMethod(DEOPT_TARGET_METHOD), null, new EntryPointReason())); + }).forEach(deoptMethod -> { + ensureParsed(deoptMethod, null, new EntryPointReason()); + }); } else { /* * Deoptimization target code for all methods that were manually marked as @@ -890,12 +903,16 @@ protected void compileAll() throws InterruptedException { public void scheduleEntryPoints() { for (HostedMethod method : universe.getMethods()) { for (MultiMethod multiMethod : method.getAllMultiMethods()) { - if (multiMethod.isDeoptTarget()) { - // deoptimization targets are compiled in a later phase + HostedMethod hMethod = (HostedMethod) multiMethod; + if (hMethod.isDeoptTarget() || SubstrateCompilationDirectives.isRuntimeCompiledMethod(hMethod)) { + /* + * Deoptimization targets are parsed in a later phase. + * + * Runtime compiled methods are compiled and encoded in a separate process. + */ continue; } - HostedMethod hMethod = (HostedMethod) multiMethod; if (hMethod.isEntryPoint() || SubstrateCompilationDirectives.singleton().isForcedCompilation(hMethod) || hMethod.wrapped.isDirectRootMethod() && hMethod.wrapped.isImplementationInvoked()) { ensureCompiled(hMethod, new EntryPointReason()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/DeoptimizationUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/DeoptimizationUtils.java index 02214692f4e2..2e593a567870 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/DeoptimizationUtils.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/DeoptimizationUtils.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.code; +import static com.oracle.svm.common.meta.MultiMethod.DEOPT_TARGET_METHOD; + import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; @@ -31,6 +33,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.graph.Node; @@ -62,6 +65,11 @@ import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase; import org.graalvm.compiler.virtual.phases.ea.ReadEliminationPhase; +import com.oracle.graal.pointsto.PointsToAnalysis; +import com.oracle.graal.pointsto.flow.MethodFlowsGraph; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; +import com.oracle.svm.common.meta.MultiMethod; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.code.FrameInfoEncoder; import com.oracle.svm.core.deopt.DeoptEntryInfopoint; @@ -110,17 +118,17 @@ static void insertDeoptTests(HostedMethod method, StructuredGraph graph) { } } - private static boolean containsStackValueNode(HostedUniverse universe, HostedMethod method) { - return universe.getBigBang().getHostVM().containsStackValueNode(method.wrapped); - } - /** * Returns true if a method should be considered as deoptimization source. This is only a * feature for testing. Note that usually all image compiled methods cannot deoptimize. * * Note this should only be called within CompileQueue#parseAheadOfTimeCompiledMethods */ - static boolean canDeoptForTesting(HostedUniverse universe, HostedMethod method, boolean deoptimizeAll) { + public static boolean canDeoptForTesting(AnalysisMethod method, boolean deoptimizeAll, Supplier containsStackValueNodes) { + if (SubstrateCompilationDirectives.singleton().isRegisteredForDeoptTesting(method)) { + return true; + } + if (method.getName().equals("")) { /* Cannot deoptimize into static initializers. */ return false; @@ -130,6 +138,24 @@ static boolean canDeoptForTesting(HostedUniverse universe, HostedMethod method, return true; } + if (!deoptimizeAll) { + /* When DeoptimizeAll is not set, then only methods marked via DeoptTest can deopt. */ + return false; + } + + if (containsStackValueNodes.get()) { + /* + * Stack allocated memory is not seen by the deoptimization code, i.e., it is not copied + * in case of deoptimization. Also, pointers to it can be used for arbitrary address + * arithmetic, so we would not know how to update derived pointers into stack memory + * during deoptimization. Therefore, we cannot allow methods that allocate stack memory + * for runtime compilation. To remove this limitation, we would need to change how we + * handle stack allocated memory in Graal. + */ + + return false; + } + if (method.isEntryPoint()) { /* * Entry points from C have special entry/exit nodes added, so they cannot be @@ -144,7 +170,7 @@ static boolean canDeoptForTesting(HostedUniverse universe, HostedMethod method, */ return false; } - if (method.wrapped.isIntrinsicMethod()) { + if (method.isIntrinsicMethod()) { return false; } if (Uninterruptible.Utils.isUninterruptible(method)) { @@ -157,47 +183,45 @@ static boolean canDeoptForTesting(HostedUniverse universe, HostedMethod method, /* Deoptimization runtime cannot fill the callee saved registers. */ return false; } - if (containsStackValueNode(universe, method)) { - /* - * Stack allocated memory is not seen by the deoptimization code, i.e., it is not copied - * in case of deoptimization. Also, pointers to it can be used for arbitrary address - * arithmetic, so we would not know how to update derived pointers into stack memory - * during deoptimization. Therefore, we cannot allow methods that allocate stack memory - * for runtime compilation. To remove this limitation, we would need to change how we - * handle stack allocated memory in Graal. - */ + + /* + * The DeoptimizeAll option is set. So we use all methods for deoptimization testing. + * Exclude some "runtime" methods, like the heap code, via this blacklist. Issue GR-1706 + * tracks the bug in DebugValueMap. + */ + String className = method.getDeclaringClass().getName(); + if (className.contains("/svm/core/code/CodeInfoEncoder") || + className.contains("com/oracle/svm/core/thread/JavaThreads") || + className.contains("com/oracle/svm/core/thread/PlatformThreads") || + className.contains("com/oracle/svm/core/heap/") || + className.contains("com/oracle/svm/core/genscavenge/") || + className.contains("com/oracle/svm/core/thread/VMOperationControl") || + className.contains("debug/internal/DebugValueMap") && method.getName().equals("registerTopLevel")) { return false; } - - if (deoptimizeAll) { - /* - * The DeoptimizeAll option is set. So we use all methods for deoptimization testing. - * Exclude some "runtime" methods, like the heap code, via this blacklist. Issue GR-1706 - * tracks the bug in DebugValueMap. - */ - String className = method.getDeclaringClass().getName(); - if (className.contains("/svm/core/code/CodeInfoEncoder") || - className.contains("com/oracle/svm/core/thread/JavaThreads") || - className.contains("com/oracle/svm/core/thread/PlatformThreads") || - className.contains("com/oracle/svm/core/heap/") || - className.contains("com/oracle/svm/core/genscavenge/") || - className.contains("com/oracle/svm/core/thread/VMOperationControl") || - className.contains("debug/internal/DebugValueMap") && method.getName().equals("registerTopLevel")) { - return false; - } - /* - * Method without bytecodes, e.g., methods that have a manually constructed graph, are - * usually not deoptimizable. This needs to change as soon as we want to runtime compile - * our synthetic annotation methods. - */ - if (method.getCode() == null) { - return false; - } - - return true; - } else { + /* + * Method without bytecodes, e.g., methods that have a manually constructed graph, are + * usually not deoptimizable. This needs to change as soon as we want to runtime compile our + * synthetic annotation methods. + */ + if (method.getCode() == null) { return false; } + + return true; + + } + + private static boolean containsStackValueNode(HostedUniverse universe, HostedMethod method) { + return universe.getBigBang().getHostVM().containsStackValueNode(method.wrapped); + } + + /** + * Returns true if a method should be considered as deoptimization source. This is only a + * feature for testing. Note that usually all image compiled methods cannot deoptimize. + */ + static boolean canDeoptForTesting(HostedUniverse universe, HostedMethod method, boolean deoptimizeAll) { + return canDeoptForTesting(method.wrapped, deoptimizeAll, () -> containsStackValueNode(universe, method)); } static void removeDeoptTargetOptimizations(Suites suites) { @@ -363,6 +387,34 @@ public interface DeoptTargetRetriever { ResolvedJavaMethod getDeoptTarget(ResolvedJavaMethod method); } + public static void registerDeoptEntriesForDeoptTesting(PointsToAnalysis bb, StructuredGraph graph, PointsToAnalysisMethod aMethod) { + assert aMethod.isOriginalMethod(); + /* + * Register all FrameStates as DeoptEntries. + * + * Because this graph will have its flowgraph immediately updated after registration, there + * is no reason to make this method's flowgraph a stub on creation. + */ + Collection recomputeMethods = DeoptimizationUtils.registerDeoptEntries(graph, true, + (deoptEntryMethod -> ((PointsToAnalysisMethod) deoptEntryMethod).getOrCreateMultiMethod(DEOPT_TARGET_METHOD))); + + AnalysisMethod deoptMethod = aMethod.getMultiMethod(DEOPT_TARGET_METHOD); + if (deoptMethod != null && SubstrateCompilationDirectives.singleton().isRegisteredDeoptTarget(deoptMethod)) { + /* + * If there exists a deopt target for this method, then it is allowed to deopt. + */ + SubstrateCompilationDirectives.singleton().registerForDeoptTesting(aMethod); + } + + /* + * If new frame states are found, then redo the type flow. + */ + for (ResolvedJavaMethod method : recomputeMethods) { + assert MultiMethod.isDeoptTarget(method); + ((PointsToAnalysisMethod) method).getTypeFlow().updateFlowsGraph(bb, MethodFlowsGraph.GraphKind.FULL, null, true); + } + } + /** * @return the DeoptTarget methods which had new frame registered. */ diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateCompilationDirectives.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateCompilationDirectives.java index 306caf86ae00..5802a4edcf63 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateCompilationDirectives.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateCompilationDirectives.java @@ -150,7 +150,8 @@ public DeoptSourceFrameInfo mergeStateInfo(FrameState state) { private final Set forcedCompilations = ConcurrentHashMap.newKeySet(); private final Set frameInformationRequired = ConcurrentHashMap.newKeySet(); - private final Map> deoptEntries = new ConcurrentHashMap<>(); + private Map> deoptEntries = new ConcurrentHashMap<>(); + private final Set deoptForTestingMethods = ConcurrentHashMap.newKeySet(); private final Set deoptInliningExcludes = ConcurrentHashMap.newKeySet(); public static SubstrateCompilationDirectives singleton() { @@ -207,11 +208,29 @@ public boolean registerDeoptEntry(FrameState state, ResolvedJavaMethod method) { return newEntry; } + /* + * Register a method which can deopt for testing + */ + public void registerForDeoptTesting(ResolvedJavaMethod method) { + assert deoptInfoModifiable(); + deoptForTestingMethods.add((AnalysisMethod) method); + } + + public boolean isRegisteredForDeoptTesting(ResolvedJavaMethod method) { + assert deoptInfoQueryable(); + return deoptForTestingMethods.contains(toAnalysisMethod(method)); + } + public boolean isRegisteredDeoptTarget(ResolvedJavaMethod method) { assert deoptInfoQueryable(); return deoptEntries.containsKey(toAnalysisMethod(method)); } + public void registerDeoptTarget(ResolvedJavaMethod method) { + assert deoptInfoModifiable(); + deoptEntries.computeIfAbsent(toAnalysisMethod(method), m -> new ConcurrentHashMap<>()); + } + public boolean isDeoptEntry(MultiMethod method, int bci, boolean duringCall, boolean rethrowException) { assert deoptInfoQueryable(); @@ -281,7 +300,15 @@ private boolean deoptInfoModifiable() { */ public void resetDeoptEntries() { assert !deoptInfoSealed; - deoptEntries.clear(); + // all methods which are registered for deopt testing cannot be cleared + Map> newDeoptEntries = new ConcurrentHashMap<>(); + for (var deoptForTestingMethod : deoptForTestingMethods) { + var key = deoptForTestingMethod.getMultiMethod(MultiMethod.DEOPT_TARGET_METHOD); + var value = deoptEntries.get(key); + assert key != null && value != null : "Unexpected null value " + key + ", " + value; + newDeoptEntries.put(key, value); + } + deoptEntries = newDeoptEntries; // all methods which require frame information must have a deoptimization entry frameInformationRequired.forEach(m -> deoptEntries.computeIfAbsent(m, n -> new ConcurrentHashMap<>())); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index cb8fcd9c29be..79a2a2a6518b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -1015,8 +1015,11 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec ResolvedJavaMethod method = b.getGraph().method(); if (SubstrateOptions.parseOnce()) { - throw VMError.unimplemented("Intrinsification of isDeoptimizationTarget not done yet"); - + if (MultiMethod.isDeoptTarget(method)) { + b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true)); + } else { + b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(false)); + } } else { if (method instanceof SharedMethod) { if (MultiMethod.isDeoptTarget(method)) { diff --git a/vm/mx.vm/mx_vm_gate.py b/vm/mx.vm/mx_vm_gate.py index 67bbee23acff..87e25681d805 100644 --- a/vm/mx.vm/mx_vm_gate.py +++ b/vm/mx.vm/mx_vm_gate.py @@ -623,6 +623,7 @@ def _collect_excludes(suite, suite_import, excludes): '-H:ClassInitialization=:build_time', '-H:+EnforceMaxRuntimeCompileMethods', '-H:-InlineBeforeAnalysis', + '-H:-ParseOnceJIT', #GR-47163 '-cp', cp, '-H:-FoldSecurityManagerGetter', From 9825febbe6bd80f37ecdcb061c289d25bb9cbf0d Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Mon, 17 Jul 2023 18:10:53 +0200 Subject: [PATCH 2/3] address reviewer comments. --- .../oracle/graal/pointsto/ReachabilityAnalysis.java | 5 ++++- .../graal/pointsto/meta/PointsToAnalysisMethod.java | 4 ++-- .../hosted/ParseOnceRuntimeCompilationFeature.java | 2 +- .../snippets/SubstrateGraphBuilderPlugins.java | 12 ++---------- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java index da45a14d7bd8..d0d2858cccdf 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java @@ -59,7 +59,7 @@ public interface ReachabilityAnalysis { AnalysisType addRootField(Class clazz, String fieldName); /** - * Registers the method as root. + * Registers the method as root. Must be an {@link MultiMethod#ORIGINAL_METHOD}. * * Static methods are immediately analyzed and marked as implementation-invoked which will also * trigger their compilation. @@ -71,6 +71,9 @@ public interface ReachabilityAnalysis { * is instantiated will actually be linked. Trying to register an abstract method as a special * invoked root will result in an error. * + * If {@code otherRoots} are specified, these versions of the method will also be registered as + * root methods. + * * @param aMethod the method to register as root * @param invokeSpecial if true only the target method is analyzed, even if it has overrides, or * it is itself an override. If the method is static this flag is ignored. diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java index b6abf954b306..659320cae413 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisMethod.java @@ -243,8 +243,8 @@ public boolean isImplementationInvokable() { /* * If only a stub is ever created for this method, then it will not be invoked. * - * However, for deopt targets it is possible for a root to temporarily to be a stub - * before a full flow graph is created. + * However, for deopt targets it is possible for a root to temporarily be a stub before + * a full flow graph is created. */ return !getTypeFlow().getMethodFlowsGraphInfo().isStub() || (isDirectRootMethod() && isDeoptTarget()); } 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/ParseOnceRuntimeCompilationFeature.java index 2cb4b62da18f..ab9459c967a7 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/ParseOnceRuntimeCompilationFeature.java @@ -263,7 +263,7 @@ private static final class RuntimeGraphBuilderPhase extends AnalysisGraphBuilder private RuntimeGraphBuilderPhase(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes, SVMHost hostVM) { - super(providers, graphBuilderConfig.withEagerResolving(true), optimisticOpts, initialIntrinsicContext, wordTypes, hostVM); + super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes, hostVM); } static RuntimeGraphBuilderPhase createRuntimeGraphBuilderPhase(BigBang bb, Providers providers, diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 79a2a2a6518b..90eada4913f5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -1015,18 +1015,10 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec ResolvedJavaMethod method = b.getGraph().method(); if (SubstrateOptions.parseOnce()) { - if (MultiMethod.isDeoptTarget(method)) { - b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true)); - } else { - b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(false)); - } + b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(MultiMethod.isDeoptTarget(method))); } else { if (method instanceof SharedMethod) { - if (MultiMethod.isDeoptTarget(method)) { - b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true)); - } else { - b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(false)); - } + b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(MultiMethod.isDeoptTarget(method))); } else { // In analysis the value is always true. b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true)); From a30c8beb5af80eb4321ddb4e0d2e9b11beed750c Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Tue, 18 Jul 2023 14:25:23 +0200 Subject: [PATCH 3/3] Enable class simulation in ParseOnceJIT. --- .../classinitialization/SimulateClassInitializerSupport.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java index e24cb407ed46..345f54194859 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerSupport.java @@ -65,7 +65,6 @@ import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisGraphDecoder; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; -import com.oracle.svm.core.deopt.DeoptimizationSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; @@ -175,7 +174,7 @@ public class SimulateClassInitializerSupport { * Simulation of class initializer (like {@link InlineBeforeAnalysis}) requires ParseOnce, * because otherwise graphs parsed for static analysis omits exception edges. */ - protected final boolean enabled = ClassInitializationOptions.SimulateClassInitializer.getValue() && SubstrateOptions.parseOnce() && !DeoptimizationSupport.enabled(); + protected final boolean enabled = ClassInitializationOptions.SimulateClassInitializer.getValue() && SubstrateOptions.parseOnce(); /* Cached value of options to avoid frequent lookup of option values. */ protected final boolean collectAllReasons = ClassInitializationOptions.SimulateClassInitializerCollectAllReasons.getValue();