Class JitPcodeThread

All Implemented Interfaces:
PcodeThread<byte[]>

public class JitPcodeThread extends BytesPcodeThread
A JIT-accelerated thread of p-code emulation

This class implements the actual JIT-accelerated execution loop. In contrast to the normal per-instruction Fetch-Execute-Store loop inherited from DefaultPcodeThread, this thread's run() method implements a per-passage Fetch-Decode-Translate-Execute loop.

Fetch

The Fetch step involves checking the code cache for an existing translation at the thread's current counter and decode context. Cache entries are keyed by passage entry point, that is an address (and context reg value, if applicable) within a passage where execution is permitted to enter. This typically consists of the passage's seed as well as each branch target in the same passage. If one is found, we skip the Decode and Translate steps, and proceed directly to Execute.

Decode

The Decode step involves decoding and selecting several instructions into a passage. A passage may comprise of several instructions connected by control flow. Often it is a few long strides of instructions connected by a few branches. The decoder will avoid selecting instructions that are already included in an existing translated passage. The reason for this complexity is that JVM bytecode cannot be rewritten or patched once loaded. For more details, see JitPassageDecoder.

Translate

The Translate step involves translating the selected passage of instructions. The result of this translation implements JitCompiledPassage. For details of this translation process, see JitCompiler. The compiled passage provides a list of its entry points. Each is added to the emulator's code cache. Among those should be the seed required by this iteration of the execution loop, and so that entry point is chosen.

Execute

