1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//! 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`](crate::vm::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 (return `Ok(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-`None` refinements 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_call` for
//! the call before invoking the hook; on a hook hit, the arm
//! emits `tracer.exit_ok` itself. Hook implementors must not
//! touch the tracer.
use crateValue;
use crateVmError;
/// Hook into the VM dispatch loop for `Op::Call`.
///
/// See the module docs for the contract.