diff --git a/AstSemantics.md b/AstSemantics.md index cb73c637..f1031a83 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -202,11 +202,18 @@ Out of bounds accesses trap. In the MVP, linear memory can be resized by a `grow_memory` operator. The operand to this operator is in units of the WebAssembly page size, -which is 64KiB on all engines (though large page support may be added in +which is defined to be 64KiB (though large page support may be added in the [future](FutureFeatures.md#large-page-support)). * `grow_memory` : grow linear memory by a given unsigned delta of pages. - Return the previous memory size in bytes. + Return the previous memory size in units of pages or -1 on failure. + +When a maximum memory size is declared in the [memory section](Module.md#linear-memory-section), +`grow_memory` must fail if it would grow past the maximum. However, +`grow_memory` may still fail before the maximum if it was not possible to +reserve the space up front or if enabling the reserved memory fails. +When there is no maximum memory size declared, `grow_memory` is expected +to perform a system allocation which may fail. As stated [above](AstSemantics.md#linear-memory), linear memory is contiguous, meaning there are no "holes" in the linear address space. After the diff --git a/Modules.md b/Modules.md index b303eddf..5c52792e 100644 --- a/Modules.md +++ b/Modules.md @@ -161,15 +161,20 @@ by the module. If the section is absent, the linear memory operators The linear memory section declares the initial [memory size](AstSemantics.md#linear-memory) (which may be subsequently increased by [`grow_memory`](AstSemantics.md#resizing)). +The linear memory section may optionally declare a maximum memory size. +[`grow_memory`](AstSemantics.md#resizing) is guaranteed to fail if attempting to +grow past the declared maximum. When declared, implementations *should* +(non-normative) attempt to reserve virtual memory up to the maximum size. While +failure to allocate the *initial* memory size is a runtime error, failure to +reserve up to the *maximum* is not. When a maximum memory size is *not* declared, +on architectures with limited virtual address space, engines should allocate +only the initial size and reallocate on demand. + The initial contents of linear memory are zero by default. However, the memory section contains a possibly-empty array of *segments* (analogous to `.data`) which can specify the initial contents of fixed `(offset, length)` ranges of memory. -The linear memory section may also contain an optional hint declaring the expected -maximum heap usage. This hint is not semantically visible but can help a -WebAssembly engine to optimize `grow_memory`. - The linear memory section may optionally declare that the instance's linear memory is *externally aliasable*. How linear memory is aliased is up to the host environment (as with all module exports). The diff --git a/Rationale.md b/Rationale.md index e4cf8ec8..c225d3a3 100644 --- a/Rationale.md +++ b/Rationale.md @@ -124,6 +124,38 @@ already be communicating between threads in order to properly allocate the sum of the allocation requests, so it's expected that they can provide the needed information without significant extra effort. +The [optional maximum size](Modules.md#linear-memory-section) is designed to +address a number of competing constraints: +1. Allow WebAssembly modules to grab large regions of contiguous memory in a + 32-bit address space early in an application's startup before the virtual + address space becomes fragmented by execution of the application. +2. Allow many small WebAssembly instances to execute in a single 32-bit process. + (For example, it is common for a single web application to use dozens of + libraries, each of which may, over time, include WebAssembly modules as + implementation details.) +3. Avoid *forcing* every developer using WebAssembly to understand their precise + maximum heap usage. +4. When threading and shared memory are added to WebAssembly + [post-MVP](PostMVP.md#threads), the design should not require memory growth + to `realloc` since this implies significant implementation complexity, + security hazards, and optimization challenges. + +The optional maximum addresses these constraints: +* (1) is addressed by specifying a large maximum memory size. Simply setting a + large *initial* memory size has problems due to (3) and the fact that a + failure to allocate initial is a fatal error which makes the choice of "how + big?" difficult. +* (2) and (3) are addressed by making the maximum optional combined with the + implied implementation that, on 32-bit, engines will not allocate + significantly more than the current memory size, *and* the compiler sets the + initial size to just enough to hold static data. +* (4) is addressed assuming that, when threading is added, a new, optional + "shared" flag is added to the memory section that must be set to enable shared + memory and the shared flag forces the maximum to be specified. In this case, + shared memory never moves; the only thing that changes is that the bounds + grows which does not have all the abovementioned hazards. In particular, any + extant `SharedArrayBuffer`s that alias linear memory stay valid without + any updates. ## Linear memory disabled if no linear memory section