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
619 let is_subtype: bool = store.engine().signatures().is_subtype(actual, expected);
620
621 log::trace!("is_subtype(actual={actual:?}, expected={expected:?}) -> {is_subtype}",);
622 is_subtype as u32
623}
624
625#[cfg(feature = "threads")]
627fn memory_atomic_notify(
628 store: &mut dyn VMStore,
629 instance: InstanceId,
630 memory_index: u32,
631 addr_index: u64,
632 count: u32,
633) -> Result<u32, Trap> {
634 let memory = DefinedMemoryIndex::from_u32(memory_index);
635 store
636 .instance_mut(instance)
637 .get_defined_memory_mut(memory)
638 .atomic_notify(addr_index, count)
639}
640
641#[cfg(feature = "threads")]
643fn memory_atomic_wait32(
644 store: &mut dyn VMStore,
645 instance: InstanceId,
646 memory_index: u32,
647 addr_index: u64,
648 expected: u32,
649 timeout: u64,
650) -> Result<u32, Trap> {
651 let timeout = (timeout as i64 >= 0).then(|| Duration::from_nanos(timeout));
652 let memory = DefinedMemoryIndex::from_u32(memory_index);
653 Ok(store
654 .instance_mut(instance)
655 .get_defined_memory_mut(memory)
656 .atomic_wait32(addr_index, expected, timeout)? as u32)
657}
658
659#[cfg(feature = "threads")]
661fn memory_atomic_wait64(
662 store: &mut dyn VMStore,
663 instance: InstanceId,
664 memory_index: u32,
665 addr_index: u64,
666 expected: u64,
667 timeout: u64,
668) -> Result<u32, Trap> {
669 let timeout = (timeout as i64 >= 0).then(|| Duration::from_nanos(timeout));
670 let memory = DefinedMemoryIndex::from_u32(memory_index);
671 Ok(store
672 .instance_mut(instance)
673 .get_defined_memory_mut(memory)
674 .atomic_wait64(addr_index, expected, timeout)? as u32)
675}
676
677fn out_of_gas(store: &mut dyn VMStore, _instance: InstanceId) -> Result<()> {
679 block_on!(store, async |store, _| {
680 if !store.refuel() {
681 return Err(Trap::OutOfFuel.into());
682 }
683 #[cfg(feature = "async")]
684 if store.fuel_yield_interval.is_some() {
685 store.yield_now().await;
686 }
687 Ok(())
688 })?
689}
690
691#[cfg(target_has_atomic = "64")]
693fn new_epoch(store: &mut dyn VMStore, _instance: InstanceId) -> Result<NextEpoch> {
694 use crate::UpdateDeadline;
695
696 #[cfg(feature = "debug")]
697 {
698 store.block_on_debug_handler(crate::DebugEvent::EpochYield)?;
699 }
700
701 let update_deadline = store.new_epoch_updated_deadline()?;
702 block_on!(store, async move |store, asyncness| {
703 #[cfg(not(feature = "async"))]
704 let _ = asyncness;
705
706 let delta = match update_deadline {
707 UpdateDeadline::Interrupt => return Err(Trap::Interrupt.into()),
708 UpdateDeadline::Continue(delta) => delta,
709
710 #[cfg(feature = "async")]
714 UpdateDeadline::Yield(delta) => {
715 if asyncness != Asyncness::Yes {
716 bail!(
717 "cannot use `UpdateDeadline::Yield` without using \
718 an async wasm entrypoint",
719 );
720 }
721 store.yield_now().await;
722 delta
723 }
724 #[cfg(feature = "async")]
725 UpdateDeadline::YieldCustom(delta, future) => {
726 if asyncness != Asyncness::Yes {
727 bail!(
728 "cannot use `UpdateDeadline::YieldCustom` without using \
729 an async wasm entrypoint",
730 );
731 }
732 future.await;
733 delta
734 }
735 };
736
737 store.set_epoch_deadline(delta);
740 Ok(NextEpoch(store.get_epoch_deadline()))
741 })?
742}
743
744struct NextEpoch(u64);
745
746unsafe impl HostResultHasUnwindSentinel for NextEpoch {
747 type Abi = u64;
748 const SENTINEL: u64 = u64::MAX;
749 fn into_abi(self) -> u64 {
750 self.0
751 }
752}
753
754#[cfg(feature = "wmemcheck")]
756fn check_malloc(store: &mut dyn VMStore, instance: InstanceId, addr: u32, len: u32) -> Result<()> {
757 let instance = store.instance_mut(instance);
758 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
759 let result = wmemcheck_state.malloc(addr as usize, len as usize);
760 wmemcheck_state.memcheck_on();
761 match result {
762 Ok(()) => {}
763 Err(DoubleMalloc { addr, len }) => {
764 bail!("Double malloc at addr {:#x} of size {}", addr, len)
765 }
766 Err(OutOfBounds { addr, len }) => {
767 bail!("Malloc out of bounds at addr {:#x} of size {}", addr, len);
768 }
769 _ => {
770 panic!("unreachable")
771 }
772 }
773 }
774 Ok(())
775}
776
777#[cfg(feature = "wmemcheck")]
779fn check_free(store: &mut dyn VMStore, instance: InstanceId, addr: u32) -> Result<()> {
780 let instance = store.instance_mut(instance);
781 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
782 let result = wmemcheck_state.free(addr as usize);
783 wmemcheck_state.memcheck_on();
784 match result {
785 Ok(()) => {}
786 Err(InvalidFree { addr }) => {
787 bail!("Invalid free at addr {:#x}", addr)
788 }
789 _ => {
790 panic!("unreachable")
791 }
792 }
793 }
794 Ok(())
795}
796
797#[cfg(feature = "wmemcheck")]
799fn check_load(
800 store: &mut dyn VMStore,
801 instance: InstanceId,
802 num_bytes: u32,
803 addr: u32,
804 offset: u32,
805) -> Result<()> {
806 let instance = store.instance_mut(instance);
807 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
808 let result = wmemcheck_state.read(addr as usize + offset as usize, num_bytes as usize);
809 match result {
810 Ok(()) => {}
811 Err(InvalidRead { addr, len }) => {
812 bail!("Invalid load at addr {:#x} of size {}", addr, len);
813 }
814 Err(OutOfBounds { addr, len }) => {
815 bail!("Load out of bounds at addr {:#x} of size {}", addr, len);
816 }
817 _ => {
818 panic!("unreachable")
819 }
820 }
821 }
822 Ok(())
823}
824
825#[cfg(feature = "wmemcheck")]
827fn check_store(
828 store: &mut dyn VMStore,
829 instance: InstanceId,
830 num_bytes: u32,
831 addr: u32,
832 offset: u32,
833) -> Result<()> {
834 let instance = store.instance_mut(instance);
835 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
836 let result = wmemcheck_state.write(addr as usize + offset as usize, num_bytes as usize);
837 match result {
838 Ok(()) => {}
839 Err(InvalidWrite { addr, len }) => {
840 bail!("Invalid store at addr {:#x} of size {}", addr, len)
841 }
842 Err(OutOfBounds { addr, len }) => {
843 bail!("Store out of bounds at addr {:#x} of size {}", addr, len)
844 }
845 _ => {
846 panic!("unreachable")
847 }
848 }
849 }
850 Ok(())
851}
852
853#[cfg(feature = "wmemcheck")]
855fn malloc_start(store: &mut dyn VMStore, instance: InstanceId) {
856 let instance = store.instance_mut(instance);
857 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
858 wmemcheck_state.memcheck_off();
859 }
860}
861
862#[cfg(feature = "wmemcheck")]
864fn free_start(store: &mut dyn VMStore, instance: InstanceId) {
865 let instance = store.instance_mut(instance);
866 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
867 wmemcheck_state.memcheck_off();
868 }
869}
870
871#[cfg(feature = "wmemcheck")]
873fn update_stack_pointer(_store: &mut dyn VMStore, _instance: InstanceId, _value: u32) {
874 }
881
882#[cfg(feature = "wmemcheck")]
884fn update_mem_size(store: &mut dyn VMStore, instance: InstanceId, num_pages: u32) {
885 let instance = store.instance_mut(instance);
886 if let Some(wmemcheck_state) = instance.wmemcheck_state_mut() {
887 const KIB: usize = 1024;
888 let num_bytes = num_pages as usize * 64 * KIB;
889 wmemcheck_state.update_mem_size(num_bytes);
890 }
891}
892
893fn floor_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
894 val.wasm_floor()
895}
896
897fn floor_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
898 val.wasm_floor()
899}
900
901fn ceil_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
902 val.wasm_ceil()
903}
904
905fn ceil_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
906 val.wasm_ceil()
907}
908
909fn trunc_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
910 val.wasm_trunc()
911}
912
913fn trunc_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
914 val.wasm_trunc()
915}
916
917fn nearest_f32(_store: &mut dyn VMStore, _instance: InstanceId, val: f32) -> f32 {
918 val.wasm_nearest()
919}
920
921fn nearest_f64(_store: &mut dyn VMStore, _instance: InstanceId, val: f64) -> f64 {
922 val.wasm_nearest()
923}
924
925#[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
928fn i8x16_swizzle(_store: &mut dyn VMStore, _instance: InstanceId, a: i8x16, b: i8x16) -> i8x16 {
929 union U {
930 reg: i8x16,
931 mem: [u8; 16],
932 }
933
934 unsafe {
935 let a = U { reg: a }.mem;
936 let b = U { reg: b }.mem;
937
938 let select = |arr: &[u8; 16], byte: u8| {
942 if byte >= 16 { 0x00 } else { arr[byte as usize] }
943 };
944
945 U {
946 mem: [
947 select(&a, b[0]),
948 select(&a, b[1]),
949 select(&a, b[2]),
950 select(&a, b[3]),
951 select(&a, b[4]),
952 select(&a, b[5]),
953 select(&a, b[6]),
954 select(&a, b[7]),
955 select(&a, b[8]),
956 select(&a, b[9]),
957 select(&a, b[10]),
958 select(&a, b[11]),
959 select(&a, b[12]),
960 select(&a, b[13]),
961 select(&a, b[14]),
962 select(&a, b[15]),
963 ],
964 }
965 .reg
966 }
967}
968
969#[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))]
970fn i8x16_swizzle(_store: &mut dyn VMStore, _instance: InstanceId, _a: i8x16, _b: i8x16) -> i8x16 {
971 unreachable!()
972}
973
974#[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
977fn i8x16_shuffle(
978 _store: &mut dyn VMStore,
979 _instance: InstanceId,
980 a: i8x16,
981 b: i8x16,
982 c: i8x16,
983) -> i8x16 {
984 union U {
985 reg: i8x16,
986 mem: [u8; 16],
987 }
988
989 unsafe {
990 let ab = [U { reg: a }.mem, U { reg: b }.mem];
991 let c = U { reg: c }.mem;
992
993 let select = |arr: &[[u8; 16]; 2], byte: u8| {
997 if byte >= 32 {
998 0x00
999 } else if byte >= 16 {
1000 arr[1][byte as usize - 16]
1001 } else {
1002 arr[0][byte as usize]
1003 }
1004 };
1005
1006 U {
1007 mem: [
1008 select(&ab, c[0]),
1009 select(&ab, c[1]),
1010 select(&ab, c[2]),
1011 select(&ab, c[3]),
1012 select(&ab, c[4]),
1013 select(&ab, c[5]),
1014 select(&ab, c[6]),
1015 select(&ab, c[7]),
1016 select(&ab, c[8]),
1017 select(&ab, c[9]),
1018 select(&ab, c[10]),
1019 select(&ab, c[11]),
1020 select(&ab, c[12]),
1021 select(&ab, c[13]),
1022 select(&ab, c[14]),
1023 select(&ab, c[15]),
1024 ],
1025 }
1026 .reg
1027 }
1028}
1029
1030#[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))]
1031fn i8x16_shuffle(
1032 _store: &mut dyn VMStore,
1033 _instance: InstanceId,
1034 _a: i8x16,
1035 _b: i8x16,
1036 _c: i8x16,
1037) -> i8x16 {
1038 unreachable!()
1039}
1040
1041fn fma_f32x4(
1042 _store: &mut dyn VMStore,
1043 _instance: InstanceId,
1044 x: f32x4,
1045 y: f32x4,
1046 z: f32x4,
1047) -> f32x4 {
1048 union U {
1049 reg: f32x4,
1050 mem: [f32; 4],
1051 }
1052
1053 unsafe {
1054 let x = U { reg: x }.mem;
1055 let y = U { reg: y }.mem;
1056 let z = U { reg: z }.mem;
1057
1058 U {
1059 mem: [
1060 x[0].wasm_mul_add(y[0], z[0]),
1061 x[1].wasm_mul_add(y[1], z[1]),
1062 x[2].wasm_mul_add(y[2], z[2]),
1063 x[3].wasm_mul_add(y[3], z[3]),
1064 ],
1065 }
1066 .reg
1067 }
1068}
1069
1070fn fma_f64x2(
1071 _store: &mut dyn VMStore,
1072 _instance: InstanceId,
1073 x: f64x2,
1074 y: f64x2,
1075 z: f64x2,
1076) -> f64x2 {
1077 union U {
1078 reg: f64x2,
1079 mem: [f64; 2],
1080 }
1081
1082 unsafe {
1083 let x = U { reg: x }.mem;
1084 let y = U { reg: y }.mem;
1085 let z = U { reg: z }.mem;
1086
1087 U {
1088 mem: [x[0].wasm_mul_add(y[0], z[0]), x[1].wasm_mul_add(y[1], z[1])],
1089 }
1090 .reg
1091 }
1092}
1093
1094fn trap(_store: &mut dyn VMStore, _instance: InstanceId, code: u8) -> Result<Infallible> {
1100 match CompiledTrap::from_u8(code).unwrap() {
1101 CompiledTrap::Normal(trap) => Err(trap.into()),
1102 CompiledTrap::InternalAssert => bail_bug!("internal assert hit in wasm"),
1103 CompiledTrap::GcHeapCorrupt => bail_bug!("GC heap corruption detected"),
1104 }
1105}
1106
1107fn raise(store: &mut dyn VMStore, _instance: InstanceId) {
1108 unsafe { crate::runtime::vm::traphandlers::raise_preexisting_trap(store) }
1112}
1113
1114#[cfg(feature = "stack-switching")]
1117fn cont_new(
1118 store: &mut dyn VMStore,
1119 instance: InstanceId,
1120 func: *mut u8,
1121 param_count: u32,
1122 result_count: u32,
1123) -> Result<Option<AllocationSize>> {
1124 let ans =
1125 crate::vm::stack_switching::cont_new(store, instance, func, param_count, result_count)?;
1126 Ok(Some(AllocationSize(ans.cast::<u8>() as usize)))
1127}
1128
1129#[cfg(feature = "gc")]
1130fn get_instance_id(_store: &mut dyn VMStore, instance: InstanceId) -> u32 {
1131 instance.as_u32()
1132}
1133
1134#[cfg(feature = "gc")]
1135fn throw_ref(store: &mut dyn VMStore, _instance: InstanceId, exnref: u32) -> Result<()> {
1136 let exnref = VMGcRef::from_raw_u32(exnref).ok_or_else(|| Trap::NullReference)?;
1137 Err(store.set_pending_exception(&exnref))
1138}
1139
1140fn breakpoint(store: &mut dyn VMStore, _instance: InstanceId) -> Result<()> {
1141 #[cfg(feature = "debug")]
1142 {
1143 store.block_on_debug_handler(crate::DebugEvent::Breakpoint)?;
1144 }
1145 let _ = store;
1147 Ok(())
1148}