wasmtime_runtime/gc/
gc_ref.rs

1use crate::{GcHeap, GcStore, VMSharedTypeIndex, I31};
2use anyhow::{Context, Result};
3use std::num::NonZeroU32;
4use wasmtime_environ::VMGcKind;
5
6/// The common header for all objects allocated in a GC heap.
7///
8/// This header is shared across all collectors, although particular collectors
9/// may always add their own trailing fields to this header for all of their own
10/// GC objects.
11///
12/// This is a bit-packed structure that logically has the following fields:
13///
14/// ```ignore
15/// struct VMGcHeader {
16///     // Highest 2 bits.
17///     kind: VMGcKind,
18///
19///     // 30 bits available for the `GcRuntime` to make use of however it sees fit.
20///     reserved: u30,
21///
22///     // The `VMSharedTypeIndex` for this GC object, if it isn't an
23///     // `externref` (or an `externref` re-wrapped as an `anyref`). `None` is
24///     // represented with `VMSharedTypeIndex::default()`.
25///     ty: Option<VMSharedTypeIndex>,
26/// }
27/// ```
28#[repr(transparent)]
29pub struct VMGcHeader(u64);
30
31unsafe impl GcHeapObject for VMGcHeader {
32    #[inline]
33    fn is(_: &VMGcHeader) -> bool {
34        true
35    }
36}
37
38impl VMGcHeader {
39    /// Create the header for an `externref`.
40    pub fn externref() -> Self {
41        let kind = VMGcKind::ExternRef as u32;
42        let upper = u64::from(kind) << 32;
43        let lower = u64::from(u32::MAX);
44        Self(upper | lower)
45    }
46
47    /// Get the kind of GC object that this is.
48    pub fn kind(&self) -> VMGcKind {
49        let upper = u32::try_from(self.0 >> 32).unwrap();
50        VMGcKind::from_u32(upper)
51    }
52
53    /// Get the reserved 30 bits in this header.
54    ///
55    /// These are bits are reserved for `GcRuntime` implementations to make use
56    /// of however they see fit.
57    pub fn reserved_u30(&self) -> u32 {
58        let upper = u32::try_from(self.0 >> 32).unwrap();
59        upper & VMGcKind::UNUSED_MASK
60    }
61
62    /// Set the 30-bit reserved value.
63    ///
64    /// # Panics
65    ///
66    /// Panics if the given `value` has any of the upper 2 bits set.
67    pub fn set_reserved_u30(&mut self, value: u32) {
68        assert_eq!(
69            value & VMGcKind::MASK,
70            0,
71            "VMGcHeader::set_reserved_u30 with value using more than 30 bits"
72        );
73        self.0 |= u64::from(value) << 32;
74    }
75
76    /// Set the 30-bit reserved value.
77    ///
78    /// # Safety
79    ///
80    /// The given `value` must only use the lower 30 bits; its upper 2 bits must
81    /// be unset.
82    pub unsafe fn unchecked_set_reserved_u30(&mut self, value: u32) {
83        self.0 |= u64::from(value) << 32;
84    }
85
86    /// Get this object's specific concrete type.
87    pub fn ty(&self) -> Option<VMSharedTypeIndex> {
88        let lower_mask = u64::from(u32::MAX);
89        let lower = u32::try_from(self.0 & lower_mask).unwrap();
90        if lower == u32::MAX {
91            None
92        } else {
93            Some(VMSharedTypeIndex::new(lower))
94        }
95    }
96}
97
98#[cfg(test)]
99mod vm_gc_header_tests {
100    use super::*;
101
102    #[test]
103    fn size_align() {
104        assert_eq!(std::mem::size_of::<VMGcHeader>(), 8);
105        assert_eq!(std::mem::align_of::<VMGcHeader>(), 8);
106    }
107}
108
109/// A raw, unrooted GC reference.
110///
111/// A `VMGcRef` is either:
112///
113/// * A reference to some kind of object on the GC heap, but we don't know
114///   exactly which kind without further reflection. Furthermore, this is not
115///   actually a pointer, but a compact index into a Wasm GC heap.
116///
117/// * An `i31ref`: it doesn't actually reference an object in the GC heap, but
118///   is instead an inline, unboxed 31-bit integer.
119///
120/// ## `VMGcRef` and GC Barriers
121///
122/// Depending on the garbage collector in use, cloning, writing, and dropping a
123/// `VMGcRef` may require invoking GC barriers (little snippets of code provided
124/// by the collector to ensure it is correctly tracking all GC references).
125///
126/// Therefore, to encourage correct usage of GC barriers, this type does *NOT*
127/// implement `Clone` or `Copy`. Use `GcStore::clone_gc_ref`,
128/// `GcStore::write_gc_ref`, and `GcStore::drop_gc_ref` to clone, write, and
129/// drop `VMGcRef`s respectively.
130///
131/// As an escape hatch, if you really need to copy a `VMGcRef` without invoking
132/// GC barriers and you understand why that will not lead to GC bugs in this
133/// particular case, you can use the `unchecked_copy` method.
134#[derive(Debug, PartialEq, Eq, Hash)]
135#[repr(transparent)]
136pub struct VMGcRef(NonZeroU32);
137
138impl<T> From<TypedGcRef<T>> for VMGcRef {
139    #[inline]
140    fn from(value: TypedGcRef<T>) -> Self {
141        value.gc_ref
142    }
143}
144
145impl std::fmt::LowerHex for VMGcRef {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        self.0.fmt(f)
148    }
149}
150
151impl std::fmt::UpperHex for VMGcRef {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        self.0.fmt(f)
154    }
155}
156
157impl std::fmt::Pointer for VMGcRef {
158    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159        write!(f, "{:#x}", self)
160    }
161}
162
163impl VMGcRef {
164    /// The only type of valid `VMGcRef` is currently `VMExternRef`.
165    ///
166    /// Assert on this anywhere you are making that assumption, so that we know
167    /// all the places to update when it no longer holds true.
168    pub const ONLY_EXTERN_REF_AND_I31: bool = true;
169
170    /// If this bit is set on a GC reference, then the GC reference is actually an
171    /// unboxed `i31`.
172    ///
173    /// Must be kept in sync with `wasmtime_cranelift::I31_REF_DISCRIMINANT`.
174    pub const I31_REF_DISCRIMINANT: u32 = 1;
175
176    /// Create a new `VMGcRef` from the given raw u32 value.
177    ///
178    /// Does not discriminate between indices into a GC heap and `i31ref`s.
179    ///
180    /// Returns `None` for zero values.
181    ///
182    /// The given index should point to a valid GC-managed object within this
183    /// reference's associated heap. Failure to uphold this will be memory safe,
184    /// but will lead to general failures such as panics or incorrect results.
185    pub fn from_raw_u32(raw: u32) -> Option<Self> {
186        Some(Self::from_raw_non_zero_u32(NonZeroU32::new(raw)?))
187    }
188
189    /// Create a new `VMGcRef` from the given index into a GC heap.
190    ///
191    /// The given index should point to a valid GC-managed object within this
192    /// reference's associated heap. Failure to uphold this will be memory safe,
193    /// but will lead to general failures such as panics or incorrect results.
194    ///
195    /// Returns `None` when the index is not 2-byte aligned and therefore
196    /// conflicts with the `i31ref` discriminant.
197    pub fn from_heap_index(index: NonZeroU32) -> Option<Self> {
198        if (index.get() & Self::I31_REF_DISCRIMINANT) == 0 {
199            Some(Self::from_raw_non_zero_u32(index))
200        } else {
201            None
202        }
203    }
204
205    /// Create a new `VMGcRef` from the given raw value.
206    ///
207    /// Does not discriminate between indices into a GC heap and `i31ref`s.
208    pub fn from_raw_non_zero_u32(raw: NonZeroU32) -> Self {
209        VMGcRef(raw)
210    }
211
212    /// Construct a new `VMGcRef` from an unboxed 31-bit integer.
213    #[inline]
214    pub fn from_i31(val: I31) -> Self {
215        let val = (val.get_u32() << 1) | Self::I31_REF_DISCRIMINANT;
216        debug_assert_ne!(val, 0);
217        let non_zero = unsafe { NonZeroU32::new_unchecked(val) };
218        VMGcRef::from_raw_non_zero_u32(non_zero)
219    }
220
221    /// Create a new `VMGcRef` from a raw `r64` value from Cranelift.
222    ///
223    /// Returns an error if `raw` cannot be losslessly converted from a `u64`
224    /// into a `u32`.
225    ///
226    /// Returns `Ok(None)` if `raw` is zero (aka a "null" `VMGcRef`).
227    ///
228    /// This method only exists because we can't currently use Cranelift's `r32`
229    /// type on 64-bit platforms. We should instead have a `from_r32` method.
230    pub fn from_r64(raw: u64) -> Result<Option<Self>> {
231        let raw = u32::try_from(raw & (u32::MAX as u64))
232            .with_context(|| format!("invalid r64: {raw:#x} cannot be converted into a u32"))?;
233        Ok(Self::from_raw_u32(raw))
234    }
235
236    /// Copy this `VMGcRef` without running the GC's clone barriers.
237    ///
238    /// Prefer calling `clone(&mut GcStore)` instead! This is mostly an internal
239    /// escape hatch for collector implementations.
240    ///
241    /// Failure to run GC barriers when they would otherwise be necessary can
242    /// lead to leaks, panics, and wrong results. It cannot lead to memory
243    /// unsafety, however.
244    pub fn unchecked_copy(&self) -> Self {
245        VMGcRef(self.0)
246    }
247
248    /// Get this GC reference as a u32 index into its GC heap.
249    ///
250    /// Returns `None` for `i31ref`s.
251    pub fn as_heap_index(&self) -> Option<NonZeroU32> {
252        if self.is_i31() {
253            None
254        } else {
255            Some(self.0)
256        }
257    }
258
259    /// Get this GC refererence as a raw u32 value, regardless whether it is
260    /// actually a reference to a GC object or is an `i31ref`.
261    pub fn as_raw_u32(&self) -> u32 {
262        self.0.get()
263    }
264
265    /// Get this GC reference as a raw `r64` value for passing to Cranelift.
266    ///
267    /// This method only exists because we can't currently use Cranelift's `r32`
268    /// type on 64-bit platforms. We should instead be able to pass `VMGcRef`
269    /// into compiled code directly.
270    pub fn into_r64(self) -> u64 {
271        u64::from(self.0.get())
272    }
273
274    /// Get this GC reference as a raw `r64` value for passing to Cranelift.
275    ///
276    /// This method only exists because we can't currently use Cranelift's `r32`
277    /// type on 64-bit platforms. We should instead be able to pass `VMGcRef`
278    /// into compiled code directly.
279    pub fn as_r64(&self) -> u64 {
280        u64::from(self.0.get())
281    }
282
283    /// Creates a typed GC reference from `self`, checking that `self` actually
284    /// is a `T`.
285    ///
286    /// If this is not a GC reference to a `T`, then `Err(self)` is returned.
287    pub fn into_typed<T>(self, gc_heap: &impl GcHeap) -> Result<TypedGcRef<T>, Self>
288    where
289        T: GcHeapObject,
290    {
291        if self.is_i31() {
292            return Err(self);
293        }
294        if T::is(gc_heap.header(&self)) {
295            Ok(TypedGcRef {
296                gc_ref: self,
297                _phantom: std::marker::PhantomData,
298            })
299        } else {
300            Err(self)
301        }
302    }
303
304    /// Creates a typed GC reference without actually checking that `self` is a
305    /// `T`.
306    ///
307    /// `self` should point to a `T` object. Failure to uphold this invariant is
308    /// memory safe, but will lead to general incorrectness such as panics or
309    /// wrong results.
310    pub fn into_typed_unchecked<T>(self) -> TypedGcRef<T>
311    where
312        T: GcHeapObject,
313    {
314        debug_assert!(!self.is_i31());
315        TypedGcRef {
316            gc_ref: self,
317            _phantom: std::marker::PhantomData,
318        }
319    }
320
321    /// Borrow `self` as a typed GC reference, checking that `self` actually is
322    /// a `T`.
323    pub fn as_typed<T>(&self, gc_heap: &impl GcHeap) -> Option<&TypedGcRef<T>>
324    where
325        T: GcHeapObject,
326    {
327        if self.is_i31() {
328            return None;
329        }
330        if T::is(gc_heap.header(&self)) {
331            let ptr = self as *const VMGcRef;
332            let ret = unsafe { &*ptr.cast() };
333            assert!(matches!(
334                ret,
335                TypedGcRef {
336                    gc_ref: VMGcRef(_),
337                    _phantom
338                }
339            ));
340            Some(ret)
341        } else {
342            None
343        }
344    }
345
346    /// Creates a typed GC reference without actually checking that `self` is a
347    /// `T`.
348    ///
349    /// `self` should point to a `T` object. Failure to uphold this invariant is
350    /// memory safe, but will lead to general incorrectness such as panics or
351    /// wrong results.
352    pub fn as_typed_unchecked<T>(&self) -> &TypedGcRef<T>
353    where
354        T: GcHeapObject,
355    {
356        debug_assert!(!self.is_i31());
357        let ptr = self as *const VMGcRef;
358        let ret = unsafe { &*ptr.cast() };
359        assert!(matches!(
360            ret,
361            TypedGcRef {
362                gc_ref: VMGcRef(_),
363                _phantom
364            }
365        ));
366        ret
367    }
368
369    /// Get a reference to the GC header that this GC reference is pointing to.
370    ///
371    /// Returns `None` when this is an `i31ref` and doesn't actually point to a
372    /// GC header.
373    pub fn gc_header<'a>(&self, gc_heap: &'a dyn GcHeap) -> Option<&'a VMGcHeader> {
374        if self.is_i31() {
375            None
376        } else {
377            Some(gc_heap.header(self))
378        }
379    }
380
381    /// Is this `VMGcRef` actually an unboxed 31-bit integer, and not actually a
382    /// GC reference?
383    #[inline]
384    pub fn is_i31(&self) -> bool {
385        let val = self.0.get();
386        (val & Self::I31_REF_DISCRIMINANT) != 0
387    }
388
389    /// Get the underlying `i31`, if any.
390    #[inline]
391    pub fn as_i31(&self) -> Option<I31> {
392        if self.is_i31() {
393            let val = self.0.get();
394            Some(I31::wrapping_u32(val >> 1))
395        } else {
396            None
397        }
398    }
399
400    /// Get the underlying `i31`, panicking if this is not an `i31`.
401    #[inline]
402    pub fn unwrap_i31(&self) -> I31 {
403        self.as_i31().unwrap()
404    }
405
406    /// Is this `VMGcRef` a `VMExternRef`?
407    #[inline]
408    pub fn is_extern_ref(&self) -> bool {
409        assert!(Self::ONLY_EXTERN_REF_AND_I31);
410        !self.is_i31()
411    }
412}
413
414/// A trait implemented by all objects allocated inside a GC heap.
415///
416/// # Safety
417///
418/// All implementations must:
419///
420/// * Be `repr(C)` or `repr(transparent)`
421///
422/// * Begin with a `VMGcHeader` as their first field
423///
424/// * Not have `Drop` implementations (aka, `std::mem::needs_drop::<Self>()`
425///   should return `false`).
426///
427/// * Be memory safe to transmute to from an arbitrary byte sequence (that is,
428///   it is okay if some bit patterns are invalid with regards to correctness,
429///   so long as these invalid bit patterns cannot lead to memory unsafety).
430pub unsafe trait GcHeapObject: Send + Sync {
431    /// Check whether the GC object with the given header is an instance of
432    /// `Self`.
433    fn is(header: &VMGcHeader) -> bool;
434}
435
436/// A GC reference to a heap object of concrete type `T`.
437///
438/// Create typed GC refs via `VMGcRef::into_typed` and `VMGcRef::as_typed`, as
439/// well as via their unchecked equivalents `VMGcRef::into_typed_unchecked` and
440/// `VMGcRef::as_typed_unchecked`.
441#[derive(Debug, PartialEq, Eq, Hash)]
442#[repr(transparent)]
443pub struct TypedGcRef<T> {
444    gc_ref: VMGcRef,
445    _phantom: std::marker::PhantomData<*mut T>,
446}
447
448impl<T> TypedGcRef<T>
449where
450    T: GcHeapObject,
451{
452    /// Clone this `VMGcRef`, running any GC barriers as necessary.
453    pub fn clone(&self, gc_store: &mut GcStore) -> Self {
454        Self {
455            gc_ref: gc_store.clone_gc_ref(&self.gc_ref),
456            _phantom: std::marker::PhantomData,
457        }
458    }
459
460    /// Explicitly drop this GC reference, running any GC barriers as necessary.
461    pub fn drop(self, gc_store: &mut GcStore) {
462        gc_store.drop_gc_ref(self.gc_ref);
463    }
464
465    /// Copy this GC reference without running the GC's clone barriers.
466    ///
467    /// Prefer calling `clone(&mut GcStore)` instead! This is mostly an internal
468    /// escape hatch for collector implementations.
469    ///
470    /// Failure to run GC barriers when they would otherwise be necessary can
471    /// lead to leaks, panics, and wrong results. It cannot lead to memory
472    /// unsafety, however.
473    pub fn unchecked_copy(&self) -> Self {
474        Self {
475            gc_ref: self.gc_ref.unchecked_copy(),
476            _phantom: std::marker::PhantomData,
477        }
478    }
479}
480
481impl<T> TypedGcRef<T> {
482    /// Get the untyped version of this GC reference.
483    pub fn as_untyped(&self) -> &VMGcRef {
484        &self.gc_ref
485    }
486}