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}