Skip to content

Commit c21353b

Browse files
authored
Modernize architecture documentation on linear memory (bytecodealliance#10364)
The previous docs had fallen a bit behind the times.
1 parent 61feceb commit c21353b

File tree

1 file changed

+75
-39
lines changed

1 file changed

+75
-39
lines changed

docs/contributing-architecture.md

Lines changed: 75 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -366,48 +366,84 @@ and generated code need to handle.
366366
First there are a number of properties about linear memory which can be
367367
configured:
368368

369-
* `wasmtime::Config::static_memory_maximum_size`
370-
* `wasmtime::Config::static_memory_guard_size`
371-
* `wasmtime::Config::dynamic_memory_guard_size`
369+
* `wasmtime::Config::memory_reservation`
370+
* `wasmtime::Config::memory_may_move`
371+
* `wasmtime::Config::memory_guard_size`
372+
* `wasmtime::Config::memory_reservation_for_growth`
373+
* `wasmtime::Config::memory_init_cow`
372374
* `wasmtime::Config::guard_before_linear_memory`
375+
* `wasmtime::Config::signals_based_traps`
373376

374377
The methods on `Config` have a good bit of documentation to go over some
375-
nitty-gritty, but the general gist is that Wasmtime has two modes of memory:
376-
static and dynamic. Static memories represent an address space reservation that
377-
never moves and pages are committed to represent memory growth. Dynamic
378-
memories represent allocations where the committed portion exactly matches the
379-
wasm memory's size and growth happens by allocating a bigger chunk of memory.
380-
381-
The guard size configuration indicates the size of the guard region that
382-
happens after linear memory. This guard size affects whether generated JIT code
383-
emits bounds checks or not. Bounds checks are elided if out-of-bounds addresses
384-
provably encounter the guard pages.
385-
386-
The `guard_before_linear_memory` configuration additionally places guard pages
387-
in front of linear memory as well as after linear memory (the same size on both
388-
ends). This is only used to protect against possible Cranelift bugs and
389-
otherwise serves no purpose.
390-
391-
The defaults for Wasmtime on 64-bit platforms are:
392-
393-
* 4GB static maximum size meaning all 32-bit memories are static and 64-bit
394-
memories are dynamic.
395-
* 2GB static guard size meaning all loads/stores with less than 2GB offset
396-
don't need bounds checks with 32-bit memories.
397-
* Guard pages before linear memory are enabled.
398-
399-
Altogether this means that 32-bit linear memories result in an 8GB virtual
400-
address space reservation by default in Wasmtime. With the pooling allocator
401-
where we know that linear memories are contiguous this results in a 6GB
402-
reservation per memory because the guard region after one memory is the guard
403-
region before the next.
404-
405-
Note that 64-bit memories (the memory64 proposal for WebAssembly) can be
406-
configured to be static but will never be able to elide bounds checks at this
407-
time. This configuration is possible through the `static_memory_forced`
408-
configuration option. Additionally note that support for 64-bit memories in
409-
Wasmtime is functional but not yet tuned at this time so there's probably still
410-
some performance work and better defaults to manage.
378+
nitty-gritty. Wasmtime also has some `#[cfg]` directives which are calculated by
379+
`crates/wasmtime/build.rs` which affects the defaults of various strategies. For
380+
example `has_native_signals` means that segfaults are allowed to happen at
381+
runtime and are caught in a signal handler. Additionally `has_virtual_memory`
382+
means that `mmap` is available and will be used (otherwise a fallback to
383+
`malloc` is implemented). The matrix of all of these combinations is then used
384+
to implement a linear memory for a WebAssembly instance.
385+
386+
It's generally best to consult the documentation of `Config` for the most
387+
up-to-date information. Additionally code comments throughout the codebase can
388+
also be useful for understanding the impact of some of these options. Some
389+
example scenarios though are:
390+
391+
* **`(memory 1)` on 64-bit platforms** - by default this WebAssembly memory has
392+
unlimited size, meaning it's only limited by its index type (`i32`) meaning it
393+
can grow up to 4GiB if the host/embedder allows it. This is implemented with a
394+
8GiB virtual memory reservation -- 2GiB unmapped before linear memory, 4GiB
395+
for linear memory itself (but only 1 wasm page, 64KiB, read/write at the
396+
start), and 2GiB unmapped afterwards. The guard region before linear memory is
397+
a defense-in-depth measure and should never be hit under any operation. The
398+
guard region after linear memory is present to eliminate bounds checks in the
399+
wasm module (WebAssembly addresses are effective 33-bit addresses when the
400+
static `offset` is taken into account).
401+
402+
* **`(memory i64 1)` on 64-bit platforms** - this WebAssembly memory uses 64-bit
403+
indexes instead of 32-bit indexes. This means that the configuration looks
404+
similar to `(memory 1)` above except that growth beyond 4GiB will copy all the
405+
contents of linear memory to a new location. Embedders might want to raise
406+
`Config::memory_reservation` in this situation. This configuration mode cannot
407+
remove any bounds checks, but guard pages are still used to deduplicate bounds
408+
checks where possible (so segfaults may still be caught at runtime for
409+
out-of-bounds accesses).
410+
411+
* **`(memory 1)` on 64-bit platforms with the pooling allocator** - the pooling
412+
allocator has a few important differences than the default settings. First is
413+
that the pooling allocator is able to "overlap" the before/after guard regions
414+
meaning that the virtual memory cost per-linear-memory is 6GiB by default
415+
instead of 8GiB. Additionally the pooling allocator cannot resize memory so if
416+
`Config::memory_reservation` is less than 4GiB then that's a hard limit on the
417+
size of linear memory rather than being able to copy to a new location.
418+
419+
* **`(memory 1)` on 64-bit platforms with a smaller reservation** - if the
420+
`Config::memory_reservation` option is configured less than the default (the
421+
default is 4GiB) then the virtual memory allocated for all linear memories
422+
will be less than the 8GiB default. This means that linear memories may move
423+
over time if they grow beyond their initial limit (assuming such growth is
424+
allowed) and additionally bounds checks will be required for memory accesses.
425+
426+
* **`(memory 1)` on 32-bit platforms** - unlike 64-bit platforms this memory
427+
cannot have a 4GiB virtual memory reservation. Instead the linear memory is
428+
allocated with `Config::memory_reservation_for_growth` unmapped bytes after it
429+
to amortize the reallocation overhead of copying bytes. Guard pages are still
430+
used and signals are used where available to deduplicate bounds checks.
431+
432+
* **`(memory 1 (pagesize 1))` on any platforms** - this WebAssembly linear
433+
memory, with a page size of 1 byte, means that virtual memory cannot be used
434+
to catch traps. Instead explicit bounds checks are always required on all
435+
accesses. This is still allocated with virtual memory where possible, however.
436+
437+
There's quite a few possible combinations for how all of these options interact
438+
with each other. The high-level design goal of Wasmtime is such that each option
439+
is independent from all the others and is a knob for just its behavior. In this
440+
way it should be possible to customize the needs of embedders. Wasmtime
441+
additionally has different default behavior across platforms, such as 32-bit and
442+
64-bit platforms. Some platforms additionally don't have `mmap` by default and
443+
Wasmtime will adapt to that as well. The intention, however, is that it should
444+
be possible to mirror the default configuration on any platform into a
445+
"full-featured" platform such as 64-bit to assist with testing, fuzzing, and
446+
debugging.
411447

412448
## Tables and `externref`
413449

0 commit comments

Comments
 (0)