Skip to main content

BlockExecutor

Trait BlockExecutor 

Source
pub trait BlockExecutor {
    // Required method
    fn run_block(
        &mut self,
        ctx: &Context<'_>,
        emu: &mut StandaloneEmulator<VmMemory>,
        block: BlockId,
        start: usize,
        chain: bool,
    ) -> Result<Option<Executed>, EmulatorErrorKind>;
}
Expand description

An alternative way to execute a block’s body.

The interpreter is always present and always correct; an executor is an optimisation that may decline any block for any reason, in which case the interpreter runs it unchanged. That is what lets a backend be partial: a JIT need only handle the shapes it handles well.

An executor runs the block’s body, not its terminator. Control flow, block parameters and call semantics stay in one implementation.

It is handed the whole emulator rather than just its memory because the interpreter still has to run the terminator, and a terminator reads operands — a cbranch condition, a branch’s block arguments. Those are values the body produced, so an executor that keeps them somewhere other than the interpreter’s value table must put them back before returning.

Required Methods§

Source

fn run_block( &mut self, ctx: &Context<'_>, emu: &mut StandaloneEmulator<VmMemory>, block: BlockId, start: usize, chain: bool, ) -> Result<Option<Executed>, EmulatorErrorKind>

Runs everything in block except its terminator.

Ok(None) means “not mine” and is not an error — the caller falls back to the interpreter.

On Ok(Some(_)) every value the terminator of Executed::block reads must be readable from emu.insn_values, exactly as if the interpreter had run that body.

chain lets the executor run on past block into successors it also handles, instead of handing control back after one. Deciding a branch itself is how an executor keeps control inside its own code rather than paying a round trip per block. The caller withholds it when something needs to observe every block — a breakpoint is set, say — because blocks crossed this way are never offered to the interpreter.

start is the body index to begin at. It is 0 when a block is entered, and the instruction after an interrupting op when the interpreter has run the block up to there and hands the rest over. Results the interpreter already computed for the block are in emu.insn_values; an executor may read them or decline.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§