Skip to main content

luna_core/jit/
mod.rs

1//! luna-core JIT surface — **trait + types only, zero Cranelift**.
2//!
3//! The interpreter dispatcher in [`crate::vm::exec`] routes JIT calls
4//! through [`IntChunkCompiler`] / [`TraceCompiler`] trait objects;
5//! luna-core ships only the no-op [`NullJitBackend`], which makes the
6//! whole crate Cranelift-free (and therefore zero-three-party-dep,
7//! per v1.1 F1).
8//!
9//! The Cranelift-backed implementations (`CraneliftBackend`, the 26
10//! `luna_jit_*` extern "C" helpers, the `JIT_CACHE` thread-local,
11//! `enter_jit`, `cache_lookup_or_compile`, `try_compile_int_chunk`,
12//! `try_compile_trace_with_options`, …) live in the `luna` crate
13//! under `luna::jit_backend`. Embedders who want the JIT use
14//! `luna::Vm::new_minimal_with_jit(version)` instead of
15//! `luna_core::vm::Vm::new_minimal(version)`.
16
17// v1.1 A1 Session B — pure data types + small cranelift-free helpers
18// for the trace JIT live here. Session C moves the file under
19// luna-core unchanged.
20pub mod trace_types;
21pub use trace_types::*;
22
23// v1.3 Phase AOT Stage 7 sub-piece 4 — wire format for AOT trace
24// metadata (header + index entry + encode/decode). Pure data; both
25// `luna-aot` (compile-time encode) and `luna-runtime-helpers`
26// (deploy-time decode) depend on this single module so the on-disk
27// shape stays in lock-step.
28pub mod aot_meta;
29
30// v1.1 A1 Session A — backend trait surface introduced in-place.
31// Session C moves it to luna-core (this file) and extracts the
32// Cranelift-bound CraneliftBackend struct out into luna's
33// jit_backend module.
34mod abi;
35pub use abi::{
36    CompileResult, IntChunkCompiler, IntChunkFn, IntFn1, IntFn2, IntFn3, IntFn4, MAX_JIT_ARITY,
37    NullJitBackend, TraceCompiler,
38};
39
40// v2.0 Track J sub-step J-B — per-Vm JIT storage trait. Holds the
41// (formerly thread-local) JIT cache + handle collections so a Vm
42// carries its own JIT state across thread moves (Send-prep for J-D).
43mod storage;
44pub use storage::{JitStorage, NullJitStorage};
45
46// v2.1 Track J-C — cfg-gated Send-friendly aliases & wrappers for
47// the trace IR interior-mutability types. Default-feature path is
48// 0-cost (`Rc` / `Cell` / `RefCell`); `feature = "send"` flips to
49// `Arc` / `AtomicU32` / `AtomicBool` / `AtomicPtr<u8>` / `RwLock` so
50// `CompiledTrace` + `Proto::traces` become structurally Send + Sync.
51pub mod send_compat;
52pub use send_compat::{TArc, TCellBool, TCellPtr, TCellU32, TRefLock};
53
54// Compatibility re-export so external `use luna::jit::trace::*` paths
55// (and the historical `crate::jit::trace::*` accesses inside this
56// crate) keep resolving even after Session C's physical split. In
57// luna-core `trace` is just a namespace alias for `trace_types`; in
58// luna it's enriched with codegen-bearing items via a different
59// re-export.
60/// Trace-JIT types namespace. In `luna-core` this is a thin re-export of
61/// [`trace_types`]; the `luna` crate enriches it with the Cranelift-backed
62/// codegen entry points (`try_compile_trace`, etc.).
63pub mod trace {
64    pub use super::trace_types::*;
65}
66
67/// Construct an inert [`JitVmGuard`] that performs neither TLS install
68/// nor TLS clear. Used by [`NullJitBackend::enter`]: since
69/// `try_compile` / `try_compile_trace` always skip work, no JIT mcode
70/// ever fires and no helper consults the TLS slots, so the guard only
71/// has to keep the trait method signature symmetric with luna's
72/// `CraneliftBackend::enter`. The inert guard carries no restore
73/// callback, so its drop is a no-op.
74#[inline]
75pub fn noop_jit_guard() -> JitVmGuard {
76    JitVmGuard { restore: None }
77}
78
79/// P11-S5c — RAII guard pinning the active `Vm` (and optional closure)
80/// pointer for JIT-emitted Rust helper calls.
81///
82/// # v2.0 Track J sub-step J-D — real RAII rebind
83///
84/// Prior to J-D, this guard's drop was a deliberate no-op: the
85/// dispatcher always re-installed `JIT_VM` / `JIT_CL` on every
86/// `enter_jit` call, so the previous-dispatch values would simply be
87/// overwritten on the next entry. That trick saved 2 TLS writes per
88/// dispatch (~5-10 cycles each on arm64) — measurable on fib_28's
89/// 434k dispatches (~1.5 ms aggregate).
90///
91/// Track J Option B targets cross-thread Vm move under
92/// `feature = "send"` (J-E flip). Once a Vm parks on thread A, moves
93/// to thread B, and dispatches there, the per-thread `JIT_VM` slot on
94/// thread B is null/stale at entry — `enter_jit` installs it fine on
95/// the way in, but on the way out the no-op drop left stale Vm
96/// pointers behind for any **nested** JIT entry (Lua-from-Rust-from-
97/// JIT call chains, e.g. metamethod dispatch under a JIT'd op). With
98/// nested entries restoring an outer Vm pointer becomes load-bearing.
99///
100/// J-D therefore turns the guard into a real RAII: `enter_jit`
101/// captures the prior `(JIT_VM, JIT_CL)` values into the guard, and
102/// `Drop` restores them. The cost is the 2 TLS writes we previously
103/// elided per dispatch. The single-thread-no-nesting case is
104/// semantically equivalent: prior values restored on exit are the
105/// same null/stale ones the next `enter_jit` would overwrite anyway;
106/// no observable behavior change for existing call sites.
107///
108/// `NullJitBackend::enter` keeps the no-op shape via
109/// [`noop_jit_guard`] — its guard has `restore = None`.
110///
111/// Debug-mode assertions still catch misuse via `current_jit_vm`'s
112/// is_null check IF a helper runs outside `enter_jit` (would happen
113/// only if some bug calls a helper symbol from interp code, which
114/// never happens by design).
115#[must_use = "the guard must outlive the JIT entry call"]
116pub struct JitVmGuard {
117    /// `None` = inert (NullJitBackend's no-op). `Some` = real RAII:
118    /// the dispatcher's prior `(JIT_VM, JIT_CL)` values are captured
119    /// here on entry, and `restore_fn` is the luna-jit-side restorer
120    /// that writes them back on drop.
121    ///
122    /// Field is `pub(crate)` for [`noop_jit_guard`] (this file); the
123    /// other constructor (luna-jit's `enter_jit`) lives in a separate
124    /// crate, so [`JitVmRebindRestore`] itself is the public seam.
125    pub(crate) restore: Option<JitVmRebindRestore>,
126}
127
128impl JitVmGuard {
129    /// Construct a guard from a captured restore record. Called only
130    /// by luna-jit's `enter_jit`; embedders never call this directly.
131    #[doc(hidden)]
132    #[inline]
133    pub fn from_restore(r: JitVmRebindRestore) -> Self {
134        Self { restore: Some(r) }
135    }
136}
137
138/// Capture of the previous `(JIT_VM, JIT_CL)` slot contents at the
139/// moment [`JitVmGuard`] takes ownership, plus a function pointer
140/// that writes them back. The function pointer lets luna-core hold
141/// the drop semantics without referencing Cranelift's TLS storage
142/// (the cells themselves live in `luna_jit::jit_backend` because
143/// that's where the `thread_local!` block is, but the cells hold
144/// luna-core types — `*mut Vm` and `*const LuaClosure`).
145///
146/// Fields are `pub` rather than `pub(crate)` because luna-jit's
147/// `enter_jit` needs to construct an instance, and luna-jit is a
148/// separate crate. The whole type is `#[doc(hidden)]` so it stays
149/// out of the embedder surface.
150#[doc(hidden)]
151pub struct JitVmRebindRestore {
152    pub prev_vm: *mut crate::vm::Vm,
153    pub prev_cl: *const crate::runtime::LuaClosure,
154    /// luna-jit-side function that writes `(prev_vm, prev_cl)` back
155    /// into the TLS cells. Set by `enter_jit`; never null when this
156    /// struct exists.
157    pub restore_fn:
158        unsafe fn(prev_vm: *mut crate::vm::Vm, prev_cl: *const crate::runtime::LuaClosure),
159}
160
161impl Drop for JitVmGuard {
162    fn drop(&mut self) {
163        if let Some(r) = self.restore.take() {
164            // SAFETY: `restore_fn` is set only by luna-jit's
165            // `enter_jit`, which threads the prior TLS values into
166            // the guard at construction time. The fn ptr type is
167            // `unsafe fn` because the cells participate in the JIT
168            // helper SAFETY contract (`current_jit_vm` /
169            // `current_jit_closure` rely on slot validity during the
170            // dispatch window).
171            unsafe { (r.restore_fn)(r.prev_vm, r.prev_cl) };
172        }
173    }
174}