shape_jit/ffi/gc.rs
1// Heap allocation audit (PR-9 V8 Gap Closure):
2// Category A (NaN-boxed returns): 0 sites
3// Category B (intermediate/consumed): 0 sites
4// Category C (heap islands): 0 sites
5// (GC module performs safepoint polling only, no allocations)
6//!
7//! GC integration FFI functions for JIT-compiled code
8//!
9//! Provides safepoint polling and root scanning for GC-enabled builds.
10//! Without `gc` feature: no-ops that compile away.
11
12use crate::context::JITContext;
13
14/// GC safepoint poll called from JIT code at loop headers.
15///
16/// This function is called at every loop back-edge in JIT-compiled code.
17/// Fast path (no GC pending): checks a single byte and returns.
18/// Slow path (GC requested): scans JITContext roots and participates in collection.
19///
20/// # Safety
21/// `ctx` must point to a valid JITContext.
22#[unsafe(no_mangle)]
23pub extern "C" fn jit_gc_safepoint(ctx: *mut JITContext) {
24 if ctx.is_null() {
25 return;
26 }
27
28 let ctx = unsafe { &*ctx };
29
30 // Fast path: check if GC safepoint flag pointer is set
31 if ctx.gc_safepoint_flag_ptr.is_null() {
32 return;
33 }
34
35 // Load the flag byte (AtomicBool's raw storage)
36 let flag = unsafe { *ctx.gc_safepoint_flag_ptr };
37 if flag == 0 {
38 return;
39 }
40
41 // Slow path: GC is requested, scan roots
42 #[cfg(feature = "gc")]
43 gc_scan_jit_roots(ctx);
44}
45
46/// Scan JITContext roots for the garbage collector.
47///
48/// Traces all NaN-boxed values in locals and stack that may contain
49/// heap pointers, allowing the GC to find all live objects reachable
50/// from JIT-compiled code.
51#[cfg(feature = "gc")]
52fn gc_scan_jit_roots(ctx: &JITContext) {
53 use shape_gc::safepoint::safepoint_poll;
54
55 // If a GcHeap is available, participate in the safepoint protocol
56 if !ctx.gc_heap_ptr.is_null() {
57 let heap = unsafe { &*(ctx.gc_heap_ptr as *const shape_gc::GcHeap) };
58 safepoint_poll(heap.safepoint());
59 }
60
61 // Root scanning is handled by the VM's gc_integration module which
62 // has access to the full VM state. The JIT safepoint just needs to
63 // signal that this thread has reached a safe point. The actual root
64 // scanning of JITContext locals/stack happens when the VM calls
65 // run_gc_collection() which iterates through the JIT context.
66}
67
68/// Write barrier for heap pointer overwrites in JIT-compiled code.
69///
70/// Called before overwriting a heap slot. `old_bits` is the NaN-boxed value
71/// being replaced; `new_bits` is the value about to be written.
72///
73/// Without `gc` feature: unconditional no-op (compiles to a single `ret`).
74/// With `gc` feature: enqueues the old reference into the SATB buffer if
75/// an incremental marking cycle is active, and marks the card table dirty.
76#[unsafe(no_mangle)]
77pub extern "C" fn jit_write_barrier(_old_bits: u64, _new_bits: u64) {
78 #[cfg(feature = "gc")]
79 {
80 // Will wire to shape_gc write_barrier / write_barrier_combined here
81 // when GC is activated. The JITContext will carry a GcHeap pointer
82 // that can be used to call heap.write_barrier(old_ptr).
83 }
84}