Skip to main content

luna_core/jit/
send_compat.rs

1//! v2.1 Track J-C — cfg-gated Send-friendly aliases & wrappers for
2//! trace IR interior-mutability types.
3//!
4//! Two cfg modes:
5//! - `#[cfg(not(feature = "send"))]` — the default. All aliases
6//!   resolve to `Rc` / `Cell` / `RefCell`; identical layout, identical
7//!   behavior, identical perf as pre-J-C. **Bare `Vm` stays 0-cost**.
8//! - `#[cfg(feature = "send")]` — Send build. Aliases switch to
9//!   `Arc` / `AtomicU32` / `AtomicBool` / `AtomicPtr<u8>` / `RwLock`
10//!   so `CompiledTrace` + `Proto.traces` become structurally `Send +
11//!   Sync` (combined with the J-A/J-B/J-D/J-E sleeves). No `unsafe
12//!   impl Send` lifted in J-C — the lifts happen because the inner
13//!   types are already Send.
14//!
15//! Wrapper newtypes (`TCellU32`, `TCellBool`, `TCellPtr`, `TRefLock`)
16//! expose Cell/RefCell-shaped methods (`.get()`/`.set()`/`.borrow()`/
17//! `.borrow_mut()`) so call sites stay path-identical to the pre-J-C
18//! `std::cell::*` shape — the cfg switch happens inside the wrapper.
19//!
20//! `TArc<T>` is a pure type alias to `Rc<T>` or `Arc<T>`. Both
21//! stdlib types share the same inherent-method names
22//! (`::new` / `::from` / `::clone` / `::as_ptr` / `::strong_count`)
23//! so the alias works for all call shapes that previously used
24//! `std::rc::Rc`.
25//!
26//! Performance note (default build): all wrappers are
27//! `#[repr(transparent)]` over their inner `Cell` / `RefCell`. The
28//! generated code is identical to direct Cell/RefCell access (the
29//! wrapper methods inline trivially). Size assertions in
30//! `tests/j_c_zero_cost_default.rs` pin this.
31//!
32//! See `.dev/rfcs/v2.1-track-j-c-verdict.md` for the full migration
33//! list + cfg-gating pattern shape.
34
35// ============================================================
36// TArc<T> — `Rc<T>` (default) or `Arc<T>` (send).
37// ============================================================
38
39/// J-C cfg-gated reference count. `Rc<T>` under the default feature
40/// set, `Arc<T>` under `feature = "send"`. Construction and method
41/// shapes match between the two (`new`, `from`, `clone`, `as_ptr`,
42/// `strong_count`), so most call sites swap `Rc` → `TArc` without
43/// further changes.
44#[cfg(not(feature = "send"))]
45pub type TArc<T> = std::rc::Rc<T>;
46/// J-C cfg-gated reference count (send build). See `feature = "send"`-off
47/// alias above.
48#[cfg(feature = "send")]
49pub type TArc<T> = std::sync::Arc<T>;
50
51// ============================================================
52// TCellU32 — Cell<u32> (default) or AtomicU32 (send).
53// ============================================================
54
55/// J-C cfg-gated `u32` cell. Same API surface as `std::cell::Cell<u32>`
56/// (`new`, `get`, `set`); the send build swaps in `AtomicU32` with
57/// `Relaxed` ordering — the SendVm RwLock supplies the cross-thread
58/// happens-before, so per-op atomic ordering can be relaxed.
59#[repr(transparent)]
60#[derive(Debug)]
61pub struct TCellU32 {
62    #[cfg(not(feature = "send"))]
63    inner: std::cell::Cell<u32>,
64    #[cfg(feature = "send")]
65    inner: std::sync::atomic::AtomicU32,
66}
67
68impl TCellU32 {
69    /// Construct a new cell holding `v`.
70    #[inline]
71    pub const fn new(v: u32) -> Self {
72        #[cfg(not(feature = "send"))]
73        {
74            Self {
75                inner: std::cell::Cell::new(v),
76            }
77        }
78        #[cfg(feature = "send")]
79        {
80            Self {
81                inner: std::sync::atomic::AtomicU32::new(v),
82            }
83        }
84    }
85
86    /// Read the current value.
87    #[inline]
88    pub fn get(&self) -> u32 {
89        #[cfg(not(feature = "send"))]
90        {
91            self.inner.get()
92        }
93        #[cfg(feature = "send")]
94        {
95            self.inner.load(std::sync::atomic::Ordering::Relaxed)
96        }
97    }
98
99    /// Store `v` into the cell.
100    #[inline]
101    pub fn set(&self, v: u32) {
102        #[cfg(not(feature = "send"))]
103        {
104            self.inner.set(v);
105        }
106        #[cfg(feature = "send")]
107        {
108            self.inner.store(v, std::sync::atomic::Ordering::Relaxed);
109        }
110    }
111}
112
113impl Clone for TCellU32 {
114    fn clone(&self) -> Self {
115        Self::new(self.get())
116    }
117}
118
119impl Default for TCellU32 {
120    fn default() -> Self {
121        Self::new(0)
122    }
123}
124
125// ============================================================
126// TCellBool — Cell<bool> (default) or AtomicBool (send).
127// ============================================================
128
129/// J-C cfg-gated `bool` cell. Same API as `std::cell::Cell<bool>`
130/// (`new`, `get`, `set`).
131#[repr(transparent)]
132#[derive(Debug)]
133pub struct TCellBool {
134    #[cfg(not(feature = "send"))]
135    inner: std::cell::Cell<bool>,
136    #[cfg(feature = "send")]
137    inner: std::sync::atomic::AtomicBool,
138}
139
140impl TCellBool {
141    /// Construct a new cell holding `v`.
142    #[inline]
143    pub const fn new(v: bool) -> Self {
144        #[cfg(not(feature = "send"))]
145        {
146            Self {
147                inner: std::cell::Cell::new(v),
148            }
149        }
150        #[cfg(feature = "send")]
151        {
152            Self {
153                inner: std::sync::atomic::AtomicBool::new(v),
154            }
155        }
156    }
157
158    /// Read the current value.
159    #[inline]
160    pub fn get(&self) -> bool {
161        #[cfg(not(feature = "send"))]
162        {
163            self.inner.get()
164        }
165        #[cfg(feature = "send")]
166        {
167            self.inner.load(std::sync::atomic::Ordering::Relaxed)
168        }
169    }
170
171    /// Store `v` into the cell.
172    #[inline]
173    pub fn set(&self, v: bool) {
174        #[cfg(not(feature = "send"))]
175        {
176            self.inner.set(v);
177        }
178        #[cfg(feature = "send")]
179        {
180            self.inner.store(v, std::sync::atomic::Ordering::Relaxed);
181        }
182    }
183}
184
185// ============================================================
186// TCellPtr — Cell<*const u8> (default) or AtomicPtr<u8> (send).
187// ============================================================
188
189/// J-C cfg-gated raw-pointer cell. Same API as `std::cell::Cell<*const u8>`
190/// (`new`, `get`, `set`).
191///
192/// **IR layout invariant** (preserved): both `Cell<*const u8>` and
193/// `AtomicPtr<u8>` are 8-byte-sized, pointer-aligned, and store the
194/// raw pointer bits at offset 0. The Cranelift IR emits
195/// `iconst(I64, cell_addr) + load.i64` to read these cells; under
196/// `feature = "send"` the same load reads the AtomicPtr's bits with
197/// equivalent semantics — `AtomicPtr::load(Relaxed)` lowers to a plain
198/// pointer-sized load on the targets luna supports (arm64, x86_64),
199/// matching the pre-J-C `Cell::get` codegen byte-for-byte.
200#[repr(transparent)]
201pub struct TCellPtr {
202    #[cfg(not(feature = "send"))]
203    inner: std::cell::Cell<*const u8>,
204    #[cfg(feature = "send")]
205    inner: std::sync::atomic::AtomicPtr<u8>,
206}
207
208impl std::fmt::Debug for TCellPtr {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        f.debug_struct("TCellPtr")
211            .field("ptr", &self.get())
212            .finish()
213    }
214}
215
216impl TCellPtr {
217    /// Construct a new cell holding the null pointer.
218    #[inline]
219    pub const fn null() -> Self {
220        #[cfg(not(feature = "send"))]
221        {
222            Self {
223                inner: std::cell::Cell::new(std::ptr::null()),
224            }
225        }
226        #[cfg(feature = "send")]
227        {
228            Self {
229                inner: std::sync::atomic::AtomicPtr::new(std::ptr::null_mut()),
230            }
231        }
232    }
233
234    /// Construct a new cell holding `p`.
235    #[inline]
236    pub fn new(p: *const u8) -> Self {
237        #[cfg(not(feature = "send"))]
238        {
239            Self {
240                inner: std::cell::Cell::new(p),
241            }
242        }
243        #[cfg(feature = "send")]
244        {
245            Self {
246                inner: std::sync::atomic::AtomicPtr::new(p as *mut u8),
247            }
248        }
249    }
250
251    /// Read the current pointer bits.
252    #[inline]
253    pub fn get(&self) -> *const u8 {
254        #[cfg(not(feature = "send"))]
255        {
256            self.inner.get()
257        }
258        #[cfg(feature = "send")]
259        {
260            self.inner.load(std::sync::atomic::Ordering::Relaxed)
261        }
262    }
263
264    /// Store `p` into the cell.
265    #[inline]
266    pub fn set(&self, p: *const u8) {
267        #[cfg(not(feature = "send"))]
268        {
269            self.inner.set(p);
270        }
271        #[cfg(feature = "send")]
272        {
273            self.inner
274                .store(p as *mut u8, std::sync::atomic::Ordering::Relaxed);
275        }
276    }
277
278    /// Address of the cell itself — the value Cranelift IR bakes as
279    /// `iconst(I64, _)` to issue direct loads at runtime.
280    #[inline]
281    pub fn cell_addr(&self) -> *const () {
282        self as *const _ as *const ()
283    }
284}
285
286/// Clone mirrors `Cell<*const u8>: Clone` (`Cell<T>` is Clone whenever
287/// `T: Copy`). Produces a new cell at a different heap location with
288/// the same pointer bits. Callers that rely on heap-address stability
289/// (Cranelift IR loads of side-trace ptr cells) must NOT clone the
290/// containing `Box<TCellPtr>` once the IR has baked the original's
291/// address — same invariant as pre-J-C `Box<Cell<*const u8>>`.
292impl Clone for TCellPtr {
293    fn clone(&self) -> Self {
294        Self::new(self.get())
295    }
296}
297
298// ============================================================
299// TRefLock<T> — RefCell<T> (default) or RwLock<T> (send).
300// ============================================================
301
302/// J-C cfg-gated interior-mutable lock. Exposes `RefCell`-shaped
303/// `.borrow()` / `.borrow_mut()` whose returned guards `Deref<Target =
304/// T>`. Under the default feature set this is a thin newtype around
305/// `RefCell<T>` (zero overhead vs. the pre-J-C `RefCell` field);
306/// under `feature = "send"` it wraps `RwLock<T>` and the guards
307/// become `RwLockReadGuard` / `RwLockWriteGuard`.
308///
309/// Lock failure handling: the send build `unwrap()`s the lock result.
310/// The SendVm's outer `RwLock<()>` serializes mutator access so a
311/// poisoned lock is a real bug (a panic in a guard's user) and the
312/// propagating panic is the same UX as `RefCell::borrow_mut` on a
313/// re-entrant borrow.
314#[repr(transparent)]
315#[derive(Debug)]
316pub struct TRefLock<T: ?Sized> {
317    #[cfg(not(feature = "send"))]
318    inner: std::cell::RefCell<T>,
319    #[cfg(feature = "send")]
320    inner: std::sync::RwLock<T>,
321}
322
323impl<T> TRefLock<T> {
324    /// Construct a new lock around `v`.
325    #[inline]
326    pub const fn new(v: T) -> Self {
327        #[cfg(not(feature = "send"))]
328        {
329            Self {
330                inner: std::cell::RefCell::new(v),
331            }
332        }
333        #[cfg(feature = "send")]
334        {
335            Self {
336                inner: std::sync::RwLock::new(v),
337            }
338        }
339    }
340
341    /// Borrow the lock immutably. The returned guard derefs to `&T`;
342    /// callers use it identically to `RefCell::borrow`.
343    #[cfg(not(feature = "send"))]
344    #[inline]
345    pub fn borrow(&self) -> std::cell::Ref<'_, T> {
346        self.inner.borrow()
347    }
348
349    /// Borrow the lock immutably (send build — wraps `RwLock::read`).
350    #[cfg(feature = "send")]
351    #[inline]
352    pub fn borrow(&self) -> std::sync::RwLockReadGuard<'_, T> {
353        self.inner.read().unwrap()
354    }
355
356    /// Borrow the lock mutably. The returned guard derefs to `&mut T`;
357    /// callers use it identically to `RefCell::borrow_mut`.
358    #[cfg(not(feature = "send"))]
359    #[inline]
360    pub fn borrow_mut(&self) -> std::cell::RefMut<'_, T> {
361        self.inner.borrow_mut()
362    }
363
364    /// Borrow the lock mutably (send build — wraps `RwLock::write`).
365    #[cfg(feature = "send")]
366    #[inline]
367    pub fn borrow_mut(&self) -> std::sync::RwLockWriteGuard<'_, T> {
368        self.inner.write().unwrap()
369    }
370}