public interface BytecodeOSRNode extends NodeInterface
There are a few restrictions Bytecode OSR nodes must satisfy in order for OSR to work correctly:
Node
or a subclass of Node
.@CompilationFinal
, and the
BytecodeOSRNode.getOSRMetadata()
and BytecodeOSRNode.setOSRMetadata(java.lang.Object)
methods must
proxy accesses to it.BytecodeOSRNode.pollOSRBackEdge(com.oracle.truffle.api.nodes.BytecodeOSRNode)
and
BytecodeOSRNode.tryOSR(com.oracle.truffle.api.nodes.BytecodeOSRNode, int, java.lang.Object, java.lang.Runnable, com.oracle.truffle.api.frame.VirtualFrame)
as described by their documentation.
For performance reasons, the parent frame may be copied into a new frame used for OSR. If this
happens, BytecodeOSRNode.copyIntoOSRFrame(VirtualFrame, VirtualFrame, int, Object)
is
used to perform the copy, and BytecodeOSRNode.restoreParentFrame(com.oracle.truffle.api.frame.VirtualFrame, com.oracle.truffle.api.frame.VirtualFrame)
is used to copy the OSR
frame contents back into the parent frame after OSR. A node may override these methods; by
default, they perform slot-wise copies.
A node may also wish to override BytecodeOSRNode.prepareOSR(int)
to perform initialization.
This method will be called before compilation, and can be useful to avoid deoptimizing inside
compiled code.
Modifier and Type | Method and Description |
---|---|
default void |
copyIntoOSRFrame(VirtualFrame osrFrame,
VirtualFrame parentFrame,
int target,
Object targetMetadata)
Copies the contents of the
parentFrame into the osrFrame used to execute OSR. |
Object |
executeOSR(VirtualFrame osrFrame,
int target,
Object interpreterState)
Entrypoint to invoke this node through OSR.
|
Object |
getOSRMetadata()
Gets the OSR metadata for this instance.
|
static boolean |
pollOSRBackEdge(BytecodeOSRNode osrNode)
Reports a back edge, returning whether to try performing OSR.
|
default void |
prepareOSR(int target)
Initialization hook which will be invoked before OSR compilation.
|
default void |
restoreParentFrame(VirtualFrame osrFrame,
VirtualFrame parentFrame)
Restores the contents of the
osrFrame back into the parentFrame after OSR. |
default Frame |
restoreParentFrameFromArguments(Object[] arguments)
Return the parent frame that was stored in an arguments array by a previous call to
BytecodeOSRNode.storeParentFrameInArguments(com.oracle.truffle.api.frame.VirtualFrame) . |
void |
setOSRMetadata(Object osrMetadata)
Sets the OSR metadata for this instance.
|
default Object[] |
storeParentFrameInArguments(VirtualFrame parentFrame)
Produce the arguments that will be used to perform the call to the new OSR root.
|
static Object |
tryOSR(BytecodeOSRNode osrNode,
int target,
Object interpreterState,
Runnable beforeTransfer,
VirtualFrame parentFrame)
Tries to perform OSR.
|
Object executeOSR(VirtualFrame osrFrame, int target, Object interpreterState)
target
location.
The osrFrame
may be the parent frame, but for performance reasons could also be a new
frame. In case a new frame is created, the frame's arguments
will be provided by BytecodeOSRNode.storeParentFrameInArguments(com.oracle.truffle.api.frame.VirtualFrame)
.
Typically, a bytecode node's execute(VirtualFrame)
method already contains a dispatch loop. This loop can be extracted
into a separate method which can also be used by this method. For example:
Object execute(VirtualFrame frame) { return dispatchFromBCI(frame, 0); } Object executeOSR(VirtualFrame osrFrame, int target, Object interpreterState) { return dispatchFromBCI(osrFrame, target); } Object dispatchFromBCI(VirtualFrame frame, int bci) { // main dispatch loop while(true) { switch(instructions[bci]) { ... } } }
osrFrame
- the frame to use for OSR.target
- the target location to execute from (e.g., bytecode index).interpreterState
- other interpreter state used to resume execution. See
BytecodeOSRNode.tryOSR(com.oracle.truffle.api.nodes.BytecodeOSRNode, int, java.lang.Object, java.lang.Runnable, com.oracle.truffle.api.frame.VirtualFrame)
for more details.Object getOSRMetadata()
The metadata must be stored on a
@CompilationFinal
instance
field. Refer to the documentation for this interface for a more complete description.
void setOSRMetadata(Object osrMetadata)
The metadata must be stored on a
@CompilationFinal
instance
field. Refer to the documentation for this interface for a more complete description.
osrMetadata
- the OSR metadata.default void copyIntoOSRFrame(VirtualFrame osrFrame, VirtualFrame parentFrame, int target, Object targetMetadata)
parentFrame
into the osrFrame
used to execute OSR.
By default, performs a slot-wise copy of the frame.
NOTE: This method is only used if the Truffle runtime decides to copy the frame. OSR may also reuse the parent frame directly.
osrFrame
- the frame to use for OSR.parentFrame
- the frame used before performing OSR.target
- the target location OSR will execute from (e.g., bytecode index).targetMetadata
- Additional metadata associated with this target
for the default
frame transfer behavior.default void restoreParentFrame(VirtualFrame osrFrame, VirtualFrame parentFrame)
osrFrame
back into the parentFrame
after OSR. By
default, performs a slot-wise copy of the frame.
Though a bytecode interpreter might not explicitly use parentFrame
after OSR, it is
necessary to restore the state into parentFrame
if it may be accessed through
instrumentation.
NOTE: This method is only used if the Truffle runtime decided to copy the frame using
BytecodeOSRNode.copyIntoOSRFrame(VirtualFrame, VirtualFrame, int, Object)
.
osrFrame
- the frame which was used for OSR.parentFrame
- the frame which will be used by the parent after returning from OSR.default void prepareOSR(int target)
For example, consider a field which must be initialized in the interpreter:
@CompilationFinal Object field; Object getField() { if (field == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); field = new Object(); } return field; }
If the field is accessed from compiled OSR code, it may trigger a deoptimization in order to
initialize the field. Using BytecodeOSRNode.prepareOSR(int)
to initialize the field can
prevent this.
target
- the target location OSR will execute from (e.g., bytecode index).static boolean pollOSRBackEdge(BytecodeOSRNode osrNode)
An interpreter must ensure this method returns true
immediately before calling
BytecodeOSRNode.tryOSR(com.oracle.truffle.api.nodes.BytecodeOSRNode, int, java.lang.Object, java.lang.Runnable, com.oracle.truffle.api.frame.VirtualFrame)
. For example:
if (BytecodeOSRNode.pollOSRBackEdge(this)) { Object osrResult = BytecodeOSRNode.tryOSR(...); ... }
osrNode
- the node to report a back-edge for.static Object tryOSR(BytecodeOSRNode osrNode, int target, Object interpreterState, Runnable beforeTransfer, VirtualFrame parentFrame)
true
result
from BytecodeOSRNode.pollOSRBackEdge(com.oracle.truffle.api.nodes.BytecodeOSRNode)
.
Depending on the Truffle runtime, this method can trigger OSR compilation and then (typically
in a subsequent call) transfer to OSR code. If OSR occurs, this method returns the result of
OSR execution. The caller of this method can forward the result back to its caller rather
than continuing execution from the target
. For example:
if (BytecodeOSRNode.pollOSRBackEdge(this)) { Object osrResult = BytecodeOSRNode.tryOSR(...); if (osrResult != null) return osrResult; }
The optional interpreterState
parameter will be forwarded to
BytecodeOSRNode.executeOSR(com.oracle.truffle.api.frame.VirtualFrame, int, java.lang.Object)
when OSR is performed. It should consist of additional
interpreter state (e.g., data pointers) needed to resume execution from target
. The
state should be fixed (i.e., final) for the given target
. For example:
// class definition class InterpreterState { final int dataPtr; InterpreterState(int dataPtr) { ... } } // call site Object osrResult = BytecodeOSRNode.tryOSR(this, target, new InterpreterState(dataPtr), ...); // executeOSR Object executeOSR(VirtualFrame osrFrame, int target, Object interpreterState) { InterpreterState state = (InterpreterState) interpreterState; return dispatchFromBCI(osrFrame, target, interpreterState.dataPtr); }
The optional beforeTransfer
callback will be called before transferring control to
the OSR target. Since this method may or may not perform a transfer, it is a way to ensure
certain actions (e.g., instrumentation events) occur before transferring to OSR code. For
example:
// call site Object osrResult = BytecodeNode.tryOSR(this, target, ..., () -> { instrument.notify(current, target); });
osrNode
- the node to try OSR for.target
- the target location OSR will execute from (e.g., bytecode index).interpreterState
- other interpreter state used to resume execution.beforeTransfer
- a callback invoked before OSR. Can be null
.parentFrame
- frame at the current point of execution.null
otherwise.default Object[] storeParentFrameInArguments(VirtualFrame parentFrame)
BytecodeOSRNode.executeOSR(com.oracle.truffle.api.frame.VirtualFrame, int, java.lang.Object)
in case a new frame
is generated for the call for performance reasons. The contents are up to language, Truffle
only requires that a subsequent call to BytecodeOSRNode.restoreParentFrame(com.oracle.truffle.api.frame.VirtualFrame, com.oracle.truffle.api.frame.VirtualFrame)
with the arguments array
will return the same object as was passed into the parentFrame
argument. By default,
this method creates a new one-element array, which discards the original frame arguments.
Override this method to be able to preserve a subset of the original frame arguments. It is
permitted to modify arguments array of parentFrame
and return it. This is called only
in the interpreter, therefore the frame is not virtual and it is safe to store it into the
arguments array.parentFrame
- the frame object to be stored in the resulting arguments arrayparentFrame
default Frame restoreParentFrameFromArguments(Object[] arguments)
BytecodeOSRNode.storeParentFrameInArguments(com.oracle.truffle.api.frame.VirtualFrame)
.arguments
- frame arguments originally produced by BytecodeOSRNode.storeParentFrameInArguments(com.oracle.truffle.api.frame.VirtualFrame)
.