This commit splits out a `FiberStack` from `Fiber`, allowing the instance allocator trait to return `FiberStack` rather than raw stack pointers. This keeps the stack creation mostly in `wasmtime_fiber`, but now the on-demand instance allocator can make use of it. The instance allocators no longer have to return a "not supported" error to indicate that the store should allocate its own fiber stack. This includes a bunch of cleanup in the instance allocator to scope stacks to the new "async" feature in the runtime. Closes #2708.
160 lines
6.5 KiB
ArmAsm
160 lines
6.5 KiB
ArmAsm
// A WORD OF CAUTION
|
|
//
|
|
// This entire file basically needs to be kept in sync with itself. It's not
|
|
// really possible to modify just one bit of this file without understanding
|
|
// all the other bits. Documentation tries to reference various bits here and
|
|
// there but try to make sure to read over everything before tweaking things!
|
|
|
|
#include "header.h"
|
|
|
|
// fn(top_of_stack(%rdi): *mut u8)
|
|
HIDDEN(wasmtime_fiber_switch)
|
|
GLOBL(wasmtime_fiber_switch)
|
|
.align 16
|
|
TYPE(wasmtime_fiber_switch)
|
|
FUNCTION(wasmtime_fiber_switch):
|
|
// We're switching to arbitrary code somewhere else, so pessimistically
|
|
// assume that all callee-save register are clobbered. This means we need
|
|
// to save/restore all of them.
|
|
//
|
|
// Note that this order for saving is important since we use CFI directives
|
|
// below to point to where all the saved registers are.
|
|
pushq %rbp
|
|
pushq %rbx
|
|
pushq %r12
|
|
pushq %r13
|
|
pushq %r14
|
|
pushq %r15
|
|
|
|
// Load pointer that we're going to resume at and store where we're going
|
|
// to get resumed from. This is in accordance with the diagram at the top
|
|
// of unix.rs.
|
|
movq -0x10(%rdi), %rax
|
|
mov %rsp, -0x10(%rdi)
|
|
|
|
// Swap stacks and restore all our callee-saved registers
|
|
mov %rax, %rsp
|
|
popq %r15
|
|
popq %r14
|
|
popq %r13
|
|
popq %r12
|
|
popq %rbx
|
|
popq %rbp
|
|
ret
|
|
SIZE(wasmtime_fiber_switch)
|
|
|
|
// fn(
|
|
// top_of_stack(%rdi): *mut u8,
|
|
// entry_point(%rsi): extern fn(*mut u8, *mut u8),
|
|
// entry_arg0(%rdx): *mut u8,
|
|
// )
|
|
HIDDEN(wasmtime_fiber_init)
|
|
GLOBL(wasmtime_fiber_init)
|
|
.align 16
|
|
TYPE(wasmtime_fiber_init)
|
|
FUNCTION(wasmtime_fiber_init):
|
|
// Here we're going to set up a stack frame as expected by
|
|
// `wasmtime_fiber_switch`. The values we store here will get restored into
|
|
// registers by that function and the `wasmtime_fiber_start` function will
|
|
// take over and understands which values are in which registers.
|
|
//
|
|
// The first 16 bytes of stack are reserved for metadata, so we start
|
|
// storing values beneath that.
|
|
lea FUNCTION(wasmtime_fiber_start)(%rip), %rax
|
|
movq %rax, -0x18(%rdi)
|
|
movq %rdi, -0x20(%rdi) // loaded into rbp during switch
|
|
movq %rsi, -0x28(%rdi) // loaded into rbx during switch
|
|
movq %rdx, -0x30(%rdi) // loaded into r12 during switch
|
|
|
|
// And then we specify the stack pointer resumption should begin at. Our
|
|
// `wasmtime_fiber_switch` function consumes 6 registers plus a return
|
|
// pointer, and the top 16 bytes are reserved, so that's:
|
|
//
|
|
// (6 + 1) * 16 + 16 = 0x48
|
|
lea -0x48(%rdi), %rax
|
|
movq %rax, -0x10(%rdi)
|
|
ret
|
|
SIZE(wasmtime_fiber_init)
|
|
|
|
// This is a pretty special function that has no real signature. Its use is to
|
|
// be the "base" function of all fibers. This entrypoint is used in
|
|
// `wasmtime_fiber_init` to bootstrap the execution of a new fiber.
|
|
//
|
|
// We also use this function as a persistent frame on the stack to emit dwarf
|
|
// information to unwind into the caller. This allows us to unwind from the
|
|
// fiber's stack back to the main stack that the fiber was called from. We use
|
|
// special dwarf directives here to do so since this is a pretty nonstandard
|
|
// function.
|
|
//
|
|
// If you're curious a decent introduction to CFI things and unwinding is at
|
|
// https://www.imperialviolet.org/2017/01/18/cfi.html
|
|
.align 16
|
|
TYPE(wasmtime_fiber_start)
|
|
FUNCTION(wasmtime_fiber_start):
|
|
// Use the `simple` directive on the startproc here which indicates that some
|
|
// default settings for the platform are omitted, since this function is so
|
|
// nonstandard
|
|
.cfi_startproc simple
|
|
// This is where things get special, we're specifying a custom dwarf
|
|
// expression for how to calculate the CFA. The goal here is that we need
|
|
// to load the parent's stack pointer just before the call it made into
|
|
// `wasmtime_fiber_switch`. Note that the CFA value changes over time as
|
|
// well because a fiber may be resumed multiple times from different points
|
|
// on the original stack. This means that our custom CFA directive involves
|
|
// `DW_OP_deref`, which loads data from memory.
|
|
//
|
|
// The expression we're encoding here is that the CFA, the stack pointer of
|
|
// whatever called into `wasmtime_fiber_start`, is:
|
|
//
|
|
// *$rsp + 0x38
|
|
//
|
|
// $rsp is the stack pointer of `wasmtime_fiber_start` at the time the next
|
|
// instruction after the `.cfi_escape` is executed. Our $rsp at the start
|
|
// of this function is 16 bytes below the top of the stack (0xAff0 in
|
|
// the diagram in unix.rs). The $rsp to resume at is stored at that
|
|
// location, so we dereference the stack pointer to load it.
|
|
//
|
|
// After dereferencing, though, we have the $rsp value for
|
|
// `wasmtime_fiber_switch` itself. That's a weird function which sort of
|
|
// and sort of doesn't exist on the stack. We want to point to the caller
|
|
// of `wasmtime_fiber_switch`, so to do that we need to skip the stack space
|
|
// reserved by `wasmtime_fiber_switch`, which is the 6 saved registers plus
|
|
// the return address of the caller's `call` instruction. Hence we offset
|
|
// another 0x38 bytes.
|
|
.cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \
|
|
4, /* the byte length of this expression */ \
|
|
0x57, /* DW_OP_reg7 (%rsp) */ \
|
|
0x06, /* DW_OP_deref */ \
|
|
0x23, 0x38 /* DW_OP_plus_uconst 0x38 */
|
|
|
|
// And now after we've indicated where our CFA is for our parent function,
|
|
// we can define that where all of the saved registers are located. This
|
|
// uses standard `.cfi` directives which indicate that these registers are
|
|
// all stored relative to the CFA. Note that this order is kept in sync
|
|
// with the above register spills in `wasmtime_fiber_switch`.
|
|
.cfi_rel_offset rip, -8
|
|
.cfi_rel_offset rbp, -16
|
|
.cfi_rel_offset rbx, -24
|
|
.cfi_rel_offset r12, -32
|
|
.cfi_rel_offset r13, -40
|
|
.cfi_rel_offset r14, -48
|
|
.cfi_rel_offset r15, -56
|
|
|
|
|
|
// The body of this function is pretty similar. All our parameters are
|
|
// already loaded into registers by the switch function. The
|
|
// `wasmtime_fiber_init` routine arranged the various values to be
|
|
// materialized into the registers used here. Our job is to then move the
|
|
// values into the ABI-defined registers and call the entry-point. Note that
|
|
// `callq` is used here to leave this frame on the stack so we can use the
|
|
// dwarf info here for unwinding. The trailing `ud2` is just for safety.
|
|
mov %r12,%rdi
|
|
mov %rbp,%rsi
|
|
callq *%rbx
|
|
ud2
|
|
.cfi_endproc
|
|
SIZE(wasmtime_fiber_start)
|
|
|
|
FOOTER
|
|
|