Expand description
JIT hook trait — the seam through which lex-bytecode’s
dispatch loop can delegate eligible Op::Call invocations to
a JIT tier without taking a compile-time dependency on the
JIT crate.
§Why a trait
lex-jit already depends on lex-bytecode (for Op,
Function, Value, etc.), so lex-bytecode cannot in turn
depend on lex-jit directly. The trait inverts that: callers
that want JIT register a JitHook implementation on the
Vm at construction; the dispatch loop
consults the hook on each Op::Call and falls through to the
interpreter if it returns Ok(None). No JIT in the build →
vm.jit_hook stays None and the hook check is one branch
on a null option (the optimizer should fold it).
§Contract
Implementations must be observationally equivalent to the interpreter on the calls they accept:
- Effects. Don’t accept calls into effectful functions — the dispatcher doesn’t route effect ops through the hook, so any effect call would be silently dropped.
- Refinements. The dispatch arm runs refinement checks
before calling the hook (
Op::Call’s existing path); hook implementors don’t need to re-check them, but must decline (returnOk(None)) for functions whose refinement evaluation could change observable behavior of the call. The MVP JIT’s eligibility predicate (is_jit_eligible) excludes any function with non-Nonerefinements precisely for this reason. - Memoization. The hook fires after the memo cache check, so a JIT call only happens on memo misses (or functions with memo disabled). This preserves the memo’s observable behavior (same trace-event shape on a hit).
- Tracing. The dispatch arm emits
tracer.enter_callfor the call before invoking the hook; on a hook hit, the arm emitstracer.exit_okitself. Hook implementors must not touch the tracer.
Traits§
- JitHook
- Hook into the VM dispatch loop for
Op::Call.