Package ghidra.pcode.emu.jit.decode

# Class JitPassageDecoder

java.lang.Object
ghidra.pcode.emu.jit.decode.JitPassageDecoder

public class JitPassageDecoder extends Object
The decoder of a `JitPassage` to support JIT-accelerated p-code emulation.

When the emulator encounters an address (and contextreg value) that it has not previously translated, it must decode a passage seeded at that required entry point. It must then translate the passage and collects all the resulting entry points, and finally invoke the passage's `run` method for the required entry point.

## Decoding a Passage

Decode starts with a single seed, which is the entry point required by the emulator. As such, that seed must be among the entry points exported by the translator. Decode occurs one stride at a time. Starting with the seed, we decode a stride by disassembling linearly until: 1) We encounter an instruction without fall through, 2) there's already an entry point to a translated passage at an encountered address, or 3) a user injection fails to specify control flow. Case 1 is the normal expected case. For example, when the decoder encounters an unconditional branch, the stride is terminated. Case 2 is meant to reduce duplicative translations, but it does come at some cost during decode time. Suppose execution branches into the middle of a previously translated basic block. (Note that basic blocks are only broken apart using branches in the same passage, so it is possible some branch encountered later would jump into another passage's basic block.) That previously translated passage will not have exposed an entry point at that branch target, so the emulator will begin decoding using the branch target as the seed. Ideally, the resulting passage will consist of a single stride that terminates at an existing entry point. The emulator will translate and execute the passage, which should exit at that entry point, where the emulator can then continue execution. Case 3 is just to ensure execution does not get caught in a translated infinite loop. There will still be an infinite loop, but it can be interrupted while execution is in the emulator's logic rather than the translated logic.

As the stride decoder processes each instruction, it interprets its p-code, along with any generated by user injects, to collect branch targets. For direct branches (`branch`, `cbranch`, and `call`), the target address (and appropriate contextreg value) is added to the queue of seeds, unless that target is already decoded in this passage. A bit of control flow analysis is required to determine whether each instruction (with user injects) has fall through. We borrow the `JitControlFlowModel.BlockSplitter` from the `JitControlFlowModel` to accomplish this. We append a "probe" p-code op at the very end, and then once we have the (miniature) control flow graph, we check if there's a path from instruction start to the probe op. If there is, then we can fall through, so decode proceeds to the next instruction. If not, the stride is terminated, so the decoder starts a new stride at the next seed, unless we've met the p-code op, instruction, or stride `quota`.

The seed queue is a list of `JitPassage.ExtBranch` records. Each stride is decoded by removing a seed from that queue, decoding instructions, emitting ops, and then creating an `JitPassage.IntBranch` record targeting the first op of the newly-decoded instruction. The `from` field is taken from the seed `JitPassage.ExtBranch` record. Decode will likely terminate before this queue is emptied, in which case, those remaining external branches will become part of the passage's `branches`. Direct branches to instructions already included in the passage, p-code relative branches, and queued external branches to instructions which have since been decoded all become `JitPassage.IntBranch` records, too. For indirect branches (`branchind`, `callind`, and `return`), we create `JitPassage.IndBranch` records. For error cases (e.g., `unimplemented`), we create `JitPassage.ErrBranch` records.

Implementation Notes:
  The process described above is actually implemented in `DecoderForOnePassage`. This class just keeps the configuration and some other trappings, and instantiates an actual decoder upon requesting a seed.

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `JitPassageDecoder(JitPcodeThread thread)`
  Construct a passage decoder

- ## Method Summary

  Modifier and Type
  Method
  Description
  `JitPassage`
  `decodePassage(JitPassage.AddrCtx seed, int maxOps)`
  Decode a passage starting at the given seed
  `JitPassage`
  `decodePassage(Address seed, RegisterValue ctxIn, int maxOps)`
  Decode a passage starting at the given seed

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### JitPassageDecoder

    public JitPassageDecoder(JitPcodeThread thread)
    Construct a passage decoder
    Parameters:
      `thread` - the thread whose instruction decoder, context, and userop library to use.

- ## Method Details

  - ### decodePassage

    public JitPassage decodePassage(Address seed, RegisterValue ctxIn, int maxOps)
    Decode a passage starting at the given seed
    Parameters:
      `seed` - the seed address
      `ctxIn` - the seed contextreg value
      `maxOps` - the maximum-ish number of p-code ops to emit
    Returns:
      the decoded passage
    See Also:
      - `decodePassage(AddrCtx, int)`

  - ### decodePassage

    public JitPassage decodePassage(JitPassage.AddrCtx seed, int maxOps)
    Decode a passage starting at the given seed

    We provide a `maxOps` parameter so that the configured `option` can be overridden. In particular, the bytecode emitter may exceed the maximum size of a Java method, in which case we must abort, re-decode with fewer ops, and retry. Whether this back off should persist in the configuration is yet to be determined. Output size can vary wildly depending on the number of basic blocks, scope transitions, nature of the ops, etc. We ought to be able to provide a reasonable default value that mostly avoids retries, because each retry essentially wastes an entire JIT translation. On the other hand, if we choose too small a value, we lose some of the benefits of translating the control flow and keeping variables in JVM locals.

    Parameters:
      `seed` - the required entry point, where decode will start
      `maxOps` - the maximum-ish number of p-codes to emit
    Returns:
      the decoded passage