Interface BytecodeOSRNode

All Superinterfaces:
NodeInterface

public interface BytecodeOSRNode extends NodeInterface
Interface for Truffle bytecode nodes which can be on-stack replaced (OSR).

There are a few restrictions Bytecode OSR nodes must satisfy in order for OSR to work correctly:

  1. The node must extend Node or a subclass of Node.
  2. The node must provide storage for the OSR metadata maintained by the runtime using an instance field. The field must be @CompilationFinal, and the getOSRMetadata() and setOSRMetadata(java.lang.Object) methods must proxy accesses to it.
  3. The node should call pollOSRBackEdge(com.oracle.truffle.api.nodes.BytecodeOSRNode) and 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, copyIntoOSRFrame(VirtualFrame, VirtualFrame, int, Object) is used to perform the copy, and 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 prepareOSR(int) to perform initialization. This method will be called before compilation, and can be useful to avoid deoptimizing inside compiled code.

Since:
21.3
  • Method Details

    • executeOSR

      Object executeOSR(VirtualFrame osrFrame, int target, Object interpreterState)
      Entrypoint to invoke this node through OSR. This method should execute from the 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 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]) {
                   ...
               }
           }
       }
       
      Parameters:
      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 tryOSR(com.oracle.truffle.api.nodes.BytecodeOSRNode, int, java.lang.Object, java.lang.Runnable, com.oracle.truffle.api.frame.VirtualFrame) for more details.
      Returns:
      the result of execution.
      Since:
      21.3
    • getOSRMetadata

      Object getOSRMetadata()
      Gets the OSR metadata for this instance.

      The metadata must be stored on a @CompilationFinal instance field. Refer to the documentation for this interface for a more complete description.

      Returns:
      the OSR metadata.
      Since:
      21.3
    • setOSRMetadata

      void setOSRMetadata(Object osrMetadata)
      Sets the OSR metadata for this instance.

      The metadata must be stored on a @CompilationFinal instance field. Refer to the documentation for this interface for a more complete description.

      Parameters:
      osrMetadata - the OSR metadata.
      Since:
      21.3
    • copyIntoOSRFrame

      @Deprecated(since="22.2") default void copyIntoOSRFrame(VirtualFrame osrFrame, VirtualFrame parentFrame, int target)
      Deprecated.
      since 22.2
      Note that if this method is implemented, the preferred one will not be used.
      Since:
      21.3
      See Also:
    • copyIntoOSRFrame

      default void copyIntoOSRFrame(VirtualFrame osrFrame, VirtualFrame parentFrame, int target, Object targetMetadata)
      Copies the contents of the 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.

      Parameters:
      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.
      Since:
      22.2
    • restoreParentFrame

      default void restoreParentFrame(VirtualFrame osrFrame, VirtualFrame parentFrame)
      Restores the contents of the 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 copyIntoOSRFrame(VirtualFrame, VirtualFrame, int, Object).

      Parameters:
      osrFrame - the frame which was used for OSR.
      parentFrame - the frame which will be used by the parent after returning from OSR.
      Since:
      21.3
    • prepareOSR

      default void prepareOSR(int target)
      Initialization hook which will be invoked before OSR compilation. This hook can be used to perform any necessary initialization before compilation happens.

      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 prepareOSR(int) to initialize the field can prevent this.

      Parameters:
      target - the target location OSR will execute from (e.g., bytecode index).
      Since:
      21.3
    • pollOSRBackEdge

      static boolean pollOSRBackEdge(BytecodeOSRNode osrNode)
      Reports a back edge, returning whether to try performing OSR.

      An interpreter must ensure this method returns true immediately before calling 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(...);
         ...
       }
       
      Parameters:
      osrNode - the node to report a back-edge for.
      Returns:
      whether to try OSR.
      Since:
      21.3
    • tryOSR

      static Object tryOSR(BytecodeOSRNode osrNode, int target, Object interpreterState, Runnable beforeTransfer, VirtualFrame parentFrame)
      Tries to perform OSR. This method must only be called immediately after a true result from 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 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);
       });
       
      Parameters:
      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.
      Returns:
      the result if OSR was performed, or null otherwise.
      Since:
      21.3
    • storeParentFrameInArguments

      default Object[] storeParentFrameInArguments(VirtualFrame parentFrame)
      Produce the arguments that will be used to perform the call to the new OSR root. It will become the arguments array of the frame passed into 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 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.
      Parameters:
      parentFrame - the frame object to be stored in the resulting arguments array
      Returns:
      arguments array containing parentFrame
      Since:
      22.2
    • restoreParentFrameFromArguments

      default Frame restoreParentFrameFromArguments(Object[] arguments)
      Return the parent frame that was stored in an arguments array by a previous call to storeParentFrameInArguments(com.oracle.truffle.api.frame.VirtualFrame).
      Parameters:
      arguments - frame arguments originally produced by storeParentFrameInArguments(com.oracle.truffle.api.frame.VirtualFrame).
      Returns:
      stored parent frame
      Since:
      22.2