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 #[unsafe(naked)]
11 pub(crate) unsafe extern "C" fn wasmtime_fiber_switch(top_of_stack: *mut u8 /* x0 */) {
12     naked_asm!(
13         "
14         // Save all callee-saved registers on the stack since we're assuming
15         // they're clobbered as a result of the stack switch.
16         stmg %r6, %r15, 48(%r15)
17         aghi %r15, -64
18         std %f8, 0(%r15)
19         std %f9, 8(%r15)
20         std %f10, 16(%r15)
21         std %f11, 24(%r15)
22         std %f12, 32(%r15)
23         std %f13, 40(%r15)
24         std %f14, 48(%r15)
25         std %f15, 56(%r15)
26 
27         // Load our previously saved stack pointer to resume to, and save off our
28         // current stack pointer on where to come back to eventually.
29         lg %r1, -16(%r2)
30         stg %r15, -16(%r2)
31 
32         // Switch to the new stack and restore all our callee-saved registers after
33         // the switch and return to our new stack.
34         ld %f8, 0(%r1)
35         ld %f9, 8(%r1)
36         ld %f10, 16(%r1)
37         ld %f11, 24(%r1)
38         ld %f12, 32(%r1)
39         ld %f13, 40(%r1)
40         ld %f14, 48(%r1)
41         ld %f15, 56(%r1)
42         lmg %r6, %r15, 112(%r1)
43         br %r14
44         ",
45     );
46 }
47 
48 #[unsafe(naked)]
49 pub(crate) unsafe extern "C" fn wasmtime_fiber_init(
50     top_of_stack: *mut u8,                        // x0
51     entry_point: extern "C" fn(*mut u8, *mut u8), // x1
52     entry_arg0: *mut u8,                          // x2
53 ) {
54     naked_asm!(
55         "
56         larl %r1, {}
57         stg %r1, -48(%r2)  // wasmtime_fiber_start - restored into %r14
58         stg %r2, -112(%r2) // top_of_stack - restored into %r6
59         stg %r3, -104(%r2) // entry_point - restored into %r7
60         stg %r4, -96(%r2)  // entry_arg0 - restored into %r8
61         aghi %r2, -160     // 160 bytes register save area
62         stg %r2, 120(%r2)  // bottom of register save area - restored into %r15
63 
64         // `wasmtime_fiber_switch` has a 64 byte stack.
65         aghi %r2, -64
66         stg %r2, 208(%r2)
67         br %r14
68         ",
69         sym wasmtime_fiber_start,
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 
80         // See the x86_64 file for more commentary on what these CFI directives are
81         // doing. Like over there note that the relative offsets to registers here
82         // match the frame layout in `wasmtime_fiber_switch`.
83         .cfi_escape 0x0f,    /* DW_CFA_def_cfa_expression */ \
84             7,               /* the byte length of this expression */ \
85             0x7f, 0x90, 0x1, /* DW_OP_breg15 0x90 */ \
86             0x06,            /* DW_OP_deref */ \
87             0x23, 0xe0, 0x1  /* DW_OP_plus_uconst 0xe0 */
88 
89         .cfi_rel_offset 6, -112
90         .cfi_rel_offset 7, -104
91         .cfi_rel_offset 8, -96
92         .cfi_rel_offset 9, -88
93         .cfi_rel_offset 10, -80
94         .cfi_rel_offset 11, -72
95         .cfi_rel_offset 12, -64
96         .cfi_rel_offset 13, -56
97         .cfi_rel_offset 14, -48
98         .cfi_rel_offset 15, -40
99 
100         // Load our two arguments prepared by `wasmtime_fiber_init`.
101         lgr %r2, %r8  // entry_arg0
102         lgr %r3, %r6  // top_of_stack
103 
104         // ... and then we call the function! Note that this is a function call so
105         // our frame stays on the stack to backtrace through.
106         basr %r14, %r7  // entry_point
107         // .. technically we shouldn't get here, so just trap.
108         .word 0x0000
109         .cfi_endproc
110         ",
111     );
112 }
113