1use crate::bail_bug;
58use crate::prelude::*;
59use crate::runtime::store::{Asyncness, InstanceId, StoreOpaque};
60#[cfg(feature = "gc")]
61use crate::runtime::vm::VMGcRef;
62use crate::runtime::vm::{self, HostResultHasUnwindSentinel, VMStore, f32x4, f64x2, i8x16};
63use core::convert::Infallible;
64use core::ptr::NonNull;
65#[cfg(feature = "threads")]
66use core::time::Duration;
67use wasmtime_core::math::WasmFloat;
68use wasmtime_environ::{
69 CompiledTrap, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, PassiveElemIndex, TableIndex,
70 Trap,
71};
72#[cfg(feature = "wmemcheck")]
73use wasmtime_wmemcheck::AccessError::{
74 DoubleMalloc, InvalidFree, InvalidRead, InvalidWrite, OutOfBounds,
75};
76
77pub mod raw {
95 use crate::runtime::vm::{Instance, VMContext, f32x4, f64x2, i8x16};
96 use core::ptr::NonNull;
97
98 macro_rules! libcall {
99 (
100 $(
101 $( #[cfg($attr:meta)] )?
102 $name:ident( vmctx: vmctx $(, $pname:ident: $param:ident )* ) $(-> $result:ident)?;
103 )*
104 ) => {
105 $(
106 #[allow(improper_ctypes_definitions, reason = "__m128i known not FFI-safe")]
113 #[allow(unused_variables, reason = "macro-generated")]
114 #[allow(unreachable_code, reason = "some types uninhabited on some platforms")]
115 pub unsafe extern "C" fn $name(
116 vmctx: NonNull<VMContext>,
117 $( $pname : libcall!(@ty $param), )*
118 ) $(-> libcall!(@ty $result))? {
119 $(#[cfg($attr)])?
120 unsafe {
121 Instance::enter_host_from_wasm(vmctx, |store, instance| {
122 super::$name(store, instance, $($pname),*)
123 })
124 }
125 $(
126 #[cfg(not($attr))]
127 {
128 let _ = vmctx;
129 unreachable!();
130 }
131 )?
132 }
133
134 #[allow(improper_ctypes_definitions, reason = "__m128i known not FFI-safe")]
138 const _: () = {
139 #[used]
140 static I_AM_USED: unsafe extern "C" fn(
141 NonNull<VMContext>,
142 $( $pname : libcall!(@ty $param), )*
143 ) $( -> libcall!(@ty $result))? = $name;
144 };
145 )*
146 };
147
148 (@ty u32) => (u32);
149 (@ty u64) => (u64);
150 (@ty f32) => (f32);
151 (@ty f64) => (f64);
152 (@ty u8) => (u8);
153 (@ty i8x16) => (i8x16);
154 (@ty f32x4) => (f32x4);
155 (@ty f64x2) => (f64x2);
156 (@ty bool) => (bool);
157 (@ty pointer) => (*mut u8);
158 (@ty size) => (usize);
159 }
160
161 wasmtime_environ::foreach_builtin_function!(libcall);
162}
163
164macro_rules! block_on {
182 ($store:expr, $f:expr) => {{
183 let store: &mut StoreOpaque = $store;
184 let closure = assert_async_fn_closure($f);
185
186 if store.can_block() {
187 #[cfg(feature = "async")]
190 {
191 store.with_blocking(|store, cx| cx.block_on(closure(store, Asyncness::Yes)))
192 }
193 #[cfg(not(feature = "async"))]
194 {
195 unreachable!()
196 }
197 } else {
198 vm::one_poll(closure(store, Asyncness::No)).ok_or_else(|| {
219 crate::format_err!(
220 "
221
222A synchronously called wasm function invoked an async-defined libcall which
223failed to complete synchronously and is thus raising a trap. It's expected
224that this indicates that the store was configured to do async things after the
225original synchronous entrypoint to wasm was called. That's generally not
226supported in Wasmtime and async entrypoint should be used instead. If you're
227seeing this message in error please file an issue on Wasmtime.
228
229"
230 )
231 })
232 }
233 }};
234}
235
236fn assert_async_fn_closure<F, R>(f: F) -> F
237where
238 F: AsyncFnOnce(&mut StoreOpaque, Asyncness) -> R,
239{
240 f
241}
242
243fn memory_grow(
244 store: &mut dyn VMStore,
245 instance: InstanceId,
246 delta: u64,
247 memory_index: u32,
248) -> Result<Option<AllocationSize>> {
249 let memory_index = DefinedMemoryIndex::from_u32(memory_index);
250 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
251 let limiter = limiter.as_mut();
252 block_on!(store, async |store, _| {
253 let instance = store.instance_mut(instance);
254 let module = instance.env_module();
255 let page_size_log2 = module.memories[module.memory_index(memory_index)].page_size_log2;
256
257 let result = instance
258 .memory_grow(limiter, memory_index, delta)
259 .await?
260 .map(|size_in_bytes| AllocationSize(size_in_bytes >> page_size_log2));
261
262 Ok(result)
263 })?
264}
265
266struct AllocationSize(usize);
275
276unsafe impl HostResultHasUnwindSentinel for Option<AllocationSize> {
289 type Abi = *mut u8;
290 const SENTINEL: *mut u8 = (usize::MAX - 1) as *mut u8;
291
292 fn into_abi(self) -> *mut u8 {
293 match self {
294 Some(size) => {
295 debug_assert!(size.0 < (usize::MAX - 1));
296 size.0 as *mut u8
297 }
298 None => usize::MAX as *mut u8,
299 }
300 }
301}
302
303unsafe fn table_grow(
305 store: &mut dyn VMStore,
306 instance: InstanceId,
307 defined_table_index: u32,
308 delta: u64,
309) -> Result<Option<AllocationSize>> {
310 let defined_table_index = DefinedTableIndex::from_u32(defined_table_index);
311 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
312 let limiter = limiter.as_mut();
313 block_on!(store, async |store, _| unsafe {
314 let result = store
315 .instance_mut(instance)
316 .defined_table_grow(defined_table_index, limiter, delta)
317 .await?
318 .map(AllocationSize);
319 Ok(result)
320 })?
321}
322
323fn passive_elem_segment_len(
324 store: &mut dyn VMStore,
325 instance: InstanceId,
326 elem_index: u32,
327) -> usize {
328 let elem_index = PassiveElemIndex::from_u32(elem_index);
329 store
330 .instance_mut(instance)
331 .passive_element_segment(elem_index)
332 .len()
333}
334
335fn passive_elem_segment_base(
336 store: &mut dyn VMStore,
337 instance: InstanceId,
338 elem_index: u32,
339) -> *mut u8 {
340 let elem_index = PassiveElemIndex::from_u32(elem_index);
341 store
342 .instance_mut(instance)
343 .passive_element_segment(elem_index)
344 .as_mut_ptr()
345 .cast()
346}
347
348fn passive_elem_segment_drop(
350 store: &mut dyn VMStore,
351 instance: InstanceId,
352 elem_index: u32,
353) -> Result<()> {
354 let elem_index = PassiveElemIndex::from_u32(elem_index);
355 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(instance);
356 instance.passive_elem_drop(gc_store, elem_index)?;
357 Ok(())
358}
359
360unsafe fn memory_copy(
362 _store: &mut dyn VMStore,
363 _instance: InstanceId,
364 dst: *mut u8,
365 src: *mut u8,
366 len: usize,
367) {
368 let src = src.cast_const();
369 unsafe { src.copy_to(dst, len) }
372}
373
374unsafe fn memory_fill(
375 _store: &mut dyn VMStore,
376 _instance: InstanceId,
377 dst: *mut u8,
378 val: u32,
379 len: usize,
380) {
381 unsafe {
384 #[expect(
385 clippy::cast_possible_truncation,
386 reason = "the libcall intentionally takes the raw 32-bit value, \
387 and semantically that's intentionally truncated"
388 )]
389 dst.write_bytes(val as u8, len);
390 }
391}
392
393fn ref_func(store: &mut dyn VMStore, instance: InstanceId, func_index: u32) -> NonNull<u8> {
395 let (instance, registry) = store.instance_and_module_registry_mut(instance);
396 instance
397 .get_func_ref(registry, FuncIndex::from_u32(func_index))
398 .expect("ref_func: funcref should always be available for given func index")
399 .cast()
400}
401
402fn table_get_lazy_init_func_ref(
404 store: &mut dyn VMStore,
405 instance: InstanceId,
406 table_index: u32,
407 index: u64,
408) -> *mut u8 {
409 let table_index = TableIndex::from_u32(table_index);
410 let (instance, registry) = store.instance_and_module_registry_mut(instance);
411 let table = instance.get_table_with_lazy_init(registry, table_index, core::iter::once(index));
412 let elem = table
413 .get_func(index)
414 .expect("table access already bounds-checked");
415
416 match elem {
417 Some(ptr) => ptr.as_ptr().cast(),
418 None => core::ptr::null_mut(),
419 }
420}
421
422#[cfg(feature = "gc-drc")]
424fn drop_gc_ref(store: &mut dyn VMStore, _instance: InstanceId, gc_ref: u32) {
425 log::trace!("libcalls::drop_gc_ref({gc_ref:#x})");
426 let gc_ref = VMGcRef::from_raw_u32(gc_ref).expect("non-null VMGcRef");
427 store
428 .store_opaque_mut()
429 .unwrap_gc_store_mut()
430 .drop_gc_ref(gc_ref);
431}
432
433#[cfg(feature = "gc-drc")]
435fn force_gc(store: &mut dyn VMStore, _instance: InstanceId) -> Result<()> {
436 let store = store.store_opaque_mut();
437 block_on!(store, async |store, asyncness| {
438 store.gc(None, None, None, asyncness).await?;
439 Ok::<(), Error>(())
440 })??;
441 Ok(())
442}
443
444#[cfg(feature = "gc-null")]
446fn grow_gc_heap(store: &mut dyn VMStore, _instance: InstanceId, bytes_needed: u64) -> Result<()> {
447 let orig_len = u64::try_from(
448 store
449 .require_gc_store()?
450 .gc_heap
451 .vmmemory()
452 .current_length(),
453 )
454 .unwrap();
455
456 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
457 block_on!(store, async |store, asyncness| {
458 let _ = store
461 .grow_gc_heap(limiter.as_mut(), bytes_needed, asyncness)
462 .await;
463 })?;
464
465 let new_len = u64::try_from(
468 store
469 .require_gc_store()?
470 .gc_heap
471 .vmmemory()
472 .current_length(),
473 )
474 .unwrap();
475 if orig_len
476 .checked_add(bytes_needed)
477 .is_none_or(|expected_len| new_len < expected_len)
478 {
479 return Err(crate::Trap::AllocationTooLarge.into());
480 }
481
482 Ok(())
483}
484
485#[cfg(any(feature = "gc-drc", feature = "gc-copying"))]
489fn gc_alloc_raw(
490 store: &mut dyn VMStore,
491 _instance: InstanceId,
492 kind_and_reserved: u32,
493 shared_type_index: u32,
494 size: u32,
495 align: u32,
496) -> Result<core::num::NonZeroU32> {
497 use crate::vm::VMGcHeader;
498 use core::alloc::Layout;
499 use wasmtime_environ::{VMGcKind, VMSharedTypeIndex};
500
501 let kind = VMGcKind::from_high_bits_of_u32(kind_and_reserved);
502 log::trace!("gc_alloc_raw(kind={kind:?}, size={size}, align={align})");
503
504 let shared_type_index = VMSharedTypeIndex::from_u32(shared_type_index);
505 let mut header = VMGcHeader::from_kind_and_index(kind, shared_type_index);
506 header.set_reserved_u26(kind_and_reserved & VMGcKind::UNUSED_MASK);
507
508 let size = usize::try_from(size).unwrap();
509 let align = usize::try_from(align).unwrap();
510 assert!(align.is_power_of_two());
511 let layout = Layout::from_size_align(size, align).map_err(|e| {
512 let err = Error::from(crate::Trap::AllocationTooLarge);
513 err.context(e)
514 })?;
515
516 let opaque = store.store_opaque_mut();
519 if let Some(gc_store) = opaque.try_gc_store_mut() {
520 if let Ok(gc_ref) = gc_store.alloc_raw(header, layout)? {
521 let raw = gc_store.expose_gc_ref_to_wasm(gc_ref)?;
522 return Ok(raw);
523 }
524 }
525
526 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
527 block_on!(store, async |store, asyncness| {
528 let gc_ref = store
529 .retry_after_gc_async(limiter.as_mut(), (), asyncness, |store, ()| {
530 store
531 .unwrap_gc_store_mut()
532 .alloc_raw(header, layout)?
533 .map_err(|bytes_needed| crate::GcHeapOutOfMemory::new((), bytes_needed).into())
534 })
535 .await?;
536
537 store.unwrap_gc_store_mut().expose_gc_ref_to_wasm(gc_ref)
538 })?
539}
540
541#[cfg(feature = "gc")]
545unsafe fn intern_func_ref_for_gc_heap(
546 store: &mut dyn VMStore,
547 _instance: InstanceId,
548 func_ref: *mut u8,
549) -> Result<u32> {
550 use crate::runtime::vm::vmcontext::VMFuncRef;
551 use crate::{store::AutoAssertNoGc, vm::SendSyncPtr};
552 use core::ptr::NonNull;
553
554 let mut store = AutoAssertNoGc::new(store.store_opaque_mut());
555
556 let func_ref = func_ref.cast::<VMFuncRef>();
557 let func_ref = NonNull::new(func_ref).map(SendSyncPtr::new);
558
559 let func_ref_id = unsafe {
560 store
561 .require_gc_store_mut()?
562 .func_ref_table
563 .intern(func_ref)
564 };
565 Ok(func_ref_id.into_raw())
566}
567
568#[cfg(feature = "gc")]
573fn get_interned_func_ref(
574 store: &mut dyn VMStore,
575 instance: InstanceId,
576 func_ref_id: u32,
577 module_interned_type_index: u32,
578) -> Result<*mut u8> {
579 use super::FuncRefTableId;
580 use crate::store::AutoAssertNoGc;
581 use wasmtime_environ::{ModuleInternedTypeIndex, packed_option::ReservedValue};
582
583 let store = AutoAssertNoGc::new(store.store_opaque_mut());
584
585 let func_ref_id = FuncRefTableId::from_raw(func_ref_id);
586 let module_interned_type_index = ModuleInternedTypeIndex::from_bits(module_interned_type_index);
587
588 let func_ref = if module_interned_type_index.is_reserved_value() {
589 store
590 .unwrap_gc_store()
591 .func_ref_table
592 .get_untyped(func_ref_id)?
593 } else {
594 let types = store.engine().signatures();
595 let engine_ty = store
596 .instance(instance)
597 .engine_type_index(module_interned_type_index);
598 store
599 .unwrap_gc_store()
600 .func_ref_table
601 .get_typed(types, func_ref_id, engine_ty)?
602 };
603
604 Ok(func_ref.map_or(core::ptr::null_mut(), |f| f.as_ptr().cast()))
605}
606
607#[cfg(feature = "gc")]
608fn is_subtype(
609 store: &mut dyn VMStore,
610 _instance: InstanceId,
611 actual_engine_type: u32,
612 expected_engine_type: u32,
613) -> u32 {
614 use wasmtime_environ::VMSharedTypeIndex;
615
616 let actual = VMSharedTypeIndex::from_u32(actual_engine_type);
617 let expected = VMSharedTypeIndex::from_u32(expected_engine_type);
618 let is_subtype = store.is_subtype_cached(actual, expected);
619
620 log::trace!("is_subtype(actual={actual:?}, expected={expected:?}) -> {is_subtype}",);
621 u32::from(is_subtype)
622}
623
624#[cfg(feature = "threads")]
626fn memory_atomic_notify(
627 store: &mut dyn VMStore,
628 instance: InstanceId,
629 memory_index: u32,
630 addr_index: u64,
631 count: u32,
632) -> Result<u32, Trap> {
633 let memory = DefinedMemoryIndex::from_u32(memory_index);
634 store
635 .instance_mut(instance)
636 .get_defined_memory_mut(memory)
637 .atomic_notify(addr_index, count)
638}
639
640#[cfg(feature = "threads")]
642fn memory_atomic_wait32(
643 store: &mut dyn VMStore,
644 instance: InstanceId,
645 memory_index: u32,
646 addr_index: u64,
647 expected: u32,
648 timeout: u64,
649) -> Result<u32, Trap> {
650 let timeout = (timeout as i64 >= 0).then(|| Duration::from_nanos(timeout));
651 let memory = DefinedMemoryIndex::from_u32(memory_index);
652 Ok(store
653 .instance_mut(instance)
654 .get_defined_memory_mut(memory)
655 .atomic_wait32(addr_index, expected, timeout)? as u32)
656}
657
658#[cfg(feature = "threads")]
660fn memory_atomic_wait64(
661 store: &mut dyn VMStore,
662 instance: InstanceId,
663 memory_index: u32,
664 addr_index: u64,
665 expected: u64,
666 timeout: u64,
667) -> Result<u32, Trap> {
668 let timeout = (timeout as i64 >= 0).then(|| Duration::from_nanos(timeout));
669 let memory = DefinedMemoryIndex::from_u32(memory_index);
670 Ok(store
671 .instance_mut(instance)
672 .get_defined_memory_mut(memory)
673 .atomic_wait64(addr_index, expected, timeout)? as u32)
674}
675
676fn out_of_gas(store: &mut dyn VMStore, _instance: InstanceId) -> Result<()> {
678 block_on!(store, async |store, _| {
679 if !store.refuel() {
680 return Err(Trap::OutOfFuel.into());
681 }
682 #[cfg(feature = "async")]
683 if store.fuel_yield_interval.is_some() {
684 store.yield_now().await;
685 }
686 Ok(())
687 })?
688}
689
690#[cfg(target_has_atomic = "64")]
692fn new_epoch(store: &mut dyn VMStore, _instance: InstanceId) -> Result<NextEpoch> {
693 use crate::UpdateDeadline;
694
695 #[cfg(feature = "debug")]
696 {
697 store.block_on_debug_handler(crate::DebugEvent::EpochYield)?;
698 }
699
700 let update_deadline = store.new_epoch_updated_deadline()?;
701 block_on!(store, async move |store, asyncness| {
702 #[cfg(not(feature = "async"))]
703 let _ = asyncness;
704
705 let delta = match update_deadline {
706 UpdateDeadline::Interrupt => return Err(Trap::Interrupt.into()),
707 UpdateDeadline::Continue(delta) => delta,
708
709 #[cfg(feature = "async")]
713 UpdateDeadline::Yield(delta) => {
714 if asyncness != Asyncness::Yes {
715 bail!(
716 "cannot use `UpdateDeadline::Yield` without using \
717 an async wasm entrypoint",
718 );
719 }
720 store.yield_now().await;
721 delta
722 }
723 #[cfg(feature = "async")]
724 UpdateDeadline::YieldCustom(delta, future) => {
725 if asyncness != Asyncness::Yes {
726 bail!(
727 "cannot use `UpdateDeadline::YieldCustom` without using \
728 an async wasm entrypoint",
729 );
730 }
731 future.await;
732 delta
733 }
734 };
735
736 store.set_epoch_deadline(delta);
739 Ok(NextEpoch(store.get_epoch_deadline()))
740 })?
741}
742
743struct NextEpoch(u64);
744
745unsafe impl HostResultHasUnwindSentinel for NextEpoch {
746 type Abi = u64;
747 const SENTINEL: u64 = u64::MAX;
748 fn into_abi(self) -> u64 {
749 self.0
750 }
751}
752
753#[cfg(feature = "wmemcheck")]
755fn check_malloc(store: &mut dyn VMStore, instance: InstanceId, addr: u32, len: u32) -> Result<()> {
756 let instance = store.instance_mut(instance);
757 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
758 let result = wmemcheck_state.malloc(addr as usize, len as usize);
759 wmemcheck_state.memcheck_on();
760 match result {
761 Ok(()) => {}
762 Err(DoubleMalloc { addr, len }) => {
763 bail!("Double malloc at addr {:#x} of size {}", addr, len)
764 }
765 Err(OutOfBounds { addr, len }) => {
766 bail!("Malloc out of bounds at addr {:#x} of size {}", addr, len);
767 }
768 _ => {
769 panic!("unreachable")
770 }
771 }
772 }
773 Ok(())
774}
775
776#[cfg(feature = "wmemcheck")]
778fn check_free(store: &mut dyn VMStore, instance: InstanceId, addr: u32) -> Result<()> {
779 let instance = store.instance_mut(instance);
780 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
781 let result = wmemcheck_state.free(addr as usize);
782 wmemcheck_state.memcheck_on();
783 match result {
784 Ok(()) => {}
785 Err(InvalidFree { addr }) => {
786 bail!("Invalid free at addr {:#x}", addr)
787 }
788 _ => {
789 panic!("unreachable")
790 }
791 }
792 }
793 Ok(())
794}
795
796#[cfg(feature = "wmemcheck")]
798fn check_load(
799 store: &mut dyn VMStore,
800 instance: InstanceId,
801 num_bytes: u32,
802 addr: u32,
803 offset: u32,
804) -> Result<()> {
805 let instance = store.instance_mut(instance);
806 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
807 let result = wmemcheck_state.read(addr as usize + offset as usize, num_bytes as usize);
808 match result {
809 Ok(()) => {}
810 Err(InvalidRead { addr, len }) => {
811 bail!("Invalid load at addr {:#x} of size {}", addr, len);
812 }
813 Err(OutOfBounds { addr, len }) => {
814 bail!("Load out of bounds at addr {:#x} of size {}", addr, len);
815 }
816 _ => {
817 panic!("unreachable")
818 }
819 }
820 }
821 Ok(())
822}
823
824#[cfg(feature = "wmemcheck")]
826fn check_store(
827 store: &mut dyn VMStore,
828 instance: InstanceId,
829 num_bytes: u32,
830 addr: u32,
831 offset: u32,
832) -> Result<()> {
833 let instance = store.instance_mut(instance);
834 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
835 let result = wmemcheck_state.write(addr as usize + offset as usize, num_bytes as usize);
836 match result {
837 Ok(()) => {}
838 Err(InvalidWrite { addr, len }) => {
839 bail!("Invalid store at addr {:#x} of size {}", addr, len)
840 }
841 Err(OutOfBounds { addr, len }) => {
842 bail!("Store out of bounds at addr {:#x} of size {}", addr, len)
843 }
844 _ => {
845 panic!("unreachable")
846 }
847 }
848 }
849 Ok(())
850}
851
852#[cfg(feature = "wmemcheck")]
854fn malloc_start(store: &mut dyn VMStore, instance: InstanceId) {
855 let instance = store.instance_mut(instance);
856 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
857 wmemcheck_state.memcheck_off();
858 }
859}
860
861#[cfg(feature = "wmemcheck")]
863fn free_start(store: &mut dyn VMStore, instance: InstanceId) {
864 let instance = store.instance_mut(instance);
865 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
866 wmemcheck_state.memcheck_off();
867 }
868}
869
870#[cfg(feature = "wmemcheck")]
872fn update_stack_pointer(_store: &mut dyn VMStore, _instance: InstanceId, _value: u32) {
873 }
880
881#[cfg(feature = "wmemcheck")]
883fn update_mem_size(store: &mut dyn VMStore, instance: InstanceId, num_pages: u32) {
884 let instance = store.instance_mut(instance);
885 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
886 const KIB: usize = 1024;
887 let num_bytes = num_pages as usize * 64 * KIB;
888 wmemcheck_state.update_mem_size(num_bytes);
889 }
890}
891
892fn floor_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
893 val.wasm_floor()
894}
895
896fn floor_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
897 val.wasm_floor()
898}
899
900fn ceil_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
901 val.wasm_ceil()
902}
903
904fn ceil_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
905 val.wasm_ceil()
906}
907
908fn trunc_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
909 val.wasm_trunc()
910}
911
912fn trunc_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
913 val.wasm_trunc()
914}
915
916fn nearest_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
917 val.wasm_nearest()
918}
919
920fn nearest_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
921 val.wasm_nearest()
922}
923
924#[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
927fn i8x16_swizzle(_store: &mut dyn VMStore, _instance: InstanceId, a: i8x16, b: i8x16) -> i8x16 {
928 union U {
929 reg: i8x16,
930 mem: [u8; 16],
931 }
932
933 unsafe {
934 let a = U { reg: a }.mem;
935 let b = U { reg: b }.mem;
936
937 let select = |arr: &[u8; 16], byte: u8| {
941 if byte >= 16 { 0x00 } else { arr[byte as usize] }
942 };
943
944 U {
945 mem: [
946 select(&a, b[0]),
947 select(&a, b[1]),
948 select(&a, b[2]),
949 select(&a, b[3]),
950 select(&a, b[4]),
951 select(&a, b[5]),
952 select(&a, b[6]),
953 select(&a, b[7]),
954 select(&a, b[8]),
955 select(&a, b[9]),
956 select(&a, b[10]),
957 select(&a, b[11]),
958 select(&a, b[12]),
959 select(&a, b[13]),
960 select(&a, b[14]),
961 select(&a, b[15]),
962 ],
963 }
964 .reg
965 }
966}
967
968#[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))]
969fn i8x16_swizzle(_store: &mut dyn VMStore, _instance: InstanceId, _a: i8x16, _b: i8x16) -> i8x16 {
970 unreachable!()
971}
972
973#[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
976fn i8x16_shuffle(
977 _store: &mut dyn VMStore,
978 _instance: InstanceId,
979 a: i8x16,
980 b: i8x16,
981 c: i8x16,
982) -> i8x16 {
983 union U {
984 reg: i8x16,
985 mem: [u8; 16],
986 }
987
988 unsafe {
989 let ab = [U { reg: a }.mem, U { reg: b }.mem];
990 let c = U { reg: c }.mem;
991
992 let select = |arr: &[[u8; 16]; 2], byte: u8| {
996 if byte >= 32 {
997 0x00
998 } else if byte >= 16 {
999 arr[1][byte as usize - 16]
1000 } else {
1001 arr[0][byte as usize]
1002 }
1003 };
1004
1005 U {
1006 mem: [
1007 select(&ab, c[0]),
1008 select(&ab, c[1]),
1009 select(&ab, c[2]),
1010 select(&ab, c[3]),
1011 select(&ab, c[4]),
1012 select(&ab, c[5]),
1013 select(&ab, c[6]),
1014 select(&ab, c[7]),
1015 select(&ab, c[8]),
1016 select(&ab, c[9]),
1017 select(&ab, c[10]),
1018 select(&ab, c[11]),
1019 select(&ab, c[12]),
1020 select(&ab, c[13]),
1021 select(&ab, c[14]),
1022 select(&ab, c[15]),
1023 ],
1024 }
1025 .reg
1026 }
1027}
1028
1029#[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))]
1030fn i8x16_shuffle(
1031 _store: &mut dyn VMStore,
1032 _instance: InstanceId,
1033 _a: i8x16,
1034 _b: i8x16,
1035 _c: i8x16,
1036) -> i8x16 {
1037 unreachable!()
1038}
1039
1040fn fma_f32x4(
1041 _store: &mut dyn VMStore,
1042 _instance: InstanceId,
1043 x: f32x4,
1044 y: f32x4,
1045 z: f32x4,
1046) -> f32x4 {
1047 union U {
1048 reg: f32x4,
1049 mem: [f32; 4],
1050 }
1051
1052 unsafe {
1053 let x = U { reg: x }.mem;
1054 let y = U { reg: y }.mem;
1055 let z = U { reg: z }.mem;
1056
1057 U {
1058 mem: [
1059 x[0].wasm_mul_add(y[0], z[0]),
1060 x[1].wasm_mul_add(y[1], z[1]),
1061 x[2].wasm_mul_add(y[2], z[2]),
1062 x[3].wasm_mul_add(y[3], z[3]),
1063 ],
1064 }
1065 .reg
1066 }
1067}
1068
1069fn fma_f64x2(
1070 _store: &mut dyn VMStore,
1071 _instance: InstanceId,
1072 x: f64x2,
1073 y: f64x2,
1074 z: f64x2,
1075) -> f64x2 {
1076 union U {
1077 reg: f64x2,
1078 mem: [f64; 2],
1079 }
1080
1081 unsafe {
1082 let x = U { reg: x }.mem;
1083 let y = U { reg: y }.mem;
1084 let z = U { reg: z }.mem;
1085
1086 U {
1087 mem: [x[0].wasm_mul_add(y[0], z[0]), x[1].wasm_mul_add(y[1], z[1])],
1088 }
1089 .reg
1090 }
1091}
1092
1093fn trap(_store: &mut dyn VMStore, _instance: InstanceId, code: u8) -> Result<Infallible> {
1099 match CompiledTrap::from_u8(code).unwrap() {
1100 CompiledTrap::Normal(trap) => Err(trap.into()),
1101 CompiledTrap::InternalAssert => bail_bug!("internal assert hit in wasm"),
1102 CompiledTrap::GcHeapCorrupt => bail_bug!("GC heap corruption detected"),
1103 }
1104}
1105
1106fn raise(store: &mut dyn VMStore, _instance: InstanceId) {
1107 unsafe { crate::runtime::vm::traphandlers::raise_preexisting_trap(store) }
1111}
1112
1113#[cfg(feature = "stack-switching")]
1116fn cont_new(
1117 store: &mut dyn VMStore,
1118 instance: InstanceId,
1119 func: *mut u8,
1120 param_count: u32,
1121 result_count: u32,
1122) -> Result<Option<AllocationSize>> {
1123 let ans =
1124 crate::vm::stack_switching::cont_new(store, instance, func, param_count, result_count)?;
1125 Ok(Some(AllocationSize(ans.cast::<u8>() as usize)))
1126}
1127
1128#[cfg(feature = "gc")]
1129fn get_instance_id(_store: &mut dyn VMStore, instance: InstanceId) -> u32 {
1130 instance.as_u32()
1131}
1132
1133#[cfg(feature = "gc")]
1134fn throw_ref(store: &mut dyn VMStore, _instance: InstanceId, exnref: u32) -> Result<()> {
1135 let exnref = VMGcRef::from_raw_u32(exnref).ok_or_else(|| Trap::NullReference)?;
1136 Err(store.set_pending_exception(&exnref))
1137}
1138
1139fn breakpoint(store: &mut dyn VMStore, _instance: InstanceId) -> Result<()> {
1140 #[cfg(feature = "debug")]
1141 {
1142 store.block_on_debug_handler(crate::DebugEvent::Breakpoint)?;
1143 }
1144 let _ = store;
1146 Ok(())
1147}