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