From 9db8fab8973e84617af26e01df20c1428e547d84 Mon Sep 17 00:00:00 2001 From: Christian Humer Date: Thu, 7 Nov 2024 19:38:18 +0100 Subject: [PATCH 1/4] Add javadoc to the Node class. --- .../com/oracle/truffle/api/nodes/Node.java | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java index bee27c914608..772f88a77bca 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java @@ -68,8 +68,43 @@ import com.oracle.truffle.api.source.SourceSection; /** - * Abstract base class for all Truffle nodes. + * Abstract base class for all nodes in the Truffle Abstract Syntax Tree (AST). + *

+ * The {@code Node} class serves as the foundational class for all nodes within the Truffle + * framework. It provides the core functionality required to build interpreters for guest languages + * by allowing language implementers to define custom nodes representing language constructs. + *

+ * Key responsibilities and features of the {@code Node} class include: + *

+ *

+ * Language implementers typically create subclasses of {@code Node} to represent specific language + * constructs and operations. Subclasses can use the {@link Child} and {@link Children} annotations + * to declare child nodes, which the Truffle framework manages automatically. + *

+ * The {@code Node} class also provides important methods such as {@link #replace(Node)} for + * replacing nodes in the AST, {@link #getParent()} and {@link #getRootNode()} for navigating the + * tree, and {@link #getDescription()} for obtaining a user-readable description of the node. * + * @see RootNode + * @see NodeVisitor + * @see NodeInterface * @since 0.8 or earlier */ public abstract class Node implements NodeInterface, Cloneable { From 8f98ffb7ec219c1d8b4e328b4e4a48550601beaa Mon Sep 17 00:00:00 2001 From: Christian Humer Date: Mon, 11 Nov 2024 15:11:38 +0100 Subject: [PATCH 2/4] Compiler changes for prepareForCompilation. (to-backport) --- .../test/AgnosticInliningPhaseTest.java | 5 +- .../truffle/test/PartialEvaluationTest.java | 3 +- .../libgraal/truffle/HSTruffleCompilable.java | 4 +- .../compiler/truffle/TruffleCompilerImpl.java | 3 +- .../compiler/truffle/TruffleTierContext.java | 93 +++++++++++++--- .../truffle/phases/inlining/CallNode.java | 41 ++++--- .../inlining/DefaultInliningPolicy.java | 13 ++- .../truffle/phases/inlining/GraphManager.java | 17 +-- .../hotspot/libgraal/FromLibGraalCalls.java | 8 ++ .../libgraal/TruffleFromLibGraalCalls.java | 5 +- .../TruffleFromLibGraalStartPoints.java | 100 ++++++++++++++---- .../IsolatedCompilableTruffleAST.java | 13 +-- .../truffle/compiler/TruffleCompilable.java | 20 +++- 13 files changed, 242 insertions(+), 83 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/AgnosticInliningPhaseTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/AgnosticInliningPhaseTest.java index 2f4cf6337c8e..5922323227b2 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/AgnosticInliningPhaseTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/AgnosticInliningPhaseTest.java @@ -63,9 +63,10 @@ public String toString(Verbosity verbosity) { return ""; } }; - final TruffleTierContext context = new TruffleTierContext(partialEvaluator, + final TruffleTierContext context = TruffleTierContext.createInitialContext( + partialEvaluator, compiler.getOrCreateCompilerOptions(callTarget), - getDebugContext(), callTarget, partialEvaluator.rootForCallTarget(callTarget), + getDebugContext(), callTarget, compilationIdentifier, getSpeculationLog(), new TruffleCompilationTask() { diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PartialEvaluationTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PartialEvaluationTest.java index a184d783ff55..955410443010 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PartialEvaluationTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PartialEvaluationTest.java @@ -299,10 +299,9 @@ private StructuredGraph partialEval(OptimizedCallTarget compilable, Object[] arg final PartialEvaluator partialEvaluator = compiler.getPartialEvaluator(); try (PerformanceInformationHandler handler = PerformanceInformationHandler.install( compiler.getConfig().runtime(), compiler.getOrCreateCompilerOptions(compilable))) { - final TruffleTierContext context = new TruffleTierContext(partialEvaluator, + final TruffleTierContext context = TruffleTierContext.createInitialContext(partialEvaluator, compiler.getOrCreateCompilerOptions(compilable), debug, compilable, - partialEvaluator.rootForCallTarget(compilable), compilation.getCompilationId(), speculationLog, task, handler); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/truffle/HSTruffleCompilable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/truffle/HSTruffleCompilable.java index 2973f46ad57f..a3c3839e9c0c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/truffle/HSTruffleCompilable.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/truffle/HSTruffleCompilable.java @@ -87,9 +87,9 @@ public long engineId() { } @Override - public void prepareForCompilation() { + public boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) { try { - HANDLES.prepareForCompilation.invoke(hsHandle); + return (boolean) HANDLES.prepareForCompilation.invoke(hsHandle, rootCompilation, compilationTier, lastTier); } catch (Throwable t) { throw handleException(t); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleCompilerImpl.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleCompilerImpl.java index 7c6822eb43d2..773c6d51e1b4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleCompilerImpl.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleCompilerImpl.java @@ -538,11 +538,10 @@ private StructuredGraph truffleTier(TruffleCompilationWrapper wrapper, DebugCont * TODO GR-37097 Merge TruffleTierConfiguration and TruffleCompilationWrapper so that * there is one place where compilation data lives */ - TruffleTierContext context = new TruffleTierContext(partialEvaluator, + TruffleTierContext context = TruffleTierContext.createInitialContext(partialEvaluator, wrapper.compilerOptions, debug, wrapper.compilable, - partialEvaluator.rootForCallTarget(wrapper.compilable), wrapper.compilationId, TruffleTierContext.getSpeculationLog(wrapper), wrapper.task, handler); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleTierContext.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleTierContext.java index eb67685d9d4e..153f2d246d60 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleTierContext.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/TruffleTierContext.java @@ -26,7 +26,12 @@ import java.util.Objects; +import com.oracle.truffle.compiler.TruffleCompilable; +import com.oracle.truffle.compiler.TruffleCompilationTask; +import com.oracle.truffle.compiler.TruffleCompilerRuntime; + import jdk.graal.compiler.core.common.CompilationIdentifier; +import jdk.graal.compiler.core.common.RetryableBailoutException; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.nodes.Cancellable; @@ -37,11 +42,6 @@ import jdk.graal.compiler.phases.tiers.HighTierContext; import jdk.graal.compiler.phases.util.Providers; import jdk.graal.compiler.truffle.nodes.TruffleAssumption; - -import com.oracle.truffle.compiler.TruffleCompilable; -import com.oracle.truffle.compiler.TruffleCompilationTask; -import com.oracle.truffle.compiler.TruffleCompilerRuntime; - import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -62,12 +62,13 @@ public final class TruffleTierContext extends HighTierContext { public final StructuredGraph graph; public final PerformanceInformationHandler handler; - public TruffleTierContext(PartialEvaluator partialEvaluator, + private TruffleTierContext(PartialEvaluator partialEvaluator, OptionValues compilerOptions, DebugContext debug, - TruffleCompilable compilable, ResolvedJavaMethod method, + TruffleCompilable compilable, CompilationIdentifier compilationId, SpeculationLog log, - TruffleCompilationTask task, PerformanceInformationHandler handler) { + TruffleCompilationTask task, PerformanceInformationHandler handler, + ResolvedJavaMethod initialMethod) { super(partialEvaluator.getProviders(), new PhaseSuite<>(), OptimisticOptimizations.NONE); Objects.requireNonNull(debug); Objects.requireNonNull(compilable); @@ -82,12 +83,71 @@ public TruffleTierContext(PartialEvaluator partialEvaluator, this.log = log; this.task = task; this.handler = handler; - this.graph = createInitialGraph(method); + this.graph = createInitialGraph(initialMethod); } - private StructuredGraph createInitialGraph(ResolvedJavaMethod method) { - compilable.prepareForCompilation(); + private TruffleTierContext(TruffleTierContext parent, + TruffleCompilable compilable, + ResolvedJavaMethod initialMethod) { + this(parent.partialEvaluator, + parent.compilerOptions, + parent.debug, + compilable, + parent.compilationId, + parent.log, + parent.task, + parent.handler, + initialMethod); + } + + public static TruffleTierContext createInitialContext(PartialEvaluator partialEvaluator, + OptionValues compilerOptions, + DebugContext debug, + TruffleCompilable compilable, + CompilationIdentifier compilationId, SpeculationLog log, + TruffleCompilationTask task, PerformanceInformationHandler handler) { + + boolean readyForCompilation = compilable.prepareForCompilation(true, task.tier(), task.isLastTier()); + if (!readyForCompilation) { + /* + * If the root node not ready for compilation we throw a retryable bailout for root + * compilations. This will have the effect that the method will be called again in the + * interpreter to reprofile. + */ + throw new RetryableBailoutException("Compilable not ready for compilation."); + } + ResolvedJavaMethod method = partialEvaluator.rootForCallTarget(compilable); + TruffleTierContext context = new TruffleTierContext(partialEvaluator, compilerOptions, debug, compilable, compilationId, log, task, handler, method); + context.recordStabilityAssumptions(); + return context; + } + + public TruffleTierContext createInlineContext(TruffleCompilable inlinedCompilable) { + boolean readyForCompilation = inlinedCompilable.prepareForCompilation(false, task.tier(), task.isLastTier()); + if (!readyForCompilation) { + /* + * If the root node not ready for compilation we throw a retryable bailout. For inlining + * this bailout triggers the Bailout state in the inlining call tree which forces the + * call to a materialize and not inline. + */ + throw new RetryableBailoutException("Compilable not ready for compilation."); + } + TruffleTierContext context = new TruffleTierContext(this, inlinedCompilable, + partialEvaluator.inlineRootForCallTarget(compilable)); + context.recordStabilityAssumptions(); + return context; + } + /** + * This creates a compilation context for call site finalization during inlining. We PE all + * candidates up to callInlined, then when we are done with inlining we finalize the graph by PE + * from callDirect to the call boundary for call sites not inlined. + */ + public TruffleTierContext createFinalizationContext(TruffleCompilable inlinedCompilable) { + return new TruffleTierContext(this, inlinedCompilable, partialEvaluator.getCallDirect()); + } + + private StructuredGraph createInitialGraph(ResolvedJavaMethod method) { // @formatter:off StructuredGraph.Builder builder = new StructuredGraph.Builder(this.debug.getOptions(), this.debug, StructuredGraph.AllowAssumptions.YES). name(this.compilable.getName()). @@ -97,11 +157,12 @@ private StructuredGraph createInitialGraph(ResolvedJavaMethod method) { trackNodeSourcePosition(partialEvaluator.graphBuilderConfigForParsing.trackNodeSourcePosition()). cancellable(new CancellableTask(this.task)); // @formatter:on - builder = partialEvaluator.customizeStructuredGraphBuilder(builder); - StructuredGraph g = builder.build(); - g.getAssumptions().record(new TruffleAssumption(getValidRootAssumption(partialEvaluator.getProviders()))); - g.getAssumptions().record(new TruffleAssumption(getNodeRewritingAssumption(partialEvaluator.getProviders()))); - return g; + return partialEvaluator.customizeStructuredGraphBuilder(builder).build(); + } + + private void recordStabilityAssumptions() { + graph.getAssumptions().record(new TruffleAssumption(getValidRootAssumption(partialEvaluator.getProviders()))); + graph.getAssumptions().record(new TruffleAssumption(getNodeRewritingAssumption(partialEvaluator.getProviders()))); } public PartialEvaluator getPartialEvaluator() { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/CallNode.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/CallNode.java index 56a6c338bc51..ea7a10aaaa48 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/CallNode.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/CallNode.java @@ -37,7 +37,7 @@ import com.oracle.truffle.compiler.TruffleCompilable; import com.oracle.truffle.compiler.TruffleCompilationTask; -import jdk.graal.compiler.core.common.PermanentBailoutException; +import jdk.graal.compiler.core.common.GraalBailoutException; import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.graph.Node; @@ -278,6 +278,13 @@ private void addIndirectChildren(GraphManager.Entry entry) { } public void expand() { + if (state == State.Expanded) { + /* + * The CallNode may be already expanded for trivial call nodes which are expanded in the + * afterExpand method. + */ + return; + } assert state == State.Cutoff : "Cannot expand a non-cutoff node. Node is " + state; assert getParent() != null; state = State.Expanded; @@ -288,7 +295,7 @@ public void expand() { GraphManager manager = getCallTree().getGraphManager(); try { entry = manager.pe(directCallTarget); - } catch (PermanentBailoutException e) { + } catch (GraalBailoutException e) { state = State.BailedOut; return; } @@ -466,17 +473,25 @@ public int compareTo(CallNode o) { } public void finalizeGraph() { - if (state == State.Inlined) { - for (CallNode child : children) { - child.finalizeGraph(); - } - } - if (state == State.Cutoff || state == State.Expanded || state == State.BailedOut) { - if (invoke.isAlive()) { - getCallTree().getGraphManager().finalizeGraph(invoke, directCallTarget); - } else { - state = State.Removed; - } + switch (state) { + case Inlined: + for (CallNode child : children) { + child.finalizeGraph(); + } + break; + case Cutoff: + case Expanded: + case BailedOut: + if (invoke.isAlive()) { + getCallTree().getGraphManager().finalizeGraph(invoke, directCallTarget); + } else { + state = State.Removed; + } + break; + case Indirect: + case Removed: + // nothing to finalize + break; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/DefaultInliningPolicy.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/DefaultInliningPolicy.java index 827cce0da71d..64de9e7dfb23 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/DefaultInliningPolicy.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/DefaultInliningPolicy.java @@ -90,8 +90,17 @@ private void analyse(CallNode node) { if (node.getState() == CallNode.State.Expanded) { data.callDiff = -1 * node.getRootRelativeFrequency(); for (CallNode child : node.getChildren()) { - if (child.getState() != CallNode.State.Indirect && child.getState() != CallNode.State.Removed && child.getState() != CallNode.State.BailedOut) { - data.callDiff += data(child).callDiff; + switch (child.getState()) { + case Indirect: + case Removed: + case BailedOut: + // nothing to do + break; + case Cutoff: + case Inlined: + case Expanded: + data.callDiff += data(child).callDiff; + break; } } if (data.callDiff > 0) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/GraphManager.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/GraphManager.java index 0de3d8149853..3e73e8beb5d7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/GraphManager.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/GraphManager.java @@ -79,7 +79,7 @@ Entry pe(TruffleCompilable truffleAST) { // the guest scope represents the guest language method of truffle try (AutoCloseable guestScope = rootContext.debug.scope("Truffle", new TruffleDebugJavaMethod(rootContext.task, truffleAST))) { final PEAgnosticInlineInvokePlugin plugin = newPlugin(); - final TruffleTierContext context = newContext(truffleAST, false); + final TruffleTierContext context = rootContext.createInlineContext(truffleAST); try (Scope hostScope = context.debug.scope("CreateGraph", context.graph); Indent indent = context.debug.logAndIndent("evaluate %s", context.graph);) { partialEvaluator.doGraphPE(context, plugin, graphCacheForInlining); @@ -99,19 +99,6 @@ Entry pe(TruffleCompilable truffleAST) { return entry; } - private TruffleTierContext newContext(TruffleCompilable truffleAST, boolean finalize) { - return new TruffleTierContext( - partialEvaluator, - rootContext.compilerOptions, - rootContext.debug, - truffleAST, - finalize ? partialEvaluator.getCallDirect() : partialEvaluator.inlineRootForCallTarget(truffleAST), - rootContext.compilationId, - rootContext.log, - rootContext.task, - rootContext.handler); - } - private PEAgnosticInlineInvokePlugin newPlugin() { return new PEAgnosticInlineInvokePlugin(partialEvaluator); } @@ -135,7 +122,7 @@ UnmodifiableEconomicMap doInline(Invoke invoke, StructuredGraph ir, } void finalizeGraph(Invoke invoke, TruffleCompilable truffleAST) { - final TruffleTierContext context = newContext(truffleAST, true); + final TruffleTierContext context = rootContext.createFinalizationContext(truffleAST); partialEvaluator.doGraphPE(context, new InlineInvokePlugin() { @Override public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/FromLibGraalCalls.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/FromLibGraalCalls.java index 9b1eec61b9cc..d774f3286525 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/FromLibGraalCalls.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/FromLibGraalCalls.java @@ -95,6 +95,14 @@ public String toString() { } } + JClass getPeer() { + return peer; + } + + JNICalls getJNICalls() { + return hotSpotCalls; + } + public final void callVoid(JNIEnv env, T id, JValue args) { JNIMethodImpl method = getJNIMethod(env, id, void.class); hotSpotCalls.callStaticVoid(env, peer, method, args); diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalCalls.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalCalls.java index 2e5246905d71..644cee8f9809 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalCalls.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalCalls.java @@ -24,13 +24,14 @@ */ package com.oracle.svm.graal.hotspot.libgraal; -import com.oracle.truffle.compiler.hotspot.libgraal.TruffleFromLibGraal.Id; +import static org.graalvm.jniutils.JNIUtil.NewGlobalRef; + import org.graalvm.jniutils.JNI.JClass; import org.graalvm.jniutils.JNI.JNIEnv; import org.graalvm.jniutils.JNI.JObject; import org.graalvm.jniutils.JNIUtil; -import static org.graalvm.jniutils.JNIUtil.NewGlobalRef; +import com.oracle.truffle.compiler.hotspot.libgraal.TruffleFromLibGraal.Id; final class TruffleFromLibGraalCalls extends FromLibGraalCalls { diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java index 9d6cf19898dc..be9b0f5a6213 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java @@ -24,24 +24,6 @@ */ package com.oracle.svm.graal.hotspot.libgraal; -import com.oracle.truffle.compiler.hotspot.libgraal.TruffleFromLibGraal; -import com.oracle.truffle.compiler.hotspot.libgraal.TruffleFromLibGraal.Id; -import org.graalvm.jniutils.HSObject; -import org.graalvm.jniutils.JNI.JClass; -import org.graalvm.jniutils.JNI.JObject; -import org.graalvm.jniutils.JNI.JByteArray; -import org.graalvm.jniutils.JNI.JNIEnv; -import org.graalvm.jniutils.JNI.JavaVM; -import org.graalvm.jniutils.JNIMethodScope; -import org.graalvm.jniutils.JNIUtil; -import org.graalvm.nativebridge.BinaryInput; -import org.graalvm.nativeimage.StackValue; -import org.graalvm.nativeimage.c.type.CCharPointer; -import org.graalvm.word.WordFactory; - -import java.util.LinkedHashMap; -import java.util.Map; - import static com.oracle.svm.graal.hotspot.libgraal.TruffleFromLibGraalStartPointsGen.callAddInlinedTarget; import static com.oracle.svm.graal.hotspot.libgraal.TruffleFromLibGraalStartPointsGen.callAddTargetToDequeue; import static com.oracle.svm.graal.hotspot.libgraal.TruffleFromLibGraalStartPointsGen.callAsJavaConstant; @@ -90,7 +72,34 @@ import static com.oracle.svm.graal.hotspot.libgraal.TruffleFromLibGraalStartPointsGen.callRegisterOptimizedAssumptionDependency; import static com.oracle.svm.graal.hotspot.libgraal.TruffleFromLibGraalStartPointsGen.callSetCallCounts; import static org.graalvm.jniutils.JNIMethodScope.env; +import static org.graalvm.jniutils.JNIUtil.ExceptionClear; +import static org.graalvm.jniutils.JNIUtil.GetStaticMethodID; import static org.graalvm.jniutils.JNIUtil.createString; +import static org.graalvm.nativeimage.c.type.CTypeConversion.toCString; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.graalvm.jniutils.HSObject; +import org.graalvm.jniutils.JNI.JByteArray; +import org.graalvm.jniutils.JNI.JClass; +import org.graalvm.jniutils.JNI.JMethodID; +import org.graalvm.jniutils.JNI.JNIEnv; +import org.graalvm.jniutils.JNI.JObject; +import org.graalvm.jniutils.JNI.JValue; +import org.graalvm.jniutils.JNI.JavaVM; +import org.graalvm.jniutils.JNICalls.JNIMethod; +import org.graalvm.jniutils.JNIMethodScope; +import org.graalvm.jniutils.JNIUtil; +import org.graalvm.nativebridge.BinaryInput; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; +import org.graalvm.word.WordFactory; + +import com.oracle.truffle.compiler.hotspot.libgraal.FromLibGraalId; +import com.oracle.truffle.compiler.hotspot.libgraal.TruffleFromLibGraal; +import com.oracle.truffle.compiler.hotspot.libgraal.TruffleFromLibGraal.Id; /** * JNI calls to HotSpot called by guest Graal using method handles. @@ -211,9 +220,60 @@ public static long engineId(Object hsHandle) { } @TruffleFromLibGraal(Id.PrepareForCompilation) - public static void prepareForCompilation(Object hsHandle) { + public static boolean prepareForCompilation(Object hsHandle, boolean p1, int p2, boolean p3) { JNIEnv env = JNIMethodScope.env(); - callPrepareForCompilation(calls, env, ((HSObject) hsHandle).getHandle()); + JNIMethod newMethod = findPrepareForCompilationNewMethod(env); + if (newMethod != null) { + return callPrepareForCompilationNew(newMethod, env, ((HSObject) hsHandle).getHandle(), p1, p2, p3); + } else { + callPrepareForCompilation(calls, env, ((HSObject) hsHandle).getHandle()); + return true; + } + } + + private static volatile JNIMethod prepareForCompilationNewMethod; + + private static JNIMethod findPrepareForCompilationNewMethod(JNIEnv env) { + JNIMethod res = prepareForCompilationNewMethod; + if (res == null) { + res = findJNIMethod(env, "prepareForCompilation", boolean.class, Object.class, boolean.class, int.class, boolean.class); + prepareForCompilationNewMethod = res; + } + return res.getJMethodID().isNonNull() ? res : null; + } + + static boolean callPrepareForCompilationNew(JNIMethod method, JNIEnv env, JObject p0, boolean p1, int p2, boolean p3) { + JValue args = StackValue.get(4, JValue.class); + args.addressOf(0).setJObject(p0); + args.addressOf(1).setBoolean(p1); + args.addressOf(2).setInt(p2); + args.addressOf(3).setBoolean(p3); + return calls.getJNICalls().callStaticBoolean(env, calls.getPeer(), method, args); + } + + private static JNIMethod findJNIMethod(JNIEnv env, String methodName, Class returnType, Class... parameterTypes) { + try (CTypeConversion.CCharPointerHolder cname = toCString(methodName); + CTypeConversion.CCharPointerHolder csig = toCString(FromLibGraalId.encodeMethodSignature(returnType, parameterTypes))) { + JMethodID jniId = GetStaticMethodID(env, calls.getPeer(), cname.get(), csig.get()); + if (jniId.isNull()) { + /* + * The `onFailure` method with 7 arguments is not available in Truffle runtime 24.0, + * clear pending NoSuchMethodError. + */ + ExceptionClear(env); + } + return new JNIMethod() { + @Override + public JMethodID getJMethodID() { + return jniId; + } + + @Override + public String getDisplayName() { + return methodName; + } + }; + } } @TruffleFromLibGraal(Id.IsTrivial) diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/isolated/IsolatedCompilableTruffleAST.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/isolated/IsolatedCompilableTruffleAST.java index 56251459f75e..62da775116e2 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/isolated/IsolatedCompilableTruffleAST.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/isolated/IsolatedCompilableTruffleAST.java @@ -28,8 +28,6 @@ import java.util.Map; import java.util.function.Supplier; -import com.oracle.svm.graal.isolated.IsolatedHandles; -import jdk.graal.compiler.debug.GraalError; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; @@ -43,6 +41,7 @@ import com.oracle.svm.graal.isolated.IsolatedCodeInstallBridge; import com.oracle.svm.graal.isolated.IsolatedCompileClient; import com.oracle.svm.graal.isolated.IsolatedCompileContext; +import com.oracle.svm.graal.isolated.IsolatedHandles; import com.oracle.svm.graal.isolated.IsolatedObjectConstant; import com.oracle.svm.graal.isolated.IsolatedObjectProxy; import com.oracle.svm.graal.isolated.IsolatedSpeculationLog; @@ -50,6 +49,7 @@ import com.oracle.svm.truffle.isolated.BinaryOutput.ByteArrayBinaryOutput; import com.oracle.truffle.compiler.TruffleCompilable; +import jdk.graal.compiler.debug.GraalError; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.SpeculationLog; @@ -128,8 +128,8 @@ public int getKnownCallSiteCount() { } @Override - public void prepareForCompilation() { - prepareForCompilation0(IsolatedCompileContext.get().getClient(), handle); + public boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) { + return prepareForCompilation0(IsolatedCompileContext.get().getClient(), handle, rootCompilation, compilationTier, lastTier); } @Override @@ -225,9 +225,10 @@ private static int getKnownCallSiteCount0(@SuppressWarnings("unused") ClientIsol } @CEntryPoint(include = CEntryPoint.NotIncludedAutomatically.class, publishAs = CEntryPoint.Publish.NotPublished) - private static void prepareForCompilation0(@SuppressWarnings("unused") ClientIsolateThread client, ClientHandle handle) { + private static boolean prepareForCompilation0(@SuppressWarnings("unused") ClientIsolateThread client, ClientHandle handle, + boolean rootCompilation, int compilationTier, boolean lastTier) { TruffleCompilable ast = IsolatedCompileClient.get().unhand(handle); - ast.prepareForCompilation(); + return ast.prepareForCompilation(rootCompilation, compilationTier, lastTier); } @CEntryPoint(include = CEntryPoint.NotIncludedAutomatically.class, publishAs = CEntryPoint.Publish.NotPublished) diff --git a/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilable.java b/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilable.java index 43f1a0d36d6e..287516efd0ce 100644 --- a/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilable.java +++ b/truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilable.java @@ -133,8 +133,26 @@ public interface TruffleCompilable { /** * Called before call target is used for runtime compilation, either as root compilation or via * inlining. + * + * @deprecated use {@link #prepareForCompilation(boolean, int, boolean)} instead. */ - void prepareForCompilation(); + @Deprecated + default void prepareForCompilation() { + prepareForCompilation(true, 2, true); + } + + /** + * Called before call target is used for runtime compilation, either as root compilation or via + * inlining. + * + * @param rootCompilation whether this compilation is compiled as root method + * @param compilationTier which tier this compilation is compiled with + * @param lastTier + */ + default boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) { + prepareForCompilation(); + return true; + } /** * Returns {@code e} serialized as a string. The format of the returned string is: From 08165c2e144dc99dc0db10f90857bc9061885050 Mon Sep 17 00:00:00 2001 From: Christian Humer Date: Mon, 11 Nov 2024 22:40:45 +0100 Subject: [PATCH 3/4] Add RootNode.prepareForCompilation. --- .../test/ClassLinkedByCompilerTest.java | 1 + .../truffle/test/EncodedGraphCacheTest.java | 1 + .../test/MultiTierCompilationTest.java | 8 +- .../test/NodeSplittingStrategyTest.java | 2 +- .../truffle/test/PerformanceWarningTest.java | 1 + .../truffle/test/RootNodeCompilationTest.java | 145 ++++++++++++++++++ .../TruffleFromLibGraalStartPoints.java | 17 +- truffle/CHANGELOG.md | 2 + .../com.oracle.truffle.api/snapshot.sigtest | 3 +- .../com/oracle/truffle/api/impl/Accessor.java | 2 + .../truffle/api/nodes/NodeAccessor.java | 5 + .../oracle/truffle/api/nodes/RootNode.java | 70 +++++++++ .../truffle/runtime/BaseOSRRootNode.java | 9 ++ .../truffle/runtime/OptimizedCallTarget.java | 58 ++++++- .../TruffleFromLibGraalEntryPoints.java | 6 + 15 files changed, 311 insertions(+), 19 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/ClassLinkedByCompilerTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/ClassLinkedByCompilerTest.java index 6344e686a54d..28ee0c2d5cb1 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/ClassLinkedByCompilerTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/ClassLinkedByCompilerTest.java @@ -38,6 +38,7 @@ public class ClassLinkedByCompilerTest extends PartialEvaluationTest { public void testClassLinkedByCompiler() { RootNode root = new RootNodeImpl(); OptimizedCallTarget compilable = (OptimizedCallTarget) root.getCallTarget(); + compilable.ensureInitialized(); TruffleCompilationTask task = newTask(); TruffleCompilerImpl compiler = getTruffleCompiler(compilable); ResolvedJavaType unlinked = getMetaAccess().lookupJavaType(Unlinked.class); diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/EncodedGraphCacheTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/EncodedGraphCacheTest.java index 5ff39efeaeee..9e8e55fdaa2d 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/EncodedGraphCacheTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/EncodedGraphCacheTest.java @@ -140,6 +140,7 @@ private static OptimizedCallTarget compileAST(RootNode rootNode) { try (DebugContext.Scope s = debug.scope("EncodedGraphCacheTest")) { TruffleCompilationTask task = newTask(); try (TruffleCompilation compilation = compiler.openCompilation(task, target)) { + target.ensureInitialized(); getTruffleCompilerFromRuntime(target).compileAST(debug, target, compilation.getCompilationId(), task, null); assertTrue(target.isValid()); } diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/MultiTierCompilationTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/MultiTierCompilationTest.java index 3a3139da5289..1b8e55df771e 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/MultiTierCompilationTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/MultiTierCompilationTest.java @@ -75,11 +75,13 @@ private static class MultiTierRootNode extends RootNode { @Override public Object execute(VirtualFrame frame) { + boundary(); + Object result = callNode.call(frame.getArguments()); if (CompilerDirectives.inInterpreter()) { return "root:interpreter"; } boundary(); - return callNode.call(frame.getArguments()); + return result; } } @@ -196,10 +198,6 @@ public void testDefault() { for (int i = 0; i < firstTierCompilationThreshold; i++) { multiTierTarget.call(); } - Assert.assertEquals("callee:interpreter", multiTierTarget.call()); - for (int i = 0; i < firstTierCompilationThreshold; i++) { - multiTierTarget.call(); - } Assert.assertEquals("callee:first-tier", multiTierTarget.call()); for (int i = 0; i < compilationThreshold; i++) { multiTierTarget.call(); diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/NodeSplittingStrategyTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/NodeSplittingStrategyTest.java index 67292fbcb238..2d9c91a66b7c 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/NodeSplittingStrategyTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/NodeSplittingStrategyTest.java @@ -556,7 +556,7 @@ private void assertExpectations() { @Test public void testSplittingBudgetLimit() { - try (Context c = Context.newBuilder(SplittingLimitTestLanguage.ID).build()) { + try (Context c = Context.newBuilder(SplittingLimitTestLanguage.ID).option("engine.CompileImmediately", "false").build()) { c.eval(SplittingLimitTestLanguage.ID, ""); } } diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PerformanceWarningTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PerformanceWarningTest.java index 6afacbe42f5c..5124ceae1bff 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PerformanceWarningTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/PerformanceWarningTest.java @@ -127,6 +127,7 @@ private void testHelper(RootNode rootNode, boolean expectException, String... ou DebugContext debug = new Builder(compiler.getOrCreateCompilerOptions(target)).build(); try (DebugCloseable d = debug.disableIntercept(); DebugContext.Scope s = debug.scope("PerformanceWarningTest")) { final OptimizedCallTarget compilable = target; + compilable.ensureInitialized(); TruffleCompilationTask task = PartialEvaluationTest.newTask(); try (TruffleCompilation compilation = compiler.openCompilation(task, compilable)) { compiler.compileAST(debug, compilable, compilation.getCompilationId(), task, null); diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/RootNodeCompilationTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/RootNodeCompilationTest.java index deb455402a94..686fea852d86 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/RootNodeCompilationTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/RootNodeCompilationTest.java @@ -25,6 +25,8 @@ package jdk.graal.compiler.truffle.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.List; @@ -32,6 +34,8 @@ import org.junit.Test; import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.exception.AbstractTruffleException; @@ -133,6 +137,147 @@ protected int findBytecodeIndex(Node node, Frame frame) { } } + @Test + public void testPrepareForCompilationLastTier() { + PrepareRootNode node = new PrepareRootNode(true); + OptimizedCallTarget target = (OptimizedCallTarget) node.getCallTarget(); + target.compile(true); + target.waitForCompilation(); + assertTrue(target.isValidLastTier()); + assertEquals(new CompilationData(true, 2, true), target.call()); + assertEquals(1, node.prepareCount); + assertTrue(target.isValidLastTier()); + } + + @Test + public void testPrepareForCompilationLastTierReprofile() { + PrepareRootNode node = new PrepareRootNode(false); + OptimizedCallTarget target = (OptimizedCallTarget) node.getCallTarget(); + target.compile(true); + target.waitForCompilation(); + assertFalse(target.isValidLastTier()); + assertNull(target.call()); + assertEquals(new CompilationData(true, 2, true), node.compilationData); + assertEquals(1, node.prepareCount); + + target.invalidate("test"); + node.returnValue = true; + + target.compile(true); + target.waitForCompilation(); + assertEquals(new CompilationData(true, 2, true), target.call()); + assertEquals(2, node.prepareCount); + assertTrue(target.isValidLastTier()); + } + + @Test + public void testPrepareForCompilationFirstTier() { + PrepareRootNode node = new PrepareRootNode(true); + OptimizedCallTarget target = (OptimizedCallTarget) node.getCallTarget(); + target.compile(false); + target.waitForCompilation(); + assertTrue(target.isValid()); + assertEquals(new CompilationData(true, 1, false), target.call()); + assertEquals(1, node.prepareCount); + assertTrue(target.isValid()); + } + + @Test + public void testPrepareForCompilationFirstTierReprofile() { + PrepareRootNode node = new PrepareRootNode(false); + OptimizedCallTarget target = (OptimizedCallTarget) node.getCallTarget(); + target.compile(false); + target.waitForCompilation(); + assertFalse(target.isValid()); + assertNull(target.call()); + assertEquals(new CompilationData(true, 1, false), node.compilationData); + assertEquals(1, node.prepareCount); + + target.invalidate("test"); + node.returnValue = true; + + target.compile(false); + target.waitForCompilation(); + assertEquals(new CompilationData(true, 1, false), target.call()); + assertEquals(2, node.prepareCount); + assertTrue(target.isValid()); + + } + + @Test + public void testPrepareForCompilationInlined() { + PrepareRootNode node = new PrepareRootNode(true); + node.getCallTarget().call(); // ensure initialized for inlining + ConstantTargetRootNode call = new ConstantTargetRootNode(node); + OptimizedCallTarget target = (OptimizedCallTarget) call.getCallTarget(); + target.compile(true); + target.waitForCompilation(); + assertTrue(target.isValidLastTier()); + assertEquals(1, node.prepareCount); + assertEquals(new CompilationData(false, 2, true), target.call()); + assertTrue(target.isValidLastTier()); + } + + @Test + public void testPrepareForCompilationInlinedReprofile() { + PrepareRootNode node = new PrepareRootNode(false); + node.getCallTarget().call(); // ensure initialized for inlining + ConstantTargetRootNode call = new ConstantTargetRootNode(node); + OptimizedCallTarget target = (OptimizedCallTarget) call.getCallTarget(); + target.compile(true); + target.waitForCompilation(); + assertTrue(target.isValidLastTier()); + assertNull(target.call()); + assertEquals(new CompilationData(false, 2, true), node.compilationData); + assertEquals(1, node.prepareCount); + + target.invalidate("test"); + node.returnValue = true; + + target.compile(true); + target.waitForCompilation(); + assertTrue(target.isValidLastTier()); + assertEquals(2, node.prepareCount); + assertEquals(new CompilationData(false, 2, true), target.call()); + assertTrue(target.isValidLastTier()); + } + + record CompilationData(boolean rootCompilation, int compilationTier, boolean lastTier) { + } + + static final class PrepareRootNode extends BaseRootNode { + + private volatile int prepareCount = 0; + @CompilationFinal volatile boolean returnValue; + @CompilationFinal volatile CompilationData compilationData; + + PrepareRootNode(boolean returnValue) { + this.returnValue = returnValue; + } + + @Override + public Object execute(VirtualFrame frame) { + if (CompilerDirectives.inCompiledCode()) { + return compilationData; + } + return null; + } + + @Override + protected boolean isTrivial() { + return true; + } + + @SuppressWarnings("hiding") + @Override + protected boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) { + this.prepareCount++; + this.compilationData = new CompilationData(rootCompilation, compilationTier, lastTier); + return returnValue; + } + + } + static class ConstantTargetRootNode extends BaseRootNode { // deliberately diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java index be9b0f5a6213..082800d4ac3d 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/TruffleFromLibGraalStartPoints.java @@ -106,21 +106,26 @@ */ public final class TruffleFromLibGraalStartPoints { - private static TruffleFromLibGraalCalls calls; + private static volatile TruffleFromLibGraalCalls calls; private static JavaVM javaVM; private TruffleFromLibGraalStartPoints() { } static void initializeJNI(JClass runtimeClass) { - if (calls == null) { - calls = new TruffleFromLibGraalCalls(JNIMethodScope.env(), runtimeClass); - JavaVM vm = JNIUtil.GetJavaVM(JNIMethodScope.env()); - assert javaVM.isNull() || javaVM.equal(vm); - javaVM = vm; + TruffleFromLibGraalCalls localCalls = calls; + if (localCalls == null) { + initialize(runtimeClass); } } + private static synchronized void initialize(JClass runtimeClass) { + calls = new TruffleFromLibGraalCalls(JNIMethodScope.env(), runtimeClass); + JavaVM vm = JNIUtil.GetJavaVM(JNIMethodScope.env()); + assert javaVM.isNull() || javaVM.equal(vm); + javaVM = vm; + } + @TruffleFromLibGraal(Id.OnIsolateShutdown) public static void onIsolateShutdown(long isolateId) { JNIEnv env = JNIUtil.GetEnv(javaVM); diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 5bdf5a7316d5..638bf492a14e 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -17,6 +17,8 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-30264 `TruffleLanguage#initializeThread(Object, Thread)` is now guaranteed to be executed on the thread that is being initialized. Additional changes: * Added `ThreadLocalAction#notifyBlocked(Access)` and `ThreadLocalAction#notifyUnblocked(Access)` to notify thread local actions that their processing has been blocked/unblocked due to a blocked call (see `ThreadLocalAction` documentation). * `TruffleSafepoint#poll(Node)` does not require a non-null location anymore. However, it is still recommended to always pass a location node, if available. +* GR-59565 Added `RootNode.prepareForCompilation` which allows root nodes to offload expensive computation to the compiler thread and to delay compilation if they are not yet fully profiled. + ## Version 24.1.0 * GR-43839 Added optional parameter to TruffleString.ByteIndexOfCodePointSetNode to choose whether the node may calculate the input string's precise code range. diff --git a/truffle/src/com.oracle.truffle.api/snapshot.sigtest b/truffle/src/com.oracle.truffle.api/snapshot.sigtest index 7c8b966c40fc..0fc2145c46ac 100644 --- a/truffle/src/com.oracle.truffle.api/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api/snapshot.sigtest @@ -1376,6 +1376,7 @@ meth protected boolean isCloneUninitializedSupported() meth protected boolean isInstrumentable() meth protected boolean isSameFrame(com.oracle.truffle.api.frame.Frame,com.oracle.truffle.api.frame.Frame) meth protected boolean isTrivial() +meth protected boolean prepareForCompilation(boolean,int,boolean) meth protected com.oracle.truffle.api.frame.FrameDescriptor getParentFrameDescriptor() meth protected com.oracle.truffle.api.nodes.ExecutionSignature prepareForAOT() meth protected com.oracle.truffle.api.nodes.RootNode cloneUninitialized() @@ -1645,7 +1646,7 @@ meth public void printStackTrace(java.io.PrintStream) meth public void printStackTrace(java.io.PrintWriter) meth public void setStackTrace(java.lang.StackTraceElement[]) supr java.lang.Object -hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,depth,detailMessage,serialVersionUID,stackTrace,suppressedExceptions +hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,depth,detailMessage,jfrTracing,serialVersionUID,stackTrace,suppressedExceptions hcls PrintStreamOrWriter,SentinelHolder,WrappedPrintStream,WrappedPrintWriter CLSS public abstract interface java.lang.annotation.Annotation diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index 1111df1d7a5f..fd16ef8fa67f 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -219,6 +219,8 @@ public abstract LanguageInfo createLanguage(Object cache, String id, String name public abstract int findBytecodeIndex(RootNode rootNode, Node callNode, Frame frame); public abstract boolean isCaptureFramesForTrace(RootNode rootNode, boolean compiled); + + public abstract boolean prepareForCompilation(RootNode rootNode, boolean rootCompilation, int compilationTier, boolean lastTier); } public abstract static class SourceSupport extends Support { diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeAccessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeAccessor.java index 2886d3bac789..d3da4b839b76 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeAccessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeAccessor.java @@ -207,6 +207,11 @@ public int findBytecodeIndex(RootNode rootNode, Node callNode, Frame frame) { public boolean isCaptureFramesForTrace(RootNode rootNode, boolean compiled) { return rootNode.isCaptureFramesForTrace(compiled); } + + @Override + public boolean prepareForCompilation(RootNode rootNode, boolean rootCompilation, int compilationTier, boolean lastTier) { + return rootNode.prepareForCompilation(rootCompilation, compilationTier, lastTier); + } } } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java index 401f76994ab0..c080f924df12 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java @@ -50,11 +50,14 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.ContextLocal; +import com.oracle.truffle.api.ContextThreadLocal; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.ContextReference; import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.TruffleLanguage.LanguageReference; import com.oracle.truffle.api.TruffleLanguage.ParsingRequest; import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.frame.Frame; @@ -498,6 +501,71 @@ protected boolean isInstrumentable() { return true; } + /** + * Prepares this {@link RootNode} for compilation. This method is guaranteed to be called at + * least once before any code paths are executed as compiled code (see + * {@link CompilerDirectives#inCompiledCode()}) for a given compilation tier. It may be called + * multiple times per compilation and can be invoked concurrently by multiple compiler threads + * without synchronization; therefore, it must be thread-safe. + * + * By default, this method returns true to indicate sufficient profiling for + * compilation, but implementations can return false if not. Returning + * false for root compilations will defer the compilation to allow for additional + * profiling, whereas otherwise returning false during inlining will disable + * inlining for this call site. Any exception thrown will fail the compilation for this + * {@link RootNode} permanently. + * + * Compilations are initiated by the runtime for optimization through partial evaluation and + * compilation. The timing and threading of compilations are runtime-specific and should not be + * relied upon. This method is not called for Truffle runtimes that do not support compilation, + * such as the fallback runtime. + * + * Compilations may be initiated for the following reasons: + *

+ * + * Work performed in this method counts towards compilation time, not interpretation time. + * Implementing this method can help offload expensive computations to compiler threads. Cache + * any preparation work to avoid repeating it for every compilation and inlining. To prevent + * deadlocks when using synchronization, adhere to the following guidelines: + * + * + * Note that during the execution of this method, no language context is entered. Therefore, you + * cannot use {@link ContextThreadLocal}, {@link ContextLocal}, {@link LanguageReference}, or + * {@link ContextReference}. + * + * @param rootCompilation true if this is a root compilation; false if + * inlining this root. + * @param compilationTier the current compilation tier, as per + * {@link FrameInstance#getCompilationTier()}. + * @param lastTier true if this is the last compilation tier for which this + * {@link RootNode} is prepared; false otherwise. Useful for performing + * preparation only at the highest tier. + * @return true if the method is sufficiently profiled; false + * otherwise. + * + * @since 24.2 + */ + protected boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) { + return true; + } + /** * Is this root node to be considered trivial by the runtime. * @@ -587,7 +655,9 @@ protected Object translateStackTraceElement(TruffleStackTraceElement element) { * any thread, even threads unknown to the guest language implementation. It is allowed to * create new {@link CallTarget call targets} during preparation of the root node or perform * modifications to the {@link TruffleLanguage language} of this root node. + *

* + * @see #prepareForCompilation(boolean, int, boolean) * @since 20.3 */ protected ExecutionSignature prepareForAOT() { diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BaseOSRRootNode.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BaseOSRRootNode.java index 8a68b47561b3..e2936b2e103b 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BaseOSRRootNode.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/BaseOSRRootNode.java @@ -45,6 +45,7 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInterface; import com.oracle.truffle.api.nodes.RootNode; @@ -78,6 +79,14 @@ public final Object execute(VirtualFrame frame) { } } + @Override + protected final boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) { + Node node = (Node) loopNode; // safe to cast, always a Node + RootNode root = node.getRootNode(); + assert root != null : "Loop and OSR nodes must be adopted"; + return OptimizedRuntimeAccessor.NODES.prepareForCompilation(root, rootCompilation, compilationTier, lastTier); + } + /** * Entrypoint for OSR root nodes. */ diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java index 028aafc0fabe..b37d020a11f5 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java @@ -80,6 +80,7 @@ import com.oracle.truffle.api.nodes.NodeVisitor; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.compiler.TruffleCompilable; +import com.oracle.truffle.runtime.OptimizedBlockNode.PartialBlockRootNode; import com.oracle.truffle.runtime.OptimizedRuntimeOptions.ExceptionAction; import jdk.vm.ci.meta.JavaConstant; @@ -394,11 +395,24 @@ private int uninitializedNodeCount(RootNode rootNode) { return size > 0 ? size : childrenCount; } - @Override + /* + * Legacy implementation. + */ + @SuppressWarnings("deprecation") public final void prepareForCompilation() { - if (rootNode == null) { + RootNode root = this.rootNode; + if (root == null) { throw CompilerDirectives.shouldNotReachHere("Initialization call targets cannot be compiled."); } + /* + * Compared to the new prepareForCompilation we do not return for not initialized call + * targets. This is on purpose, as the return value of prepareForCompilation has no effect. + */ + OptimizedRuntimeAccessor.NODES.prepareForCompilation(root, true, 2, true); + /* + * We need to unconditionally initialize the assumptions as the return value is not + * interpreted for the legacy implementation. + */ if (nodeRewritingAssumption == null) { initializeNodeRewritingAssumption(); } @@ -407,6 +421,34 @@ public final void prepareForCompilation() { } } + @Override + public final boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) { + RootNode root = this.rootNode; + if (root == null) { + throw CompilerDirectives.shouldNotReachHere("Initialization call targets cannot be compiled."); + } + + if (!initialized && !isOSR() && !isPartialBlock()) { + /* + * We must not compile a method that was not yet initialized, as they may likely + * deoptimize and their instrumentation is not yet attached. OSR and partial blocks do + * not need initialization. + */ + return false; + } + + boolean result = OptimizedRuntimeAccessor.NODES.prepareForCompilation(root, rootCompilation, compilationTier, lastTier); + if (result) { + if (nodeRewritingAssumption == null) { + initializeNodeRewritingAssumption(); + } + if (validRootAssumption == null) { + initializeValidRootAssumption(); + } + } + return result; + } + final Assumption getNodeRewritingAssumption() { Assumption assumption = nodeRewritingAssumption; if (assumption == null) { @@ -803,7 +845,7 @@ protected static OptimizedTruffleRuntime runtime() { return (OptimizedTruffleRuntime) Truffle.getRuntime(); } - private void ensureInitialized() { + public final void ensureInitialized() { if (!initialized) { CompilerDirectives.transferToInterpreterAndInvalidate(); initialize(true); @@ -1848,17 +1890,21 @@ private boolean prepareForAOTImpl() { return true; } - boolean isOSR() { + final boolean isOSR() { return rootNode instanceof BaseOSRRootNode; } + final boolean isPartialBlock() { + return rootNode instanceof PartialBlockRootNode; + } + @Override - public long engineId() { + public final long engineId() { return engine.id; } @Override - public Map getCompilerOptions() { + public final Map getCompilerOptions() { return engine.getCompilerOptions(); } diff --git a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/libgraal/TruffleFromLibGraalEntryPoints.java b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/libgraal/TruffleFromLibGraalEntryPoints.java index 7b0ff06c8e8c..2f8bbe9308a7 100644 --- a/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/libgraal/TruffleFromLibGraalEntryPoints.java +++ b/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/libgraal/TruffleFromLibGraalEntryPoints.java @@ -231,11 +231,17 @@ static byte[] getCompilerOptions(Object o) { return output.getArray(); } + @SuppressWarnings("deprecation") @TruffleFromLibGraal(Id.PrepareForCompilation) static void prepareForCompilation(Object compilable) { ((TruffleCompilable) compilable).prepareForCompilation(); } + // new method for new target reflectively resolved + static boolean prepareForCompilation(Object compilable, boolean rootCompilation, int tier, boolean lastTier) { + return ((TruffleCompilable) compilable).prepareForCompilation(rootCompilation, tier, lastTier); + } + @TruffleFromLibGraal(GetURI) static String getURI(Object position) { URI uri = ((TruffleSourceLanguagePosition) position).getURI(); From 8ac9409824b97d1968876b24624b2d3ccd716c14 Mon Sep 17 00:00:00 2001 From: Christian Humer Date: Tue, 12 Nov 2024 15:31:29 +0100 Subject: [PATCH 4/4] Use GCUtils for SourceInternalizationTest.testSourceInterning to avoid transient failures (GR-59764) --- .../api/test/source/SourceInternalizationTest.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceInternalizationTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceInternalizationTest.java index d4d703a61166..a3c405a3539a 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceInternalizationTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceInternalizationTest.java @@ -64,7 +64,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; @@ -231,15 +230,9 @@ public void testSourceInterning() { List> sources = new ArrayList<>(); for (int i = 0; i < GCUtils.GC_TEST_ITERATIONS; i++) { sources.add(new WeakReference<>(createTestSource(testString, i), queue)); - System.gc(); } - int refsCleared = 0; - while (queue.poll() != null) { - refsCleared++; - } - // we need to have any refs cleared for this test to have any value - Assert.assertTrue(refsCleared > 0); + GCUtils.assertGc(testString, sources); } private static Object createTestSource(String testString, int i) {