Class JitCodeGenerator<THIS extends JitCompiledPassage>

java.lang.Object
ghidra.pcode.emu.jit.gen.JitCodeGenerator<THIS>
Type Parameters:
THIS - the type of the generated passage

public class JitCodeGenerator<THIS extends JitCompiledPassage> extends Object
The bytecode generator for JIT-accelerated emulation.

This implements the Code Generation phase of the JitCompiler. With all the prior analysis, code generation is just a careful process of visiting all of the ops, variables, and analytic results to ensure everything is incorporated and accounted for.

The Target Classfile

The target is a classfile that implements JitCompiledPassage. As such, it must implement all of the specified methods in that interface as well as a constructor having a specific signature. That signature takes a JitPcodeThread and, being a constructor, returns void. We will also need to generate a static initializer to populate some metadata and pre-fetch any static things, e.g., the SleighLanguage for the emulation target. The fields are:

Static Initializer

In the Java language, statements in a class's static block, as well as the initial values of static fields are implemented by the classfile's <clinit> method. We use it to pre-construct contextreg values and varnode refs for use in birthing and retirement. They are kept in static fields. We also initialize the static ENTRIES field, which is public (via reflection) and describes each entry point generated. It has the type List<JitPassage.AddrCtx>. A call to JitCompiledPassage.run(int) should pass in the position of the desired entry point in the ENTRIES list.

Constructor

In the Java language, statements in a class's constructor, as well as the initial values of instance fields are implemented by the classfile's <init> methods. We provide a single constructor that accepts a JitPcodeThread. Upon construction, the generated JitCompiledPassage is "bound" to the given thread. The constructor pre-fetches parts of the thread's state and userop definitions, and it allocates JitCompiledPassage.ExitSlots. Each of these are kept in instance fields.

thread() Method

This method implements JitCompiledPassage.thread(), a simple getter for the thread field.

run() Method

This method implements JitCompiledPassage.run(int), the actual semantics of the translated machine instructions selected for the passage. It accepts a single parameter, which is the position in the ENTRIES list of the desired entry point blockId. The structure is as follows:

  1. Entry point dispatch - a large switch statement on the entry blockId
  2. P-code translation - the block-by-block op-by-op translation of the p-code to bytecode
  3. Exception handlers - exception handlers as requested by various elements of the p-code translation
  4. Parameter declarations - this and blockId
  5. Allocated local declarations - declares all locals allocated by JitAllocationModel

Entry Point Dispatch

This part of the run method dispatches execution to the correct entry point within the translated passage. It consists of these sub-parts:

  1. Switch table - a tableswitch to jump to the code for the scope transition into the entry block given by blockId
  2. Scope transitions - for each block, birth its live varnodes then jump to the block's translation
  3. Default case - throws an IllegalArgumentException for an invalid blockId

This first ensure that a valid entry point was given in blockId. If not, we jump to the default case which throws an exception. Otherwise, we jump to the appropriate entry transition. Every block flow edge is subject to a scope transition wherein varnodes that leave scope must be retired and varnodes that enter scope must be birthed. We generate an entry transition for each possible entry block. That transition births all the varnodes that are in scope for that entry block then jumps to the entry block's p-code translation.

P-code Translation

Here, most of the generation is performed via delegation to an object model, based on the use-def graph. We first iterate over the blocks, in the same order as they appear in the decoded passage. This will ensure that fall-through control transitions in the p-code map to fall-through transitions in the emitted bytecode. If the block is the target of a bytecode jump, i.e., it's an entry block or the target of a p-code branch, then we emit a label at the start of the block. We then iterate over each p-code op in the block delegating each to the appropriate generator. We emit "line number" information for each op to help debug crashes. A generator may register an exception handler to be emitted later in the "exception handlers" part of the run method. If the block has fall through, we emit the appropriate scope transition before proceeding to the next block. Note that scope transitions for branch ops are emitted by the generators for those ops.