The chosen entry point is then executed. This step is as simple as invoking the JitCompiledPassage.EntryPoint.run() method. This, in turn, invokes JitCompiledPassage.run(int), providing the entry point's index as an argument. The index identifies to the translated passage the desired address of entry, and so it jumps directly to the corresponding translation. That translation performs all the equivalent operations of the selected instructions, adhering to any control flow within. When control flow exits the passage, the method returns, and the loop repeats.

  • Field Details

    • passageDecoder

      protected final JitPassageDecoder passageDecoder
      This thread's passage decoder, which is based on its instruction decoder.
    • codeCache

      This thread's cache of translations instantiated for this thread.

      As an optimization, the translator generates classes which pre-fetch portions of the thread's state. Thus, the class must be instantiated for each particular thread needing to execute it.

      TODO: Invalidation of entries. There are several reasons an entry may need to be invalidated: Expiration, eviction, or perhaps because the JitCompiledPassage.EntryPointPrototype (from the emulator) was invalidated.

  • Constructor Details

    • JitPcodeThread

      public JitPcodeThread(String name, JitPcodeEmulator machine)
      Create a thread

      This should only be called by the emulator and its test suites.

      Parameters:
      name - the name of the thread
      machine - the machine creating the thread
  • Method Details

    • createThreadState

      protected ThreadPcodeExecutorState<byte[]> createThreadState(PcodeExecutorState<byte[]> sharedState, PcodeExecutorState<byte[]> localState)
      Description copied from class: DefaultPcodeThread
      A factory method for the thread's (multiplexed) state
      Overrides:
      createThreadState in class DefaultPcodeThread<byte[]>
      Parameters:
      sharedState - the shared part of the state
      localState - the thread-local part of the state
      Returns:
      the complete state
    • createPassageDecoder

      protected JitPassageDecoder createPassageDecoder()
      Create the passage decoder

      This is an extension point in case the decoder needs to be replaced with a further extension.

      Returns:
      the new passage decoder
    • getMachine

      public JitPcodeEmulator getMachine()
      Description copied from interface: PcodeThread
      Get the machine within which this thread executes
      Specified by:
      getMachine in interface PcodeThread<byte[]>
      Overrides:
      getMachine in class DefaultPcodeThread<byte[]>
      Returns:
      the containing machine
    • getState

      Description copied from interface: PcodeThread
      Get the thread's memory and register state

      The memory part of this state is shared among all threads in the same machine. See PcodeMachine.getSharedState().

      Specified by:
      getState in interface PcodeThread<byte[]>
      Overrides:
      getState in class DefaultPcodeThread<byte[]>
      Returns:
      the state
    • getInject

      public PcodeProgram getInject(Address address)
      Description copied from class: DefaultPcodeThread
      Check for a p-code injection (override) at the given address

      This checks this thread's particular injects and then defers to the machine's injects.

      Overrides:
      getInject in class DefaultPcodeThread<byte[]>
      Parameters:
      address - the address, usually the program counter
      Returns:
      the injected program, most likely null
    • getDecoder

      public InstructionDecoder getDecoder()
      An accessor so the passage decoder can retrieve its thread's instruction decoder.
      Returns:
      the decoder
    • getDefaultContext

      public ProgramContext getDefaultContext()
      An accessor so the passage decoder can query the language's default program context.
      Returns:
      the context
    • inject

      public void inject(Address address, String source)
      Description copied from interface: PcodeThread
      Override the p-code at the given address with the given Sleigh source for only this thread

      This works the same PcodeMachine.inject(Address, String) but on a per-thread basis. Where there is both a machine-level and thread-level inject the thread inject takes precedence. Furthermore, the machine-level inject cannot be accessed by the thread-level inject.

      Specified by:
      inject in interface PcodeThread<byte[]>
      Overrides:
      inject in class DefaultPcodeThread<byte[]>
      Parameters:
      address - the address to inject at
      source - the Sleigh source to compile and inject
    • hasEntry

      public boolean hasEntry(JitPassage.AddrCtx pcCtx)
      Check if the emulator has an entry prototype for the given address and contextreg value.

      This simply passes through to the emulator. It does not matter whether or not this thread has instantiated the prototype or not. If any thread has caused the emulator to translate the given entry, this will return true.

      Parameters:
      pcCtx - the address and contextreg to check
      Returns:
      true if the emulator has a translation which can be entered at the given pcCtx.
      See Also:
    • getEntry

      Get the translated and instantiated entry point for the given address and contextreg value.

      An entry point is an instance of a class representing a translated passage and an index identifying the point at which to enter the passage. In essence, it is an instance of an entry prototype for this thread.

      This will first check the cache for an existing instance. Then, it will delegate to the emulator. The emulator will check its cache for an existing translation. If one is found, we simply take it and instantiate it for this thread. Otherwise, the emulator translates a new passage at the given seed, and we instantiate it for this thread.

      Parameters:
      pcCtx - the counter and decoder context
      Returns:
      the entry point
      See Also:
    • run

      public void run()
      Emulate indefinitely

      This begins or resumes execution of the emulator. If there is a current instruction, that instruction is finished. By calling this method, you are "donating" the current Java thread to the emulator. This method will not likely return, but instead only terminates via exception, e.g., hitting a user breakpoint or becoming suspended. Depending on the use case, this method might be invoked from a Java thread dedicated to this emulated thread.

      We override only this method to accelerate execution using JIT translation. Implementing single stepping via JIT doesn't make much sense from an efficiency standpoint. However, this thread still supports stepping via interpretation (as inherited). Our implementation permits mixing the two execution paradigms; however, using JIT after a few single steps will incur some waste as the JIT translates an otherwise uncommon entry point. Depending on circumstances and the order of operations, the effect of this on overall efficiency may vary because of caching.

      Specified by:
      run in interface PcodeThread<byte[]>
      Overrides:
      run in class DefaultPcodeThread<byte[]>
    • count

      public void count(int instructions, int trailingOps)
      This is called before each basic block is executed.

      This gives the thread an opportunity to track and control execution, if desired. It provides the number of instructions and additional p-code ops about to be completed. If the counts exceed a desired schedule, or if the thread is suspended, this method may throw an exception to interrupt execution. This can be toggled in the emulator's configuration.

      Parameters:
      instructions - the number of instruction about to be completed
      trailingOps - the number of ops of a final partial instruction about to be completed. If the block does not complete any instruction, this is the number of ops continuing in the current (partial) instruction.
      See Also: