1 //! Runtime library calls.
2 //!
3 //! Note that Wasm compilers may sometimes perform these inline rather than
4 //! calling them, particularly when CPUs have special instructions which compute
5 //! them directly.
6 //!
7 //! These functions are called by compiled Wasm code, and therefore must take
8 //! certain care about some things:
9 //!
10 //! * They must only contain basic, raw i32/i64/f32/f64/pointer parameters that
11 //!   are safe to pass across the system ABI.
12 //!
13 //! * If any nested function propagates an `Err(trap)` out to the library
14 //!   function frame, we need to raise it. This involves some nasty and quite
15 //!   unsafe code under the covers! Notably, after raising the trap, drops
16 //!   **will not** be run for local variables! This can lead to things like
17 //!   leaking `InstanceHandle`s which leads to never deallocating JIT code,
18 //!   instances, and modules if we are not careful!
19 //!
20 //! * The libcall must be entered via a Wasm-to-libcall trampoline that saves
21 //!   the last Wasm FP and PC for stack walking purposes. (For more details, see
22 //!   `crates/wasmtime/src/runtime/vm/backtrace.rs`.)
23 //!
24 //! To make it easier to correctly handle all these things, **all** libcalls
25 //! must be defined via the `libcall!` helper macro! See its doc comments below
26 //! for an example, or just look at the rest of the file.
27 //!
28 //! ## Dealing with `externref`s
29 //!
30 //! When receiving a raw `*mut u8` that is actually a `VMExternRef` reference,
31 //! convert it into a proper `VMExternRef` with `VMExternRef::clone_from_raw` as
32 //! soon as apossible. Any GC before raw pointer is converted into a reference
33 //! can potentially collect the referenced object, which could lead to use after
34 //! free.
35 //!
36 //! Avoid this by eagerly converting into a proper `VMExternRef`! (Unfortunately
37 //! there is no macro to help us automatically get this correct, so stay
38 //! vigilant!)
39 //!
40 //! ```ignore
41 //! pub unsafe extern "C" my_libcall_takes_ref(raw_extern_ref: *mut u8) {
42 //!     // Before `clone_from_raw`, `raw_extern_ref` is potentially unrooted,
43 //!     // and doing GC here could lead to use after free!
44 //!
45 //!     let my_extern_ref = if raw_extern_ref.is_null() {
46 //!         None
47 //!     } else {
48 //!         Some(VMExternRef::clone_from_raw(raw_extern_ref))
49 //!     };
50 //!
51 //!     // Now that we did `clone_from_raw`, it is safe to do a GC (or do
52 //!     // anything else that might transitively GC, like call back into
53 //!     // Wasm!)
54 //! }
55 //! ```
56 
57 #[cfg(feature = "stack-switching")]
58 use super::stack_switching::VMContObj;
59 use crate::prelude::*;
60 use crate::runtime::store::StoreInstanceId;
61 #[cfg(feature = "gc")]
62 use crate::runtime::vm::VMGcRef;
63 use crate::runtime::vm::table::TableElementType;
64 use crate::runtime::vm::vmcontext::VMFuncRef;
65 use crate::runtime::vm::{
66     HostResultHasUnwindSentinel, Instance, TrapReason, VMStore, f32x4, f64x2, i8x16,
67 };
68 use core::convert::Infallible;
69 use core::pin::Pin;
70 use core::ptr::NonNull;
71 #[cfg(feature = "threads")]
72 use core::time::Duration;
73 use wasmtime_environ::{
74     DataIndex, DefinedMemoryIndex, DefinedTableIndex, ElemIndex, FuncIndex, MemoryIndex,
75     TableIndex, Trap,
76 };
77 #[cfg(feature = "wmemcheck")]
78 use wasmtime_wmemcheck::AccessError::{
79     DoubleMalloc, InvalidFree, InvalidRead, InvalidWrite, OutOfBounds,
80 };
81 
82 /// Raw functions which are actually called from compiled code.
83 ///
84 /// Invocation of a builtin currently looks like:
85 ///
86 /// * A wasm function calls a cranelift-compiled trampoline that's generated
87 ///   once-per-builtin.
88 /// * The cranelift-compiled trampoline performs any necessary actions to exit
89 ///   wasm, such as dealing with fp/pc/etc.
90 /// * The cranelift-compiled trampoline loads a function pointer from an array
91 ///   stored in `VMContext` That function pointer is defined in this module.
92 /// * This module runs, handling things like `catch_unwind` and `Result` and
93 ///   such.
94 /// * This module delegates to the outer module (this file) which has the actual
95 ///   implementation.
96 ///
97 /// For more information on converting from host-defined values to Cranelift ABI
98 /// values see the `catch_unwind_and_record_trap` function.
99 pub mod raw {
100     use crate::runtime::vm::{InstanceAndStore, VMContext, f32x4, f64x2, i8x16};
101     use core::ptr::NonNull;
102 
103     macro_rules! libcall {
104         (
105             $(
106                 $( #[cfg($attr:meta)] )?
107                 $name:ident( vmctx: vmctx $(, $pname:ident: $param:ident )* ) $(-> $result:ident)?;
108             )*
109         ) => {
110             $(
111                 // This is the direct entrypoint from the compiled module which
112                 // still has the raw signature.
113                 //
114                 // This will delegate to the outer module to the actual
115                 // implementation and automatically perform `catch_unwind` along
116                 // with conversion of the return value in the face of traps.
117                 #[allow(improper_ctypes_definitions, reason = "__m128i known not FFI-safe")]
118                 pub unsafe extern "C" fn $name(
119                     vmctx: NonNull<VMContext>,
120                     $( $pname : libcall!(@ty $param), )*
121                 ) $(-> libcall!(@ty $result))? {
122                     $(#[cfg($attr)])?
123                     {
124                         crate::runtime::vm::traphandlers::catch_unwind_and_record_trap(|| unsafe {
125                             InstanceAndStore::from_vmctx(vmctx, |pair| {
126                                 let (instance, store) = pair.unpack_mut();
127                                 super::$name(store, instance, $($pname),*)
128                             })
129                         })
130                     }
131                     $(
132                         #[cfg(not($attr))]
133                         {
134                             let _ = vmctx;
135                             unreachable!();
136                         }
137                     )?
138                 }
139 
140                 // This works around a `rustc` bug where compiling with LTO
141                 // will sometimes strip out some of these symbols resulting
142                 // in a linking failure.
143                 #[allow(improper_ctypes_definitions, reason = "__m128i known not FFI-safe")]
144                 const _: () = {
145                     #[used]
146                     static I_AM_USED: unsafe extern "C" fn(
147                         NonNull<VMContext>,
148                         $( $pname : libcall!(@ty $param), )*
149                     ) $( -> libcall!(@ty $result))? = $name;
150                 };
151             )*
152         };
153 
154         (@ty u32) => (u32);
155         (@ty u64) => (u64);
156         (@ty f32) => (f32);
157         (@ty f64) => (f64);
158         (@ty u8) => (u8);
159         (@ty i8x16) => (i8x16);
160         (@ty f32x4) => (f32x4);
161         (@ty f64x2) => (f64x2);
162         (@ty bool) => (bool);
163         (@ty pointer) => (*mut u8);
164     }
165 
166     wasmtime_environ::foreach_builtin_function!(libcall);
167 }
168 
169 fn memory_grow(
170     store: &mut dyn VMStore,
171     mut instance: Pin<&mut Instance>,
172     delta: u64,
173     memory_index: u32,
174 ) -> Result<Option<AllocationSize>, TrapReason> {
175     let memory_index = DefinedMemoryIndex::from_u32(memory_index);
176     let module = instance.env_module();
177     let page_size_log2 = module.memories[module.memory_index(memory_index)].page_size_log2;
178 
179     let result = instance
180         .as_mut()
181         .memory_grow(store, memory_index, delta)?
182         .map(|size_in_bytes| AllocationSize(size_in_bytes >> page_size_log2));
183 
184     Ok(result)
185 }
186 
187 /// A helper structure to represent the return value of a memory or table growth
188 /// call.
189 ///
190 /// This represents a byte or element-based count of the size of an item on the
191 /// host. For example a memory is how many bytes large the memory is, or a table
192 /// is how many elements large it is. It's assumed that the value here is never
193 /// -1 or -2 as that would mean the entire host address space is allocated which
194 /// is not possible.
195 struct AllocationSize(usize);
196 
197 /// Special implementation for growth-related libcalls.
198 ///
199 /// Here the optional return value means:
200 ///
201 /// * `Some(val)` - the growth succeeded and the previous size of the item was
202 ///   `val`.
203 /// * `None` - the growth failed.
204 ///
205 /// The failure case returns -1 (or `usize::MAX` as an unsigned integer) and the
206 /// successful case returns the `val` itself. Note that -2 (`usize::MAX - 1`
207 /// when unsigned) is unwind as a sentinel to indicate an unwind as no valid
208 /// allocation can be that large.
209 unsafe impl HostResultHasUnwindSentinel for Option<AllocationSize> {
210     type Abi = *mut u8;
211     const SENTINEL: *mut u8 = (usize::MAX - 1) as *mut u8;
212 
213     fn into_abi(self) -> *mut u8 {
214         match self {
215             Some(size) => {
216                 debug_assert!(size.0 < (usize::MAX - 1));
217                 size.0 as *mut u8
218             }
219             None => usize::MAX as *mut u8,
220         }
221     }
222 }
223 
224 /// Implementation of `table.grow` for `funcref` tables.
225 unsafe fn table_grow_func_ref(
226     store: &mut dyn VMStore,
227     mut instance: Pin<&mut Instance>,
228     defined_table_index: u32,
229     delta: u64,
230     init_value: *mut u8,
231 ) -> Result<Option<AllocationSize>> {
232     let defined_table_index = DefinedTableIndex::from_u32(defined_table_index);
233     let table_index = instance.env_module().table_index(defined_table_index);
234     debug_assert!(matches!(
235         instance.as_mut().table_element_type(table_index),
236         TableElementType::Func,
237     ));
238     let element = NonNull::new(init_value.cast::<VMFuncRef>());
239     let result = instance
240         .defined_table_grow(defined_table_index, |table| unsafe {
241             table.grow_func(store, delta, element)
242         })?
243         .map(AllocationSize);
244     Ok(result)
245 }
246 
247 /// Implementation of `table.grow` for GC-reference tables.
248 #[cfg(feature = "gc")]
249 unsafe fn table_grow_gc_ref(
250     store: &mut dyn VMStore,
251     mut instance: Pin<&mut Instance>,
252     defined_table_index: u32,
253     delta: u64,
254     init_value: u32,
255 ) -> Result<Option<AllocationSize>> {
256     let defined_table_index = DefinedTableIndex::from_u32(defined_table_index);
257     let table_index = instance.env_module().table_index(defined_table_index);
258     debug_assert!(matches!(
259         instance.as_mut().table_element_type(table_index),
260         TableElementType::GcRef,
261     ));
262 
263     let element = VMGcRef::from_raw_u32(init_value);
264     let result = instance
265         .defined_table_grow(defined_table_index, |table| unsafe {
266             table.grow_gc_ref(store, delta, element.as_ref())
267         })?
268         .map(AllocationSize);
269     Ok(result)
270 }
271 
272 #[cfg(feature = "stack-switching")]
273 unsafe fn table_grow_cont_obj(
274     store: &mut dyn VMStore,
275     mut instance: Pin<&mut Instance>,
276     defined_table_index: u32,
277     delta: u64,
278     // The following two values together form the initial Option<VMContObj>.
279     // A None value is indicated by the pointer being null.
280     init_value_contref: *mut u8,
281     init_value_revision: u64,
282 ) -> Result<Option<AllocationSize>> {
283     let defined_table_index = DefinedTableIndex::from_u32(defined_table_index);
284     let table_index = instance.env_module().table_index(defined_table_index);
285     debug_assert!(matches!(
286         instance.as_mut().table_element_type(table_index),
287         TableElementType::Cont,
288     ));
289     let element = unsafe { VMContObj::from_raw_parts(init_value_contref, init_value_revision) };
290     let result = instance
291         .defined_table_grow(defined_table_index, |table| unsafe {
292             table.grow_cont(store, delta, element)
293         })?
294         .map(AllocationSize);
295     Ok(result)
296 }
297 
298 /// Implementation of `table.fill` for `funcref`s.
299 unsafe fn table_fill_func_ref(
300     _store: &mut dyn VMStore,
301     instance: Pin<&mut Instance>,
302     table_index: u32,
303     dst: u64,
304     val: *mut u8,
305     len: u64,
306 ) -> Result<()> {
307     let table_index = DefinedTableIndex::from_u32(table_index);
308     let table = instance.get_defined_table(table_index);
309     match table.element_type() {
310         TableElementType::Func => {
311             let val = NonNull::new(val.cast::<VMFuncRef>());
312             table.fill_func(dst, val, len)?;
313             Ok(())
314         }
315         TableElementType::GcRef => unreachable!(),
316         TableElementType::Cont => unreachable!(),
317     }
318 }
319 
320 #[cfg(feature = "gc")]
321 unsafe fn table_fill_gc_ref(
322     store: &mut dyn VMStore,
323     instance: Pin<&mut Instance>,
324     table_index: u32,
325     dst: u64,
326     val: u32,
327     len: u64,
328 ) -> Result<()> {
329     let table_index = DefinedTableIndex::from_u32(table_index);
330     let table = instance.get_defined_table(table_index);
331     match table.element_type() {
332         TableElementType::Func => unreachable!(),
333         TableElementType::GcRef => {
334             let gc_store = store.store_opaque_mut().unwrap_gc_store_mut();
335             let gc_ref = VMGcRef::from_raw_u32(val);
336             table.fill_gc_ref(Some(gc_store), dst, gc_ref.as_ref(), len)?;
337             Ok(())
338         }
339 
340         TableElementType::Cont => unreachable!(),
341     }
342 }
343 
344 #[cfg(feature = "stack-switching")]
345 unsafe fn table_fill_cont_obj(
346     _store: &mut dyn VMStore,
347     instance: Pin<&mut Instance>,
348     table_index: u32,
349     dst: u64,
350     value_contref: *mut u8,
351     value_revision: u64,
352     len: u64,
353 ) -> Result<()> {
354     let table_index = DefinedTableIndex::from_u32(table_index);
355     let table = instance.get_defined_table(table_index);
356     match table.element_type() {
357         TableElementType::Cont => {
358             let contobj = unsafe { VMContObj::from_raw_parts(value_contref, value_revision) };
359             table.fill_cont(dst, contobj, len)?;
360             Ok(())
361         }
362         _ => panic!("Wrong table filling function"),
363     }
364 }
365 
366 // Implementation of `table.copy`.
367 fn table_copy(
368     store: &mut dyn VMStore,
369     mut instance: Pin<&mut Instance>,
370     dst_table_index: u32,
371     src_table_index: u32,
372     dst: u64,
373     src: u64,
374     len: u64,
375 ) -> Result<(), Trap> {
376     let dst_table_index = TableIndex::from_u32(dst_table_index);
377     let src_table_index = TableIndex::from_u32(src_table_index);
378     let store = store.store_opaque_mut();
379 
380     // Convert the two table indices relative to `instance` into two
381     // defining instances and the defined table index within that instance.
382     let (dst_def_index, dst_instance) = instance
383         .as_mut()
384         .defined_table_index_and_instance(dst_table_index);
385     let dst_instance_id = dst_instance.id();
386     let (src_def_index, src_instance) = instance
387         .as_mut()
388         .defined_table_index_and_instance(src_table_index);
389     let src_instance_id = src_instance.id();
390 
391     let src_table = crate::Table::from_raw(
392         StoreInstanceId::new(store.id(), src_instance_id),
393         src_def_index,
394     );
395     let dst_table = crate::Table::from_raw(
396         StoreInstanceId::new(store.id(), dst_instance_id),
397         dst_def_index,
398     );
399 
400     // SAFETY: this is only safe if the two tables have the same type, and that
401     // was validated during wasm-validation time.
402     unsafe { crate::Table::copy_raw(store, &dst_table, dst, &src_table, src, len) }
403 }
404 
405 // Implementation of `table.init`.
406 fn table_init(
407     store: &mut dyn VMStore,
408     instance: Pin<&mut Instance>,
409     table_index: u32,
410     elem_index: u32,
411     dst: u64,
412     src: u64,
413     len: u64,
414 ) -> Result<(), Trap> {
415     let table_index = TableIndex::from_u32(table_index);
416     let elem_index = ElemIndex::from_u32(elem_index);
417     instance.table_init(
418         store.store_opaque_mut(),
419         table_index,
420         elem_index,
421         dst,
422         src,
423         len,
424     )
425 }
426 
427 // Implementation of `elem.drop`.
428 fn elem_drop(_store: &mut dyn VMStore, instance: Pin<&mut Instance>, elem_index: u32) {
429     let elem_index = ElemIndex::from_u32(elem_index);
430     instance.elem_drop(elem_index)
431 }
432 
433 // Implementation of `memory.copy`.
434 fn memory_copy(
435     _store: &mut dyn VMStore,
436     instance: Pin<&mut Instance>,
437     dst_index: u32,
438     dst: u64,
439     src_index: u32,
440     src: u64,
441     len: u64,
442 ) -> Result<(), Trap> {
443     let src_index = MemoryIndex::from_u32(src_index);
444     let dst_index = MemoryIndex::from_u32(dst_index);
445     instance.memory_copy(dst_index, dst, src_index, src, len)
446 }
447 
448 // Implementation of `memory.fill` for locally defined memories.
449 fn memory_fill(
450     _store: &mut dyn VMStore,
451     instance: Pin<&mut Instance>,
452     memory_index: u32,
453     dst: u64,
454     val: u32,
455     len: u64,
456 ) -> Result<(), Trap> {
457     let memory_index = DefinedMemoryIndex::from_u32(memory_index);
458     #[expect(clippy::cast_possible_truncation, reason = "known to truncate here")]
459     instance.memory_fill(memory_index, dst, val as u8, len)
460 }
461 
462 // Implementation of `memory.init`.
463 fn memory_init(
464     _store: &mut dyn VMStore,
465     instance: Pin<&mut Instance>,
466     memory_index: u32,
467     data_index: u32,
468     dst: u64,
469     src: u32,
470     len: u32,
471 ) -> Result<(), Trap> {
472     let memory_index = MemoryIndex::from_u32(memory_index);
473     let data_index = DataIndex::from_u32(data_index);
474     instance.memory_init(memory_index, data_index, dst, src, len)
475 }
476 
477 // Implementation of `ref.func`.
478 fn ref_func(
479     _store: &mut dyn VMStore,
480     instance: Pin<&mut Instance>,
481     func_index: u32,
482 ) -> NonNull<u8> {
483     instance
484         .get_func_ref(FuncIndex::from_u32(func_index))
485         .expect("ref_func: funcref should always be available for given func index")
486         .cast()
487 }
488 
489 // Implementation of `data.drop`.
490 fn data_drop(_store: &mut dyn VMStore, instance: Pin<&mut Instance>, data_index: u32) {
491     let data_index = DataIndex::from_u32(data_index);
492     instance.data_drop(data_index)
493 }
494 
495 // Returns a table entry after lazily initializing it.
496 unsafe fn table_get_lazy_init_func_ref(
497     _store: &mut dyn VMStore,
498     instance: Pin<&mut Instance>,
499     table_index: u32,
500     index: u64,
501 ) -> *mut u8 {
502     let table_index = TableIndex::from_u32(table_index);
503     let table = instance.get_table_with_lazy_init(table_index, core::iter::once(index));
504     let elem = (*table)
505         .get_func(index)
506         .expect("table access already bounds-checked");
507 
508     match elem {
509         Some(ptr) => ptr.as_ptr().cast(),
510         None => core::ptr::null_mut(),
511     }
512 }
513 
514 /// Drop a GC reference.
515 #[cfg(feature = "gc-drc")]
516 unsafe fn drop_gc_ref(store: &mut dyn VMStore, _instance: Pin<&mut Instance>, gc_ref: u32) {
517     log::trace!("libcalls::drop_gc_ref({gc_ref:#x})");
518     let gc_ref = VMGcRef::from_raw_u32(gc_ref).expect("non-null VMGcRef");
519     store
520         .store_opaque_mut()
521         .unwrap_gc_store_mut()
522         .drop_gc_ref(gc_ref);
523 }
524 
525 /// Grow the GC heap.
526 #[cfg(feature = "gc-null")]
527 unsafe fn grow_gc_heap(
528     store: &mut dyn VMStore,
529     _instance: Pin<&mut Instance>,
530     bytes_needed: u64,
531 ) -> Result<()> {
532     let orig_len = u64::try_from(
533         store
534             .require_gc_store()?
535             .gc_heap
536             .vmmemory()
537             .current_length(),
538     )
539     .unwrap();
540 
541     unsafe {
542         store
543             .maybe_async_gc(None, Some(bytes_needed))
544             .context("failed to grow the GC heap")
545             .context(crate::Trap::AllocationTooLarge)?;
546     }
547 
548     // JIT code relies on the memory having grown by `bytes_needed` bytes if
549     // this libcall returns successfully, so trap if we didn't grow that much.
550     let new_len = u64::try_from(
551         store
552             .require_gc_store()?
553             .gc_heap
554             .vmmemory()
555             .current_length(),
556     )
557     .unwrap();
558     if orig_len
559         .checked_add(bytes_needed)
560         .is_none_or(|expected_len| new_len < expected_len)
561     {
562         return Err(crate::Trap::AllocationTooLarge.into());
563     }
564 
565     Ok(())
566 }
567 
568 /// Allocate a raw, unininitialized GC object for Wasm code.
569 ///
570 /// The Wasm code is responsible for initializing the object.
571 #[cfg(feature = "gc-drc")]
572 unsafe fn gc_alloc_raw(
573     store: &mut dyn VMStore,
574     instance: Pin<&mut Instance>,
575     kind_and_reserved: u32,
576     module_interned_type_index: u32,
577     size: u32,
578     align: u32,
579 ) -> Result<core::num::NonZeroU32> {
580     use crate::vm::VMGcHeader;
581     use core::alloc::Layout;
582     use wasmtime_environ::{ModuleInternedTypeIndex, VMGcKind};
583 
584     let kind = VMGcKind::from_high_bits_of_u32(kind_and_reserved);
585     log::trace!("gc_alloc_raw(kind={kind:?}, size={size}, align={align})");
586 
587     let module = instance
588         .runtime_module()
589         .expect("should never allocate GC types defined in a dummy module");
590 
591     let module_interned_type_index = ModuleInternedTypeIndex::from_u32(module_interned_type_index);
592     let shared_type_index = module
593         .signatures()
594         .shared_type(module_interned_type_index)
595         .expect("should have engine type index for module type index");
596 
597     let mut header = VMGcHeader::from_kind_and_index(kind, shared_type_index);
598     header.set_reserved_u26(kind_and_reserved & VMGcKind::UNUSED_MASK);
599 
600     let size = usize::try_from(size).unwrap();
601     let align = usize::try_from(align).unwrap();
602     assert!(align.is_power_of_two());
603     let layout = Layout::from_size_align(size, align).map_err(|e| {
604         let err = Error::from(crate::Trap::AllocationTooLarge);
605         err.context(e)
606     })?;
607 
608     let store = store.store_opaque_mut();
609     let gc_ref = unsafe {
610         store.retry_after_gc_maybe_async((), |store, ()| {
611             store
612                 .unwrap_gc_store_mut()
613                 .alloc_raw(header, layout)?
614                 .map_err(|bytes_needed| crate::GcHeapOutOfMemory::new((), bytes_needed).into())
615         })?
616     };
617 
618     let raw = store.unwrap_gc_store_mut().expose_gc_ref_to_wasm(gc_ref);
619     Ok(raw)
620 }
621 
622 // Intern a `funcref` into the GC heap, returning its `FuncRefTableId`.
623 //
624 // This libcall may not GC.
625 #[cfg(feature = "gc")]
626 unsafe fn intern_func_ref_for_gc_heap(
627     store: &mut dyn VMStore,
628     _instance: Pin<&mut Instance>,
629     func_ref: *mut u8,
630 ) -> Result<u32> {
631     use crate::{store::AutoAssertNoGc, vm::SendSyncPtr};
632     use core::ptr::NonNull;
633 
634     let mut store = AutoAssertNoGc::new(store.store_opaque_mut());
635 
636     let func_ref = func_ref.cast::<VMFuncRef>();
637     let func_ref = NonNull::new(func_ref).map(SendSyncPtr::new);
638 
639     let func_ref_id = unsafe {
640         store
641             .require_gc_store_mut()?
642             .func_ref_table
643             .intern(func_ref)
644     };
645     Ok(func_ref_id.into_raw())
646 }
647 
648 // Get the raw `VMFuncRef` pointer associated with a `FuncRefTableId` from an
649 // earlier `intern_func_ref_for_gc_heap` call.
650 //
651 // This libcall may not GC.
652 #[cfg(feature = "gc")]
653 unsafe fn get_interned_func_ref(
654     store: &mut dyn VMStore,
655     instance: Pin<&mut Instance>,
656     func_ref_id: u32,
657     module_interned_type_index: u32,
658 ) -> *mut u8 {
659     use super::FuncRefTableId;
660     use crate::store::AutoAssertNoGc;
661     use wasmtime_environ::{ModuleInternedTypeIndex, packed_option::ReservedValue};
662 
663     let store = AutoAssertNoGc::new(store.store_opaque_mut());
664 
665     let func_ref_id = FuncRefTableId::from_raw(func_ref_id);
666     let module_interned_type_index = ModuleInternedTypeIndex::from_bits(module_interned_type_index);
667 
668     let func_ref = if module_interned_type_index.is_reserved_value() {
669         store
670             .unwrap_gc_store()
671             .func_ref_table
672             .get_untyped(func_ref_id)
673     } else {
674         let types = store.engine().signatures();
675         let engine_ty = instance.engine_type_index(module_interned_type_index);
676         store
677             .unwrap_gc_store()
678             .func_ref_table
679             .get_typed(types, func_ref_id, engine_ty)
680     };
681 
682     func_ref.map_or(core::ptr::null_mut(), |f| f.as_ptr().cast())
683 }
684 
685 /// Implementation of the `array.new_data` instruction.
686 #[cfg(feature = "gc")]
687 unsafe fn array_new_data(
688     store: &mut dyn VMStore,
689     instance: Pin<&mut Instance>,
690     array_type_index: u32,
691     data_index: u32,
692     src: u32,
693     len: u32,
694 ) -> Result<core::num::NonZeroU32> {
695     use crate::ArrayType;
696     use wasmtime_environ::ModuleInternedTypeIndex;
697 
698     let store = store.store_opaque_mut();
699     let array_type_index = ModuleInternedTypeIndex::from_u32(array_type_index);
700     let data_index = DataIndex::from_u32(data_index);
701 
702     // Calculate the byte-length of the data (as opposed to the element-length
703     // of the array).
704     let data_range = instance.wasm_data_range(data_index);
705     let shared_ty = instance.engine_type_index(array_type_index);
706     let array_ty = ArrayType::from_shared_type_index(store.engine(), shared_ty);
707     let one_elem_size = array_ty
708         .element_type()
709         .data_byte_size()
710         .expect("Wasm validation ensures that this type have a defined byte size");
711     let byte_len = len
712         .checked_mul(one_elem_size)
713         .and_then(|x| usize::try_from(x).ok())
714         .ok_or_else(|| Trap::MemoryOutOfBounds)?;
715 
716     // Get the data from the segment, checking bounds.
717     let src = usize::try_from(src).map_err(|_| Trap::MemoryOutOfBounds)?;
718     let data = instance
719         .wasm_data(data_range)
720         .get(src..)
721         .and_then(|d| d.get(..byte_len))
722         .ok_or_else(|| Trap::MemoryOutOfBounds)?;
723 
724     // Allocate the (uninitialized) array.
725     let gc_layout = store
726         .engine()
727         .signatures()
728         .layout(shared_ty)
729         .expect("array types have GC layouts");
730     let array_layout = gc_layout.unwrap_array();
731     let array_ref = unsafe {
732         store.retry_after_gc_maybe_async((), |store, ()| {
733             store
734                 .unwrap_gc_store_mut()
735                 .alloc_uninit_array(shared_ty, len, &array_layout)?
736                 .map_err(|bytes_needed| crate::GcHeapOutOfMemory::new((), bytes_needed).into())
737         })?
738     };
739 
740     // Copy the data into the array, initializing it.
741     store
742         .unwrap_gc_store_mut()
743         .gc_object_data(array_ref.as_gc_ref())
744         .copy_from_slice(array_layout.base_size, data);
745 
746     // Return the array to Wasm!
747     let raw = store
748         .unwrap_gc_store_mut()
749         .expose_gc_ref_to_wasm(array_ref.into());
750     Ok(raw)
751 }
752 
753 /// Implementation of the `array.init_data` instruction.
754 #[cfg(feature = "gc")]
755 unsafe fn array_init_data(
756     store: &mut dyn VMStore,
757     instance: Pin<&mut Instance>,
758     array_type_index: u32,
759     array: u32,
760     dst: u32,
761     data_index: u32,
762     src: u32,
763     len: u32,
764 ) -> Result<()> {
765     use crate::ArrayType;
766     use wasmtime_environ::ModuleInternedTypeIndex;
767 
768     let array_type_index = ModuleInternedTypeIndex::from_u32(array_type_index);
769     let data_index = DataIndex::from_u32(data_index);
770 
771     log::trace!(
772         "array.init_data(array={array:#x}, dst={dst}, data_index={data_index:?}, src={src}, len={len})",
773     );
774 
775     // Null check the array.
776     let gc_ref = VMGcRef::from_raw_u32(array).ok_or_else(|| Trap::NullReference)?;
777     let array = gc_ref
778         .into_arrayref(&*store.unwrap_gc_store().gc_heap)
779         .expect("gc ref should be an array");
780 
781     let dst = usize::try_from(dst).map_err(|_| Trap::MemoryOutOfBounds)?;
782     let src = usize::try_from(src).map_err(|_| Trap::MemoryOutOfBounds)?;
783     let len = usize::try_from(len).map_err(|_| Trap::MemoryOutOfBounds)?;
784 
785     // Bounds check the array.
786     let array_len = array.len(store.store_opaque());
787     let array_len = usize::try_from(array_len).map_err(|_| Trap::ArrayOutOfBounds)?;
788     if dst.checked_add(len).ok_or_else(|| Trap::ArrayOutOfBounds)? > array_len {
789         return Err(Trap::ArrayOutOfBounds.into());
790     }
791 
792     // Calculate the byte length from the array length.
793     let shared_ty = instance.engine_type_index(array_type_index);
794     let array_ty = ArrayType::from_shared_type_index(store.engine(), shared_ty);
795     let one_elem_size = array_ty
796         .element_type()
797         .data_byte_size()
798         .expect("Wasm validation ensures that this type have a defined byte size");
799     let data_len = len
800         .checked_mul(usize::try_from(one_elem_size).unwrap())
801         .ok_or_else(|| Trap::MemoryOutOfBounds)?;
802 
803     // Get the data from the segment, checking its bounds.
804     let data_range = instance.wasm_data_range(data_index);
805     let data = instance
806         .wasm_data(data_range)
807         .get(src..)
808         .and_then(|d| d.get(..data_len))
809         .ok_or_else(|| Trap::MemoryOutOfBounds)?;
810 
811     // Copy the data into the array.
812 
813     let dst_offset = u32::try_from(dst)
814         .unwrap()
815         .checked_mul(one_elem_size)
816         .unwrap();
817 
818     let array_layout = store
819         .engine()
820         .signatures()
821         .layout(shared_ty)
822         .expect("array types have GC layouts");
823     let array_layout = array_layout.unwrap_array();
824 
825     let obj_offset = array_layout.base_size.checked_add(dst_offset).unwrap();
826 
827     store
828         .unwrap_gc_store_mut()
829         .gc_object_data(array.as_gc_ref())
830         .copy_from_slice(obj_offset, data);
831 
832     Ok(())
833 }
834 
835 #[cfg(feature = "gc")]
836 unsafe fn array_new_elem(
837     store: &mut dyn VMStore,
838     mut instance: Pin<&mut Instance>,
839     array_type_index: u32,
840     elem_index: u32,
841     src: u32,
842     len: u32,
843 ) -> Result<core::num::NonZeroU32> {
844     use crate::{
845         ArrayRef, ArrayRefPre, ArrayType, Func, OpaqueRootScope, RootedGcRefImpl, Val,
846         store::AutoAssertNoGc,
847         vm::const_expr::{ConstEvalContext, ConstExprEvaluator},
848     };
849     use wasmtime_environ::{ModuleInternedTypeIndex, TableSegmentElements};
850 
851     // Convert indices to their typed forms.
852     let array_type_index = ModuleInternedTypeIndex::from_u32(array_type_index);
853     let elem_index = ElemIndex::from_u32(elem_index);
854 
855     let mut storage = None;
856     let elements = instance.passive_element_segment(&mut storage, elem_index);
857 
858     let src = usize::try_from(src).map_err(|_| Trap::TableOutOfBounds)?;
859     let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
860 
861     let shared_ty = instance.engine_type_index(array_type_index);
862     let array_ty = ArrayType::from_shared_type_index(store.engine(), shared_ty);
863     let pre = ArrayRefPre::_new(store, array_ty);
864 
865     let mut store = OpaqueRootScope::new(&mut **store);
866     // Turn the elements into `Val`s.
867     let mut vals = Vec::with_capacity(usize::try_from(elements.len()).unwrap());
868     match elements {
869         TableSegmentElements::Functions(fs) => {
870             vals.extend(
871                 fs.get(src..)
872                     .and_then(|s| s.get(..len))
873                     .ok_or_else(|| Trap::TableOutOfBounds)?
874                     .iter()
875                     .map(|f| {
876                         let raw_func_ref = instance.as_mut().get_func_ref(*f);
877                         let func =
878                             unsafe { raw_func_ref.map(|p| Func::from_vm_func_ref(store.id(), p)) };
879                         Val::FuncRef(func)
880                     }),
881             );
882         }
883         TableSegmentElements::Expressions(xs) => {
884             let xs = xs
885                 .get(src..)
886                 .and_then(|s| s.get(..len))
887                 .ok_or_else(|| Trap::TableOutOfBounds)?;
888 
889             let mut const_context = ConstEvalContext::new(instance.id());
890             let mut const_evaluator = ConstExprEvaluator::default();
891 
892             vals.extend(xs.iter().map(|x| unsafe {
893                 *const_evaluator
894                     .eval(&mut store, &mut const_context, x)
895                     .expect("const expr should be valid")
896             }));
897         }
898     }
899 
900     let array = unsafe { ArrayRef::new_fixed_maybe_async(&mut store, &pre, &vals)? };
901 
902     let mut store = AutoAssertNoGc::new(&mut store);
903     let gc_ref = array.try_clone_gc_ref(&mut store)?;
904     let raw = store.unwrap_gc_store_mut().expose_gc_ref_to_wasm(gc_ref);
905     Ok(raw)
906 }
907 
908 #[cfg(feature = "gc")]
909 unsafe fn array_init_elem(
910     store: &mut dyn VMStore,
911     mut instance: Pin<&mut Instance>,
912     array_type_index: u32,
913     array: u32,
914     dst: u32,
915     elem_index: u32,
916     src: u32,
917     len: u32,
918 ) -> Result<()> {
919     use crate::{
920         ArrayRef, Func, OpaqueRootScope, Val,
921         store::AutoAssertNoGc,
922         vm::const_expr::{ConstEvalContext, ConstExprEvaluator},
923     };
924     use wasmtime_environ::{ModuleInternedTypeIndex, TableSegmentElements};
925 
926     let mut store = OpaqueRootScope::new(store.store_opaque_mut());
927 
928     // Convert the indices into their typed forms.
929     let _array_type_index = ModuleInternedTypeIndex::from_u32(array_type_index);
930     let elem_index = ElemIndex::from_u32(elem_index);
931 
932     log::trace!(
933         "array.init_elem(array={array:#x}, dst={dst}, elem_index={elem_index:?}, src={src}, len={len})",
934     );
935 
936     // Convert the raw GC ref into a `Rooted<ArrayRef>`.
937     let array = VMGcRef::from_raw_u32(array).ok_or_else(|| Trap::NullReference)?;
938     let array = store.unwrap_gc_store_mut().clone_gc_ref(&array);
939     let array = {
940         let mut no_gc = AutoAssertNoGc::new(&mut store);
941         ArrayRef::from_cloned_gc_ref(&mut no_gc, array)
942     };
943 
944     // Bounds check the destination within the array.
945     let array_len = array._len(&store)?;
946     log::trace!("array_len = {array_len}");
947     if dst.checked_add(len).ok_or_else(|| Trap::ArrayOutOfBounds)? > array_len {
948         return Err(Trap::ArrayOutOfBounds.into());
949     }
950 
951     // Get the passive element segment.
952     let mut storage = None;
953     let elements = instance.passive_element_segment(&mut storage, elem_index);
954 
955     // Convert array offsets into `usize`s.
956     let src = usize::try_from(src).map_err(|_| Trap::TableOutOfBounds)?;
957     let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
958 
959     // Turn the elements into `Val`s.
960     let vals = match elements {
961         TableSegmentElements::Functions(fs) => fs
962             .get(src..)
963             .and_then(|s| s.get(..len))
964             .ok_or_else(|| Trap::TableOutOfBounds)?
965             .iter()
966             .map(|f| {
967                 let raw_func_ref = instance.as_mut().get_func_ref(*f);
968                 let func = unsafe { raw_func_ref.map(|p| Func::from_vm_func_ref(store.id(), p)) };
969                 Val::FuncRef(func)
970             })
971             .collect::<Vec<_>>(),
972         TableSegmentElements::Expressions(xs) => {
973             let mut const_context = ConstEvalContext::new(instance.id());
974             let mut const_evaluator = ConstExprEvaluator::default();
975 
976             xs.get(src..)
977                 .and_then(|s| s.get(..len))
978                 .ok_or_else(|| Trap::TableOutOfBounds)?
979                 .iter()
980                 .map(|x| unsafe {
981                     *const_evaluator
982                         .eval(&mut store, &mut const_context, x)
983                         .expect("const expr should be valid")
984                 })
985                 .collect::<Vec<_>>()
986         }
987     };
988 
989     // Copy the values into the array.
990     for (i, val) in vals.into_iter().enumerate() {
991         let i = u32::try_from(i).unwrap();
992         let j = dst.checked_add(i).unwrap();
993         array._set(&mut store, j, val)?;
994     }
995 
996     Ok(())
997 }
998 
999 // TODO: Specialize this libcall for only non-GC array elements, so we never
1000 // have to do GC barriers and their associated indirect calls through the `dyn
1001 // GcHeap`. Instead, implement those copies inline in Wasm code. Then, use bulk
1002 // `memcpy`-style APIs to do the actual copies here.
1003 #[cfg(feature = "gc")]
1004 unsafe fn array_copy(
1005     store: &mut dyn VMStore,
1006     _instance: Pin<&mut Instance>,
1007     dst_array: u32,
1008     dst: u32,
1009     src_array: u32,
1010     src: u32,
1011     len: u32,
1012 ) -> Result<()> {
1013     use crate::{ArrayRef, OpaqueRootScope, store::AutoAssertNoGc};
1014 
1015     log::trace!(
1016         "array.copy(dst_array={dst_array:#x}, dst_index={dst}, src_array={src_array:#x}, src_index={src}, len={len})",
1017     );
1018 
1019     let mut store = OpaqueRootScope::new(store.store_opaque_mut());
1020     let mut store = AutoAssertNoGc::new(&mut store);
1021 
1022     // Convert the raw GC refs into `Rooted<ArrayRef>`s.
1023     let dst_array = VMGcRef::from_raw_u32(dst_array).ok_or_else(|| Trap::NullReference)?;
1024     let dst_array = store.unwrap_gc_store_mut().clone_gc_ref(&dst_array);
1025     let dst_array = ArrayRef::from_cloned_gc_ref(&mut store, dst_array);
1026     let src_array = VMGcRef::from_raw_u32(src_array).ok_or_else(|| Trap::NullReference)?;
1027     let src_array = store.unwrap_gc_store_mut().clone_gc_ref(&src_array);
1028     let src_array = ArrayRef::from_cloned_gc_ref(&mut store, src_array);
1029 
1030     // Bounds check the destination array's elements.
1031     let dst_array_len = dst_array._len(&store)?;
1032     if dst.checked_add(len).ok_or_else(|| Trap::ArrayOutOfBounds)? > dst_array_len {
1033         return Err(Trap::ArrayOutOfBounds.into());
1034     }
1035 
1036     // Bounds check the source array's elements.
1037     let src_array_len = src_array._len(&store)?;
1038     if src.checked_add(len).ok_or_else(|| Trap::ArrayOutOfBounds)? > src_array_len {
1039         return Err(Trap::ArrayOutOfBounds.into());
1040     }
1041 
1042     let mut store = AutoAssertNoGc::new(&mut store);
1043     // If `src_array` and `dst_array` are the same array, then we are
1044     // potentially doing an overlapping copy, so make sure to copy elements in
1045     // the order that doesn't clobber the source elements before they are
1046     // copied. If they are different arrays, the order doesn't matter, but we
1047     // simply don't bother checking.
1048     if src > dst {
1049         for i in 0..len {
1050             let src_elem = src_array._get(&mut store, src + i)?;
1051             let dst_i = dst + i;
1052             dst_array._set(&mut store, dst_i, src_elem)?;
1053         }
1054     } else {
1055         for i in (0..len).rev() {
1056             let src_elem = src_array._get(&mut store, src + i)?;
1057             let dst_i = dst + i;
1058             dst_array._set(&mut store, dst_i, src_elem)?;
1059         }
1060     }
1061     Ok(())
1062 }
1063 
1064 #[cfg(feature = "gc")]
1065 unsafe fn is_subtype(
1066     store: &mut dyn VMStore,
1067     _instance: Pin<&mut Instance>,
1068     actual_engine_type: u32,
1069     expected_engine_type: u32,
1070 ) -> u32 {
1071     use wasmtime_environ::VMSharedTypeIndex;
1072 
1073     let actual = VMSharedTypeIndex::from_u32(actual_engine_type);
1074     let expected = VMSharedTypeIndex::from_u32(expected_engine_type);
1075 
1076     let is_subtype: bool = store.engine().signatures().is_subtype(actual, expected);
1077 
1078     log::trace!("is_subtype(actual={actual:?}, expected={expected:?}) -> {is_subtype}",);
1079     is_subtype as u32
1080 }
1081 
1082 // Implementation of `memory.atomic.notify` for locally defined memories.
1083 #[cfg(feature = "threads")]
1084 fn memory_atomic_notify(
1085     _store: &mut dyn VMStore,
1086     instance: Pin<&mut Instance>,
1087     memory_index: u32,
1088     addr_index: u64,
1089     count: u32,
1090 ) -> Result<u32, Trap> {
1091     let memory = DefinedMemoryIndex::from_u32(memory_index);
1092     instance
1093         .get_defined_memory_mut(memory)
1094         .atomic_notify(addr_index, count)
1095 }
1096 
1097 // Implementation of `memory.atomic.wait32` for locally defined memories.
1098 #[cfg(feature = "threads")]
1099 fn memory_atomic_wait32(
1100     _store: &mut dyn VMStore,
1101     instance: Pin<&mut Instance>,
1102     memory_index: u32,
1103     addr_index: u64,
1104     expected: u32,
1105     timeout: u64,
1106 ) -> Result<u32, Trap> {
1107     let timeout = (timeout as i64 >= 0).then(|| Duration::from_nanos(timeout));
1108     let memory = DefinedMemoryIndex::from_u32(memory_index);
1109     Ok(instance
1110         .get_defined_memory_mut(memory)
1111         .atomic_wait32(addr_index, expected, timeout)? as u32)
1112 }
1113 
1114 // Implementation of `memory.atomic.wait64` for locally defined memories.
1115 #[cfg(feature = "threads")]
1116 fn memory_atomic_wait64(
1117     _store: &mut dyn VMStore,
1118     instance: Pin<&mut Instance>,
1119     memory_index: u32,
1120     addr_index: u64,
1121     expected: u64,
1122     timeout: u64,
1123 ) -> Result<u32, Trap> {
1124     let timeout = (timeout as i64 >= 0).then(|| Duration::from_nanos(timeout));
1125     let memory = DefinedMemoryIndex::from_u32(memory_index);
1126     Ok(instance
1127         .get_defined_memory_mut(memory)
1128         .atomic_wait64(addr_index, expected, timeout)? as u32)
1129 }
1130 
1131 // Hook for when an instance runs out of fuel.
1132 fn out_of_gas(store: &mut dyn VMStore, _instance: Pin<&mut Instance>) -> Result<()> {
1133     store.out_of_gas()
1134 }
1135 
1136 // Hook for when an instance observes that the epoch has changed.
1137 #[cfg(target_has_atomic = "64")]
1138 fn new_epoch(store: &mut dyn VMStore, _instance: Pin<&mut Instance>) -> Result<NextEpoch> {
1139     store.new_epoch().map(NextEpoch)
1140 }
1141 
1142 struct NextEpoch(u64);
1143 
1144 unsafe impl HostResultHasUnwindSentinel for NextEpoch {
1145     type Abi = u64;
1146     const SENTINEL: u64 = u64::MAX;
1147     fn into_abi(self) -> u64 {
1148         self.0
1149     }
1150 }
1151 
1152 // Hook for validating malloc using wmemcheck_state.
1153 #[cfg(feature = "wmemcheck")]
1154 unsafe fn check_malloc(
1155     _store: &mut dyn VMStore,
1156     instance: Pin<&mut Instance>,
1157     addr: u32,
1158     len: u32,
1159 ) -> Result<()> {
1160     if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
1161         let result = wmemcheck_state.malloc(addr as usize, len as usize);
1162         wmemcheck_state.memcheck_on();
1163         match result {
1164             Ok(()) => {}
1165             Err(DoubleMalloc { addr, len }) => {
1166                 bail!("Double malloc at addr {:#x} of size {}", addr, len)
1167             }
1168             Err(OutOfBounds { addr, len }) => {
1169                 bail!("Malloc out of bounds at addr {:#x} of size {}", addr, len);
1170             }
1171             _ => {
1172                 panic!("unreachable")
1173             }
1174         }
1175     }
1176     Ok(())
1177 }
1178 
1179 // Hook for validating free using wmemcheck_state.
1180 #[cfg(feature = "wmemcheck")]
1181 unsafe fn check_free(
1182     _store: &mut dyn VMStore,
1183     instance: Pin<&mut Instance>,
1184     addr: u32,
1185 ) -> Result<()> {
1186     if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
1187         let result = wmemcheck_state.free(addr as usize);
1188         wmemcheck_state.memcheck_on();
1189         match result {
1190             Ok(()) => {}
1191             Err(InvalidFree { addr }) => {
1192                 bail!("Invalid free at addr {:#x}", addr)
1193             }
1194             _ => {
1195                 panic!("unreachable")
1196             }
1197         }
1198     }
1199     Ok(())
1200 }
1201 
1202 // Hook for validating load using wmemcheck_state.
1203 #[cfg(feature = "wmemcheck")]
1204 fn check_load(
1205     _store: &mut dyn VMStore,
1206     instance: Pin<&mut Instance>,
1207     num_bytes: u32,
1208     addr: u32,
1209     offset: u32,
1210 ) -> Result<()> {
1211     if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
1212         let result = wmemcheck_state.read(addr as usize + offset as usize, num_bytes as usize);
1213         match result {
1214             Ok(()) => {}
1215             Err(InvalidRead { addr, len }) => {
1216                 bail!("Invalid load at addr {:#x} of size {}", addr, len);
1217             }
1218             Err(OutOfBounds { addr, len }) => {
1219                 bail!("Load out of bounds at addr {:#x} of size {}", addr, len);
1220             }
1221             _ => {
1222                 panic!("unreachable")
1223             }
1224         }
1225     }
1226     Ok(())
1227 }
1228 
1229 // Hook for validating store using wmemcheck_state.
1230 #[cfg(feature = "wmemcheck")]
1231 fn check_store(
1232     _store: &mut dyn VMStore,
1233     instance: Pin<&mut Instance>,
1234     num_bytes: u32,
1235     addr: u32,
1236     offset: u32,
1237 ) -> Result<()> {
1238     if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
1239         let result = wmemcheck_state.write(addr as usize + offset as usize, num_bytes as usize);
1240         match result {
1241             Ok(()) => {}
1242             Err(InvalidWrite { addr, len }) => {
1243                 bail!("Invalid store at addr {:#x} of size {}", addr, len)
1244             }
1245             Err(OutOfBounds { addr, len }) => {
1246                 bail!("Store out of bounds at addr {:#x} of size {}", addr, len)
1247             }
1248             _ => {
1249                 panic!("unreachable")
1250             }
1251         }
1252     }
1253     Ok(())
1254 }
1255 
1256 // Hook for turning wmemcheck load/store validation off when entering a malloc function.
1257 #[cfg(feature = "wmemcheck")]
1258 fn malloc_start(_store: &mut dyn VMStore, instance: Pin<&mut Instance>) {
1259     if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
1260         wmemcheck_state.memcheck_off();
1261     }
1262 }
1263 
1264 // Hook for turning wmemcheck load/store validation off when entering a free function.
1265 #[cfg(feature = "wmemcheck")]
1266 fn free_start(_store: &mut dyn VMStore, instance: Pin<&mut Instance>) {
1267     if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
1268         wmemcheck_state.memcheck_off();
1269     }
1270 }
1271 
1272 // Hook for tracking wasm stack updates using wmemcheck_state.
1273 #[cfg(feature = "wmemcheck")]
1274 fn update_stack_pointer(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, _value: u32) {
1275     // TODO: stack-tracing has yet to be finalized. All memory below
1276     // the address of the top of the stack is marked as valid for
1277     // loads and stores.
1278     // if let Some(wmemcheck_state) = &mut instance.wmemcheck_state {
1279     //     instance.wmemcheck_state.update_stack_pointer(value as usize);
1280     // }
1281 }
1282 
1283 // Hook updating wmemcheck_state memory state vector every time memory.grow is called.
1284 #[cfg(feature = "wmemcheck")]
1285 fn update_mem_size(_store: &mut dyn VMStore, instance: Pin<&mut Instance>, num_pages: u32) {
1286     if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
1287         const KIB: usize = 1024;
1288         let num_bytes = num_pages as usize * 64 * KIB;
1289         wmemcheck_state.update_mem_size(num_bytes);
1290     }
1291 }
1292 
1293 fn floor_f32(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f32) -> f32 {
1294     wasmtime_math::WasmFloat::wasm_floor(val)
1295 }
1296 
1297 fn floor_f64(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f64) -> f64 {
1298     wasmtime_math::WasmFloat::wasm_floor(val)
1299 }
1300 
1301 fn ceil_f32(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f32) -> f32 {
1302     wasmtime_math::WasmFloat::wasm_ceil(val)
1303 }
1304 
1305 fn ceil_f64(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f64) -> f64 {
1306     wasmtime_math::WasmFloat::wasm_ceil(val)
1307 }
1308 
1309 fn trunc_f32(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f32) -> f32 {
1310     wasmtime_math::WasmFloat::wasm_trunc(val)
1311 }
1312 
1313 fn trunc_f64(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f64) -> f64 {
1314     wasmtime_math::WasmFloat::wasm_trunc(val)
1315 }
1316 
1317 fn nearest_f32(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f32) -> f32 {
1318     wasmtime_math::WasmFloat::wasm_nearest(val)
1319 }
1320 
1321 fn nearest_f64(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>, val: f64) -> f64 {
1322     wasmtime_math::WasmFloat::wasm_nearest(val)
1323 }
1324 
1325 // This intrinsic is only used on x86_64 platforms as an implementation of
1326 // the `i8x16.swizzle` instruction when `pshufb` in SSSE3 is not available.
1327 #[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
1328 fn i8x16_swizzle(
1329     _store: &mut dyn VMStore,
1330     _instance: Pin<&mut Instance>,
1331     a: i8x16,
1332     b: i8x16,
1333 ) -> i8x16 {
1334     union U {
1335         reg: i8x16,
1336         mem: [u8; 16],
1337     }
1338 
1339     unsafe {
1340         let a = U { reg: a }.mem;
1341         let b = U { reg: b }.mem;
1342 
1343         // Use the `swizzle` semantics of returning 0 on any out-of-bounds
1344         // index, rather than the x86 pshufb semantics, since Wasmtime uses
1345         // this to implement `i8x16.swizzle`.
1346         let select = |arr: &[u8; 16], byte: u8| {
1347             if byte >= 16 { 0x00 } else { arr[byte as usize] }
1348         };
1349 
1350         U {
1351             mem: [
1352                 select(&a, b[0]),
1353                 select(&a, b[1]),
1354                 select(&a, b[2]),
1355                 select(&a, b[3]),
1356                 select(&a, b[4]),
1357                 select(&a, b[5]),
1358                 select(&a, b[6]),
1359                 select(&a, b[7]),
1360                 select(&a, b[8]),
1361                 select(&a, b[9]),
1362                 select(&a, b[10]),
1363                 select(&a, b[11]),
1364                 select(&a, b[12]),
1365                 select(&a, b[13]),
1366                 select(&a, b[14]),
1367                 select(&a, b[15]),
1368             ],
1369         }
1370         .reg
1371     }
1372 }
1373 
1374 #[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))]
1375 fn i8x16_swizzle(
1376     _store: &mut dyn VMStore,
1377     _instance: Pin<&mut Instance>,
1378     _a: i8x16,
1379     _b: i8x16,
1380 ) -> i8x16 {
1381     unreachable!()
1382 }
1383 
1384 // This intrinsic is only used on x86_64 platforms as an implementation of
1385 // the `i8x16.shuffle` instruction when `pshufb` in SSSE3 is not available.
1386 #[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
1387 fn i8x16_shuffle(
1388     _store: &mut dyn VMStore,
1389     _instance: Pin<&mut Instance>,
1390     a: i8x16,
1391     b: i8x16,
1392     c: i8x16,
1393 ) -> i8x16 {
1394     union U {
1395         reg: i8x16,
1396         mem: [u8; 16],
1397     }
1398 
1399     unsafe {
1400         let ab = [U { reg: a }.mem, U { reg: b }.mem];
1401         let c = U { reg: c }.mem;
1402 
1403         // Use the `shuffle` semantics of returning 0 on any out-of-bounds
1404         // index, rather than the x86 pshufb semantics, since Wasmtime uses
1405         // this to implement `i8x16.shuffle`.
1406         let select = |arr: &[[u8; 16]; 2], byte: u8| {
1407             if byte >= 32 {
1408                 0x00
1409             } else if byte >= 16 {
1410                 arr[1][byte as usize - 16]
1411             } else {
1412                 arr[0][byte as usize]
1413             }
1414         };
1415 
1416         U {
1417             mem: [
1418                 select(&ab, c[0]),
1419                 select(&ab, c[1]),
1420                 select(&ab, c[2]),
1421                 select(&ab, c[3]),
1422                 select(&ab, c[4]),
1423                 select(&ab, c[5]),
1424                 select(&ab, c[6]),
1425                 select(&ab, c[7]),
1426                 select(&ab, c[8]),
1427                 select(&ab, c[9]),
1428                 select(&ab, c[10]),
1429                 select(&ab, c[11]),
1430                 select(&ab, c[12]),
1431                 select(&ab, c[13]),
1432                 select(&ab, c[14]),
1433                 select(&ab, c[15]),
1434             ],
1435         }
1436         .reg
1437     }
1438 }
1439 
1440 #[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))]
1441 fn i8x16_shuffle(
1442     _store: &mut dyn VMStore,
1443     _instance: Pin<&mut Instance>,
1444     _a: i8x16,
1445     _b: i8x16,
1446     _c: i8x16,
1447 ) -> i8x16 {
1448     unreachable!()
1449 }
1450 
1451 fn fma_f32x4(
1452     _store: &mut dyn VMStore,
1453     _instance: Pin<&mut Instance>,
1454     x: f32x4,
1455     y: f32x4,
1456     z: f32x4,
1457 ) -> f32x4 {
1458     union U {
1459         reg: f32x4,
1460         mem: [f32; 4],
1461     }
1462 
1463     unsafe {
1464         let x = U { reg: x }.mem;
1465         let y = U { reg: y }.mem;
1466         let z = U { reg: z }.mem;
1467 
1468         U {
1469             mem: [
1470                 wasmtime_math::WasmFloat::wasm_mul_add(x[0], y[0], z[0]),
1471                 wasmtime_math::WasmFloat::wasm_mul_add(x[1], y[1], z[1]),
1472                 wasmtime_math::WasmFloat::wasm_mul_add(x[2], y[2], z[2]),
1473                 wasmtime_math::WasmFloat::wasm_mul_add(x[3], y[3], z[3]),
1474             ],
1475         }
1476         .reg
1477     }
1478 }
1479 
1480 fn fma_f64x2(
1481     _store: &mut dyn VMStore,
1482     _instance: Pin<&mut Instance>,
1483     x: f64x2,
1484     y: f64x2,
1485     z: f64x2,
1486 ) -> f64x2 {
1487     union U {
1488         reg: f64x2,
1489         mem: [f64; 2],
1490     }
1491 
1492     unsafe {
1493         let x = U { reg: x }.mem;
1494         let y = U { reg: y }.mem;
1495         let z = U { reg: z }.mem;
1496 
1497         U {
1498             mem: [
1499                 wasmtime_math::WasmFloat::wasm_mul_add(x[0], y[0], z[0]),
1500                 wasmtime_math::WasmFloat::wasm_mul_add(x[1], y[1], z[1]),
1501             ],
1502         }
1503         .reg
1504     }
1505 }
1506 
1507 /// This intrinsic is just used to record trap information.
1508 ///
1509 /// The `Infallible` "ok" type here means that this never returns success, it
1510 /// only ever returns an error, and this hooks into the machinery to handle
1511 /// `Result` values to record such trap information.
1512 fn trap(
1513     _store: &mut dyn VMStore,
1514     _instance: Pin<&mut Instance>,
1515     code: u8,
1516 ) -> Result<Infallible, TrapReason> {
1517     Err(TrapReason::Wasm(
1518         wasmtime_environ::Trap::from_u8(code).unwrap(),
1519     ))
1520 }
1521 
1522 fn raise(_store: &mut dyn VMStore, _instance: Pin<&mut Instance>) {
1523     // SAFETY: this is only called from compiled wasm so we know that wasm has
1524     // already been entered. It's a dynamic safety precondition that the trap
1525     // information has already been arranged to be present.
1526     #[cfg(has_host_compiler_backend)]
1527     unsafe {
1528         crate::runtime::vm::traphandlers::raise_preexisting_trap()
1529     }
1530 
1531     // When Cranelift isn't in use then this is an unused libcall for Pulley, so
1532     // just insert a stub to catch bugs if it's accidentally called.
1533     #[cfg(not(has_host_compiler_backend))]
1534     unreachable!()
1535 }
1536 
1537 // Builtins for continuations. These are thin wrappers around the
1538 // respective definitions in stack_switching.rs.
1539 #[cfg(feature = "stack-switching")]
1540 fn cont_new(
1541     store: &mut dyn VMStore,
1542     instance: Pin<&mut Instance>,
1543     func: *mut u8,
1544     param_count: u32,
1545     result_count: u32,
1546 ) -> Result<Option<AllocationSize>, TrapReason> {
1547     let ans =
1548         crate::vm::stack_switching::cont_new(store, instance, func, param_count, result_count)?;
1549     Ok(Some(AllocationSize(ans.cast::<u8>() as usize)))
1550 }
1551