Path: blob/main/crates/fiber/src/stackswitch/x86_64.rs
1692 views
// A WORD OF CAUTION1//2// This entire file basically needs to be kept in sync with itself. It's not3// really possible to modify just one bit of this file without understanding4// all the other bits. Documentation tries to reference various bits here and5// there but try to make sure to read over everything before tweaking things!67use wasmtime_asm_macros::asm_func;89// fn(top_of_stack(rdi): *mut u8)10asm_func!(11wasmtime_versioned_export_macros::versioned_stringify_ident!(wasmtime_fiber_switch),12"13// We're switching to arbitrary code somewhere else, so pessimistically14// assume that all callee-save register are clobbered. This means we need15// to save/restore all of them.16//17// Note that this order for saving is important since we use CFI directives18// below to point to where all the saved registers are.19push rbp20push rbx21push r1222push r1323push r1424push r152526// Load pointer that we're going to resume at and store where we're going27// to get resumed from. This is in accordance with the diagram at the top28// of unix.rs.29mov rax, -0x10[rdi]30mov -0x10[rdi], rsp3132// Swap stacks and restore all our callee-saved registers33mov rsp, rax34pop r1535pop r1436pop r1337pop r1238pop rbx39pop rbp40ret41",42);4344// fn(45// top_of_stack(rdi): *mut u8,46// entry_point(rsi): extern fn(*mut u8, *mut u8),47// entry_arg0(rdx): *mut u8,48// )49#[rustfmt::skip]50asm_func!(51wasmtime_versioned_export_macros::versioned_stringify_ident!(wasmtime_fiber_init),52"53// Here we're going to set up a stack frame as expected by54// `wasmtime_fiber_switch`. The values we store here will get restored into55// registers by that function and the `wasmtime_fiber_start` function will56// take over and understands which values are in which registers.57//58// The first 16 bytes of stack are reserved for metadata, so we start59// storing values beneath that.60lea rax, {start}[rip]61mov -0x18[rdi], rax62mov -0x20[rdi], rdi // loaded into rbp during switch63mov -0x28[rdi], rsi // loaded into rbx during switch64mov -0x30[rdi], rdx // loaded into r12 during switch6566// And then we specify the stack pointer resumption should begin at. Our67// `wasmtime_fiber_switch` function consumes 6 registers plus a return68// pointer, and the top 16 bytes are reserved, so that's:69//70// (6 + 1) * 16 + 16 = 0x4871lea rax, -0x48[rdi]72mov -0x10[rdi], rax73ret74",75start = sym super::wasmtime_fiber_start,76);7778// This is a pretty special function that has no real signature. Its use is to79// be the "base" function of all fibers. This entrypoint is used in80// `wasmtime_fiber_init` to bootstrap the execution of a new fiber.81//82// We also use this function as a persistent frame on the stack to emit dwarf83// information to unwind into the caller. This allows us to unwind from the84// fiber's stack back to the main stack that the fiber was called from. We use85// special dwarf directives here to do so since this is a pretty nonstandard86// function.87//88// If you're curious a decent introduction to CFI things and unwinding is at89// https://www.imperialviolet.org/2017/01/18/cfi.html90asm_func!(91wasmtime_versioned_export_macros::versioned_stringify_ident!(wasmtime_fiber_start),92"93// Use the `simple` directive on the startproc here which indicates that94// some default settings for the platform are omitted, since this95// function is so nonstandard96.cfi_startproc simple97.cfi_def_cfa_offset 09899// This is where things get special, we're specifying a custom dwarf100// expression for how to calculate the CFA. The goal here is that we101// need to load the parent's stack pointer just before the call it made102// into `wasmtime_fiber_switch`. Note that the CFA value changes over103// time as well because a fiber may be resumed multiple times from104// different points on the original stack. This means that our custom105// CFA directive involves `DW_OP_deref`, which loads data from memory.106//107// The expression we're encoding here is that the CFA, the stack pointer108// of whatever called into `wasmtime_fiber_start`, is:109//110// *$rsp + 0x38111//112// $rsp is the stack pointer of `wasmtime_fiber_start` at the time the113// next instruction after the `.cfi_escape` is executed. Our $rsp at the114// start of this function is 16 bytes below the top of the stack (0xAff0115// in the diagram in unix.rs). The $rsp to resume at is stored at that116// location, so we dereference the stack pointer to load it.117//118// After dereferencing, though, we have the $rsp value for119// `wasmtime_fiber_switch` itself. That's a weird function which sort of120// and sort of doesn't exist on the stack. We want to point to the121// caller of `wasmtime_fiber_switch`, so to do that we need to skip the122// stack space reserved by `wasmtime_fiber_switch`, which is the 6 saved123// registers plus the return address of the caller's `call` instruction.124// Hence we offset another 0x38 bytes.125.cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \1264, /* the byte length of this expression */ \1270x57, /* DW_OP_reg7 (rsp) */ \1280x06, /* DW_OP_deref */ \1290x23, 0x38 /* DW_OP_plus_uconst 0x38 */130131// And now after we've indicated where our CFA is for our parent132// function, we can define that where all of the saved registers are133// located. This uses standard `.cfi` directives which indicate that134// these registers are all stored relative to the CFA. Note that this135// order is kept in sync with the above register spills in136// `wasmtime_fiber_switch`.137.cfi_rel_offset rip, -8138.cfi_rel_offset rbp, -16139.cfi_rel_offset rbx, -24140.cfi_rel_offset r12, -32141.cfi_rel_offset r13, -40142.cfi_rel_offset r14, -48143.cfi_rel_offset r15, -56144145// The body of this function is pretty similar. All our parameters are146// already loaded into registers by the switch function. The147// `wasmtime_fiber_init` routine arranged the various values to be148// materialized into the registers used here. Our job is to then move149// the values into the ABI-defined registers and call the entry-point.150// Note that `call` is used here to leave this frame on the stack so we151// can use the dwarf info here for unwinding. The trailing `ud2` is just152// for safety.153mov rdi, r12154mov rsi, rbp155call rbx156ud2157.cfi_endproc158",159);160161162