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)
wasmtime_fiber_switch(top_of_stack: *mut u8)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)]
wasmtime_fiber_switch_(top_of_stack: *mut u8 )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
wasmtime_fiber_init( top_of_stack: *mut u8, entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, )37 pub(crate) unsafe fn wasmtime_fiber_init(
38 top_of_stack: *mut u8,
39 entry_point: extern "C" fn(*mut u8, *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 r8: wasmtime_fiber_switch_ as *mut u8,
64 r9: entry_arg0,
65 r10: entry_point as *mut u8,
66 r11: top_of_stack,
67 lr: wasmtime_fiber_start as *mut u8,
68 last_sp: initial_stack.cast(),
69 ..InitialStack::default()
70 });
71 }
72 }
73
74 #[unsafe(naked)]
wasmtime_fiber_start() -> !75 unsafe extern "C" fn wasmtime_fiber_start() -> ! {
76 naked_asm!(
77 "
78 .cfi_startproc simple
79 .cfi_def_cfa_offset 0
80 // See the x86_64 file for more commentary on what these CFI directives
81 // are doing. Like over there note that the relative offsets to
82 // registers here match the frame layout in `wasmtime_fiber_switch`.
83 //
84 // TODO: this is only lightly tested. This gets backtraces in gdb but
85 // not at runtime. Perhaps the libgcc at runtime was too old? Doesn't
86 // support something here? Unclear. Will need investigation if someone
87 // ends up needing this and it still doesn't work.
88 .cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \
89 5, /* the byte length of this expression */ \
90 0x7d, 0x00, /* DW_OP_breg14(%sp) + 0 */ \
91 0x06, /* DW_OP_deref */ \
92 0x23, 0x24 /* DW_OP_plus_uconst 0x24 */
93
94 .cfi_rel_offset lr, -0x04
95 .cfi_rel_offset r11, -0x08
96 .cfi_rel_offset r10, -0x0c
97 .cfi_rel_offset r9, -0x10
98 .cfi_rel_offset r8, -0x14
99 .cfi_rel_offset r7, -0x18
100 .cfi_rel_offset r6, -0x1c
101 .cfi_rel_offset r5, -0x20
102 .cfi_rel_offset r4, -0x24
103
104 mov r1, r11
105 mov r0, r9
106 blx r10
107 blx r8
108 .cfi_endproc
109 ",
110 );
111 }
112