1 //! Traits for abstracting over our different garbage collectors.
2 
3 use crate::prelude::*;
4 use crate::runtime::vm::{
5     ExternRefHostDataId, ExternRefHostDataTable, GcHeapObject, SendSyncPtr, TypedGcRef, VMArrayRef,
6     VMExternRef, VMGcHeader, VMGcObjectData, VMGcRef,
7 };
8 use crate::store::Asyncness;
9 use crate::vm::VMMemoryDefinition;
10 use core::ptr::NonNull;
11 use core::slice;
12 use core::{alloc::Layout, any::Any, marker, mem, ops::Range, ptr};
13 use wasmtime_environ::{GcArrayLayout, GcStructLayout, GcTypeLayouts, VMSharedTypeIndex};
14 
15 /// Trait for integrating a garbage collector with the runtime.
16 ///
17 /// This trait is responsible for:
18 ///
19 /// * GC barriers used by runtime code (as opposed to compiled Wasm code)
20 ///
21 /// * Creating and managing GC heaps for individual stores
22 ///
23 /// * Running garbage collection
24 ///
25 /// # Safety
26 ///
27 /// The collector, its GC heaps, and GC barriers when taken together as a whole
28 /// must be safe. Additionally, they must work with the GC barriers emitted into
29 /// compiled Wasm code via the collector's corresponding `GcCompiler`
30 /// implementation. That is, if callers only call safe methods on this trait
31 /// (while pairing it with its associated `GcCompiler`, `GcHeap`, and etc...)
32 /// and uphold all the documented safety invariants of this trait's unsafe
33 /// methods, then it must be impossible for callers to violate memory
34 /// safety. Implementations of this trait may not add new safety invariants, not
35 /// already documented in this trait's interface, that callers need to uphold.
36 pub unsafe trait GcRuntime: 'static + Send + Sync {
37     /// Get this collector's GC type layouts.
layouts(&self) -> &dyn GcTypeLayouts38     fn layouts(&self) -> &dyn GcTypeLayouts;
39 
40     /// Construct a new GC heap.
41     #[cfg(feature = "gc")]
new_gc_heap(&self, engine: &crate::Engine) -> Result<Box<dyn GcHeap>>42     fn new_gc_heap(&self, engine: &crate::Engine) -> Result<Box<dyn GcHeap>>;
43 }
44 
45 /// A heap that manages garbage-collected objects.
46 ///
47 /// Each `wasmtime::Store` is associated with a single `GcHeap`, and a `GcHeap`
48 /// is only ever used with one store at a time, but `GcHeap`s may be reused with
49 /// new stores after its original store is dropped. The `reset` method will be
50 /// called in between each such reuse. (This reuse allows for better integration
51 /// with the pooling allocator).
52 ///
53 /// If a `GcHeap` mapped any memory, its `Drop` implementation should unmap that
54 /// memory.
55 ///
56 /// # Safety
57 ///
58 /// The trait methods below are all safe: implementations of this trait must
59 /// ensure that these methods cannot be misused to create memory unsafety. The
60 /// expectation is that -- given that `VMGcRef` is a newtype over an index --
61 /// implementations perform similar tricks as Wasm linear memory
62 /// implementations. The heap should internally be a contiguous region of memory
63 /// and `VMGcRef` indices into the heap must be bounds checked (explicitly or
64 /// implicitly via virtual memory tricks).
65 ///
66 /// Furthermore, if heap corruption occurs because (for example) a `VMGcRef`
67 /// from a different heap is used with this heap, then that corruption must be
68 /// limited to within this heap. Every heap is a mini sandbox. It follows that
69 /// native pointers should never be written into or read out from the GC heap,
70 /// since that could spread corruption from inside the GC heap out to the native
71 /// host heap. The host data for an `externref`, therefore, is stored in a side
72 /// table (`ExternRefHostDataTable`) and never inside the heap. Only an id
73 /// referencing a slot in that table should ever be written into the GC heap.
74 ///
75 /// These constraints give us great amounts of safety compared to working with
76 /// raw pointers. The worst that could happen is corruption local to heap and a
77 /// panic, or perhaps reading stale heap data from a previous Wasm instance. A
78 /// corrupt `GcHeap` can *never* result in the native host's corruption.
79 ///
80 /// The downside is that we are introducing `heap_base + index` computations and
81 /// bounds checking to access GC memory, adding performance overhead. This is
82 /// deemed to be a worthy trade off. Furthermore, it isn't even a clear cut
83 /// performance degradation since this allows us to use 32-bit "pointers",
84 /// giving us more compact data representations and the improved cache
85 /// utilization that implies.
86 pub unsafe trait GcHeap: 'static + Send + Sync {
87     ////////////////////////////////////////////////////////////////////////////
88     // Life Cycle GC Heap Methods
89 
90     /// Is this GC heap currently attached to a memory?
is_attached(&self) -> bool91     fn is_attached(&self) -> bool;
92 
93     /// Attach this GC heap to a memory.
94     ///
95     /// Once attached, this GC heap can be used with Wasm.
attach(&mut self, memory: crate::vm::Memory)96     fn attach(&mut self, memory: crate::vm::Memory);
97 
98     /// Reset this heap.
99     ///
100     /// Calling this method unassociates this heap with the store that it has
101     /// been associated with, making it available to be associated with a new
102     /// heap.
103     ///
104     /// This should refill free lists, reset bump pointers, and etc... as if
105     /// nothing were allocated in this heap (because nothing is allocated in
106     /// this heap anymore).
107     ///
108     /// This should retain any allocated memory from the global allocator and
109     /// any virtual memory mappings.
detach(&mut self) -> crate::vm::Memory110     fn detach(&mut self) -> crate::vm::Memory;
111 
112     ////////////////////////////////////////////////////////////////////////////
113     // `Any` methods
114 
115     /// Get this heap as an `&Any`.
as_any(&self) -> &dyn Any116     fn as_any(&self) -> &dyn Any;
117 
118     /// Get this heap as an `&mut Any`.
as_any_mut(&mut self) -> &mut dyn Any119     fn as_any_mut(&mut self) -> &mut dyn Any;
120 
121     ////////////////////////////////////////////////////////////////////////////
122     // No-GC Scope Methods
123 
124     /// Enter a no-GC scope.
125     ///
126     /// Calling the `gc` method when we are inside a no-GC scope should panic.
127     ///
128     /// We can enter multiple, nested no-GC scopes and this method should
129     /// account for that.
enter_no_gc_scope(&mut self)130     fn enter_no_gc_scope(&mut self);
131 
132     /// Exit a no-GC scope.
133     ///
134     /// Dual to `enter_no_gc_scope`.
exit_no_gc_scope(&mut self)135     fn exit_no_gc_scope(&mut self);
136 
137     ////////////////////////////////////////////////////////////////////////////
138     // GC Barriers
139 
140     /// Read barrier called every time the runtime clones a GC reference.
141     ///
142     /// Callers should pass a valid `VMGcRef` that belongs to the given
143     /// heap. Failure to do so is memory safe, but may result in general
144     /// failures such as panics or incorrect results.
clone_gc_ref(&mut self, gc_ref: &VMGcRef) -> VMGcRef145     fn clone_gc_ref(&mut self, gc_ref: &VMGcRef) -> VMGcRef;
146 
147     /// Write barrier called whenever the runtime is nulling out a GC reference.
148     ///
149     /// Default implemented in terms of the `write_gc_ref` barrier.
150     ///
151     /// If an `externref` is reclaimed, then its associated entry in the
152     /// `host_data_table` should be removed.
153     ///
154     /// Callers should pass a valid `VMGcRef` that belongs to the given
155     /// heap. Failure to do so is memory safe, but may result in general
156     /// failures such as panics or incorrect results.
157     ///
158     /// The given `gc_ref` should not be used again.
drop_gc_ref(&mut self, host_data_table: &mut ExternRefHostDataTable, gc_ref: VMGcRef)159     fn drop_gc_ref(&mut self, host_data_table: &mut ExternRefHostDataTable, gc_ref: VMGcRef) {
160         let mut dest = Some(gc_ref);
161         self.write_gc_ref(host_data_table, &mut dest, None);
162     }
163 
164     /// Write barrier called every time the runtime overwrites a GC reference.
165     ///
166     /// The `source` is a borrowed GC reference, and should not have been cloned
167     /// already for this write operation. This allows implementations to fuse
168     /// the `source`'s read barrier into this write barrier.
169     ///
170     /// If an `externref` is reclaimed, then its associated entry in the
171     /// `host_data_table` should be removed.
172     ///
173     /// Callers should pass a valid `VMGcRef` that belongs to the given heap for
174     /// both the `source` and `destination`. Failure to do so is memory safe,
175     /// but may result in general failures such as panics or incorrect results.
write_gc_ref( &mut self, host_data_table: &mut ExternRefHostDataTable, destination: &mut Option<VMGcRef>, source: Option<&VMGcRef>, )176     fn write_gc_ref(
177         &mut self,
178         host_data_table: &mut ExternRefHostDataTable,
179         destination: &mut Option<VMGcRef>,
180         source: Option<&VMGcRef>,
181     );
182 
183     /// Read barrier called whenever a GC reference is passed from the runtime
184     /// to Wasm: an argument to a host-to-Wasm call, or a return from a
185     /// Wasm-to-host call.
186     ///
187     /// Callers should pass a valid `VMGcRef` that belongs to the given
188     /// heap. Failure to do so is memory safe, but may result in general
189     /// failures such as panics or incorrect results.
expose_gc_ref_to_wasm(&mut self, gc_ref: VMGcRef)190     fn expose_gc_ref_to_wasm(&mut self, gc_ref: VMGcRef);
191 
192     ////////////////////////////////////////////////////////////////////////////
193     // `externref` Methods
194 
195     /// Allocate a `VMExternRef` with space for host data described by the given
196     /// layout.
197     ///
198     /// Return values:
199     ///
200     /// * `Ok(Ok(_))`: The allocation was successful.
201     ///
202     /// * `Ok(Err(n))`: There is currently not enough available space for this
203     ///   allocation of size `n`. The caller should either grow the heap or run
204     ///   a collection to reclaim space, and then try allocating again.
205     ///
206     /// * `Err(_)`: The collector cannot satisfy this allocation request, and
207     ///   would not be able to even after the caller were to trigger a
208     ///   collection. This could be because, for example, the requested
209     ///   allocation is larger than this collector's implementation limit for
210     ///   object size.
alloc_externref( &mut self, host_data: ExternRefHostDataId, ) -> Result<Result<VMExternRef, u64>>211     fn alloc_externref(
212         &mut self,
213         host_data: ExternRefHostDataId,
214     ) -> Result<Result<VMExternRef, u64>>;
215 
216     /// Get the host data ID associated with the given `externref`.
217     ///
218     /// Callers should pass a valid `externref` that belongs to the given
219     /// heap. Failure to do so is memory safe, but may result in general
220     /// failures such as panics or incorrect results.
externref_host_data(&self, externref: &VMExternRef) -> ExternRefHostDataId221     fn externref_host_data(&self, externref: &VMExternRef) -> ExternRefHostDataId;
222 
223     ////////////////////////////////////////////////////////////////////////////
224     // Struct, array, and general GC object methods
225 
226     /// Get the header of the object that `gc_ref` points to.
header(&self, gc_ref: &VMGcRef) -> &VMGcHeader227     fn header(&self, gc_ref: &VMGcRef) -> &VMGcHeader;
228 
229     /// Get the header of the object that `gc_ref` points to.
header_mut(&mut self, gc_ref: &VMGcRef) -> &mut VMGcHeader230     fn header_mut(&mut self, gc_ref: &VMGcRef) -> &mut VMGcHeader;
231 
232     /// Get the size (in bytes) of the object referenced by `gc_ref`.
233     ///
234     /// # Panics
235     ///
236     /// Panics on out of bounds or if the `gc_ref` is an `i31ref`.
object_size(&self, gc_ref: &VMGcRef) -> usize237     fn object_size(&self, gc_ref: &VMGcRef) -> usize;
238 
239     /// Allocate a raw, uninitialized GC-managed object with the given header
240     /// and layout.
241     ///
242     /// The object's fields and elements are left uninitialized. It is the
243     /// caller's responsibility to initialize them before exposing the struct to
244     /// Wasm or triggering a GC.
245     ///
246     /// The header's described type and layout must match *for this
247     /// collector*. That is, if this collector adds an extra header word to all
248     /// objects, the given layout must already include space for that header
249     /// word. Therefore, this method is effectively only usable with layouts
250     /// derived from a `Gc{Struct,Array}Layout` returned by this collector.
251     ///
252     /// Failure to uphold any of the above is memory safe, but may result in
253     /// general failures such as panics or incorrect results.
254     ///
255     /// Return values:
256     ///
257     /// * `Ok(Ok(_))`: The allocation was successful.
258     ///
259     /// * `Ok(Err(n))`: There is currently not enough available space for this
260     ///   allocation of size `n`. The caller should either grow the heap or run
261     ///   a collection to reclaim space, and then try allocating again.
262     ///
263     /// * `Err(_)`: The collector cannot satisfy this allocation request, and
264     ///   would not be able to even after the caller were to trigger a
265     ///   collection. This could be because, for example, the requested
266     ///   alignment is larger than this collector's implementation limit.
alloc_raw(&mut self, header: VMGcHeader, layout: Layout) -> Result<Result<VMGcRef, u64>>267     fn alloc_raw(&mut self, header: VMGcHeader, layout: Layout) -> Result<Result<VMGcRef, u64>>;
268 
269     /// Allocate a GC-managed struct of the given type and layout.
270     ///
271     /// The struct's fields are left uninitialized. It is the caller's
272     /// responsibility to initialize them before exposing the struct to Wasm or
273     /// triggering a GC.
274     ///
275     /// The `ty` and `layout` must match.
276     ///
277     /// Failure to do either of the above is memory safe, but may result in
278     /// general failures such as panics or incorrect results.
279     ///
280     /// Return values:
281     ///
282     /// * `Ok(Ok(_))`: The allocation was successful.
283     ///
284     /// * `Ok(Err(n))`: There is currently not enough available space for this
285     ///   allocation of size `n`. The caller should either grow the heap or run
286     ///   a collection to reclaim space, and then try allocating again.
287     ///
288     /// * `Err(_)`: The collector cannot satisfy this allocation request, and
289     ///   would not be able to even after the caller were to trigger a
290     ///   collection. This could be because, for example, the requested
291     ///   allocation is larger than this collector's implementation limit for
292     ///   object size.
alloc_uninit_struct_or_exn( &mut self, ty: VMSharedTypeIndex, layout: &GcStructLayout, ) -> Result<Result<VMGcRef, u64>>293     fn alloc_uninit_struct_or_exn(
294         &mut self,
295         ty: VMSharedTypeIndex,
296         layout: &GcStructLayout,
297     ) -> Result<Result<VMGcRef, u64>>;
298 
299     /// Deallocate an uninitialized, GC-managed struct or exception.
300     ///
301     /// This is useful for if initialization of the struct's fields fails, so
302     /// that the struct's allocation can be eagerly reclaimed, and so that the
303     /// collector doesn't attempt to treat any of the uninitialized fields as
304     /// valid GC references, or something like that.
dealloc_uninit_struct_or_exn(&mut self, structref: VMGcRef)305     fn dealloc_uninit_struct_or_exn(&mut self, structref: VMGcRef);
306 
307     /// * `Ok(Ok(_))`: The allocation was successful.
308     ///
309     /// * `Ok(Err(n))`: There is currently not enough available space for this
310     ///   allocation of size `n`. The caller should either grow the heap or run
311     ///   a collection to reclaim space, and then try allocating again.
312     ///
313     /// * `Err(_)`: The collector cannot satisfy this allocation request, and
314     ///   would not be able to even after the caller were to trigger a
315     ///   collection. This could be because, for example, the requested
316     ///   allocation is larger than this collector's implementation limit for
317     ///   object size.
alloc_uninit_array( &mut self, ty: VMSharedTypeIndex, len: u32, layout: &GcArrayLayout, ) -> Result<Result<VMArrayRef, u64>>318     fn alloc_uninit_array(
319         &mut self,
320         ty: VMSharedTypeIndex,
321         len: u32,
322         layout: &GcArrayLayout,
323     ) -> Result<Result<VMArrayRef, u64>>;
324 
325     /// Deallocate an uninitialized, GC-managed array.
326     ///
327     /// This is useful for if initialization of the array's fields fails, so
328     /// that the array's allocation can be eagerly reclaimed, and so that the
329     /// collector doesn't attempt to treat any of the uninitialized fields as
330     /// valid GC references, or something like that.
dealloc_uninit_array(&mut self, arrayref: VMArrayRef)331     fn dealloc_uninit_array(&mut self, arrayref: VMArrayRef);
332 
333     /// Get the length of the given array.
334     ///
335     /// Panics on out-of-bounds accesses.
336     ///
337     /// The given `arrayref` should be valid and of the given size. Failure to
338     /// do so is memory safe, but may result in general failures such as panics
339     /// or incorrect results.
array_len(&self, arrayref: &VMArrayRef) -> u32340     fn array_len(&self, arrayref: &VMArrayRef) -> u32;
341 
342     ////////////////////////////////////////////////////////////////////////////
343     // Garbage Collection Methods
344 
345     /// Start a new garbage collection process.
346     ///
347     /// The given `roots` are GC roots and should not be collected (nor anything
348     /// transitively reachable from them).
349     ///
350     /// Upon reclaiming an `externref`, its associated entry in the
351     /// `host_data_table` is removed.
352     ///
353     /// Callers should pass valid GC roots that belongs to this heap, and the
354     /// host data table associated with this heap's `externref`s. Failure to do
355     /// so is memory safe, but may result in general failures such as panics or
356     /// incorrect results.
357     ///
358     /// This method should panic if we are in a no-GC scope.
gc<'a>( &'a mut self, roots: GcRootsIter<'a>, host_data_table: &'a mut ExternRefHostDataTable, ) -> Box<dyn GarbageCollection<'a> + 'a>359     fn gc<'a>(
360         &'a mut self,
361         roots: GcRootsIter<'a>,
362         host_data_table: &'a mut ExternRefHostDataTable,
363     ) -> Box<dyn GarbageCollection<'a> + 'a>;
364 
365     ////////////////////////////////////////////////////////////////////////////
366     // JIT-Code Interaction Methods
367 
368     /// Get the pointer that will be stored in the `VMContext::gc_heap_data`
369     /// field and be accessible from JIT code via collaboration with the
370     /// corresponding `GcCompiler` trait.
371     ///
372     /// # Safety
373     ///
374     /// The returned pointer, if any, must remain valid as long as `self` is not
375     /// dropped.
vmctx_gc_heap_data(&self) -> NonNull<u8>376     unsafe fn vmctx_gc_heap_data(&self) -> NonNull<u8>;
377 
378     ////////////////////////////////////////////////////////////////////////////
379     // Accessors for the raw bytes of the GC heap
380 
381     /// Take the underlying memory storage out of this GC heap.
382     ///
383     /// # Panics
384     ///
385     /// If this GC heap is used while the memory is taken then a panic will
386     /// occur. This will also panic if the memory is already taken.
take_memory(&mut self) -> crate::vm::Memory387     fn take_memory(&mut self) -> crate::vm::Memory;
388 
389     /// Replace this GC heap's underlying memory storage.
390     ///
391     /// # Safety
392     ///
393     /// The `memory` must have been taken via `take_memory` and the GC heap must
394     /// not have been used at all since the memory was taken. The memory must be
395     /// the same size or larger than it was.
replace_memory(&mut self, memory: crate::vm::Memory, delta_bytes_grown: u64)396     unsafe fn replace_memory(&mut self, memory: crate::vm::Memory, delta_bytes_grown: u64);
397 
398     /// Get a raw `VMMemoryDefinition` for this heap's underlying memory storage.
399     ///
400     /// If/when exposing this `VMMemoryDefinition` to Wasm, it is your
401     /// responsibility to ensure that you do not do that in such a way as to
402     /// violate Rust's borrowing rules (e.g. make sure there is no active
403     /// `heap_slice_mut()` call at the same time) and that if this GC heap is
404     /// resized (and its base potentially moves) then that Wasm gets a new,
405     /// updated `VMMemoryDefinition` record.
vmmemory(&self) -> VMMemoryDefinition406     fn vmmemory(&self) -> VMMemoryDefinition;
407 
408     /// Get a slice of the raw bytes of the GC heap.
409     #[inline]
heap_slice(&self) -> &[u8]410     fn heap_slice(&self) -> &[u8] {
411         let vmmemory = self.vmmemory();
412         let ptr = vmmemory.base.as_ptr().cast_const();
413         let len = vmmemory.current_length();
414         unsafe { slice::from_raw_parts(ptr, len) }
415     }
416 
417     /// Get a mutable slice of the raw bytes of the GC heap.
418     #[inline]
heap_slice_mut(&mut self) -> &mut [u8]419     fn heap_slice_mut(&mut self) -> &mut [u8] {
420         let vmmemory = self.vmmemory();
421         let ptr = vmmemory.base.as_ptr();
422         let len = vmmemory.current_length();
423         unsafe { slice::from_raw_parts_mut(ptr, len) }
424     }
425 
426     ////////////////////////////////////////////////////////////////////////////
427     // Provided helper methods.
428 
429     /// Index into this heap and get a shared reference to the `T` that `gc_ref`
430     /// points to.
431     ///
432     /// # Panics
433     ///
434     /// Panics on out of bounds or if the `gc_ref` is an `i31ref`.
435     #[inline]
index<T>(&self, gc_ref: &TypedGcRef<T>) -> &T where Self: Sized, T: GcHeapObject,436     fn index<T>(&self, gc_ref: &TypedGcRef<T>) -> &T
437     where
438         Self: Sized,
439         T: GcHeapObject,
440     {
441         assert!(!mem::needs_drop::<T>());
442         let gc_ref = gc_ref.as_untyped();
443         let start = gc_ref.as_heap_index().unwrap().get();
444         let start = usize::try_from(start).unwrap();
445         let len = mem::size_of::<T>();
446         let slice = &self.heap_slice()[start..][..len];
447         unsafe { &*(slice.as_ptr().cast::<T>()) }
448     }
449 
450     /// Index into this heap and get an exclusive reference to the `T` that
451     /// `gc_ref` points to.
452     ///
453     /// # Panics
454     ///
455     /// Panics on out of bounds or if the `gc_ref` is an `i31ref`.
456     #[inline]
index_mut<T>(&mut self, gc_ref: &TypedGcRef<T>) -> &mut T where Self: Sized, T: GcHeapObject,457     fn index_mut<T>(&mut self, gc_ref: &TypedGcRef<T>) -> &mut T
458     where
459         Self: Sized,
460         T: GcHeapObject,
461     {
462         assert!(!mem::needs_drop::<T>());
463         let gc_ref = gc_ref.as_untyped();
464         let start = gc_ref.as_heap_index().unwrap().get();
465         let start = usize::try_from(start).unwrap();
466         let len = mem::size_of::<T>();
467         let slice = &mut self.heap_slice_mut()[start..][..len];
468         unsafe { &mut *(slice.as_mut_ptr().cast::<T>()) }
469     }
470 
471     /// Get the range of bytes that the given object occupies in the heap.
472     ///
473     /// # Panics
474     ///
475     /// Panics on out of bounds or if the `gc_ref` is an `i31ref`.
object_range(&self, gc_ref: &VMGcRef) -> Range<usize>476     fn object_range(&self, gc_ref: &VMGcRef) -> Range<usize> {
477         let start = gc_ref.as_heap_index().unwrap().get();
478         let start = usize::try_from(start).unwrap();
479         let size = self.object_size(gc_ref);
480         let end = start.checked_add(size).unwrap();
481         start..end
482     }
483 
484     /// Get a mutable borrow of the given object's data.
485     ///
486     /// # Panics
487     ///
488     /// Panics on out-of-bounds accesses or if the `gc_ref` is an `i31ref`.
gc_object_data(&self, gc_ref: &VMGcRef) -> &VMGcObjectData489     fn gc_object_data(&self, gc_ref: &VMGcRef) -> &VMGcObjectData {
490         let range = self.object_range(gc_ref);
491         let data = &self.heap_slice()[range];
492         data.into()
493     }
494 
495     /// Get a mutable borrow of the given object's data.
496     ///
497     /// # Panics
498     ///
499     /// Panics on out-of-bounds accesses or if the `gc_ref` is an `i31ref`.
gc_object_data_mut(&mut self, gc_ref: &VMGcRef) -> &mut VMGcObjectData500     fn gc_object_data_mut(&mut self, gc_ref: &VMGcRef) -> &mut VMGcObjectData {
501         let range = self.object_range(gc_ref);
502         let data = &mut self.heap_slice_mut()[range];
503         data.into()
504     }
505 
506     /// Get a pair of mutable borrows of the given objects' data.
507     ///
508     /// # Panics
509     ///
510     /// Panics if `a == b` or on out-of-bounds accesses or if either GC ref is
511     /// an `i31ref`.
gc_object_data_pair( &mut self, a: &VMGcRef, b: &VMGcRef, ) -> (&mut VMGcObjectData, &mut VMGcObjectData)512     fn gc_object_data_pair(
513         &mut self,
514         a: &VMGcRef,
515         b: &VMGcRef,
516     ) -> (&mut VMGcObjectData, &mut VMGcObjectData) {
517         assert_ne!(a, b);
518 
519         let a_range = self.object_range(a);
520         let b_range = self.object_range(b);
521 
522         // Assert that the two objects do not overlap.
523         assert!(a_range.start <= a_range.end);
524         assert!(b_range.start <= b_range.end);
525         assert!(a_range.end <= b_range.start || b_range.end <= a_range.start);
526 
527         let (a_data, b_data) = if a_range.start < b_range.start {
528             let (a_half, b_half) = self.heap_slice_mut().split_at_mut(b_range.start);
529             let b_len = b_range.end - b_range.start;
530             (&mut a_half[a_range], &mut b_half[..b_len])
531         } else {
532             let (b_half, a_half) = self.heap_slice_mut().split_at_mut(a_range.start);
533             let a_len = a_range.end - a_range.start;
534             (&mut a_half[..a_len], &mut b_half[b_range])
535         };
536 
537         (a_data.into(), b_data.into())
538     }
539 }
540 
541 /// A list of GC roots.
542 ///
543 /// This is effectively a builder for a `GcRootsIter` that will be given to a GC
544 /// heap when it is time to perform garbage collection.
545 #[derive(Default)]
546 pub struct GcRootsList(Vec<RawGcRoot>);
547 
548 // Ideally these `*mut`s would be `&mut`s and we wouldn't need as much of this
549 // machinery around `GcRootsList`, `RawGcRoot`, `GcRoot`, and `GcRootIter` but
550 // if we try that then we run into two different kinds of lifetime issues:
551 //
552 // 1. When collecting the various roots from a `&mut StoreOpaque`, we borrow
553 //    from `self` to push new GC roots onto the roots list. But then we want to
554 //    call helper methods like `self.for_each_global(...)`, but we can't because
555 //    there are active borrows of `self` preventing it.
556 //
557 // 2. We want to reuse the roots list and its backing storage across GCs, rather
558 //    than reallocate on every GC. But the only place for the roots list to live
559 //    such that it is easily reusable across GCs is in the store itself. But the
560 //    contents of the roots list (when it is non-empty, during GCs) borrow from
561 //    the store, which creates self-references.
562 #[derive(Clone, Copy, Debug)]
563 #[cfg_attr(
564     not(feature = "gc"),
565     expect(
566         dead_code,
567         reason = "not worth it at this time to #[cfg] away these variants",
568     )
569 )]
570 enum RawGcRoot {
571     Stack(SendSyncPtr<u32>),
572     NonStack(SendSyncPtr<VMGcRef>),
573 }
574 
575 #[cfg(feature = "gc")]
576 impl GcRootsList {
577     /// Add a GC root that is inside a Wasm stack frame to this list.
578     #[inline]
add_wasm_stack_root(&mut self, ptr_to_root: SendSyncPtr<u32>)579     pub unsafe fn add_wasm_stack_root(&mut self, ptr_to_root: SendSyncPtr<u32>) {
580         unsafe {
581             log::trace!(
582                 "Adding Wasm stack root: {:#p} -> {:#p}",
583                 ptr_to_root,
584                 VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).unwrap()
585             );
586             debug_assert!(VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).is_some());
587         }
588         self.0.push(RawGcRoot::Stack(ptr_to_root));
589     }
590 
591     /// Add a GC root to this list.
592     #[inline]
add_root(&mut self, ptr_to_root: SendSyncPtr<VMGcRef>, why: &str)593     pub unsafe fn add_root(&mut self, ptr_to_root: SendSyncPtr<VMGcRef>, why: &str) {
594         unsafe {
595             log::trace!(
596                 "Adding non-stack root: {why}: {:#p}",
597                 ptr_to_root.as_ref().unchecked_copy()
598             );
599         }
600         self.0.push(RawGcRoot::NonStack(ptr_to_root))
601     }
602 
603     /// Get an iterator over all roots in this list.
604     ///
605     /// # Safety
606     ///
607     /// Callers must ensure that all the pointers to GC roots that have been
608     /// added to this list are valid for the duration of the `'a` lifetime.
609     #[inline]
iter<'a>(&'a mut self) -> GcRootsIter<'a>610     pub unsafe fn iter<'a>(&'a mut self) -> GcRootsIter<'a> {
611         GcRootsIter {
612             list: self,
613             index: 0,
614         }
615     }
616 
617     /// Is this list empty?
is_empty(&self) -> bool618     pub fn is_empty(&self) -> bool {
619         self.0.is_empty()
620     }
621 
622     /// Clear this GC roots list.
623     #[inline]
clear(&mut self)624     pub fn clear(&mut self) {
625         self.0.clear();
626     }
627 }
628 
629 /// An iterator over all the roots in a `GcRootsList`.
630 pub struct GcRootsIter<'a> {
631     list: &'a mut GcRootsList,
632     index: usize,
633 }
634 
635 impl<'a> Iterator for GcRootsIter<'a> {
636     type Item = GcRoot<'a>;
637 
638     #[inline]
next(&mut self) -> Option<Self::Item>639     fn next(&mut self) -> Option<Self::Item> {
640         let root = GcRoot {
641             raw: self.list.0.get(self.index).copied()?,
642             _phantom: marker::PhantomData,
643         };
644         self.index += 1;
645         Some(root)
646     }
647 }
648 
649 /// A GC root.
650 ///
651 /// This is, effectively, a mutable reference to a `VMGcRef`.
652 ///
653 /// Collector implementations should update the `VMGcRef` if they move the
654 /// `VMGcRef`'s referent during the course of a GC.
655 #[derive(Debug)]
656 pub struct GcRoot<'a> {
657     raw: RawGcRoot,
658     _phantom: marker::PhantomData<&'a mut VMGcRef>,
659 }
660 
661 impl GcRoot<'_> {
662     /// Is this root from inside a Wasm stack frame?
663     #[inline]
is_on_wasm_stack(&self) -> bool664     pub fn is_on_wasm_stack(&self) -> bool {
665         matches!(self.raw, RawGcRoot::Stack(_))
666     }
667 
668     /// Get this GC root.
669     ///
670     /// Does NOT run GC barriers.
671     #[inline]
get(&self) -> VMGcRef672     pub fn get(&self) -> VMGcRef {
673         match self.raw {
674             RawGcRoot::NonStack(ptr) => unsafe { ptr::read(ptr.as_ptr()) },
675             RawGcRoot::Stack(ptr) => unsafe {
676                 let raw: u32 = ptr::read(ptr.as_ptr());
677                 VMGcRef::from_raw_u32(raw).expect("non-null")
678             },
679         }
680     }
681 
682     /// Set this GC root.
683     ///
684     /// Does NOT run GC barriers.
685     ///
686     /// Collector implementations should use this method to update GC root
687     /// pointers after the collector moves the GC object that the root is
688     /// referencing.
set(&mut self, new_ref: VMGcRef)689     pub fn set(&mut self, new_ref: VMGcRef) {
690         match self.raw {
691             RawGcRoot::NonStack(ptr) => unsafe {
692                 ptr::write(ptr.as_ptr(), new_ref);
693             },
694             RawGcRoot::Stack(ptr) => unsafe {
695                 ptr::write(ptr.as_ptr(), new_ref.as_raw_u32());
696             },
697         }
698     }
699 }
700 
701 /// A garbage collection process.
702 ///
703 /// Implementations define the `collect_increment` method, and then consumers
704 /// can either use
705 ///
706 /// * `GarbageCollection::collect` for synchronous code, or
707 ///
708 /// * `collect_async(Box<dyn GarbageCollection>)` for async code.
709 ///
710 /// When using fuel and/or epochs, consumers can also use `collect_increment`
711 /// directly and choose to abandon further execution in this GC's heap's whole
712 /// store if the GC is taking too long to complete.
713 pub trait GarbageCollection<'a>: Send + Sync {
714     /// Perform an incremental slice of this garbage collection process.
715     ///
716     /// Upon completion of the slice, a `GcProgress` is returned which informs
717     /// the caller whether to continue driving this GC process forward and
718     /// executing more slices (`GcProgress::Continue`) or whether the GC process
719     /// has finished (`GcProgress::Complete`).
720     ///
721     /// The mutator does *not* run in between increments. This method exists
722     /// solely to allow cooperative yielding
collect_increment(&mut self) -> GcProgress723     fn collect_increment(&mut self) -> GcProgress;
724 
725     /// Run this GC process to completion.
726     ///
727     /// Keeps calling `collect_increment` in a loop until the GC process is
728     /// complete.
collect(&mut self)729     fn collect(&mut self) {
730         loop {
731             match self.collect_increment() {
732                 GcProgress::Continue => continue,
733                 GcProgress::Complete => return,
734             }
735         }
736     }
737 }
738 
739 /// The result of doing an incremental amount of GC.
740 pub enum GcProgress {
741     /// There is still more work to do.
742     Continue,
743     /// The GC is complete.
744     Complete,
745 }
746 
747 /// Asynchronously run the given garbage collection process to completion,
748 /// cooperatively yielding back to the event loop after each increment of work.
collect_async<'a>( mut collection: Box<dyn GarbageCollection<'a> + 'a>, asyncness: Asyncness, )749 pub async fn collect_async<'a>(
750     mut collection: Box<dyn GarbageCollection<'a> + 'a>,
751     asyncness: Asyncness,
752 ) {
753     loop {
754         match collection.collect_increment() {
755             GcProgress::Continue => {
756                 if asyncness != Asyncness::No {
757                     #[cfg(feature = "async")]
758                     crate::runtime::vm::Yield::new().await
759                 }
760             }
761             GcProgress::Complete => return,
762         }
763     }
764 }
765 
766 #[cfg(all(test, feature = "async"))]
767 mod collect_async_tests {
768     use super::*;
769 
770     #[test]
is_send_and_sync()771     fn is_send_and_sync() {
772         fn _assert_send_sync<T: Send + Sync>(_: T) {}
773 
774         fn _foo<'a>(collection: Box<dyn GarbageCollection<'a>>) {
775             _assert_send_sync(collect_async(collection, Asyncness::Yes));
776         }
777     }
778 }
779