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