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