use core::arch::naked_asm;
#[inline(never)] pub fn wasmtime_continuation_start_address() -> *const () {
wasmtime_continuation_start as *const ()
}
#[unsafe(naked)]
pub(crate) unsafe extern "C" fn wasmtime_continuation_start() {
naked_asm!(
"
// TODO(frank-emrich): Restore DWARF information for this function. In
// the meantime, debugging is possible using frame pointer walking.
//
// Note that the next 4 instructions amount to calling fiber_start
// with the following arguments:
// 1. func_ref
// 2. caller_vmctx
// 3. args (of type *mut ArrayRef<ValRaw>)
// 4. return_value_count
//
pop rcx // return_value_count
pop rdx // args
pop rsi // caller_vmctx
pop rdi // func_ref
// Note that RBP already contains the right frame pointer to build a
// frame pointer chain including the parent continuation:
// The current value of RBP is where we store the parent RBP in the
// control context!
call {fiber_start}
// Return to the parent continuation.
// RBP is callee-saved (no matter if it's used as a frame pointe or
// not), so its value is still TOS - 0x10.
// Use that fact to obtain saved parent RBP, RSP, and RIP from control
// context near TOS.
mov rsi, 0x08[rbp] // putting new RIP in temp register
mov rsp, -0x08[rbp]
mov rbp, [rbp]
// The stack_switch instruction uses register RDI for the payload.
// Here, the payload indicates that we are returning (value 0).
// See the test case below to keep this in sync with
// ControlEffect::return_()
mov rdi, 0
jmp rsi
",
fiber_start = sym super::fiber_start,
);
}
#[test]
fn test_return_payload() {
assert_eq!(wasmtime_environ::CONTROL_EFFECT_RETURN_DISCRIMINANT, 0);
}