1use crate::RootSet;
4use crate::error::{Context, ensure};
5use crate::hash_map::HashMap;
6use crate::module::ModuleRegistry;
7use crate::store::{
8 Asyncness, AutoAssertNoGc, InstanceId, StoreOpaque, StoreResourceLimiter, yield_now,
9};
10use crate::type_registry::RegisteredType;
11use crate::vm::{
12 self, Backtrace, Frame, GcRootsList, GcStore, InstanceAllocationRequest, NopHasher,
13 SendSyncPtr, StoreGcHostAllocTypes, TraceInfo, VMGcRef,
14};
15use crate::{
16 Engine, ExnRef, GcHeapOutOfMemory, Result, Rooted, Store, StoreContextMut, ThrownException,
17 bail,
18};
19use core::fmt;
20use core::mem::ManuallyDrop;
21use core::num::NonZeroU32;
22use core::ops::{Deref, DerefMut};
23use core::ptr::NonNull;
24use wasmtime_environ::DefinedTagIndex;
25use wasmtime_environ::packed_option::ReservedValue;
26
27#[derive(Default)]
28pub(crate) struct StoreGcData {
29 gc_roots: RootSet,
30 gc_roots_list: GcRootsList,
31 gc_host_alloc_types: StoreGcHostAllocTypes,
33 pending_exception: Option<VMGcRef>,
47
48 subtype_check_cache: HashMap<u64, bool, NopHasher>,
68}
69
70impl<T> Store<T> {
71 pub fn gc(&mut self, why: Option<&crate::GcHeapOutOfMemory<()>>) -> Result<()> {
89 StoreContextMut(&mut self.inner).gc(why)
90 }
91
92 pub fn gc_heap_capacity(&self) -> usize {
95 self.inner.gc_heap_capacity()
96 }
97
98 pub fn gc_heap_grow(&mut self, bytes: u64) -> Result<()> {
125 StoreContextMut(&mut self.inner).gc_heap_grow(bytes)
126 }
127
128 pub fn throw<R>(&mut self, exception: Rooted<ExnRef>) -> Result<R> {
160 self.inner.throw_impl(exception)
161 }
162
163 pub fn take_pending_exception(&mut self) -> Option<Rooted<ExnRef>> {
180 self.inner.take_pending_exception_rooted()
181 }
182}
183
184impl<'a, T> StoreContextMut<'a, T> {
185 pub fn gc(&mut self, why: Option<&GcHeapOutOfMemory<()>>) -> Result<()> {
189 let (mut limiter, store) = self.0.validate_sync_resource_limiter_and_store_opaque()?;
190 vm::assert_ready(store.gc(
191 limiter.as_mut(),
192 None,
193 why.map(|e| e.bytes_needed()),
194 Asyncness::No,
195 ))?;
196 Ok(())
197 }
198
199 pub fn gc_heap_grow(&mut self, bytes: u64) -> Result<()> {
203 let (mut limiter, store) = self.0.validate_sync_resource_limiter_and_store_opaque()?;
204 vm::assert_ready(store.grow_gc_heap(limiter.as_mut(), bytes, crate::store::Asyncness::No))
205 }
206
207 #[cfg(feature = "gc")]
212 pub fn throw<R>(&mut self, exception: Rooted<ExnRef>) -> Result<R> {
213 self.0.inner.throw_impl(exception)
214 }
215
216 #[cfg(feature = "gc")]
221 pub fn take_pending_exception(&mut self) -> Option<Rooted<ExnRef>> {
222 self.0.inner.take_pending_exception_rooted()
223 }
224}
225
226#[derive(Debug)]
227struct GcHeapGrowthFailed;
228
229impl fmt::Display for GcHeapGrowthFailed {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 f.write_str("GC heap growth failed")
232 }
233}
234
235impl core::error::Error for GcHeapGrowthFailed {}
236
237impl StoreOpaque {
238 pub(crate) async fn gc(
249 &mut self,
250 limiter: Option<&mut StoreResourceLimiter<'_>>,
251 root: Option<VMGcRef>,
252 bytes_needed: Option<u64>,
253 asyncness: Asyncness,
254 ) -> Result<Option<VMGcRef>> {
255 let mut scope = crate::OpaqueRootScope::new(self);
256 scope.trim_gc_liveness_flags(true);
257 let store_id = scope.id();
258 let root = root.map(|r| scope.gc_roots_mut().push_lifo_root(store_id, r));
259
260 scope
261 .collect_and_maybe_grow_gc_heap(limiter, bytes_needed, asyncness)
262 .await?;
263
264 Ok(root.map(|r| {
265 let r = r
266 .get_gc_ref(&scope)
267 .expect("still in scope")
268 .unchecked_copy();
269 scope.clone_gc_ref(&r)
270 }))
271 }
272
273 pub(crate) fn trim_gc_liveness_flags(&mut self, eager: bool) {
278 if let Some(gc_store) = self.gc_store.as_mut() {
279 self.gc_data.gc_roots.trim_liveness_flags(gc_store, eager);
280 }
281 }
282
283 async fn collect_and_maybe_grow_gc_heap(
286 &mut self,
287 limiter: Option<&mut StoreResourceLimiter<'_>>,
288 bytes_needed: Option<u64>,
289 asyncness: Asyncness,
290 ) -> Result<()> {
291 log::trace!("collect_and_maybe_grow_gc_heap(bytes_needed = {bytes_needed:#x?})");
292 self.do_gc(asyncness).await?;
293 if let Some(n) = bytes_needed
294 && n > u64::try_from(self.gc_heap_capacity())?.saturating_sub(
295 self.gc_store.as_ref().map_or(0, |gc| {
296 u64::try_from(gc.last_post_gc_allocated_bytes.unwrap_or(0)).unwrap()
297 }),
298 )
299 {
300 if let Err(e) = self.grow_gc_heap(limiter, n, asyncness).await {
301 if e.is::<GcHeapGrowthFailed>() {
302 log::trace!("ignoring GC heap growth failure: {e}");
303 } else {
304 return Err(e);
305 }
306 }
307 }
308 Ok(())
309 }
310
311 pub(crate) async fn grow_gc_heap(
315 &mut self,
316 mut limiter: Option<&mut StoreResourceLimiter<'_>>,
317 bytes_needed: u64,
318 asyncness: Asyncness,
319 ) -> Result<()> {
320 log::trace!("Attempting to grow the GC heap by at least {bytes_needed:#x} bytes");
321
322 if bytes_needed == 0 {
323 return Ok(());
324 }
325
326 if self
329 .gc_store
330 .as_ref()
331 .map_or(false, |gc| gc.gc_heap.needs_gc_before_next_growth())
332 {
333 self.do_gc(asyncness).await?;
334 debug_assert!(
335 !self
336 .gc_store
337 .as_ref()
338 .map_or(false, |gc| gc.gc_heap.needs_gc_before_next_growth()),
339 "needs_gc_before_next_growth should return false after a GC"
340 );
341 }
342
343 self.ensure_gc_store(limiter.as_deref_mut()).await?;
345
346 let page_size = self.engine().tunables().gc_heap_memory_type().page_size();
347
348 let mut heap = TakenGcHeap::new(self);
351
352 let current_size_in_bytes = u64::try_from(heap.memory.byte_size())?;
353 let current_size_in_pages = current_size_in_bytes / page_size;
354
355 let doubled_size_in_pages = current_size_in_pages.saturating_mul(2);
357 assert!(doubled_size_in_pages >= current_size_in_pages);
358 let delta_pages_for_doubling = doubled_size_in_pages - current_size_in_pages;
359
360 let max_size_in_bytes = 1 << 32;
366 let max_size_in_pages = max_size_in_bytes / page_size;
367 let delta_to_max_size_in_pages = max_size_in_pages - current_size_in_pages;
368 let delta_pages_for_alloc = delta_pages_for_doubling.min(delta_to_max_size_in_pages);
369
370 let pages_needed = bytes_needed.div_ceil(page_size);
377 assert!(pages_needed > 0);
378 let delta_pages_for_alloc = delta_pages_for_alloc.max(pages_needed);
379 assert!(delta_pages_for_alloc > 0);
380
381 unsafe {
385 heap.memory
386 .grow(delta_pages_for_alloc, limiter)
387 .await
388 .context(GcHeapGrowthFailed)?
389 .ok_or(GcHeapGrowthFailed)?;
390 }
391 *heap.store.vm_store_context.gc_heap.get_mut() = heap.memory.vmmemory();
392
393 let new_size_in_bytes = u64::try_from(heap.memory.byte_size())?;
394 assert!(new_size_in_bytes > current_size_in_bytes);
395 heap.delta_bytes_grown = new_size_in_bytes - current_size_in_bytes;
396 let delta_bytes_for_alloc = delta_pages_for_alloc.checked_mul(page_size).unwrap();
397 assert!(
398 heap.delta_bytes_grown >= delta_bytes_for_alloc,
399 "{} should be greater than or equal to {delta_bytes_for_alloc}",
400 heap.delta_bytes_grown,
401 );
402 log::trace!(
403 " -> grew GC heap by {:#x} bytes: new size is {new_size_in_bytes:#x} bytes",
404 heap.delta_bytes_grown
405 );
406 return Ok(());
407
408 struct TakenGcHeap<'a> {
409 store: &'a mut StoreOpaque,
410 memory: ManuallyDrop<vm::Memory>,
411 delta_bytes_grown: u64,
412 }
413
414 impl<'a> TakenGcHeap<'a> {
415 fn new(store: &'a mut StoreOpaque) -> TakenGcHeap<'a> {
416 TakenGcHeap {
417 memory: ManuallyDrop::new(store.unwrap_gc_store_mut().gc_heap.take_memory()),
418 store,
419 delta_bytes_grown: 0,
420 }
421 }
422 }
423
424 impl Drop for TakenGcHeap<'_> {
425 fn drop(&mut self) {
426 unsafe {
432 self.store.unwrap_gc_store_mut().gc_heap.replace_memory(
433 ManuallyDrop::take(&mut self.memory),
434 self.delta_bytes_grown,
435 );
436 }
437 }
438 }
439 }
440
441 fn replace_gc_zeal_alloc_counter(
442 &mut self,
443 new_value: Option<NonZeroU32>,
444 ) -> Option<NonZeroU32> {
445 if let Some(gc_store) = &mut self.gc_store {
446 gc_store.replace_gc_zeal_alloc_counter(new_value)
447 } else {
448 None
449 }
450 }
451
452 pub(crate) async fn retry_after_gc_async<T, U>(
461 &mut self,
462 mut limiter: Option<&mut StoreResourceLimiter<'_>>,
463 value: T,
464 asyncness: Asyncness,
465 alloc_func: impl Fn(&mut Self, T) -> Result<U>,
466 ) -> Result<U>
467 where
468 T: Send + Sync + 'static,
469 {
470 self.ensure_gc_store(limiter.as_deref_mut()).await?;
471
472 match alloc_func(self, value) {
473 Ok(x) => Ok(x),
474 Err(e) => match e.downcast::<crate::GcHeapOutOfMemory<T>>() {
475 Ok(oom) => {
476 log::trace!("Got GC heap OOM: {oom}");
477
478 let (value, oom) = oom.take_inner();
479 let bytes_needed = oom.bytes_needed();
480
481 let mut store = WithoutGcZealAllocCounter::new(self);
482
483 let gc_heap_capacity = store
484 .gc_store
485 .as_ref()
486 .map_or(0, |gc_store| gc_store.gc_heap_capacity());
487 let last_gc_heap_usage = store.gc_store.as_ref().map_or(0, |gc_store| {
488 gc_store.last_post_gc_allocated_bytes.unwrap_or(0)
489 });
490
491 if should_collect_first(bytes_needed, gc_heap_capacity, last_gc_heap_usage) {
492 log::trace!(
493 "Collecting first, then retrying; growing GC heap if collecting didn't \
494 free up enough space, then retrying again"
495 );
496 store
497 .gc(limiter.as_deref_mut(), None, None, asyncness)
498 .await?;
499
500 match alloc_func(&mut store, value) {
501 Ok(x) => Ok(x),
502 Err(e) => match e.downcast::<crate::GcHeapOutOfMemory<T>>() {
503 Ok(oom2) => {
504 let (value, _) = oom2.take_inner();
507 let _ =
511 store.grow_gc_heap(limiter, bytes_needed, asyncness).await;
512
513 alloc_func(&mut store, value)
514 }
515 Err(e) => Err(e),
516 },
517 }
518 } else {
519 log::trace!(
520 "Grow GC heap first, collecting if growth failed, then retrying"
521 );
522
523 if let Err(e) = store
524 .grow_gc_heap(limiter.as_deref_mut(), bytes_needed.max(1), asyncness)
525 .await
526 {
527 log::trace!("growing GC heap failed: {e}");
528 store.gc(limiter, None, None, asyncness).await?;
529 }
530
531 alloc_func(&mut store, value)
532 }
533 }
534 Err(e) => Err(e),
535 },
536 }
537 }
538
539 pub(crate) fn set_pending_exception(&mut self, exnref: &VMGcRef) -> crate::Error {
547 debug_assert!(exnref.is_exnref(&*self.unwrap_gc_store_mut().gc_heap));
548 let gc_store = self.gc_store.as_mut().unwrap();
549 match gc_store.write_gc_ref(&mut self.gc_data.pending_exception, Some(exnref)) {
550 Ok(()) => ThrownException.into(),
551 Err(e) => e,
552 }
553 }
554
555 pub(crate) fn expose_pending_exception_to_wasm(&mut self) -> Option<NonZeroU32> {
558 let exnref = self.gc_data.pending_exception.take()?;
559 let gc_store = self.unwrap_gc_store_mut();
560 debug_assert!(exnref.is_exnref(&*gc_store.gc_heap));
561 Some(gc_store.expose_gc_ref_to_wasm(exnref).unwrap())
562 }
563
564 fn take_pending_exception_rooted(&mut self) -> Option<Rooted<ExnRef>> {
567 let vmexnref = self.gc_data.pending_exception.take()?;
568 debug_assert!(vmexnref.is_exnref(&*self.unwrap_gc_store().gc_heap));
569 let mut nogc = AutoAssertNoGc::new(self);
570 Some(Rooted::new(&mut nogc, vmexnref))
571 }
572
573 pub(crate) fn pending_exception_tag_and_instance(
576 &mut self,
577 ) -> Option<(InstanceId, DefinedTagIndex)> {
578 let pending_exnref = self.gc_data.pending_exception.as_ref()?.unchecked_copy();
579 debug_assert!(pending_exnref.is_exnref(&*self.unwrap_gc_store_mut().gc_heap));
580 let mut store = AutoAssertNoGc::new(self);
581
582 pending_exnref.into_exnref_unchecked().tag(&mut store).ok()
589 }
590
591 #[cfg(feature = "debug")]
594 pub(crate) fn pending_exception_owned_rooted(
595 &mut self,
596 ) -> Result<Option<crate::OwnedRooted<ExnRef>>, crate::OutOfMemory> {
597 let pending = match &self.gc_data.pending_exception {
598 Some(r) => r,
599 None => return Ok(None),
600 };
601 let cloned = self.gc_store.as_mut().unwrap().clone_gc_ref(pending);
602 let mut nogc = AutoAssertNoGc::new(self);
603 Ok(Some(crate::OwnedRooted::new(&mut nogc, cloned)?))
604 }
605
606 fn throw_impl<R>(&mut self, exception: Rooted<ExnRef>) -> Result<R> {
611 let exception = exception.try_gc_ref(self)?.unchecked_copy();
612 Err(self.set_pending_exception(&exception))
613 }
614
615 #[inline]
629 pub(crate) fn require_gc_store(&self) -> Result<&GcStore> {
630 match &self.gc_store {
631 Some(gc_store) => Ok(gc_store),
632 None => bail!("GC heap not initialized yet"),
633 }
634 }
635
636 #[inline]
638 pub(crate) fn require_gc_store_mut(&mut self) -> Result<&mut GcStore> {
639 match &mut self.gc_store {
640 Some(gc_store) => Ok(gc_store),
641 None => bail!("GC heap not initialized yet"),
642 }
643 }
644
645 pub(crate) fn gc_heap_capacity(&self) -> usize {
648 match self.gc_store.as_ref() {
649 Some(gc_store) => gc_store.gc_heap_capacity(),
650 None => 0,
651 }
652 }
653
654 async fn do_gc(&mut self, asyncness: Asyncness) -> Result<()> {
655 if self.gc_store.is_none() {
657 return Ok(());
658 }
659
660 if log::log_enabled!(log::Level::Trace) {
661 let gc_store = self.gc_store.as_ref().unwrap();
662 let capacity = gc_store.gc_heap_capacity();
663 let live_set_size = gc_store.last_post_gc_allocated_bytes.unwrap_or(0);
664 let utilization = live_set_size as f64 / capacity as f64 * 100.0;
665 log::trace!(
666 "============ Begin GC ===========\n\
667 \t GC heap capacity = {capacity:#010x} bytes\n\
668 \tlast post-GC live-set size = {live_set_size:#010x} bytes\n\
669 \t GC heap utilization = {utilization:.02}%",
670 );
671 }
672
673 let mut roots = core::mem::take(&mut self.gc_data.gc_roots_list);
676
677 self.trace_roots(&mut roots, asyncness).await;
678 self.gc_store
679 .as_mut()
680 .unwrap()
681 .gc(
682 asyncness,
683 unsafe { roots.iter() },
684 &self.modules,
685 &self.gc_data.gc_host_alloc_types,
686 yield_now,
691 )
692 .await?;
693
694 roots.clear();
696 self.gc_data.gc_roots_list = roots;
697
698 if log::log_enabled!(log::Level::Trace) {
699 let gc_store = self.gc_store.as_ref().unwrap();
700 let capacity = gc_store.gc_heap_capacity();
701 let live_set_size = gc_store.last_post_gc_allocated_bytes.unwrap_or(0);
702 let utilization = live_set_size as f64 / capacity as f64 * 100.0;
703 log::trace!(
704 "============ End GC ===========\n\
705 \t GC heap capacity = {capacity:#010x} bytes\n\
706 \tpost-GC live-set size = {live_set_size:#010x} bytes\n\
707 \t GC heap utilization = {utilization:.02}%",
708 );
709 }
710 Ok(())
711 }
712
713 async fn trace_roots(&mut self, gc_roots_list: &mut GcRootsList, asyncness: Asyncness) {
714 log::trace!("Begin trace GC roots");
715
716 assert!(gc_roots_list.is_empty());
718
719 self.trace_wasm_stack_roots(gc_roots_list);
720 if asyncness != Asyncness::No {
721 self.yield_now().await;
722 }
723
724 #[cfg(feature = "stack-switching")]
725 {
726 self.trace_wasm_continuation_roots(gc_roots_list);
727 if asyncness != Asyncness::No {
728 self.yield_now().await;
729 }
730 }
731
732 self.trace_vmctx_roots(gc_roots_list);
733 if asyncness != Asyncness::No {
734 self.yield_now().await;
735 }
736
737 self.trace_instance_roots(gc_roots_list);
738 if asyncness != Asyncness::No {
739 self.yield_now().await;
740 }
741
742 self.trace_user_roots(gc_roots_list);
743 if asyncness != Asyncness::No {
744 self.yield_now().await;
745 }
746
747 self.trace_pending_exception_roots(gc_roots_list);
748
749 log::trace!("End trace GC roots")
750 }
751
752 pub(crate) fn trace_wasm_stack_frame(
753 modules: &ModuleRegistry,
754 gc_roots_list: &mut GcRootsList,
755 frame: Frame,
756 ) {
757 let pc = frame.pc();
758 debug_assert!(pc != 0, "we should always get a valid PC for Wasm frames");
759
760 let fp = frame.fp() as *mut usize;
761 debug_assert!(
762 !fp.is_null(),
763 "we should always get a valid frame pointer for Wasm frames"
764 );
765
766 let (store_code, offset) = modules
767 .store_code_by_pc(pc)
768 .expect("should have store code for Wasm frame");
769 let offset = u32::try_from(offset).unwrap();
770
771 let stack_map =
772 wasmtime_environ::StackMap::lookup(offset, store_code.code_memory().stack_map_data());
773
774 if let Some(stack_map) = stack_map {
775 log::trace!(
776 "We have a stack map that maps {} bytes in this Wasm frame",
777 stack_map.frame_size()
778 );
779
780 let sp = unsafe { stack_map.sp(fp) };
781 for stack_slot in unsafe { stack_map.live_gc_refs(sp) } {
782 unsafe {
783 Self::trace_wasm_stack_slot(gc_roots_list, stack_slot);
784 }
785 }
786 }
787
788 #[cfg(feature = "debug")]
789 if let Some(frame_table) = store_code.code_memory().frame_table() {
790 for stack_slot in crate::debug::gc_refs_in_frame(frame_table, offset, fp) {
791 unsafe {
792 Self::trace_wasm_stack_slot(gc_roots_list, stack_slot);
793 }
794 }
795 }
796 }
797
798 unsafe fn trace_wasm_stack_slot(gc_roots_list: &mut GcRootsList, stack_slot: *mut u32) {
799 let raw: u32 = unsafe { core::ptr::read(stack_slot) };
800 log::trace!("Stack slot @ {stack_slot:p} = {raw:#x}");
801
802 let gc_ref = vm::VMGcRef::from_raw_u32(raw);
803 if gc_ref.is_some() {
804 unsafe {
805 gc_roots_list
806 .add_wasm_stack_root(SendSyncPtr::new(NonNull::new(stack_slot).unwrap()));
807 }
808 }
809 }
810
811 fn trace_wasm_stack_roots(&mut self, gc_roots_list: &mut GcRootsList) {
812 log::trace!("Begin trace GC roots :: Wasm stack");
813
814 Backtrace::trace(self, |frame| {
815 Self::trace_wasm_stack_frame(self.modules(), gc_roots_list, frame);
816 core::ops::ControlFlow::Continue(())
817 });
818
819 #[cfg(feature = "component-model-async")]
820 if self.concurrency_support() {
821 let unwind = self.unwinder();
822 let StoreOpaque {
823 modules,
824 store_data,
825 ..
826 } = self;
827 store_data
828 .components
829 .task_state_mut()
830 .concurrent_state_mut()
831 .trace_fiber_roots(modules, unwind, gc_roots_list);
832 }
833
834 log::trace!("End trace GC roots :: Wasm stack");
835 }
836
837 #[cfg(feature = "stack-switching")]
838 fn trace_wasm_continuation_roots(&mut self, gc_roots_list: &mut GcRootsList) {
839 use crate::vm::{VMPayloads, VMStackState, ValRaw};
840
841 unsafe fn trace_payload_roots(gc_roots_list: &mut GcRootsList, payloads: &VMPayloads) {
842 let gc_ref_data = payloads.gc_ref_data;
843 let payloads = &payloads.buffer;
844 assert!(payloads.length <= payloads.capacity);
845 let Some(gc_ref_data) = gc_ref_data else {
846 return;
847 };
848 let gc_ref_data = gc_ref_data.as_ptr();
849
850 for index in 0..usize::try_from(payloads.length).unwrap() {
851 let marker = unsafe { gc_ref_data.add(index).read() };
852 if marker == wasmtime_environ::CONTINUATION_PAYLOAD_GC_REF {
853 let slot = unsafe { payloads.data.cast::<ValRaw>().add(index).cast::<u32>() };
854 unsafe {
855 StoreOpaque::trace_wasm_stack_slot(gc_roots_list, slot);
856 }
857 }
858 }
859 }
860
861 log::trace!("Begin trace GC roots :: continuations");
862
863 for continuation in &self.continuations {
864 let state = continuation.common_stack_information.state;
865
866 match state {
867 VMStackState::Suspended => {
868 unsafe {
869 trace_payload_roots(gc_roots_list, &continuation.values);
870 }
871 Backtrace::trace_suspended_continuation(self, continuation.deref(), |frame| {
872 Self::trace_wasm_stack_frame(self.modules(), gc_roots_list, frame);
873 core::ops::ControlFlow::Continue(())
874 });
875 }
876 VMStackState::Running => {
877 }
879 VMStackState::Parent => {
880 }
884 VMStackState::Fresh => unsafe {
885 trace_payload_roots(gc_roots_list, &continuation.args);
886 },
887 VMStackState::Returned | VMStackState::Trapped => {
888 }
890 }
891 }
892
893 log::trace!("End trace GC roots :: continuations");
894 }
895
896 fn trace_vmctx_roots(&mut self, gc_roots_list: &mut GcRootsList) {
897 log::trace!("Begin trace GC roots :: vmctx");
898 self.for_each_global(|store, global| global.trace_root(store, gc_roots_list));
899 self.for_each_table(|store, table| table.trace_roots(store, gc_roots_list));
900 log::trace!("End trace GC roots :: vmctx");
901 }
902
903 fn trace_instance_roots(&mut self, gc_roots_list: &mut GcRootsList) {
904 log::trace!("Begin trace GC roots :: instance");
905 for (_id, instance) in &mut self.instances {
906 unsafe {
909 instance
910 .handle
911 .get_mut()
912 .trace_element_segment_roots(gc_roots_list);
913 }
914 }
915 log::trace!("End trace GC roots :: instance");
916 }
917
918 fn trace_user_roots(&mut self, gc_roots_list: &mut GcRootsList) {
919 log::trace!("Begin trace GC roots :: user");
920 self.gc_data.gc_roots.trace_roots(gc_roots_list);
921 log::trace!("End trace GC roots :: user");
922 }
923
924 fn trace_pending_exception_roots(&mut self, gc_roots_list: &mut GcRootsList) {
925 log::trace!("Begin trace GC roots :: pending exception");
926 if let Some(pending_exception) = self.gc_data.pending_exception.as_mut() {
927 unsafe {
928 gc_roots_list.add_vmgcref_root(pending_exception.into(), "Pending exception");
929 }
930 }
931 log::trace!("End trace GC roots :: pending exception");
932 }
933
934 pub(crate) fn insert_gc_host_alloc_type(&mut self, ty: RegisteredType) {
947 assert!(
948 Engine::same(self.engine(), ty.engine()),
949 "type used with wrong engine"
950 );
951 let trace_info = ty.layout().map(TraceInfo::new);
952 self.gc_data
953 .gc_host_alloc_types
954 .insert(ty.index(), (ty, trace_info));
955 }
956
957 #[inline]
965 pub(crate) async fn ensure_gc_store(
966 &mut self,
967 limiter: Option<&mut StoreResourceLimiter<'_>>,
968 ) -> Result<&mut GcStore> {
969 if self.gc_store.is_some() {
970 return Ok(self.gc_store.as_mut().unwrap());
971 }
972 self.allocate_gc_store(limiter).await
973 }
974
975 #[inline(never)]
976 async fn allocate_gc_store(
977 &mut self,
978 limiter: Option<&mut StoreResourceLimiter<'_>>,
979 ) -> Result<&mut GcStore> {
980 log::trace!("allocating GC heap for store {:?}", self.id());
981
982 assert!(self.gc_store.is_none());
983 assert_eq!(
984 self.vm_store_context.gc_heap.get_mut().base.as_non_null(),
985 NonNull::dangling(),
986 );
987 assert_eq!(self.vm_store_context.gc_heap.get_mut().current_length(), 0);
988
989 let engine = self.engine();
990 let mem_ty = engine.tunables().gc_heap_memory_type();
991 ensure!(
992 engine.features().gc_types(),
993 "cannot allocate a GC store when GC is disabled at configuration time"
994 );
995 let gc_runtime = engine
996 .gc_runtime()
997 .context("no GC runtime: GC disabled at compile time or configuration time")?;
998
999 let mut request = InstanceAllocationRequest {
1001 id: InstanceId::reserved_value(),
1002 runtime_info: engine.empty_module_runtime_info(),
1003 imports: vm::Imports::default(),
1004 store: self,
1005 limiter,
1006 };
1007
1008 let (mem_alloc_index, mem) = engine
1009 .allocator()
1010 .allocate_memory(
1011 &mut request,
1012 &mem_ty,
1013 None,
1014 wasmtime_environ::MemoryKind::GcHeap,
1015 )
1016 .await?;
1017
1018 let (index, mut heap) =
1021 match engine
1022 .allocator()
1023 .allocate_gc_heap(engine, &**gc_runtime, mem_alloc_index)
1024 {
1025 Ok(pair) => pair,
1026 Err(e) => unsafe {
1027 engine
1028 .allocator()
1029 .deallocate_memory(None, mem_alloc_index, mem);
1030 return Err(e);
1031 },
1032 };
1033 heap.attach(mem);
1034
1035 let gc_store = GcStore::new(index, heap, engine.tunables().gc_zeal_alloc_counter);
1036 *self.vm_store_context.gc_heap.get_mut() = gc_store.vmmemory_definition();
1037 Ok(self.gc_store.insert(gc_store))
1038 }
1039
1040 pub fn has_pending_exception(&self) -> bool {
1042 self.gc_data.pending_exception.is_some()
1043 }
1044
1045 #[inline]
1046 pub(crate) fn gc_roots(&self) -> &RootSet {
1047 &self.gc_data.gc_roots
1048 }
1049
1050 #[inline]
1051 pub(crate) fn gc_roots_mut(&mut self) -> &mut RootSet {
1052 &mut self.gc_data.gc_roots
1053 }
1054
1055 #[inline]
1056 pub(crate) fn enter_gc_lifo_scope(&self) -> usize {
1057 self.gc_data.gc_roots.enter_lifo_scope()
1058 }
1059
1060 #[inline]
1061 pub(crate) fn exit_gc_lifo_scope(&mut self, scope: usize) {
1062 self.gc_data
1063 .gc_roots
1064 .exit_lifo_scope(self.gc_store.as_mut(), scope);
1065 }
1066
1067 pub(crate) fn is_subtype_cached(
1074 &mut self,
1075 sub: wasmtime_environ::VMSharedTypeIndex,
1076 sup: wasmtime_environ::VMSharedTypeIndex,
1077 ) -> bool {
1078 const MAX_SIZE: usize = 1 << 16; let key = (u64::from(sub.as_u32()) << 32) | u64::from(sup.as_u32());
1081 let engine_answer = || self.engine.signatures().is_subtype(sub, sup);
1082 if self.gc_data.subtype_check_cache.len() < MAX_SIZE {
1083 *self
1084 .gc_data
1085 .subtype_check_cache
1086 .entry(key)
1087 .or_insert_with(engine_answer)
1088 } else {
1089 self.gc_data
1090 .subtype_check_cache
1091 .get(&key)
1092 .copied()
1093 .unwrap_or_else(engine_answer)
1094 }
1095 }
1096}
1097
1098struct WithoutGcZealAllocCounter<'a> {
1100 store: &'a mut StoreOpaque,
1101 counter: Option<NonZeroU32>,
1102}
1103
1104impl Deref for WithoutGcZealAllocCounter<'_> {
1105 type Target = StoreOpaque;
1106
1107 fn deref(&self) -> &Self::Target {
1108 &self.store
1109 }
1110}
1111
1112impl DerefMut for WithoutGcZealAllocCounter<'_> {
1113 fn deref_mut(&mut self) -> &mut Self::Target {
1114 &mut self.store
1115 }
1116}
1117
1118impl Drop for WithoutGcZealAllocCounter<'_> {
1119 fn drop(&mut self) {
1120 self.store.replace_gc_zeal_alloc_counter(self.counter);
1121 }
1122}
1123
1124impl<'a> WithoutGcZealAllocCounter<'a> {
1125 pub fn new(store: &'a mut StoreOpaque) -> Self {
1126 let counter = store.replace_gc_zeal_alloc_counter(None);
1127 WithoutGcZealAllocCounter { store, counter }
1128 }
1129}
1130
1131#[track_caller]
1140fn should_collect_first(
1141 bytes_needed: u64,
1142 gc_heap_capacity: usize,
1143 last_gc_heap_usage: usize,
1144) -> bool {
1145 debug_assert!(last_gc_heap_usage <= gc_heap_capacity);
1146
1147 if gc_heap_capacity == 0 {
1154 return false;
1155 }
1156
1157 if bytes_needed == 0 {
1160 return true;
1161 }
1162
1163 let Ok(bytes_needed) = usize::try_from(bytes_needed) else {
1164 return false;
1167 };
1168
1169 if bytes_needed > isize::MAX.cast_unsigned() {
1170 return false;
1174 }
1175
1176 let Some(predicted_usage) = last_gc_heap_usage.checked_add(bytes_needed) else {
1177 return true;
1181 };
1182
1183 predicted_usage < gc_heap_capacity / 2
1188}
1189
1190#[cfg(test)]
1191mod tests {
1192 use super::should_collect_first;
1193 use crate::{AsContextMut, Config, Engine, ExternRef, Result, Store};
1194
1195 #[test]
1196 fn test_should_collect_first() {
1197 for bytes_needed in 0..256 {
1199 assert_eq!(should_collect_first(bytes_needed, 0, 0), false);
1200 }
1201
1202 for cap in 1..256 {
1204 for usage in 0..=cap {
1205 assert_eq!(should_collect_first(0, cap, usage), true);
1206 }
1207 }
1208
1209 let max_alloc_usize = isize::MAX.cast_unsigned();
1210 let max_alloc_u64 = u64::try_from(max_alloc_usize).unwrap();
1211
1212 assert_eq!(
1215 should_collect_first(max_alloc_u64 + 1, max_alloc_usize, 0),
1216 false,
1217 );
1218
1219 assert_eq!(should_collect_first(1, usize::MAX, usize::MAX), true);
1221
1222 assert_eq!(should_collect_first(16, 1024, 64), true);
1225
1226 assert_eq!(should_collect_first(16, 1024, 512), false);
1230 }
1231
1232 #[test]
1233 fn gc_heap_initial_size() -> Result<()> {
1234 let mut config = Config::new();
1235 config.gc_heap_initial_size(1 << 20);
1236 let engine = Engine::new(&config)?;
1237 let mut store = Store::new(&engine, ());
1238 ExternRef::new(&mut store, 1)?;
1239
1240 let gc_store = store.as_context_mut().0.unwrap_gc_store();
1241 assert_eq!(gc_store.gc_heap_capacity(), 1 << 20);
1242 Ok(())
1243 }
1244}