1#[cfg(feature = "gc")]
2mod enabled;
3#[cfg(feature = "gc")]
4pub use enabled::*;
5
6#[cfg(not(feature = "gc"))]
7mod disabled;
8#[cfg(not(feature = "gc"))]
9pub use disabled::*;
10
11mod data;
12mod func_ref;
13mod gc_ref;
14mod gc_runtime;
15mod host_data;
16mod i31;
17
18pub use data::*;
19pub use func_ref::*;
20pub use gc_ref::*;
21pub use gc_runtime::*;
22pub use host_data::*;
23pub use i31::*;
24
25use crate::hash_map::HashMap;
26use crate::module::ModuleRegistry;
27use crate::prelude::*;
28use crate::runtime::vm::{GcHeapAllocationIndex, VMMemoryDefinition};
29use crate::store::Asyncness;
30use crate::type_registry::RegisteredType;
31use core::any::Any;
32use core::mem::MaybeUninit;
33use core::{alloc::Layout, num::NonZeroU32};
34use wasmtime_environ::{GcArrayLayout, GcLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex};
35
36pub struct GcStore {
44 pub allocation_index: GcHeapAllocationIndex,
47
48 pub gc_heap: Box<dyn GcHeap>,
50
51 pub host_data_table: ExternRefHostDataTable,
53
54 pub func_ref_table: FuncRefTable,
56
57 pub last_post_gc_allocated_bytes: Option<usize>,
61
62 #[cfg(gc_zeal)]
67 gc_zeal_alloc_counter: Option<NonZeroU32>,
68
69 #[cfg(gc_zeal)]
71 gc_zeal_alloc_counter_init: Option<NonZeroU32>,
72}
73
74pub type StoreGcHostAllocTypes = HashMap<VMSharedTypeIndex, (RegisteredType, Option<TraceInfo>)>;
77
78pub struct GcStoreTraceState<'a> {
81 pub host_data_table: &'a mut ExternRefHostDataTable,
84 pub modules: &'a ModuleRegistry,
86 pub gc_host_alloc_types: &'a StoreGcHostAllocTypes,
88}
89
90impl GcStore {
91 pub fn new(
93 allocation_index: GcHeapAllocationIndex,
94 gc_heap: Box<dyn GcHeap>,
95 gc_zeal_alloc_counter: Option<NonZeroU32>,
96 ) -> Self {
97 let host_data_table = ExternRefHostDataTable::default();
98 let func_ref_table = FuncRefTable::default();
99
100 let _ = &gc_zeal_alloc_counter;
101
102 Self {
103 allocation_index,
104 gc_heap,
105 host_data_table,
106 func_ref_table,
107 last_post_gc_allocated_bytes: None,
108 #[cfg(gc_zeal)]
109 gc_zeal_alloc_counter,
110 #[cfg(gc_zeal)]
111 gc_zeal_alloc_counter_init: gc_zeal_alloc_counter,
112 }
113 }
114
115 pub fn vmmemory_definition(&self) -> VMMemoryDefinition {
117 self.gc_heap.vmmemory()
118 }
119
120 pub fn gc_heap_capacity(&self) -> usize {
122 self.gc_heap.heap_slice().len()
123 }
124
125 pub async fn gc(
127 &mut self,
128 asyncness: Asyncness,
129 roots: GcRootsIter<'_>,
130 modules: &ModuleRegistry,
131 gc_host_alloc_types: &StoreGcHostAllocTypes,
132 yield_fn: impl AsyncFn(),
133 ) -> Result<()> {
134 let mut trace_state = GcStoreTraceState {
135 host_data_table: &mut self.host_data_table,
136 modules,
137 gc_host_alloc_types,
138 };
139 let collection = self.gc_heap.gc(roots, &mut trace_state);
140 collect_async(collection, asyncness, yield_fn).await?;
141 self.last_post_gc_allocated_bytes = Some({
142 let size = self.gc_heap.allocated_bytes();
143 log::trace!("After collection, GC heap's allocated bytes = {size:#x} bytes");
144 size
145 });
146 Ok(())
147 }
148
149 pub fn kind(&self, gc_ref: &VMGcRef) -> Result<VMGcKind> {
151 debug_assert!(!gc_ref.is_i31());
152 Ok(self.header(gc_ref)?.kind())
153 }
154
155 pub fn header(&self, gc_ref: &VMGcRef) -> Result<&VMGcHeader> {
157 debug_assert!(!gc_ref.is_i31());
158 self.gc_heap.header(gc_ref)
159 }
160
161 pub fn clone_gc_ref(&mut self, gc_ref: &VMGcRef) -> VMGcRef {
163 if gc_ref.is_i31() {
164 gc_ref.copy_i31()
165 } else {
166 self.gc_heap.clone_gc_ref(gc_ref)
167 }
168 }
169
170 pub fn init_gc_ref(
173 &mut self,
174 destination: &mut MaybeUninit<Option<VMGcRef>>,
175 source: Option<&VMGcRef>,
176 ) -> Result<()> {
177 let destination = destination.write(None);
180 self.write_gc_ref(destination, source)
181 }
182
183 pub(crate) fn needs_init_barrier(gc_ref: Option<&VMGcRef>) -> bool {
186 assert!(cfg!(feature = "gc") || gc_ref.is_none());
187 gc_ref.is_some_and(|r| !r.is_i31())
188 }
189
190 pub(crate) fn needs_write_barrier(
193 dest: &mut Option<VMGcRef>,
194 gc_ref: Option<&VMGcRef>,
195 ) -> bool {
196 assert!(cfg!(feature = "gc") || gc_ref.is_none());
197 assert!(cfg!(feature = "gc") || dest.is_none());
198 dest.as_ref().is_some_and(|r| !r.is_i31()) || gc_ref.is_some_and(|r| !r.is_i31())
199 }
200
201 pub(crate) fn write_gc_ref_optional_store(
209 store: Option<&mut Self>,
210 dest: &mut Option<VMGcRef>,
211 gc_ref: Option<&VMGcRef>,
212 ) -> Result<()> {
213 if Self::needs_write_barrier(dest, gc_ref) {
214 store.unwrap().write_gc_ref(dest, gc_ref)
215 } else {
216 *dest = gc_ref.map(|r| r.copy_i31());
217 Ok(())
218 }
219 }
220
221 pub fn write_gc_ref(
224 &mut self,
225 destination: &mut Option<VMGcRef>,
226 source: Option<&VMGcRef>,
227 ) -> Result<()> {
228 if Self::needs_write_barrier(destination, source) {
232 self.gc_heap.write_gc_ref(destination, source)?;
233 } else {
234 *destination = source.map(|s| s.copy_i31());
235 }
236 Ok(())
237 }
238
239 pub fn drop_gc_ref(&mut self, gc_ref: VMGcRef) {
241 if !gc_ref.is_i31() {
242 self.gc_heap.drop_gc_ref(gc_ref);
243 }
244 }
245
246 #[must_use]
251 pub fn expose_gc_ref_to_wasm(&mut self, gc_ref: VMGcRef) -> Result<NonZeroU32> {
252 let raw = gc_ref.as_raw_non_zero_u32();
253 if !gc_ref.is_i31() {
254 log::trace!("exposing GC ref to Wasm: {gc_ref:p}");
255 self.gc_heap.expose_gc_ref_to_wasm(gc_ref)?;
256 }
257 Ok(raw)
258 }
259
260 pub fn alloc_externref(
272 &mut self,
273 value: Box<dyn Any + Send + Sync>,
274 ) -> Result<Result<VMExternRef, (Box<dyn Any + Send + Sync>, u64)>> {
275 let host_data_id = self.host_data_table.alloc(value);
276 match self.gc_heap.alloc_externref(host_data_id)? {
277 Ok(x) => Ok(Ok(x)),
278 Err(n) => Ok(Err((self.host_data_table.dealloc(host_data_id)?, n))),
279 }
280 }
281
282 pub fn externref_host_data(&self, externref: &VMExternRef) -> Result<&(dyn Any + Send + Sync)> {
288 let host_data_id = self.gc_heap.externref_host_data(externref)?;
289 self.host_data_table.get(host_data_id)
290 }
291
292 pub fn externref_host_data_mut(
298 &mut self,
299 externref: &VMExternRef,
300 ) -> Result<&mut (dyn Any + Send + Sync)> {
301 let host_data_id = self.gc_heap.externref_host_data(externref)?;
302 self.host_data_table.get_mut(host_data_id)
303 }
304
305 pub fn alloc_raw(
307 &mut self,
308 header: VMGcHeader,
309 layout: Layout,
310 ) -> Result<Result<VMGcRef, u64>> {
311 #[cfg(gc_zeal)]
314 if let Some(counter) = self.gc_zeal_alloc_counter.take() {
315 match NonZeroU32::new(counter.get() - 1) {
316 Some(c) => self.gc_zeal_alloc_counter = Some(c),
317 None => {
318 log::trace!("gc_zeal: allocation counter reached zero, forcing GC");
319 self.gc_zeal_alloc_counter = self.gc_zeal_alloc_counter_init;
320 return Ok(Err(0));
321 }
322 }
323 }
324
325 self.gc_heap.alloc_raw(header, layout)
326 }
327
328 pub fn alloc_uninit_struct(
335 &mut self,
336 ty: VMSharedTypeIndex,
337 layout: &GcStructLayout,
338 ) -> Result<Result<VMStructRef, u64>> {
339 self.gc_heap
340 .alloc_uninit_struct_or_exn(ty, layout)
341 .map(|r| r.map(|r| r.into_structref_unchecked()))
342 }
343
344 pub fn dealloc_uninit_struct(&mut self, structref: VMStructRef) -> Result<()> {
346 self.gc_heap.dealloc_uninit_struct_or_exn(structref.into())
347 }
348
349 pub fn gc_object_data(&mut self, gc_ref: &VMGcRef) -> Result<&mut VMGcObjectData> {
353 self.gc_heap.gc_object_data_mut(gc_ref)
354 }
355
356 pub fn alloc_uninit_array(
363 &mut self,
364 ty: VMSharedTypeIndex,
365 len: u32,
366 layout: &GcArrayLayout,
367 ) -> Result<Result<VMArrayRef, u64>> {
368 self.gc_heap.alloc_uninit_array(ty, len, layout)
369 }
370
371 pub fn dealloc_uninit_array(&mut self, arrayref: VMArrayRef) -> Result<()> {
373 self.gc_heap.dealloc_uninit_array(arrayref)
374 }
375
376 pub fn array_len(&self, arrayref: &VMArrayRef) -> Result<u32> {
378 self.gc_heap.array_len(arrayref)
379 }
380
381 pub fn alloc_uninit_exn(
389 &mut self,
390 ty: VMSharedTypeIndex,
391 layout: &GcStructLayout,
392 ) -> Result<Result<VMExnRef, u64>> {
393 self.gc_heap
394 .alloc_uninit_struct_or_exn(ty, layout)
395 .map(|r| r.map(|r| r.into_exnref_unchecked()))
396 }
397
398 pub fn dealloc_uninit_exn(&mut self, exnref: VMExnRef) -> Result<()> {
400 self.gc_heap.dealloc_uninit_struct_or_exn(exnref.into())
401 }
402
403 #[cfg(feature = "gc")]
404 pub(crate) fn replace_gc_zeal_alloc_counter(
405 &mut self,
406 new_value: Option<NonZeroU32>,
407 ) -> Option<NonZeroU32> {
408 #[cfg(gc_zeal)]
409 return core::mem::replace(&mut self.gc_zeal_alloc_counter, new_value);
410
411 #[cfg(not(gc_zeal))]
412 {
413 let _ = new_value;
414 return None;
415 }
416 }
417}
418
419#[derive(Debug)]
421pub enum TraceInfo {
422 Array {
424 #[cfg_attr(
427 not(feature = "gc-drc"),
428 allow(dead_code, reason = "easier not to cfg on/off")
429 )]
430 gc_ref_elems: bool,
431 },
432
433 Struct {
435 gc_ref_offsets: Box<[u32]>,
438 },
439}
440
441impl TraceInfo {
442 pub(crate) fn new(gc_layout: &GcLayout) -> Self {
443 match gc_layout {
444 GcLayout::Array(l) => TraceInfo::Array {
445 gc_ref_elems: l.elems_are_gc_refs,
446 },
447 GcLayout::Struct(l) => TraceInfo::Struct {
448 gc_ref_offsets: l
449 .fields
450 .iter()
451 .filter_map(|f| if f.is_gc_ref { Some(f.offset) } else { None })
452 .collect(),
453 },
454 }
455 }
456}