Update comment on stack overflow checking (#4290)
* Update comment on stack overflow checking This commit moves the top-level comment in `crates/cranelift/src/lib.rs` into the location where the global value for the stack limit is generated. Stack overflow checking is pretty localized nowadays so there's not much need to have it at the top of the crate and most of the words there were just adapted to this new location. Closes #4286 * Review comments
This commit is contained in:
@@ -152,19 +152,38 @@ impl wasmtime_environ::Compiler for Compiler {
|
|||||||
|
|
||||||
let mut func_env = FuncEnvironment::new(isa, translation, types, tunables);
|
let mut func_env = FuncEnvironment::new(isa, translation, types, tunables);
|
||||||
|
|
||||||
// We use these as constant offsets below in
|
// The `stack_limit` global value below is the implementation of stack
|
||||||
// `stack_limit_from_arguments`, so assert their values here. This
|
// overflow checks in Wasmtime.
|
||||||
// allows the closure below to get coerced to a function pointer, as
|
|
||||||
// needed by `ir::Function`.
|
|
||||||
//
|
//
|
||||||
// Otherwise our stack limit is specially calculated from the vmctx
|
// The Wasm spec defines that stack overflows will raise a trap, and
|
||||||
// argument, where we need to load the `*const VMRuntimeLimits`
|
// there's also an added constraint where as an embedder you frequently
|
||||||
// pointer, and then from that pointer we need to load the stack
|
// are running host-provided code called from wasm. WebAssembly and
|
||||||
// limit itself. Note that manual register allocation is needed here
|
// native code currently share the same call stack, so Wasmtime needs to
|
||||||
// too due to how late in the process this codegen happens.
|
// make sure that host-provided code will have enough call-stack
|
||||||
|
// available to it.
|
||||||
//
|
//
|
||||||
// For more information about interrupts and stack checks, see the
|
// The way that stack overflow is handled here is by adding a prologue
|
||||||
// top of this file.
|
// check to all functions for how much native stack is remaining. The
|
||||||
|
// `VMContext` pointer is the first argument to all functions, and the
|
||||||
|
// first field of this structure is `*const VMRuntimeLimits` and the
|
||||||
|
// first field of that is the stack limit. Note that the stack limit in
|
||||||
|
// this case means "if the stack pointer goes below this, trap". Each
|
||||||
|
// function which consumes stack space or isn't a leaf function starts
|
||||||
|
// off by loading the stack limit, checking it against the stack
|
||||||
|
// pointer, and optionally traps.
|
||||||
|
//
|
||||||
|
// This manual check allows the embedder to give wasm a relatively
|
||||||
|
// precise amount of stack allocation. Using this scheme we reserve a
|
||||||
|
// chunk of stack for wasm code relative from where wasm code was
|
||||||
|
// called. This ensures that native code called by wasm should have
|
||||||
|
// native stack space to run, and the numbers of stack spaces here
|
||||||
|
// should all be configurable for various embeddings.
|
||||||
|
//
|
||||||
|
// Note that this check is independent of each thread's stack guard page
|
||||||
|
// here. If the stack guard page is reached that's still considered an
|
||||||
|
// abort for the whole program since the runtime limits configured by
|
||||||
|
// the embedder should cause wasm to trap before it reaches that
|
||||||
|
// (ensuring the host has enough space as well for its functionality).
|
||||||
let vmctx = context
|
let vmctx = context
|
||||||
.func
|
.func
|
||||||
.create_global_value(ir::GlobalValueData::VMContext);
|
.create_global_value(ir::GlobalValueData::VMContext);
|
||||||
|
|||||||
@@ -3,47 +3,6 @@
|
|||||||
//! This crate provides an implementation of the `wasmtime_environ::Compiler`
|
//! This crate provides an implementation of the `wasmtime_environ::Compiler`
|
||||||
//! and `wasmtime_environ::CompilerBuilder` traits.
|
//! and `wasmtime_environ::CompilerBuilder` traits.
|
||||||
|
|
||||||
// # How does Wasmtime prevent stack overflow?
|
|
||||||
//
|
|
||||||
// A few locations throughout the codebase link to this file to explain stack
|
|
||||||
// overflow. To start off, let's take a look at stack overflow. Wasm code is
|
|
||||||
// well-defined to have stack overflow being recoverable and raising a trap, so
|
|
||||||
// we need to handle this somehow! There's also an added constraint where as an
|
|
||||||
// embedder you frequently are running host-provided code called from wasm.
|
|
||||||
// WebAssembly and native code currently share the same call stack, so you want
|
|
||||||
// to make sure that your host-provided code will have enough call-stack
|
|
||||||
// available to it.
|
|
||||||
//
|
|
||||||
// Given all that, the way that stack overflow is handled is by adding a
|
|
||||||
// prologue check to all JIT functions for how much native stack is remaining.
|
|
||||||
// The `VMContext` pointer is the first argument to all functions, and the first
|
|
||||||
// field of this structure is `*const VMRuntimeLimits` and the first field of
|
|
||||||
// that is the stack limit. Note that the stack limit in this case means "if the
|
|
||||||
// stack pointer goes below this, trap". Each JIT function which consumes stack
|
|
||||||
// space or isn't a leaf function starts off by loading the stack limit,
|
|
||||||
// checking it against the stack pointer, and optionally traps.
|
|
||||||
//
|
|
||||||
// This manual check allows the embedder (us) to give wasm a relatively precise
|
|
||||||
// amount of stack allocation. Using this scheme we reserve a chunk of stack
|
|
||||||
// for wasm code relative from where wasm code was called. This ensures that
|
|
||||||
// native code called by wasm should have native stack space to run, and the
|
|
||||||
// numbers of stack spaces here should all be configurable for various
|
|
||||||
// embeddings.
|
|
||||||
//
|
|
||||||
// Note that we do not consider each thread's stack guard page here. It's
|
|
||||||
// considered that if you hit that you still abort the whole program. This
|
|
||||||
// shouldn't happen most of the time because wasm is always stack-bound and
|
|
||||||
// it's up to the embedder to bound its own native stack.
|
|
||||||
//
|
|
||||||
// So all-in-all, that's how we implement stack checks. Note that stack checks
|
|
||||||
// cannot be disabled because it's a feature of core wasm semantics. This means
|
|
||||||
// that all functions almost always have a stack check prologue, and it's up to
|
|
||||||
// us to optimize away that cost as much as we can.
|
|
||||||
//
|
|
||||||
// For more information about the tricky bits of managing the reserved stack
|
|
||||||
// size of wasm, see the implementation in `traphandlers.rs` in the
|
|
||||||
// `update_stack_limit` function.
|
|
||||||
|
|
||||||
use cranelift_codegen::binemit;
|
use cranelift_codegen::binemit;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_codegen::isa::{unwind::UnwindInfo, CallConv, TargetIsa};
|
use cranelift_codegen::isa::{unwind::UnwindInfo, CallConv, TargetIsa};
|
||||||
|
|||||||
Reference in New Issue
Block a user