1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//! Trampolines for calling into Wasm from the host and calling the host from
//! Wasm.

use crate::VMContext;
use std::mem;

/// Given a Wasm function pointer and a `vmctx`, prepare the `vmctx` for calling
/// into that Wasm function, and return the host-to-Wasm entry trampoline.
///
/// Callers must never call Wasm function pointers directly. Callers must
/// instead call this function and then enter Wasm through the returned
/// host-to-Wasm trampoline.
///
/// # Unsafety
///
/// The `vmctx` argument must be valid.
///
/// The generic type `T` must be a function pointer type and `func` must be a
/// pointer to a Wasm function of that signature.
///
/// After calling this function, you may not mess with the vmctx or any other
/// Wasm state until after you've called the trampoline returned by this
/// function.
#[inline]
pub unsafe fn prepare_host_to_wasm_trampoline<T>(vmctx: *mut VMContext, func: T) -> T {
    assert_eq!(mem::size_of::<T>(), mem::size_of::<usize>());

    // Save the callee in the `vmctx`. The trampoline will read this function
    // pointer and tail call to it.
    (*vmctx)
        .instance_mut()
        .set_callee(Some(mem::transmute_copy(&func)));

    // Give callers the trampoline, transmuted into their desired function
    // signature (the trampoline is variadic and works with all signatures).
    mem::transmute_copy(&(host_to_wasm_trampoline as usize))
}

extern "C" {
    fn host_to_wasm_trampoline();
    pub(crate) fn wasm_to_host_trampoline();
}

cfg_if::cfg_if! {
    if #[cfg(target_arch = "x86_64")] {
        #[macro_use]
        mod x86_64;
    } else if #[cfg(target_arch = "aarch64")] {
        #[macro_use]
        mod aarch64;
    } else if #[cfg(target_arch = "s390x")] {
        #[macro_use]
        mod s390x;
    } else {
        compile_error!("unsupported architecture");
    }
}