shape_gc/roots.rs
1//! Root scanning infrastructure for the GC.
2//!
3//! The `Trace` trait is implemented by types that contain GC-managed pointers.
4//! During the mark phase, the GC calls `trace()` on root objects to discover
5//! all reachable heap pointers.
6
7/// Callback type for tracing — accepts raw pointers to GC-managed objects.
8pub type TraceCallback = dyn FnMut(*mut u8);
9
10/// Trait for types that can be traced by the GC.
11///
12/// Implementors enumerate all GC-managed pointers they contain by calling
13/// the visitor callback for each pointer.
14pub trait Trace {
15 /// Trace all GC-managed pointers in this value.
16 ///
17 /// For each pointer to a GC-managed object, call `visitor(ptr)`.
18 fn trace(&self, visitor: &mut dyn FnMut(*mut u8));
19}
20
21/// Trace a NaN-boxed u64 value: if it's heap-tagged, yield the raw pointer.
22///
23/// This is used by the VM to trace stack slots and globals without requiring
24/// ValueWord to implement Trace directly (which would create a circular dependency).
25#[inline]
26pub fn trace_nanboxed_bits(bits: u64, visitor: &mut dyn FnMut(*mut u8)) {
27 // NaN-boxing constants (duplicated here to avoid circular dep on shape-value)
28 const TAG_BASE: u64 = 0xFFF8_0000_0000_0000;
29 const PAYLOAD_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
30 const TAG_SHIFT: u32 = 48;
31 const TAG_MASK: u64 = 0x0007_0000_0000_0000;
32 const TAG_HEAP: u64 = 0b000;
33
34 let is_tagged = (bits & TAG_BASE) == TAG_BASE;
35 if is_tagged {
36 let tag = (bits & TAG_MASK) >> TAG_SHIFT;
37 if tag == TAG_HEAP {
38 // Mask off bit 0 (ownership flag) — owned Box-backed values set it.
39 const HEAP_PTR_MASK: u64 = !1;
40 let ptr = (bits & PAYLOAD_MASK & HEAP_PTR_MASK) as *mut u8;
41 if !ptr.is_null() {
42 visitor(ptr);
43 }
44 }
45 }
46}
47
48/// Trace a raw u64 that may be a heap pointer (for ValueSlot).
49///
50/// Used when we know from heap_mask that a slot contains a heap pointer.
51#[inline]
52pub fn trace_heap_slot(bits: u64, visitor: &mut dyn FnMut(*mut u8)) {
53 let ptr = bits as *mut u8;
54 if !ptr.is_null() {
55 visitor(ptr);
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_trace_non_heap_is_noop() {
65 // An i48 value should not yield any pointer
66 let i48_bits: u64 = 0xFFF8_0000_0000_0001 | (0b001 << 48); // TAG_INT
67 let mut found = false;
68 trace_nanboxed_bits(i48_bits, &mut |_| found = true);
69 assert!(!found);
70 }
71
72 #[test]
73 fn test_trace_f64_is_noop() {
74 let f64_bits = 3.14_f64.to_bits();
75 let mut found = false;
76 trace_nanboxed_bits(f64_bits, &mut |_| found = true);
77 assert!(!found);
78 }
79}