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) 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)] 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 53 pub(crate) unsafe fn wasmtime_fiber_init( 54 top_of_stack: *mut u8, 55 entry_point: extern "C" fn(*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 108 last_sp: initial_stack.cast(), 109 ..InitialStack::default() 110 }); 111 } 112 } 113 114 #[unsafe(naked)] 115 unsafe extern "C" fn wasmtime_fiber_start() -> ! { 116 naked_asm!( 117 " 118 .cfi_startproc simple 119 .cfi_def_cfa_offset 0 120 121 // See the x86_64 file for more commentary on what these CFI directives are 122 // doing. Like over there note that the relative offsets to registers here 123 // match the frame layout in `wasmtime_fiber_switch`. 124 .cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \ 125 7, /* the byte length of this expression */ \ 126 0x7f, 0xa0, 0x1, /* DW_OP_breg15 0x90 */ \ 127 0x06, /* DW_OP_deref */ \ 128 0x23, 0xe0, 0x1 /* DW_OP_plus_uconst 0xe0 */ 129 130 .cfi_rel_offset 6, -112 131 .cfi_rel_offset 7, -104 132 .cfi_rel_offset 8, -96 133 .cfi_rel_offset 9, -88 134 .cfi_rel_offset 10, -80 135 .cfi_rel_offset 11, -72 136 .cfi_rel_offset 12, -64 137 .cfi_rel_offset 13, -56 138 .cfi_rel_offset 14, -48 139 .cfi_rel_offset 15, -40 140 141 // Load our two arguments prepared by `wasmtime_fiber_init`. 142 lgr %r2, %r8 // entry_arg0 143 lgr %r3, %r6 // top_of_stack 144 145 // ... and then we call the function! Note that this is a function call so 146 // our frame stays on the stack to backtrace through. 147 basr %r14, %r7 // entry_point 148 // .. technically we shouldn't get here, so just trap. 149 .word 0x0000 150 .cfi_endproc 151 ", 152 ); 153 } 154