1 // A WORD OF CAUTION
2 //
3 // This entire file basically needs to be kept in sync with itself. It's not
4 // really possible to modify just one bit of this file without understanding
5 // all the other bits. Documentation tries to reference various bits here and
6 // there but try to make sure to read over everything before tweaking things!
7 //
8 // Also at this time this file is heavily based off the x86_64 file, so you'll
9 // probably want to read that one as well.
10 
11 use core::arch::naked_asm;
12 
13 #[inline(never)] // FIXME(rust-lang/rust#148307)
14 pub(crate) unsafe extern "C" fn wasmtime_fiber_switch(top_of_stack: *mut u8) {
15     unsafe { wasmtime_fiber_switch_(top_of_stack) }
16 }
17 
18 #[unsafe(naked)]
19 unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* r0 */) {
20     naked_asm!(
21         "
22         // Save callee-saved registers
23         push {{r4-r11,lr}}
24 
25         // Swap stacks, recording our current stack pointer
26         ldr r4, [r0, #-0x08]
27         str sp, [r0, #-0x08]
28         mov sp, r4
29 
30         // Restore and return
31         pop {{r4-r11,lr}}
32         bx lr
33         ",
34     );
35 }
36 
37 pub(crate) unsafe fn wasmtime_fiber_init(
38     top_of_stack: *mut u8,
39     entry_point: extern "C" fn(*mut u8, *mut u8),
40     entry_arg0: *mut u8,
41 ) {
42     #[repr(C)]
43     #[derive(Default)]
44     struct InitialStack {
45         r4: *mut u8,
46         r5: *mut u8,
47         r6: *mut u8,
48         r7: *mut u8,
49         r8: *mut u8,
50         r9: *mut u8,
51         r10: *mut u8,
52         r11: *mut u8,
53         lr: *mut u8,
54 
55         // unix.rs reserved space
56         last_sp: *mut u8,
57         run_result: *mut u8,
58     }
59 
60     unsafe {
61         let initial_stack = top_of_stack.cast::<InitialStack>().sub(1);
62         initial_stack.write(InitialStack {
63             r9: entry_arg0,
64             r10: entry_point as *mut u8,
65             r11: top_of_stack,
66             lr: wasmtime_fiber_start as *mut u8,
67             last_sp: initial_stack.cast(),
68             ..InitialStack::default()
69         });
70     }
71 }
72 
73 #[unsafe(naked)]
74 unsafe extern "C" fn wasmtime_fiber_start() -> ! {
75     naked_asm!(
76         "
77         .cfi_startproc simple
78         .cfi_def_cfa_offset 0
79         // See the x86_64 file for more commentary on what these CFI directives
80         // are doing. Like over there note that the relative offsets to
81         // registers here match the frame layout in `wasmtime_fiber_switch`.
82         //
83         // TODO: this is only lightly tested. This gets backtraces in gdb but
84         // not at runtime. Perhaps the libgcc at runtime was too old? Doesn't
85         // support something here? Unclear. Will need investigation if someone
86         // ends up needing this and it still doesn't work.
87         .cfi_escape 0x0f,    /* DW_CFA_def_cfa_expression */ \
88             5,               /* the byte length of this expression */ \
89             0x7d, 0x00,      /* DW_OP_breg14(%sp) + 0 */ \
90             0x06,            /* DW_OP_deref */ \
91             0x23, 0x24	 /* DW_OP_plus_uconst 0x24 */
92 
93         .cfi_rel_offset lr, -0x04
94         .cfi_rel_offset r11, -0x08
95         .cfi_rel_offset r10, -0x0c
96         .cfi_rel_offset r9, -0x10
97         .cfi_rel_offset r8, -0x14
98         .cfi_rel_offset r7, -0x18
99         .cfi_rel_offset r6, -0x1c
100         .cfi_rel_offset r5, -0x20
101         .cfi_rel_offset r4, -0x24
102 
103         mov r1, r11
104         mov r0, r9
105         blx r10
106         .cfi_endproc
107         ",
108     );
109 }
110