For details about individual p-code op translations, see OpGen. For details about individual SSA value (constant and variable) translations, see VarGen. For details about emitting scope transitions, see VarGen.BlockTransition.

Implementation Notes:
Throughout most of the code that emits bytecode, there are (human-generated) comments to track the contents of the JVM stack. Items pushed onto the stack appear at the right. If type is important, then those are denoted using :TYPE after the relevant variable. TODO: It'd be nice to have a bytecode API that enforces stack structure using the compiler (somehow), but that's probably overkill. Also, I have yet to see what the official classfile API will bring.
  • Constructor Details

    • JitCodeGenerator

      Construct a code generator for the given passage's target classfile

      This constructor chooses the name for the target classfile based on the passage's entry seed. It has the form: Passage$at_address_context. The address is as rendered by Address.toString() but with characters replaced to make it a valid JVM classfile name. The decode context is rendered in hexadecimal. This constructor also declares the fields and methods, and emits the definition for JitCompiledPassage.thread().

      Parameters:
      lookup - a means of accessing user-defined components, namely userops
      context - the analysis context for the passage
      cfm - the control flow model
      dfm - the data flow model
      vsm - the variable scope model
      tm - the type model
      am - the allocation model
      oum - the op use model
  • Method Details

    • getAnalysisContext

      public JitAnalysisContext getAnalysisContext()
      Get the analysis context
      Returns:
      the context
    • getVariableScopeModel

      public JitVarScopeModel getVariableScopeModel()
      Get the variable scope model
      Returns:
      the model
    • getTypeModel

      public JitTypeModel getTypeModel()
      Get the type model
      Returns:
      the model
    • getAllocationModel

      public JitAllocationModel getAllocationModel()
      Get the allocation model
      Returns:
      the model
    • startClInitMethod

      protected Emitter<Emitter.Bot> startClInitMethod(Emitter<Emitter.Bot> em)
      Emit the first bytecodes for the static initializer

      This generates code equivalent to:

       static {
              LANGUAGE = getLanguage(LANGUAGE_ID);
              ADDRESS_FACTORY = LANGUAGE.getAddressFactory();
       }
       

      Note that LANGUAGE_ID is initialized to a constant String in its declaration. Additional static fields may be requested as the p-code translation is emitted.

    • startInitMethod

      protected Emitter<Emitter.Bot> startInitMethod(Emitter<Emitter.Bot> em, Local<Types.TRef<THIS>> localThis, Local<Types.TRef<JitPcodeThread>> localThread)
      Emit the first bytecodes for the class constructor

      This generates code equivalent to:

       public Passage$at_00400000_0(JitPcodeThread thread) {
              super(); // Implicit in Java, but we must emit i
              this.thread = thread;
              this.state = thread.GetState();
       }
       

      Additional instance fields may be requested as the p-code translation is emitted.

    • genLoadJitStateSpace

      Emit bytecode to load the given JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace onto the JVM stack

      This is equivalent to the Java expression state.getForSpace(AddressFactory.getAddressSpace(spaceId)). The id of the given space is encoded as an immediate or in the constant pool and is represented as spaceId.

      Type Parameters:
      N - the tail of the stack (...)
      Parameters:
      em - the emitter
      localThis - a handle to this
      space - the space to load at run time
      Returns:
      the emitter with ..., stateSpace
    • requestFieldForSpaceIndirect

      public FieldForSpaceIndirect requestFieldForSpaceIndirect(AddressSpace space)
      Request a field for a JitBytesPcodeExecutorStatePiece.JitBytesPcodeExecutorStateSpace for the given address space
      Parameters:
      space - the address space
      Returns:
      the field request
    • requestFieldForArrDirect

      public FieldForArrDirect requestFieldForArrDirect(Address address)
      Request a field for the bytes backing the page at the given address
      Parameters:
      address - the address contained by the desired page
      Returns:
      the field request
    • requestStaticFieldForContext

      protected ghidra.pcode.emu.jit.gen.FieldForContext requestStaticFieldForContext(RegisterValue ctx)
      Request a field for the given contextreg value
      Parameters:
      ctx - the contextreg value
      Returns:
      the field request
    • requestStaticFieldForVarnode

      public FieldForVarnode requestStaticFieldForVarnode(Varnode vn)
      Request a field for the given varnode
      Parameters:
      vn - the varnode
      Returns:
      the field request
    • requestStaticFieldForOp

      public FieldForPcodeOp requestStaticFieldForOp(PcodeOp op)
      Request a field for the given p-code op

      This will request fields for each varnode for the op's operands

      Parameters:
      op - the p-code op
      Returns:
      the field request
    • requestFieldForUserop

      public FieldForUserop requestFieldForUserop(PcodeUseropLibrary.PcodeUseropDefinition<byte[]> userop)
      Request a field for the given userop
      Parameters:
      userop - the userop
      Returns:
      the field request
    • requestFieldForExitSlot

      public FieldForExitSlot requestFieldForExitSlot(JitPassage.AddrCtx target)
      Request a field for the JitCompiledPassage.ExitSlot for the given target
      Parameters:
      target - the target address and decode context
      Returns:
      the field request
    • labelForBlock

      public Lbl<Emitter.Bot> labelForBlock(JitControlFlowModel.JitBlock block)
      Get the label at the start of a block's translation
      Parameters:
      block - the block
      Returns:
      the label
    • requestExceptionHandler

      public ExceptionHandler requestExceptionHandler(JitPassage.DecodedPcodeOp op, JitControlFlowModel.JitBlock block)
      Request an exception handler that can retire state for a given op
      Parameters:
      op - the op that might throw an exception
      block - the block containing the op
      Returns:
      the exception handler request
    • genValInit

      protected <N extends Emitter.Next> Emitter<N> genValInit(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitVal v)
      Emit into the constructor any bytecode necessary to support the given value.
      Parameters:
      v - the value from the use-def graph
    • genReadToStack

      public <T extends Types.BPrim<?>, JT extends JitType.SimpleJitType<T, JT>, N extends Emitter.Next> Emitter<Emitter.Ent<N,T>> genReadToStack(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitVal v, JT type, Opnd.Ext ext)
      Emit bytecode to read the given value onto the JVM stack.

      Although the value may be assigned a type by the JitTypeModel, the type needed by a given op might be different.

      Type Parameters:
      T - the required JVM type of the value
      JT - the required p-code type of the value
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (source) value to read
      type - the required p-code type of the value
      ext - the kind of extension to apply
      Returns:
      the code visitor typed with the resulting stack, i.e., having pushed the value
    • genReadToOpnd

      public <N extends Emitter.Next> Opnd.OpndEm<JitType.MpIntJitType,N> genReadToOpnd(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitVal v, JitType.MpIntJitType type, Opnd.Ext ext, Scope scope)
      Emit bytecode to read the given value into a series of locals
      Type Parameters:
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (source) value to read
      type - the required p-code type of the value
      ext - the kind of extension to apply
      scope - a scope for generating temporary local storage
      Returns:
      the operand containing the locals, and the emitter typed with the incoming stack
    • genReadLegToStack

      public <N extends Emitter.Next> Emitter<Emitter.Ent<N,Types.TInt>> genReadLegToStack(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitVal v, JitType.MpIntJitType type, int leg, Opnd.Ext ext)
      Emit bytecode to load one leg of a multi-precision value from the varnode onto the JVM stack.
      Type Parameters:
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (source) value to read
      type - the p-code type of the complete multi-precision value
      leg - the index of the leg to load, 0 being least significant
      ext - the kind of extension to apply
      Returns:
      the emitter typed with the resulting stack, i.e., having the int leg pushed onto it
    • genReadToArray

      public <N extends Emitter.Next> Emitter<Emitter.Ent<N,Types.TRef<int[]>>> genReadToArray(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitVal v, JitType.MpIntJitType type, Opnd.Ext ext, Scope scope, int slack)
      Emit bytecode to load the varnode's value into an integer array in little-endian order, pushing its ref onto the JVM stack.

      Ideally, multi-precision integers should be loaded into a series of locals, i.e., using genReadToOpnd(Emitter, Local, JitVal, MpIntJitType, Ext, Scope), but this may not always be the best course of action. The first case is for userops, where it'd be onerous and counter-intuitive for a user to receive a single varnode in several parameters. The annotation system to sort that all out would also be atrocious and not easily made compatible with non-JIT emulation. Instead, mp-int arguments are received via int[] parameters. The second case is for more complicated p-code ops. One notable example is int_mult. Theoretically, yes, we could emit all of the operations to compute the product using long multiplication inline; however, for large operands, that would produce an enormous number of bytecodes. Given the 64KB-per-method limit, we could quickly squeeze ourselves out of efficient translation of lengthy passages. The slack parameter is provided since some of these algorithms (e.g., division) need an extra leg as scratch space. If we don't allocate it here, we force complexity into the implementation, as it would need to provide its own locals or re-allocate and copy the array.

      Type Parameters:
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (source) value to read
      type - the p-code type of the complete multi-precision value
      ext - the kind of extension to apply
      scope - a scope for generating temporary local storage
      slack - the number of additional, more significant, elements to allocate in the array
      Returns:
      the emitter typed with the resulting stack, i.e., having the ref pushed onto it
    • genReadToBool

      public <N extends Emitter.Next> Emitter<Emitter.Ent<N,Types.TInt>> genReadToBool(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitVal v)
      Emit bytecode to load the varnode's value, interpreted as a boolean, as an integer onto the JVM stack.

      Any non-zero value is considered true, though ideally, slaspec authors should ensure all booleans are 1) 1-byte ints, and 2) only ever take the value 0 (false) or 1 (true). Nevertheless, we can't assume this guidance is followed. When we know a large (esp. multi-precision) variable is being used as a boolean, we have some opportunity for short-circuiting.

      Type Parameters:
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (source) value to read
      Returns:
      the emitter typed with the resulting stack, i.e., having the int boolean pushed onto it
    • genWriteFromStack

      public <T extends Types.BPrim<?>, JT extends JitType.SimpleJitType<T, JT>, N1 extends Emitter.Next, N0 extends Emitter.Ent<N1, T>> Emitter<N1> genWriteFromStack(Emitter<N0> em, Local<Types.TRef<THIS>> localThis, JitVar v, JT type, Opnd.Ext ext, Scope scope)
      Emit bytecode to write the value on the JVM stack into the given variable.

      Although the destination variable may be assigned a type by the JitTypeModel, the type of the value on the stack may not match. This method needs to know that type so that, if necessary, it can convert it to the appropriate JVM type for the local variable that holds it.

      Type Parameters:
      T - the JVM type of the value on the stack
      JT - the p-code type of the value on the stack
      N1 - the tail of the incoming stack
      N0 - the incoming stack having the value on top
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (destination) variable to write
      type - the p-code type of the value on the stack
      ext - the kind of extension to apply
      scope - a scope for generating temporary local storage
      Returns:
      the emitter typed with the resulting stack, i.e., having popped the value
    • genWriteFromOpnd

      public <N extends Emitter.Next> Emitter<N> genWriteFromOpnd(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitVar v, Opnd<JitType.MpIntJitType> opnd, Opnd.Ext ext, Scope scope)
      Emit bytecode to store a varnode's value from several locals.
      Type Parameters:
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (destination) variable to write
      opnd - the operand whose locals contain the value to be stored
      ext - the kind of extension to apply
      scope - a scope for generating temporary local storage
      Returns:
      the emitter typed with the incoming stack
    • genWriteFromArray

      public <N1 extends Emitter.Next, N0 extends Emitter.Ent<N1, Types.TRef<int[]>>> Emitter<N1> genWriteFromArray(Emitter<N0> em, Local<Types.TRef<THIS>> localThis, JitVar v, JitType.MpIntJitType type, Opnd.Ext ext, Scope scope)
      Emit bytecode to store a varnode's value from an array of integer legs, in little endian order
      Type Parameters:
      N1 - the tail of the incoming stack
      N0 - the incoming stack having the array ref on top
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      v - the (destination) variable to write
      type - the p-code type of the value on the stack
      ext - the kind of extension to apply
      scope - a scope for generating temporary local storage
      Returns:
      the emitter typed with the resulting stack, i.e., having popped the array
    • genOp

      Emit the bytecode translation for a given p-code op

      This first finds the use-def node for the op and then verifies that it has not been eliminated. If not, then it find the appropriate generator, emits line number information, and then emits the actual translation.

      Line number information in the JVM is a map of strictly-positive line numbers to bytecode offsets. The ASM library allows this to be populated by placing labels and then emitting a line-number-to-label entry (via MethodVisitor.visitLineNumber(int, Label). It seems the JVM presumes the entire class is defined in a single source file, so we are unable to (ab)use a filename field to encode debug information. We can encode the op index into the (integer) line number, although we have to add 1 to make it strictly positive.

      Parameters:
      em - the emitter typed with the empty stack
      localThis - a handle to the local holding the this reference
      localCtxmod - a handle to the local holding ctxmod
      retReq - an indication of what must be returned by this JitCompiledPassage.run(int) method.
      op - the op
      block - the block containing the op
      opIdx - the index of the op within the whole passage
    • genBlockOps

      protected ghidra.pcode.emu.jit.gen.JitCodeGenerator.GenBlockResult genBlockOps(Emitter<Emitter.Bot> em, Local<Types.TRef<THIS>> localThis, Local<Types.TInt> localCtxmod, Methods.RetReq<Types.TRef<JitCompiledPassage.EntryPoint>> retReq, JitControlFlowModel.JitBlock block, int opIdx)
      Emit the bytecode translation for the ops in the given p-code block

      This simply invokes genOp(Emitter, Local, Local, RetReq, PcodeOp, JitBlock, int) on each op in the block and counts up the indices. Other per-block instrumentation is not included.

      Parameters:
      em - the emitter
      localThis - a handle to this
      localCtxmod - a handle to ctxmod
      retReq - the required return type, in case an op needs to exit the passage
      block - the block
      opIdx - the index, within the whole passage, of the first op in the block
      Returns:
      the result of block generation
      See Also:
    • genBlock

      protected ghidra.pcode.emu.jit.gen.JitCodeGenerator.GenBlockResult genBlock(OpGen.OpResult prev, Local<Types.TRef<THIS>> localThis, Local<Types.TInt> localCtxmod, Methods.RetReq<Types.TRef<JitCompiledPassage.EntryPoint>> retReq, JitControlFlowModel.JitBlock block, int opIdx)
      Emit the bytecode translation for the given p-code block

      This checks if the block needs a label, i.e., it is an entry or the target of a branch, and then optionally emits an invocation of JitCompiledPassage.count(int, int). Finally, it emits the actual ops' translations via genBlockOps(Emitter, Local, Local, RetReq, JitBlock, int).

      Parameters:
      block - the block
      opIdx - the index, within the whole passage, of the first op in the block
      Returns:
      the index, within the whole passage, of the op immediately after the block
    • genAddress

      protected <N extends Emitter.Next> Emitter<Emitter.Ent<N,Types.TRef<Address>>> genAddress(Emitter<N> em, Address address)
      Emit code to load an Address onto the JVM stack

      Note this does not load the identical address, but reconstructs it at run time.

      Type Parameters:
      N - the tail of the stack (...)
      Parameters:
      em - the emitter
      address - the address to load
      Returns:
      the emitter with ..., address
    • genStaticEntry

      protected Emitter<Emitter.Bot> genStaticEntry(Emitter<Emitter.Bot> em, JitPassage.AddrCtx entry)
      Emit bytecode into the class initializer that adds the given entry point into ENTRIES.

      Consider the entry (ram:00400000,ctx=80000000). The code would be equivalent to:

       static {
              ENTRIES.add(new AddrCtx(
                      ADDRESS_FACTORY.getAddressSpace(ramId).getAddress(0x400000), CTX_80000000));
       }
       

      Note this method will request the appropriate CTX_... field.

      Parameters:
      entry - the entry point to add
    • genStaticEntries

      protected Emitter<Emitter.Bot> genStaticEntries(Emitter<Emitter.Bot> em)
      Emit code into the static initializer to initialize the ENTRIES field.

      This first constructs a new ArrayList and assigns it to the field. Then, for each block representing a possible entry, it adds an element giving the address and contextreg value for the first op of that block.

    • genClInitMethod

      protected Emitter<Emitter.Bot> genClInitMethod(Emitter<Emitter.Bot> em)
      Emit bytecode for the static initializer

      Note that this method must be called after the bytecode for the run method is generated, because that generator may request various static fields to be created and initialized. Those requests are not known until the run-method generator has finished.

      Parameters:
      em - the emitter
      Returns:
      the emitter
    • genInitMethod

      protected Emitter<Emitter.Bot> genInitMethod(Emitter<Emitter.Bot> em, Local<Types.TRef<THIS>> localThis)
      Emit all the bytecode for the constructor

      Note that this must be called after the bytecode for the run method is generated, because that generator may request various instance fields to be created and initialized. Those requests are not known until the run-method generator has finished.

      To ensure a reasonable order, for debugging's sake, we request fields (and their initializations) for all the variables and values before iterating over the ops. This ensures, e.g., locals are declared in order of address for the varnodes they hold. Similarly, the pre-fetched byte arrays, whether for uniques, registers, or memory are initialized in order of address. Were these requests not made, they'd still get requested by the op generators, but the order would be less helpful.

    • genRunMethod

      protected Emitter<Emitter.Dead> genRunMethod(Emitter<Emitter.Bot> em, Local<Types.TRef<THIS>> localThis, Local<Types.TInt> localBlockId, Methods.RetReq<Types.TRef<JitCompiledPassage.EntryPoint>> retReq)
      Emit all the bytecode for the run method.

      The structure of this method is described by this class's documentation. It first declares all the locals allocated by the JitAllocationModel. It then collects the list of entries points and assigns a label to each. These are used when emitting the entry dispatch code. Several of those labels may also be re-used when translating branch ops. We must iterate over the blocks in the same order as genStaticEntries(Emitter), so that our indices and its match. Thus, we emit a tableswitch where each value maps to the blocks label identified in the same position of the ENTRIES field. We also provide a default case that just throws an IllegalArgumentException. We do not jump directly to the block's translation. Instead we emit a prologue for each block, wherein we birth the variables that block expects to be live, and then jump to the translation. Then, we emit the translation for each block using genBlock(OpResult, Local, Local, RetReq, JitBlock, int), placing transitions between those connected by fall through using VarGen.computeBlockTransition(Local, JitCodeGenerator, JitBlock, JitBlock). Finally, we emit each requested exception handler using ExceptionHandler.genRun(Emitter, Local, JitCodeGenerator).

    • load

      public JitCompiledPassageClass load()
      Generate the classfile for this passage and load it into this JVM.
      Returns:
      the translation, wrapped in utilities that knows how to process and instantiate it
    • dumpBytecode

      protected byte[] dumpBytecode(byte[] bytes)
      For diagnostics: Dump the generated classfile to an actual file on disk
      Parameters:
      bytes - the classfile bytes
      Returns:
      the same classfile bytes
      See Also:
    • generate

      protected byte[] generate()
      Generate the classfile and get the raw bytes

      This emits all the bytecode for all the required methods, static initializer, and constructor. Once complete, this closes out the methods by letting the ASM library compute the JVM stack frames as well as the maximum stack size and local variable count. Finally, it closes out the class a retrieves the resulting bytes.

      Returns:
      the classfile bytes
      Implementation Notes:
      The frame and maximums computation does not always succeed, and unfortunately, the ASM library is not terribly keen to explain why. If JitCompiler.Diag.DUMP_CLASS is enabled, we will catch whatever hairy exception gets thrown and close out the method anyway. The resulting class will not likely load into any JVM, but at least you might be able to examine it.
    • getOpEntry

      public JitPassage.AddrCtx getOpEntry(PcodeOp op)
      Check if the given p-code op is the first of an instruction.
      Parameters:
      op - the op to check
      Returns:
      the address-context pair
      See Also:
    • getExitContext

      public RegisterValue getExitContext(PcodeOp op)
      Get the context of the instruction that generated the given p-code op.

      This is necessary when exiting the passage, whether due to an exception or "normal" exit. The emulator's context must be updated so that it can resume execution appropriately.

      Parameters:
      op - the p-code op causing the exit
      Returns:
      the contextreg value
    • genRetirePcCtx

      public <N extends Emitter.Next> Emitter<N> genRetirePcCtx(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitCodeGenerator.PcGen pcGen, RegisterValue ctx, JitCodeGenerator.RetireMode mode)
      Emit bytecode to set the emulator's counter and contextreg.

      Within a translated passage, there's no need to keep constant track of the program counter (nor decode context), since all the decoding has already been done. However, whenever we exit the passage and return control back to the emulator (whether by return or throw) we must "retire" the program counter and decode context, as if the emulator had interpreted all the instructions just executed. This ensures that the emulator has the correct seed when seeking its next entry point, which may require decoding a new passage.

      Type Parameters:
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      pcGen - a means to emit bytecode to load the counter (as a long) onto the JVM stack. For errors, this is the address of the op causing the error. For branches, this is the branch target, which may be loaded from a varnode for an indirect branch.
      ctx - the contextreg value. For errors, this is the decode context of the op causing the error. For branches, this is the decode context at the target.
      mode - whether to set the machine state, too
      Returns:
      the emitter typed with the incoming stack
    • genExit

      public <N extends Emitter.Next> Emitter<N> genExit(Emitter<N> em, Local<Types.TRef<THIS>> localThis, JitControlFlowModel.JitBlock block, JitCodeGenerator.PcGen pcGen, RegisterValue ctx)
      Emit code to exit the passage

      This retires all the variables of the current block as well as the program counter and decode context. It does not generate the actual areturn or athrow, but everything required up to that point.

      Type Parameters:
      N - the incoming stack
      Parameters:
      em - the emitter typed with the incoming stack
      localThis - a handle to the local holding the this reference
      block - the block containing the op at which we are exiting
      pcGen - as in genRetirePcCtx(Emitter, Local, PcGen, RegisterValue, RetireMode)
      ctx - as in genRetirePcCtx(Emitter, Local, PcGen, RegisterValue, RetireMode)
      Returns:
      the emitter with the incoming stack
    • getErrorMessage

      public String getErrorMessage(PcodeOp op)
      Get the error message for a given p-code op.
      Parameters:
      op - the p-code op generating the error
      Returns:
      the message
      See Also:
    • getAddressForOp

      public Address getAddressForOp(PcodeOp op)
      Get the address that generated the given p-code op.

      NOTE: The decoder rewrites ops to ensure they have the decode address, even if they were injected or from an inlined userop.

      Parameters:
      op - the op
      Returns:
      the address, i.e., the program counter at the time the op is executed
    • resolveType

      public JitType resolveType(JitVal val, JitTypeBehavior type)
      Resolve the type of the given value to the given behavior
      Parameters:
      val - the value
      type - the behavior
      Returns:
      the type