Skip to main content

wasmer_vm/
libcalls.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Runtime library calls.
5//!
6//! Note that Wasm compilers may sometimes perform these inline rather than
7//! calling them, particularly when CPUs have special instructions which compute
8//! them directly.
9//!
10//! These functions are called by compiled Wasm code, and therefore must take
11//! certain care about some things:
12//!
13//! * They must always be `pub extern "C"` and should only contain basic, raw
14//!   i32/i64/f32/f64/pointer parameters that are safe to pass across the system
15//!   ABI!
16//!
17//! * If any nested function propagates an `Err(trap)` out to the library
18//!   function frame, we need to raise it. This involves some nasty and quite
19//!   unsafe code under the covers! Notable, after raising the trap, drops
20//!   **will not** be run for local variables! This can lead to things like
21//!   leaking `VMInstance`s which leads to never deallocating JIT code,
22//!   instances, and modules! Therefore, always use nested blocks to ensure
23//!   drops run before raising a trap:
24//!
25//!   ```ignore
26//!   pub extern "C" fn my_lib_function(...) {
27//!       let result = {
28//!           // Do everything in here so drops run at the end of the block.
29//!           ...
30//!       };
31//!       if let Err(trap) = result {
32//!           // Now we can safely raise the trap without leaking!
33//!           raise_lib_trap(trap);
34//!       }
35//!   }
36//!   ```
37
38#![allow(missing_docs)] // For some reason lint fails saying that `LibCall` is not documented, when it actually is
39
40use std::{ffi::c_void, panic};
41mod eh;
42
43use crate::trap::{Trap, TrapCode, raise_lib_trap};
44use crate::vmcontext::VMContext;
45use crate::{
46    InternalStoreHandle,
47    table::{RawTableElement, TableElement},
48};
49use crate::{VMExceptionObj, probestack::PROBESTACK};
50use crate::{VMFuncRef, on_host_stack};
51pub use eh::{throw, wasmer_eh_personality, wasmer_eh_personality2};
52pub use wasmer_types::LibCall;
53use wasmer_types::{
54    DataIndex, ElemIndex, FunctionIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, RawValue,
55    TableIndex, TagIndex, Type,
56};
57
58/// Implementation of f32.ceil
59#[unsafe(no_mangle)]
60pub extern "C" fn wasmer_vm_f32_ceil(x: f32) -> f32 {
61    x.ceil()
62}
63
64/// Implementation of f32.floor
65#[unsafe(no_mangle)]
66pub extern "C" fn wasmer_vm_f32_floor(x: f32) -> f32 {
67    x.floor()
68}
69
70/// Implementation of f32.trunc
71#[unsafe(no_mangle)]
72pub extern "C" fn wasmer_vm_f32_trunc(x: f32) -> f32 {
73    x.trunc()
74}
75
76/// Implementation of f32.nearest
77#[allow(clippy::float_arithmetic, clippy::float_cmp)]
78#[unsafe(no_mangle)]
79pub extern "C" fn wasmer_vm_f32_nearest(x: f32) -> f32 {
80    // Rust doesn't have a nearest function, so do it manually.
81    if x == 0.0 {
82        // Preserve the sign of zero.
83        x
84    } else {
85        // Nearest is either ceil or floor depending on which is nearest or even.
86        let u = x.ceil();
87        let d = x.floor();
88        let um = (x - u).abs();
89        let dm = (x - d).abs();
90        if um < dm
91            || (um == dm && {
92                let h = u / 2.;
93                h.floor() == h
94            })
95        {
96            u
97        } else {
98            d
99        }
100    }
101}
102
103/// Implementation of f32.sqrt
104#[unsafe(no_mangle)]
105pub extern "C" fn wasmer_vm_f32_sqrt(x: f32) -> f32 {
106    x.sqrt()
107}
108
109/// Implementation of f64.sqrt
110#[unsafe(no_mangle)]
111pub extern "C" fn wasmer_vm_f64_sqrt(x: f64) -> f64 {
112    x.sqrt()
113}
114
115/// Implementation of f64.ceil
116#[unsafe(no_mangle)]
117pub extern "C" fn wasmer_vm_f64_ceil(x: f64) -> f64 {
118    x.ceil()
119}
120
121/// Implementation of f64.floor
122#[unsafe(no_mangle)]
123pub extern "C" fn wasmer_vm_f64_floor(x: f64) -> f64 {
124    x.floor()
125}
126
127/// Implementation of f64.trunc
128#[unsafe(no_mangle)]
129pub extern "C" fn wasmer_vm_f64_trunc(x: f64) -> f64 {
130    x.trunc()
131}
132
133/// Implementation of f64.nearest
134#[allow(clippy::float_arithmetic, clippy::float_cmp)]
135#[unsafe(no_mangle)]
136pub extern "C" fn wasmer_vm_f64_nearest(x: f64) -> f64 {
137    // Rust doesn't have a nearest function, so do it manually.
138    if x == 0.0 {
139        // Preserve the sign of zero.
140        x
141    } else {
142        // Nearest is either ceil or floor depending on which is nearest or even.
143        let u = x.ceil();
144        let d = x.floor();
145        let um = (x - u).abs();
146        let dm = (x - d).abs();
147        if um < dm
148            || (um == dm && {
149                let h = u / 2.;
150                h.floor() == h
151            })
152        {
153            u
154        } else {
155            d
156        }
157    }
158}
159
160/// Implementation of memory.grow for locally-defined 32-bit memories.
161///
162/// # Safety
163///
164/// `vmctx` must be dereferenceable.
165#[unsafe(no_mangle)]
166pub unsafe extern "C" fn wasmer_vm_memory32_grow(
167    vmctx: *mut VMContext,
168    delta_in_pages: u32,
169    memory_index: u32,
170) -> u32 {
171    unsafe {
172        on_host_stack(|| {
173            let instance = (*vmctx).instance_mut();
174            let memory_index = LocalMemoryIndex::from_u32(memory_index);
175
176            instance
177                .memory_grow(memory_index, delta_in_pages)
178                .map_or(u32::MAX, |pages| pages.0)
179        })
180    }
181}
182
183/// Implementation of memory.grow for imported 32-bit memories.
184///
185/// # Safety
186///
187/// `vmctx` must be dereferenceable.
188#[unsafe(no_mangle)]
189pub unsafe extern "C" fn wasmer_vm_imported_memory32_grow(
190    vmctx: *mut VMContext,
191    delta_in_pages: u32,
192    memory_index: u32,
193) -> u32 {
194    unsafe {
195        on_host_stack(|| {
196            let instance = (*vmctx).instance_mut();
197            let memory_index = MemoryIndex::from_u32(memory_index);
198
199            instance
200                .imported_memory_grow(memory_index, delta_in_pages)
201                .map_or(u32::MAX, |pages| pages.0)
202        })
203    }
204}
205
206/// Implementation of memory.size for locally-defined 32-bit memories.
207///
208/// # Safety
209///
210/// `vmctx` must be dereferenceable.
211#[unsafe(no_mangle)]
212pub unsafe extern "C" fn wasmer_vm_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 {
213    unsafe {
214        let instance = (*vmctx).instance();
215        let memory_index = LocalMemoryIndex::from_u32(memory_index);
216
217        instance.memory_size(memory_index).0
218    }
219}
220
221/// Implementation of memory.size for imported 32-bit memories.
222///
223/// # Safety
224///
225/// `vmctx` must be dereferenceable.
226#[unsafe(no_mangle)]
227pub unsafe extern "C" fn wasmer_vm_imported_memory32_size(
228    vmctx: *mut VMContext,
229    memory_index: u32,
230) -> u32 {
231    unsafe {
232        let instance = (*vmctx).instance();
233        let memory_index = MemoryIndex::from_u32(memory_index);
234
235        instance.imported_memory_size(memory_index).0
236    }
237}
238
239/// Implementation of `table.copy`.
240///
241/// # Safety
242///
243/// `vmctx` must be dereferenceable.
244#[unsafe(no_mangle)]
245pub unsafe extern "C" fn wasmer_vm_table_copy(
246    vmctx: *mut VMContext,
247    dst_table_index: u32,
248    src_table_index: u32,
249    dst: u32,
250    src: u32,
251    len: u32,
252) {
253    unsafe {
254        let result = {
255            let dst_table_index = TableIndex::from_u32(dst_table_index);
256            let src_table_index = TableIndex::from_u32(src_table_index);
257            (*vmctx)
258                .instance_mut()
259                .table_copy(dst_table_index, src_table_index, dst, src, len)
260        };
261        if let Err(trap) = result {
262            raise_lib_trap(trap);
263        }
264    }
265}
266
267/// Implementation of `table.init`.
268///
269/// # Safety
270///
271/// `vmctx` must be dereferenceable.
272#[unsafe(no_mangle)]
273pub unsafe extern "C" fn wasmer_vm_table_init(
274    vmctx: *mut VMContext,
275    table_index: u32,
276    elem_index: u32,
277    dst: u32,
278    src: u32,
279    len: u32,
280) {
281    unsafe {
282        let result = {
283            let table_index = TableIndex::from_u32(table_index);
284            let elem_index = ElemIndex::from_u32(elem_index);
285            let instance = (*vmctx).instance_mut();
286            instance.table_init(table_index, elem_index, dst, src, len)
287        };
288        if let Err(trap) = result {
289            raise_lib_trap(trap);
290        }
291    }
292}
293
294/// Implementation of `table.fill`.
295///
296/// # Safety
297///
298/// `vmctx` must be dereferenceable.
299#[unsafe(no_mangle)]
300pub unsafe extern "C" fn wasmer_vm_table_fill(
301    vmctx: *mut VMContext,
302    table_index: u32,
303    start_idx: u32,
304    item: RawTableElement,
305    len: u32,
306) {
307    unsafe {
308        let result = {
309            let table_index = TableIndex::from_u32(table_index);
310            let instance = (*vmctx).instance_mut();
311            let elem = match instance.get_table(table_index).ty().ty {
312                Type::ExternRef => TableElement::ExternRef(item.extern_ref),
313                Type::FuncRef => TableElement::FuncRef(item.func_ref),
314                _ => panic!("Unrecognized table type: does not contain references"),
315            };
316
317            instance.table_fill(table_index, start_idx, elem, len)
318        };
319        if let Err(trap) = result {
320            raise_lib_trap(trap);
321        }
322    }
323}
324
325/// Implementation of `table.size`.
326///
327/// # Safety
328///
329/// `vmctx` must be dereferenceable.
330#[unsafe(no_mangle)]
331pub unsafe extern "C" fn wasmer_vm_table_size(vmctx: *mut VMContext, table_index: u32) -> u32 {
332    unsafe {
333        let instance = (*vmctx).instance();
334        let table_index = LocalTableIndex::from_u32(table_index);
335
336        instance.table_size(table_index)
337    }
338}
339
340/// Implementation of `table.size` for imported tables.
341///
342/// # Safety
343///
344/// `vmctx` must be dereferenceable.
345#[unsafe(no_mangle)]
346pub unsafe extern "C" fn wasmer_vm_imported_table_size(
347    vmctx: *mut VMContext,
348    table_index: u32,
349) -> u32 {
350    unsafe {
351        let instance = (*vmctx).instance();
352        let table_index = TableIndex::from_u32(table_index);
353
354        instance.imported_table_size(table_index)
355    }
356}
357
358/// Implementation of `table.get`.
359///
360/// # Safety
361///
362/// `vmctx` must be dereferenceable.
363#[unsafe(no_mangle)]
364pub unsafe extern "C" fn wasmer_vm_table_get(
365    vmctx: *mut VMContext,
366    table_index: u32,
367    elem_index: u32,
368) -> RawTableElement {
369    unsafe {
370        let instance = (*vmctx).instance();
371        let table_index = LocalTableIndex::from_u32(table_index);
372
373        // TODO: type checking, maybe have specialized accessors
374        match instance.table_get(table_index, elem_index) {
375            Some(table_ref) => table_ref.into(),
376            None => raise_lib_trap(Trap::lib(TrapCode::TableAccessOutOfBounds)),
377        }
378    }
379}
380
381/// Implementation of `table.get` for imported tables.
382///
383/// # Safety
384///
385/// `vmctx` must be dereferenceable.
386#[unsafe(no_mangle)]
387pub unsafe extern "C" fn wasmer_vm_imported_table_get(
388    vmctx: *mut VMContext,
389    table_index: u32,
390    elem_index: u32,
391) -> RawTableElement {
392    unsafe {
393        let instance = (*vmctx).instance_mut();
394        let table_index = TableIndex::from_u32(table_index);
395
396        // TODO: type checking, maybe have specialized accessors
397        match instance.imported_table_get(table_index, elem_index) {
398            Some(table_ref) => table_ref.into(),
399            None => raise_lib_trap(Trap::lib(TrapCode::TableAccessOutOfBounds)),
400        }
401    }
402}
403
404/// Implementation of `table.set`.
405///
406/// # Safety
407///
408/// `vmctx` must be dereferenceable.
409///
410/// It is the caller's responsibility to increment the ref count of any ref counted
411/// type before passing it to this function.
412#[unsafe(no_mangle)]
413pub unsafe extern "C" fn wasmer_vm_table_set(
414    vmctx: *mut VMContext,
415    table_index: u32,
416    elem_index: u32,
417    value: RawTableElement,
418) {
419    unsafe {
420        let instance = (*vmctx).instance_mut();
421        let table_index = LocalTableIndex::from_u32(table_index);
422
423        let elem = match instance.get_local_table(table_index).ty().ty {
424            Type::ExternRef => TableElement::ExternRef(value.extern_ref),
425            Type::FuncRef => TableElement::FuncRef(value.func_ref),
426            _ => panic!("Unrecognized table type: does not contain references"),
427        };
428
429        // TODO: type checking, maybe have specialized accessors
430        let result = instance.table_set(table_index, elem_index, elem);
431
432        if let Err(trap) = result {
433            raise_lib_trap(trap);
434        }
435    }
436}
437
438/// Implementation of `table.set` for imported tables.
439///
440/// # Safety
441///
442/// `vmctx` must be dereferenceable.
443#[unsafe(no_mangle)]
444pub unsafe extern "C" fn wasmer_vm_imported_table_set(
445    vmctx: *mut VMContext,
446    table_index: u32,
447    elem_index: u32,
448    value: RawTableElement,
449) {
450    unsafe {
451        let instance = (*vmctx).instance_mut();
452        let table_index = TableIndex::from_u32(table_index);
453        let elem = match instance.get_table(table_index).ty().ty {
454            Type::ExternRef => TableElement::ExternRef(value.extern_ref),
455            Type::FuncRef => TableElement::FuncRef(value.func_ref),
456            _ => panic!("Unrecognized table type: does not contain references"),
457        };
458
459        let result = instance.imported_table_set(table_index, elem_index, elem);
460
461        if let Err(trap) = result {
462            raise_lib_trap(trap);
463        }
464    }
465}
466
467/// Implementation of `table.grow` for locally-defined tables.
468///
469/// # Safety
470///
471/// `vmctx` must be dereferenceable.
472#[unsafe(no_mangle)]
473pub unsafe extern "C" fn wasmer_vm_table_grow(
474    vmctx: *mut VMContext,
475    init_value: RawTableElement,
476    delta: u32,
477    table_index: u32,
478) -> u32 {
479    unsafe {
480        on_host_stack(|| {
481            let instance = (*vmctx).instance_mut();
482            let table_index = LocalTableIndex::from_u32(table_index);
483
484            let init_value = match instance.get_local_table(table_index).ty().ty {
485                Type::ExternRef => TableElement::ExternRef(init_value.extern_ref),
486                Type::FuncRef => TableElement::FuncRef(init_value.func_ref),
487                _ => panic!("Unrecognized table type: does not contain references"),
488            };
489
490            instance
491                .table_grow(table_index, delta, init_value)
492                .unwrap_or(u32::MAX)
493        })
494    }
495}
496
497/// Implementation of `table.grow` for imported tables.
498///
499/// # Safety
500///
501/// `vmctx` must be dereferenceable.
502#[unsafe(no_mangle)]
503pub unsafe extern "C" fn wasmer_vm_imported_table_grow(
504    vmctx: *mut VMContext,
505    init_value: RawTableElement,
506    delta: u32,
507    table_index: u32,
508) -> u32 {
509    unsafe {
510        on_host_stack(|| {
511            let instance = (*vmctx).instance_mut();
512            let table_index = TableIndex::from_u32(table_index);
513            let init_value = match instance.get_table(table_index).ty().ty {
514                Type::ExternRef => TableElement::ExternRef(init_value.extern_ref),
515                Type::FuncRef => TableElement::FuncRef(init_value.func_ref),
516                _ => panic!("Unrecognized table type: does not contain references"),
517            };
518
519            instance
520                .imported_table_grow(table_index, delta, init_value)
521                .unwrap_or(u32::MAX)
522        })
523    }
524}
525
526/// Implementation of `func.ref`.
527///
528/// # Safety
529///
530/// `vmctx` must be dereferenceable.
531#[unsafe(no_mangle)]
532pub unsafe extern "C" fn wasmer_vm_func_ref(
533    vmctx: *mut VMContext,
534    function_index: u32,
535) -> VMFuncRef {
536    unsafe {
537        let instance = (*vmctx).instance();
538        let function_index = FunctionIndex::from_u32(function_index);
539
540        instance.func_ref(function_index).unwrap()
541    }
542}
543
544/// Implementation of `elem.drop`.
545///
546/// # Safety
547///
548/// `vmctx` must be dereferenceable.
549#[unsafe(no_mangle)]
550pub unsafe extern "C" fn wasmer_vm_elem_drop(vmctx: *mut VMContext, elem_index: u32) {
551    unsafe {
552        on_host_stack(|| {
553            let elem_index = ElemIndex::from_u32(elem_index);
554            let instance = (*vmctx).instance();
555            instance.elem_drop(elem_index);
556        })
557    }
558}
559
560/// Implementation of `memory.copy`.
561///
562/// # Safety
563///
564/// `vmctx` must be dereferenceable.
565#[unsafe(no_mangle)]
566pub unsafe extern "C" fn wasmer_vm_memory32_copy(
567    vmctx: *mut VMContext,
568    dst_memory_index: u32,
569    src_memory_index: u32,
570    dst: u32,
571    src: u32,
572    len: u32,
573) {
574    unsafe {
575        let result = {
576            let dst_memory_index = MemoryIndex::from_u32(dst_memory_index);
577            let src_memory_index = MemoryIndex::from_u32(src_memory_index);
578            let instance = (*vmctx).instance();
579            instance.memory_copy(dst_memory_index, src_memory_index, dst, src, len)
580        };
581        if let Err(trap) = result {
582            raise_lib_trap(trap);
583        }
584    }
585}
586
587/// Implementation of `memory.fill` for locally defined memories.
588///
589/// # Safety
590///
591/// `vmctx` must be dereferenceable.
592#[unsafe(no_mangle)]
593pub unsafe extern "C" fn wasmer_vm_memory32_fill(
594    vmctx: *mut VMContext,
595    memory_index: u32,
596    dst: u32,
597    val: u32,
598    len: u32,
599) {
600    unsafe {
601        let result = {
602            let memory_index = LocalMemoryIndex::from_u32(memory_index);
603            let instance = (*vmctx).instance();
604            instance.local_memory_fill(memory_index, dst, val, len)
605        };
606        if let Err(trap) = result {
607            raise_lib_trap(trap);
608        }
609    }
610}
611
612/// Implementation of `memory.fill` for imported memories.
613///
614/// # Safety
615///
616/// `vmctx` must be dereferenceable.
617#[unsafe(no_mangle)]
618pub unsafe extern "C" fn wasmer_vm_imported_memory32_fill(
619    vmctx: *mut VMContext,
620    memory_index: u32,
621    dst: u32,
622    val: u32,
623    len: u32,
624) {
625    unsafe {
626        let result = {
627            let memory_index = MemoryIndex::from_u32(memory_index);
628            let instance = (*vmctx).instance();
629            instance.imported_memory_fill(memory_index, dst, val, len)
630        };
631        if let Err(trap) = result {
632            raise_lib_trap(trap);
633        }
634    }
635}
636
637/// Implementation of `memory.init`.
638///
639/// # Safety
640///
641/// `vmctx` must be dereferenceable.
642#[unsafe(no_mangle)]
643pub unsafe extern "C" fn wasmer_vm_memory32_init(
644    vmctx: *mut VMContext,
645    memory_index: u32,
646    data_index: u32,
647    dst: u32,
648    src: u32,
649    len: u32,
650) {
651    unsafe {
652        let result = {
653            let memory_index = MemoryIndex::from_u32(memory_index);
654            let data_index = DataIndex::from_u32(data_index);
655            let instance = (*vmctx).instance();
656            instance.memory_init(memory_index, data_index, dst, src, len)
657        };
658        if let Err(trap) = result {
659            raise_lib_trap(trap);
660        }
661    }
662}
663
664/// Implementation of `data.drop`.
665///
666/// # Safety
667///
668/// `vmctx` must be dereferenceable.
669#[unsafe(no_mangle)]
670pub unsafe extern "C" fn wasmer_vm_data_drop(vmctx: *mut VMContext, data_index: u32) {
671    unsafe {
672        on_host_stack(|| {
673            let data_index = DataIndex::from_u32(data_index);
674            let instance = (*vmctx).instance();
675            instance.data_drop(data_index)
676        })
677    }
678}
679
680/// Implementation for raising a trap
681///
682/// # Safety
683///
684/// Only safe to call when wasm code is on the stack, aka `wasmer_call` or
685/// `wasmer_call_trampoline` must have been previously called.
686#[unsafe(no_mangle)]
687pub unsafe extern "C" fn wasmer_vm_raise_trap(trap_code: TrapCode) -> ! {
688    unsafe {
689        let trap = Trap::lib(trap_code);
690        raise_lib_trap(trap)
691    }
692}
693
694/// (debug) Print an usize.
695#[unsafe(no_mangle)]
696pub extern "C-unwind" fn wasmer_vm_dbg_usize(value: usize) {
697    #[allow(clippy::print_stdout)]
698    {
699        println!("wasmer_vm_dbg_usize: {value}");
700    }
701}
702
703/// (debug) Print a string.
704#[unsafe(no_mangle)]
705pub extern "C-unwind" fn wasmer_vm_dbg_str(ptr: usize, len: u32) {
706    #[allow(clippy::print_stdout)]
707    unsafe {
708        let str = std::str::from_utf8(std::slice::from_raw_parts(ptr as _, len as _))
709            .unwrap_or("wasmer_vm_dbg_str failed");
710        eprintln!("{str}");
711    }
712}
713
714/// Implementation for throwing an exception.
715///
716/// # Safety
717///
718/// Calls libunwind to perform unwinding magic.
719#[unsafe(no_mangle)]
720pub unsafe extern "C-unwind" fn wasmer_vm_throw(vmctx: *mut VMContext, exnref: u32) -> ! {
721    let instance = unsafe { (*vmctx).instance() };
722    unsafe { eh::throw(instance.context(), exnref) }
723}
724
725/// Implementation for allocating an exception. Returns the exnref, i.e. a handle to the
726/// exception within the store.
727///
728/// # Safety
729///
730/// The vmctx pointer must be dereferenceable.
731#[unsafe(no_mangle)]
732pub unsafe extern "C-unwind" fn wasmer_vm_alloc_exception(vmctx: *mut VMContext, tag: u32) -> u32 {
733    let instance = unsafe { (*vmctx).instance_mut() };
734    let unique_tag = instance.shared_tag_ptr(TagIndex::from_u32(tag)).index();
735    let exn = VMExceptionObj::new_zeroed(
736        instance.context(),
737        InternalStoreHandle::from_index(unique_tag as usize).unwrap(),
738    );
739    let exnref = InternalStoreHandle::new(instance.context_mut(), exn);
740    exnref.index() as u32
741}
742
743/// Given a VMContext and an exnref (handle to an exception within the store),
744/// returns a pointer to the payload buffer of the underlying VMExceptionObj.
745#[unsafe(no_mangle)]
746pub extern "C-unwind" fn wasmer_vm_read_exnref(
747    vmctx: *mut VMContext,
748    exnref: u32,
749) -> *mut RawValue {
750    let exn = eh::exn_obj_from_exnref(vmctx, exnref);
751    unsafe { (*exn).payload().as_ptr() as *mut RawValue }
752}
753
754/// Given a pointer to a caught exception, return the exnref contained within.
755///
756/// # Safety
757///
758/// `exception` must be a pointer the platform-specific exception type; this is
759/// `UwExceptionWrapper` for gcc.
760#[unsafe(no_mangle)]
761pub unsafe extern "C-unwind" fn wasmer_vm_exception_into_exnref(exception: *mut c_void) -> u32 {
762    unsafe {
763        let exnref = eh::read_exnref(exception);
764        eh::delete_exception(exception);
765        exnref
766    }
767}
768
769/// Probestack check
770///
771/// # Safety
772///
773/// This function does not follow the standard function ABI, and is called as
774/// part of the function prologue.
775#[unsafe(no_mangle)]
776pub static WASMER_VM_PROBESTACK: unsafe extern "C" fn() = PROBESTACK;
777
778/// Implementation of memory.wait32 for locally-defined 32-bit memories.
779///
780/// # Safety
781///
782/// `vmctx` must be dereferenceable.
783#[unsafe(no_mangle)]
784pub unsafe extern "C" fn wasmer_vm_memory32_atomic_wait32(
785    vmctx: *mut VMContext,
786    memory_index: u32,
787    dst: u32,
788    val: u32,
789    timeout: i64,
790) -> u32 {
791    unsafe {
792        let result = {
793            let instance = (*vmctx).instance_mut();
794            let memory_index = LocalMemoryIndex::from_u32(memory_index);
795
796            instance.local_memory_wait32(memory_index, dst, val, timeout)
797        };
798        if let Err(trap) = result {
799            raise_lib_trap(trap);
800        }
801        result.unwrap()
802    }
803}
804
805/// Implementation of memory.wait32 for imported 32-bit memories.
806///
807/// # Safety
808///
809/// `vmctx` must be dereferenceable.
810#[unsafe(no_mangle)]
811pub unsafe extern "C" fn wasmer_vm_imported_memory32_atomic_wait32(
812    vmctx: *mut VMContext,
813    memory_index: u32,
814    dst: u32,
815    val: u32,
816    timeout: i64,
817) -> u32 {
818    unsafe {
819        let result = {
820            let instance = (*vmctx).instance_mut();
821            let memory_index = MemoryIndex::from_u32(memory_index);
822
823            instance.imported_memory_wait32(memory_index, dst, val, timeout)
824        };
825        if let Err(trap) = result {
826            raise_lib_trap(trap);
827        }
828        result.unwrap()
829    }
830}
831
832/// Implementation of memory.wait64 for locally-defined 32-bit memories.
833///
834/// # Safety
835///
836/// `vmctx` must be dereferenceable.
837#[unsafe(no_mangle)]
838pub unsafe extern "C" fn wasmer_vm_memory32_atomic_wait64(
839    vmctx: *mut VMContext,
840    memory_index: u32,
841    dst: u32,
842    val: u64,
843    timeout: i64,
844) -> u32 {
845    unsafe {
846        let result = {
847            let instance = (*vmctx).instance_mut();
848            let memory_index = LocalMemoryIndex::from_u32(memory_index);
849
850            instance.local_memory_wait64(memory_index, dst, val, timeout)
851        };
852        if let Err(trap) = result {
853            raise_lib_trap(trap);
854        }
855        result.unwrap()
856    }
857}
858
859/// Implementation of memory.wait64 for imported 32-bit memories.
860///
861/// # Safety
862///
863/// `vmctx` must be dereferenceable.
864#[unsafe(no_mangle)]
865pub unsafe extern "C" fn wasmer_vm_imported_memory32_atomic_wait64(
866    vmctx: *mut VMContext,
867    memory_index: u32,
868    dst: u32,
869    val: u64,
870    timeout: i64,
871) -> u32 {
872    unsafe {
873        let result = {
874            let instance = (*vmctx).instance_mut();
875            let memory_index = MemoryIndex::from_u32(memory_index);
876
877            instance.imported_memory_wait64(memory_index, dst, val, timeout)
878        };
879        if let Err(trap) = result {
880            raise_lib_trap(trap);
881        }
882        result.unwrap()
883    }
884}
885
886/// Implementation of memory.notify for locally-defined 32-bit memories.
887///
888/// # Safety
889///
890/// `vmctx` must be dereferenceable.
891#[unsafe(no_mangle)]
892pub unsafe extern "C" fn wasmer_vm_memory32_atomic_notify(
893    vmctx: *mut VMContext,
894    memory_index: u32,
895    dst: u32,
896    cnt: u32,
897) -> u32 {
898    unsafe {
899        let result = {
900            let instance = (*vmctx).instance_mut();
901            let memory_index = LocalMemoryIndex::from_u32(memory_index);
902
903            instance.local_memory_notify(memory_index, dst, cnt)
904        };
905        if let Err(trap) = result {
906            raise_lib_trap(trap);
907        }
908        result.unwrap()
909    }
910}
911
912/// Implementation of memory.notify for imported 32-bit memories.
913///
914/// # Safety
915///
916/// `vmctx` must be dereferenceable.
917#[unsafe(no_mangle)]
918pub unsafe extern "C" fn wasmer_vm_imported_memory32_atomic_notify(
919    vmctx: *mut VMContext,
920    memory_index: u32,
921    dst: u32,
922    cnt: u32,
923) -> u32 {
924    unsafe {
925        let result = {
926            let instance = (*vmctx).instance_mut();
927            let memory_index = MemoryIndex::from_u32(memory_index);
928
929            instance.imported_memory_notify(memory_index, dst, cnt)
930        };
931        if let Err(trap) = result {
932            raise_lib_trap(trap);
933        }
934        result.unwrap()
935    }
936}
937
938/// The function pointer to a libcall
939pub fn function_pointer(libcall: LibCall) -> usize {
940    match libcall {
941        LibCall::CeilF32 => wasmer_vm_f32_ceil as *const () as usize,
942        LibCall::CeilF64 => wasmer_vm_f64_ceil as *const () as usize,
943        LibCall::FloorF32 => wasmer_vm_f32_floor as *const () as usize,
944        LibCall::FloorF64 => wasmer_vm_f64_floor as *const () as usize,
945        LibCall::NearestF32 => wasmer_vm_f32_nearest as *const () as usize,
946        LibCall::NearestF64 => wasmer_vm_f64_nearest as *const () as usize,
947        LibCall::SqrtF32 => wasmer_vm_f32_sqrt as *const () as usize,
948        LibCall::SqrtF64 => wasmer_vm_f64_sqrt as *const () as usize,
949        LibCall::TruncF32 => wasmer_vm_f32_trunc as *const () as usize,
950        LibCall::TruncF64 => wasmer_vm_f64_trunc as *const () as usize,
951        LibCall::Memory32Size => wasmer_vm_memory32_size as *const () as usize,
952        LibCall::ImportedMemory32Size => wasmer_vm_imported_memory32_size as *const () as usize,
953        LibCall::TableCopy => wasmer_vm_table_copy as *const () as usize,
954        LibCall::TableInit => wasmer_vm_table_init as *const () as usize,
955        LibCall::TableFill => wasmer_vm_table_fill as *const () as usize,
956        LibCall::TableSize => wasmer_vm_table_size as *const () as usize,
957        LibCall::ImportedTableSize => wasmer_vm_imported_table_size as *const () as usize,
958        LibCall::TableGet => wasmer_vm_table_get as *const () as usize,
959        LibCall::ImportedTableGet => wasmer_vm_imported_table_get as *const () as usize,
960        LibCall::TableSet => wasmer_vm_table_set as *const () as usize,
961        LibCall::ImportedTableSet => wasmer_vm_imported_table_set as *const () as usize,
962        LibCall::TableGrow => wasmer_vm_table_grow as *const () as usize,
963        LibCall::ImportedTableGrow => wasmer_vm_imported_table_grow as *const () as usize,
964        LibCall::FuncRef => wasmer_vm_func_ref as *const () as usize,
965        LibCall::ElemDrop => wasmer_vm_elem_drop as *const () as usize,
966        LibCall::Memory32Copy => wasmer_vm_memory32_copy as *const () as usize,
967        LibCall::Memory32Fill => wasmer_vm_memory32_fill as *const () as usize,
968        LibCall::ImportedMemory32Fill => wasmer_vm_imported_memory32_fill as *const () as usize,
969        LibCall::Memory32Init => wasmer_vm_memory32_init as *const () as usize,
970        LibCall::DataDrop => wasmer_vm_data_drop as *const () as usize,
971        LibCall::Probestack => WASMER_VM_PROBESTACK as *const () as usize,
972        LibCall::RaiseTrap => wasmer_vm_raise_trap as *const () as usize,
973        LibCall::Memory32AtomicWait32 => wasmer_vm_memory32_atomic_wait32 as *const () as usize,
974        LibCall::ImportedMemory32AtomicWait32 => {
975            wasmer_vm_imported_memory32_atomic_wait32 as *const () as usize
976        }
977        LibCall::Memory32AtomicWait64 => wasmer_vm_memory32_atomic_wait64 as *const () as usize,
978        LibCall::ImportedMemory32AtomicWait64 => {
979            wasmer_vm_imported_memory32_atomic_wait64 as *const () as usize
980        }
981        LibCall::Memory32AtomicNotify => wasmer_vm_memory32_atomic_notify as *const () as usize,
982        LibCall::ImportedMemory32AtomicNotify => {
983            wasmer_vm_imported_memory32_atomic_notify as *const () as usize
984        }
985        LibCall::Throw => wasmer_vm_throw as *const () as usize,
986        LibCall::EHPersonality => eh::wasmer_eh_personality as *const () as usize,
987        LibCall::EHPersonality2 => eh::wasmer_eh_personality2 as *const () as usize,
988        LibCall::AllocException => wasmer_vm_alloc_exception as *const () as usize,
989        LibCall::ReadExnRef => wasmer_vm_read_exnref as *const () as usize,
990        LibCall::LibunwindExceptionIntoExnRef => {
991            wasmer_vm_exception_into_exnref as *const () as usize
992        }
993        LibCall::DebugUsize => wasmer_vm_dbg_usize as *const () as usize,
994        LibCall::DebugStr => wasmer_vm_dbg_str as *const () as usize,
995        // --- Soft-float libcalls ---
996        // compiler-rt / libgcc provides these on every std Rust target.
997        // On wasm32 the JIT engine is never active, so these variants are unreachable.
998        _lc @ (LibCall::Addsf3
999        | LibCall::Adddf3
1000        | LibCall::Subsf3
1001        | LibCall::Subdf3
1002        | LibCall::Mulsf3
1003        | LibCall::Muldf3
1004        | LibCall::Divsf3
1005        | LibCall::Divdf3
1006        | LibCall::Negsf2
1007        | LibCall::Negdf2
1008        | LibCall::Extendsfdf2
1009        | LibCall::Truncdfsf2
1010        | LibCall::Fixsfsi
1011        | LibCall::Fixdfsi
1012        | LibCall::Fixsfdi
1013        | LibCall::Fixdfdi
1014        | LibCall::Fixunssfsi
1015        | LibCall::Fixunsdfsi
1016        | LibCall::Fixunssfdi
1017        | LibCall::Fixunsdfdi
1018        | LibCall::Floatsisf
1019        | LibCall::Floatsidf
1020        | LibCall::Floatdisf
1021        | LibCall::Floatdidf
1022        | LibCall::Floatunsisf
1023        | LibCall::Floatunsidf
1024        | LibCall::Floatundisf
1025        | LibCall::Floatundidf
1026        | LibCall::Unordsf2
1027        | LibCall::Unorddf2
1028        | LibCall::Eqsf2
1029        | LibCall::Eqdf2
1030        | LibCall::Nesf2
1031        | LibCall::Nedf2
1032        | LibCall::Gesf2
1033        | LibCall::Gedf2
1034        | LibCall::Ltsf2
1035        | LibCall::Ltdf2
1036        | LibCall::Lesf2
1037        | LibCall::Ledf2
1038        | LibCall::Gtsf2
1039        | LibCall::Gtdf2) => {
1040            #[cfg(target_arch = "wasm32")]
1041            unreachable!("soft-float libcalls are not reachable on wasm32");
1042            #[cfg(not(target_arch = "wasm32"))]
1043            match _lc {
1044                LibCall::Addsf3 => __addsf3 as *const () as usize,
1045                LibCall::Adddf3 => __adddf3 as *const () as usize,
1046                LibCall::Subsf3 => __subsf3 as *const () as usize,
1047                LibCall::Subdf3 => __subdf3 as *const () as usize,
1048                LibCall::Mulsf3 => __mulsf3 as *const () as usize,
1049                LibCall::Muldf3 => __muldf3 as *const () as usize,
1050                LibCall::Divsf3 => __divsf3 as *const () as usize,
1051                LibCall::Divdf3 => __divdf3 as *const () as usize,
1052                LibCall::Negsf2 => __negsf2 as *const () as usize,
1053                LibCall::Negdf2 => __negdf2 as *const () as usize,
1054                LibCall::Extendsfdf2 => __extendsfdf2 as *const () as usize,
1055                LibCall::Truncdfsf2 => __truncdfsf2 as *const () as usize,
1056                LibCall::Fixsfsi => __fixsfsi as *const () as usize,
1057                LibCall::Fixdfsi => __fixdfsi as *const () as usize,
1058                LibCall::Fixsfdi => __fixsfdi as *const () as usize,
1059                LibCall::Fixdfdi => __fixdfdi as *const () as usize,
1060                LibCall::Fixunssfsi => __fixunssfsi as *const () as usize,
1061                LibCall::Fixunsdfsi => __fixunsdfsi as *const () as usize,
1062                LibCall::Fixunssfdi => __fixunssfdi as *const () as usize,
1063                LibCall::Fixunsdfdi => __fixunsdfdi as *const () as usize,
1064                LibCall::Floatsisf => __floatsisf as *const () as usize,
1065                LibCall::Floatsidf => __floatsidf as *const () as usize,
1066                LibCall::Floatdisf => __floatdisf as *const () as usize,
1067                LibCall::Floatdidf => __floatdidf as *const () as usize,
1068                LibCall::Floatunsisf => __floatunsisf as *const () as usize,
1069                LibCall::Floatunsidf => __floatunsidf as *const () as usize,
1070                LibCall::Floatundisf => __floatundisf as *const () as usize,
1071                LibCall::Floatundidf => __floatundidf as *const () as usize,
1072                LibCall::Unordsf2 => __unordsf2 as *const () as usize,
1073                LibCall::Unorddf2 => __unorddf2 as *const () as usize,
1074                LibCall::Eqsf2 => __eqsf2 as *const () as usize,
1075                LibCall::Eqdf2 => __eqdf2 as *const () as usize,
1076                LibCall::Nesf2 => __nesf2 as *const () as usize,
1077                LibCall::Nedf2 => __nedf2 as *const () as usize,
1078                LibCall::Gesf2 => __gesf2 as *const () as usize,
1079                LibCall::Gedf2 => __gedf2 as *const () as usize,
1080                LibCall::Ltsf2 => __ltsf2 as *const () as usize,
1081                LibCall::Ltdf2 => __ltdf2 as *const () as usize,
1082                LibCall::Lesf2 => __lesf2 as *const () as usize,
1083                LibCall::Ledf2 => __ledf2 as *const () as usize,
1084                LibCall::Gtsf2 => __gtsf2 as *const () as usize,
1085                LibCall::Gtdf2 => __gtdf2 as *const () as usize,
1086                _ => unreachable!(),
1087            }
1088        }
1089    }
1090}
1091
1092// Soft-float arithmetic routines. Provided by compiler-rt / libgcc on all non-wasm targets.
1093#[cfg(not(target_arch = "wasm32"))]
1094unsafe extern "C" {
1095    // --- f32/f64 arithmetic ---
1096    fn __addsf3(a: f32, b: f32) -> f32;
1097    fn __adddf3(a: f64, b: f64) -> f64;
1098    fn __subsf3(a: f32, b: f32) -> f32;
1099    fn __subdf3(a: f64, b: f64) -> f64;
1100    fn __mulsf3(a: f32, b: f32) -> f32;
1101    fn __muldf3(a: f64, b: f64) -> f64;
1102    fn __divsf3(a: f32, b: f32) -> f32;
1103    fn __divdf3(a: f64, b: f64) -> f64;
1104    fn __negsf2(a: f32) -> f32;
1105    fn __negdf2(a: f64) -> f64;
1106    // --- f32/f64 conversions ---
1107    fn __extendsfdf2(a: f32) -> f64;
1108    fn __truncdfsf2(a: f64) -> f32;
1109    fn __fixsfsi(a: f32) -> i32;
1110    fn __fixdfsi(a: f64) -> i32;
1111    fn __fixsfdi(a: f32) -> i64;
1112    fn __fixdfdi(a: f64) -> i64;
1113    fn __fixunssfsi(a: f32) -> u32;
1114    fn __fixunsdfsi(a: f64) -> u32;
1115    fn __fixunssfdi(a: f32) -> u64;
1116    fn __fixunsdfdi(a: f64) -> u64;
1117    fn __floatsisf(i: i32) -> f32;
1118    fn __floatsidf(i: i32) -> f64;
1119    fn __floatdisf(i: i64) -> f32;
1120    fn __floatdidf(i: i64) -> f64;
1121    fn __floatunsisf(i: u32) -> f32;
1122    fn __floatunsidf(i: u32) -> f64;
1123    fn __floatundisf(i: u64) -> f32;
1124    fn __floatundidf(i: u64) -> f64;
1125    // --- f32/f64 comparisons (return 0 / nonzero / negative per GCC ABI) ---
1126    fn __unordsf2(a: f32, b: f32) -> i32;
1127    fn __unorddf2(a: f64, b: f64) -> i32;
1128    fn __eqsf2(a: f32, b: f32) -> i32;
1129    fn __eqdf2(a: f64, b: f64) -> i32;
1130    fn __nesf2(a: f32, b: f32) -> i32;
1131    fn __nedf2(a: f64, b: f64) -> i32;
1132    fn __gesf2(a: f32, b: f32) -> i32;
1133    fn __gedf2(a: f64, b: f64) -> i32;
1134    fn __ltsf2(a: f32, b: f32) -> i32;
1135    fn __ltdf2(a: f64, b: f64) -> i32;
1136    fn __lesf2(a: f32, b: f32) -> i32;
1137    fn __ledf2(a: f64, b: f64) -> i32;
1138    fn __gtsf2(a: f32, b: f32) -> i32;
1139    fn __gtdf2(a: f64, b: f64) -> i32;
1140}