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 use core::arch::naked_asm;
9 
10 #[inline(never)] // FIXME(rust-lang/rust#148307)
wasmtime_fiber_switch(top_of_stack: *mut u8)11 pub(crate) unsafe extern "C" fn wasmtime_fiber_switch(top_of_stack: *mut u8) {
12     unsafe { wasmtime_fiber_switch_(top_of_stack) }
13 }
14 
15 #[unsafe(naked)]
wasmtime_fiber_switch_(top_of_stack: *mut u8 )16 unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* x0 */) {
17     naked_asm!(
18         "
19         // Save all callee-saved registers on the stack since we're assuming
20         // they're clobbered as a result of the stack switch.
21         stmg %r6, %r15, 48(%r15)
22         aghi %r15, -64
23         std %f8, 0(%r15)
24         std %f9, 8(%r15)
25         std %f10, 16(%r15)
26         std %f11, 24(%r15)
27         std %f12, 32(%r15)
28         std %f13, 40(%r15)
29         std %f14, 48(%r15)
30         std %f15, 56(%r15)
31 
32         // Load our previously saved stack pointer to resume to, and save off our
33         // current stack pointer on where to come back to eventually.
34         lg %r1, -16(%r2)
35         stg %r15, -16(%r2)
36 
37         // Switch to the new stack and restore all our callee-saved registers after
38         // the switch and return to our new stack.
39         ld %f8, 0(%r1)
40         ld %f9, 8(%r1)
41         ld %f10, 16(%r1)
42         ld %f11, 24(%r1)
43         ld %f12, 32(%r1)
44         ld %f13, 40(%r1)
45         ld %f14, 48(%r1)
46         ld %f15, 56(%r1)
47         lmg %r6, %r15, 112(%r1)
48         br %r14
49         ",
50     );
51 }
52 
wasmtime_fiber_init( top_of_stack: *mut u8, entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, )53 pub(crate) unsafe fn wasmtime_fiber_init(
54     top_of_stack: *mut u8,
55     entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8,
56     entry_arg0: *mut u8,
57 ) {
58     #[repr(C)]
59     #[derive(Default)]
60     struct InitialStack {
61         f8: *mut u8,
62         f9: *mut u8,
63         f10: *mut u8,
64         f11: *mut u8,
65         f12: *mut u8,
66         f13: *mut u8,
67         f14: *mut u8,
68         f15: *mut u8,
69 
70         back_chain: *mut u8,
71         compiler_reserved: *mut u8,
72 
73         r2: *mut u8,
74         r3: *mut u8,
75         r4: *mut u8,
76         r5: *mut u8,
77 
78         r6: *mut u8,
79         r7: *mut u8,
80         r8: *mut u8,
81         r9: *mut u8,
82         r10: *mut u8,
83         r11: *mut u8,
84         r12: *mut u8,
85         r13: *mut u8,
86         r14: *mut u8,
87         r15: *mut u8,
88 
89         f0: *mut u8,
90         f2: *mut u8,
91         f4: *mut u8,
92         f6: *mut u8,
93 
94         // unix.rs reserved space
95         last_sp: *mut u8,
96         run_result: *mut u8,
97     }
98 
99     unsafe {
100         let initial_stack = top_of_stack.cast::<InitialStack>().sub(1);
101         initial_stack.write(InitialStack {
102             r15: (&raw mut (*initial_stack).back_chain).cast(),
103             r14: wasmtime_fiber_start as *mut u8,
104             r6: top_of_stack,
105             r7: entry_point as *mut u8,
106             r8: entry_arg0,
107             r9: wasmtime_fiber_switch_ as *mut u8,
108 
109             last_sp: initial_stack.cast(),
110             ..InitialStack::default()
111         });
112     }
113 }
114 
115 #[unsafe(naked)]
wasmtime_fiber_start() -> !116 unsafe extern "C" fn wasmtime_fiber_start() -> ! {
117     naked_asm!(
118         "
119         .cfi_startproc simple
120         .cfi_def_cfa_offset 0
121 
122         // See the x86_64 file for more commentary on what these CFI directives are
123         // doing. Like over there note that the relative offsets to registers here
124         // match the frame layout in `wasmtime_fiber_switch`.
125         .cfi_escape 0x0f,    /* DW_CFA_def_cfa_expression */ \
126             7,               /* the byte length of this expression */ \
127             0x7f, 0xa0, 0x1, /* DW_OP_breg15 0x90 */ \
128             0x06,            /* DW_OP_deref */ \
129             0x23, 0xe0, 0x1  /* DW_OP_plus_uconst 0xe0 */
130 
131         .cfi_rel_offset 6, -112
132         .cfi_rel_offset 7, -104
133         .cfi_rel_offset 8, -96
134         .cfi_rel_offset 9, -88
135         .cfi_rel_offset 10, -80
136         .cfi_rel_offset 11, -72
137         .cfi_rel_offset 12, -64
138         .cfi_rel_offset 13, -56
139         .cfi_rel_offset 14, -48
140         .cfi_rel_offset 15, -40
141 
142         // Load our two arguments prepared by `wasmtime_fiber_init`.
143         lgr %r2, %r8  // entry_arg0
144         lgr %r3, %r6  // top_of_stack
145 
146         // ... and then we call the function! Note that this is a function call so
147         // our frame stays on the stack to backtrace through.
148         basr %r14, %r7  // entry_point
149 
150         // Perform the final switch.
151         basr %r14, %r9  // wasmtime_fiber_switch_
152 
153         // .. technically we shouldn't get here, so just trap.
154         .word 0x0000
155         .cfi_endproc
156         ",
157     );
158 }
159