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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use wasmtime_asm_macros::asm_func;

#[rustfmt::skip]
asm_func!(
    "host_to_wasm_trampoline",
    r#"
        .cfi_startproc
        bti c

        // Load the pointer to `VMRuntimeLimits` in `x9`.
        ldur x9, [x1, #8]

        // Check to see if callee is a core `VMContext` (MAGIC == "core"). NB:
        // we do not support big-endian aarch64 so the magic value is always
        // little-endian encoded.
        ldur w10, [x0]
        mov  w11, #0x6f63
        movk w11, #0x6572, lsl #16
        cmp  w10, w11

        // Store the last Wasm SP into the `last_wasm_entry_sp` in the limits, if
        // this was core Wasm, otherwise store an invalid sentinal value.
        mov  x12, #-1
        mov  x13, sp
        csel x12, x13, x12, eq
        stur x12, [x9, #40]

        // Tail call to the callee function pointer in the vmctx.
        ldur x16, [x1, #16]
        br   x16

        .cfi_endproc
    "#
);

#[cfg(test)]
mod host_to_wasm_trampoline_offsets_tests {
    use wasmtime_environ::{Module, PtrSize, VMOffsets};

    #[test]
    fn test() {
        let module = Module::new();
        let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);

        assert_eq!(8, offsets.vmctx_runtime_limits());
        assert_eq!(40, offsets.ptr.vmruntime_limits_last_wasm_entry_sp());
        assert_eq!(16, offsets.vmctx_callee());
        assert_eq!(0x65726f63, u32::from_le_bytes(*b"core"));
    }
}

asm_func!(
    "wasm_to_host_trampoline",
    "
        .cfi_startproc
        bti c

        // Load the pointer to `VMRuntimeLimits` in `x9`.
        ldur x9, [x1, #8]

        // Store the last Wasm FP into the `last_wasm_exit_fp` in the limits.
        stur fp, [x9, #24]

        // Store the last Wasm PC into the `last_wasm_exit_pc` in the limits.
        stur lr, [x9, #32]

        // Tail call to the actual host function.
        //
        // This *must* be a tail call so that we do not push to the stack and mess
        // up the offsets of stack arguments (if any).
        ldur x16, [x0, #8]
        br   x16

        .cfi_endproc
    ",
);

#[cfg(test)]
mod wasm_to_host_trampoline_offsets_tests {
    use crate::VMHostFuncContext;
    use memoffset::offset_of;
    use wasmtime_environ::{Module, PtrSize, VMOffsets};

    #[test]
    fn test() {
        let module = Module::new();
        let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);

        assert_eq!(8, offsets.vmctx_runtime_limits());
        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
        assert_eq!(8, offset_of!(VMHostFuncContext, host_func));
    }
}

#[rustfmt::skip]
macro_rules! wasm_to_libcall_trampoline {
    ($libcall:ident ; $libcall_impl:ident) => {
        wasmtime_asm_macros::asm_func!(
            stringify!($libcall),
            "
                .cfi_startproc
                bti c

                // Load the pointer to `VMRuntimeLimits` in `x9`.
                ldur x9, [x0, #8]

                // Store the last Wasm FP into the `last_wasm_exit_fp` in the limits.
                stur fp, [x9, #24]

                // Store the last Wasm PC into the `last_wasm_exit_pc` in the limits.
                stur lr, [x9, #32]

                // Tail call to the actual implementation of this libcall.
                b ", wasmtime_asm_macros::asm_sym!(stringify!($libcall_impl)), "

                .cfi_endproc
            "
        );
    };
}