Skip to main content

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}