From a85c0fdfdd51fae382e8dfb90f19193bf437282d Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 13:37:05 -0700 Subject: [PATCH 01/53] Add Rationale.md Move some of AstSemantics.md to that document. I left things pretty much as-is for now, and will defer more contended discussions to subsequent (and more targetted) edits. --- AstSemantics.md | 110 +++++-------------------- Rationale.md | 215 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 88 deletions(-) create mode 100644 Rationale.md diff --git a/AstSemantics.md b/AstSemantics.md index cb734341..1d16958d 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -1,26 +1,20 @@ # Abstract Syntax Tree Semantics -WebAssembly code is represented as an abstract syntax tree -that has a basic division between statements and -expressions. Each function body consists of exactly one statement. -All expressions and operations are typed, with no implicit conversions or -overloading rules. +WebAssembly code is represented as an Abstract Syntax Tree (AST) that has a +basic division between statements and expressions. Each function body consists +of exactly one statement. All expressions and operations are typed, with no +implicit conversions or overloading rules. Verification of WebAssembly code requires only a single pass with constant-time type checking and well-formedness checking. -Why not a stack-, register- or SSA-based bytecode? -* Trees allow a smaller binary encoding: [JSZap][], [Slim Binaries][]. -* [Polyfill prototype][] shows simple and efficient translation to asm.js. - - [JSZap]: https://research.microsoft.com/en-us/projects/jszap/ - [Slim Binaries]: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.108.1711 - [Polyfill prototype]: https://github.com/WebAssembly/polyfill-prototype-1 - WebAssembly offers a set of operations that are language-independent but closely match operations in many programming languages and are efficiently implementable on all modern computers. +The [rationale](Rationale.md) document details why WebAssembly is designed as +detailed in this document. + ## Traps Some operations may *trap* under some conditions, as noted below. In the MVP, @@ -134,8 +128,7 @@ The semantics of out-of-bounds accesses are discussed The use of infinite-precision in the effective address computation means that the addition of the offset to the address does is never wrapped, so if the address for an access is out-of-bounds, the effective address will always also -be out-of-bounds. This is intended to simplify folding of offsets into complex -address modes in hardware, and to simplify bounds checking optimizations. +be out-of-bounds. In wasm32, address operands and offset attributes have type `i32`, and linear memory sizes are limited to 4 GiB (of course, actual sizes are further limited @@ -156,53 +149,12 @@ when considering alignment. If the effective address of a memory access is a multiple of the alignment attribute value of the memory access, the memory access is considered *aligned*, otherwise it is considered *misaligned*. Aligned and misaligned accesses have -the same behavior. Alignment affects performance as follows: - - * Aligned accesses with at least natural alignment are fast. - * Aligned accesses with less than natural alignment may be somewhat slower - (think: implementation makes multiple accesses, either in software or - in hardware). - * Misaligned access of any kind may be *massively* slower - (think: implementation takes a signal and fixes things up). - -Thus, it is recommend that WebAssembly producers align frequently-used data -to permit the use of natural alignment access, and use loads and stores with -the greatest alignment values practical, while always avoiding misaligned -accesses. - -Either tooling or an explicit opt-in "debug mode" in the spec should allow -execution of a module in a mode that threw exceptions on misaligned access. -(This mode would incur some runtime cost for branching on most platforms which -is why it isn't the specified default.) - -### Out of bounds - -The ideal semantics is for out-of-bounds accesses to trap, but the implications -are not yet fully clear. - -There are several possible variations on this design being discussed and -experimented with. More measurement is required to understand the associated -tradeoffs. - - * After an out-of-bounds access, the instance can no longer execute code and any - outstanding JavaScript [ArrayBuffer][] aliasing the linear memory are detached. - * This would primarily allow hoisting bounds checks above effectful - operations. - * This can be viewed as a mild security measure under the assumption that - while the sandbox is still ensuring safety, the instance's internal state - is incoherent and further execution could lead to Bad Things (e.g., XSS - attacks). - * To allow for potentially more-efficient memory sandboxing, the semantics could - allow for a nondeterministic choice between one of the following when an - out-of-bounds access occurred. - * The ideal trap semantics. - * Loads return an unspecified value. - * Stores are either ignored or store to an unspecified location in the linear memory. - * Either tooling or an explicit opt-in "debug mode" in the spec should allow - execution of a module in a mode that threw exceptions on out-of-bounds - access. - - [ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer +the same behavior. + +### Out of Bounds + +Out of bounds accesses trap. + ### Resizing @@ -220,12 +172,9 @@ MVP, there are [future features](FutureFeatures.md#finer-grained-control-over-me proposed to allow setting protection and creating mappings within the contiguous linear memory. -The result type of `page_size` is `int32` for wasm32 and `int64` for wasm64. -The result value of `page_size` is an unsigned integer which is a power of 2. +The result type of `page_size` is `i32` for wasm32 and `i64` for wasm64. The +result value of `page_size` is an unsigned integer which is a power of 2. -(Note that the `page_size` value need not reflect the actual internal page size -of the implementation; it just needs to be a value suitable for use with -`resize_memory`) ## Local variables @@ -243,6 +192,7 @@ The details of index space for local variables and their types will be further c e.g. whether locals with type `i32` and `i64` must be contiguous and separate from others, etc. + ## Control flow structures WebAssembly offers basic structured control flow. All control flow structures @@ -286,10 +236,7 @@ Each function has a *signature*, which consists of: * Return types, which are a sequence of local types * Argument types, which are a sequence of local types -Note that WebAssembly itself does not support variable-length argument lists -(aka varargs). C and C++ compilers are expected to implement this functionality -by storing arguments in a buffer in linear memory and passing a pointer to the -buffer. +WebAssembly doesn't support variable-length argument lists (aka varargs). In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. @@ -316,14 +263,11 @@ in the function table. Function-pointer values are comparable for equality and the `addressof` operator is monomorphic. Function-pointer values can be explicitly coerced to and from -integers (which, in particular, is necessary when loading/storing to memory -since memory only provides integer types). For security and safety reasons, -the integer value of a coerced function-pointer value is an abstract index and -does not reveal the actual machine code address of the target function. +integers. In the MVP, function pointer values are local to a single module. The -[dynamic linking](DynamicLinking.md) feature is necessary for -two modules to pass function pointers back and forth. +[dynamic linking](DynamicLinking.md) feature is necessary for two modules to +pass function pointers back and forth. Multiple return value calls will be possible, though possibly not in the MVP. The details of multiple-return-value calls needs clarification. Calling a @@ -342,17 +286,7 @@ supported (including NaN values of all possible bit patterns). * `f32.const`: produce the value of an f32 immediate * `f64.const`: produce the value of an f64 immediate -## Expressions with control flow - -Expression trees offer significant size reduction by avoiding the need for -`set_local`/`get_local` pairs in the common case of an expression with only one, -immediate use. The following primitives provide AST nodes that express control -flow and thus allow more opportunities to build bigger expression trees and -further reduce `set_local`/`get_local` usage (which constitute 30-40% of total -bytes in the -[polyfill prototype](https://github.com/WebAssembly/polyfill-prototype-1)). -Additionally, these primitives are useful building blocks for -WebAssembly-generators (including the JavaScript polyfill prototype). +## Expressions with Control Flow * `comma`: evaluate and ignore the result of the first operand, evaluate and return the second operand diff --git a/Rationale.md b/Rationale.md new file mode 100644 index 00000000..ca71b804 --- /dev/null +++ b/Rationale.md @@ -0,0 +1,215 @@ +# Design Rationale + +WebAssembly was designed incrementally, and desisions were made with a solid +rationale which may turn out to be incorrect in the future. Sometimes, decisions +were made without the luxury of data, and the designers would like to revisit +certain decisions once real end-to-end usecases are met and Science can be +performed. + +You, the reader, may also be interested in understanding why +[Abstract Syntax Tree (AST)](AstSemantics.md) semantics are the way they are, +and digging through github's issues and pull requests becomes difficult. + +This rationale document tries to list how decisions were made, and where +tradeoffs were made for the sake of language ergonomics, portability, +performance, security, and Getting Things Done. + +The rationale for [limited local nondeterminism](Nondeterminism.md) is details +in its own document. + + +## Why AST? + +Why not a stack-, register- or SSA-based bytecode? +* Trees allow a smaller binary encoding: [JSZap][], [Slim Binaries][]. +* [Polyfill prototype][] shows simple and efficient translation to asm.js. + + [JSZap]: https://research.microsoft.com/en-us/projects/jszap/ + [Slim Binaries]: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.108.1711 + [Polyfill prototype]: https://github.com/WebAssembly/polyfill-prototype-1 + + +## Basic Types Only + +WebAssembly only represents [a few types](AstSemantics.md). +* More complex types can be formed from these basic types. It's up to the source + language compiler to express its own types in terms of the basic machine + types. This allows WebAssembly to present itself as a virtual ISA, and lets + compilers target it as they would any other ISA. +* These types are efficiently executed by all modern architectures. +* Smaller types (such as `i8` and `i16`) are no more efficient and are only + semantically meaningful for memory accesses. +* Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing + hardware and can be supported by runtime libraries if developers wish to use + them. They can be added to WebAssembly later without compromising MVP. +* More complex object types aren't sematcically useful for MVP. They may become + useful to support other languages, especially when considering + [garbage collection](GC.md). + + +## Load/Store Addressing + +Load/store instructions include and immediate offset used for +[addressing](AstSemantics.md#Addressing). This is intended to simplify folding +of offsets into complex address modes in hardware, and to simplify bounds +checking optimizations. It offloads some of the optimization work to the +compiler that targets WebAssembly, executing on the developer's machine, instead +of performing that work in the WebAssembly compiler on the user's machine. + + +## Alignment Hints + +Load/store instructions contain +[alignment hints](AstSemantics.md#Alignment). This makes it easier to generate +efficient code on certain hardware architectures. + +Alignment affects performance as follows: + + * Aligned accesses with at least natural alignment are fast. + * Aligned accesses with less than natural alignment may be somewhat slower + (think: implementation makes multiple accesses, either in software or in + hardware). + * Misaligned access of any kind may be *massively* slower (think: + implementation takes a signal and fixes things up). + +Thus, it is recommend that WebAssembly producers align frequently-used data to +permit the use of natural alignment access, and use loads and stores with the +greatest alignment values practical, while always avoiding misaligned accesses. + +Either tooling or an explicit opt-in "debug mode" in the spec should allow +execution of a module in a mode that threw exceptions on misaligned access. +(This mode would incur some runtime cost for branching on most platforms which +is why it isn't the specified default.) + + +## Out of Bounds + +The ideal semantics is for +[out-of-bounds accesses](AstSemantics.md#Out-of-Bounds) to trap, but the +implications are not yet fully clear. + +There are several possible variations on this design being discussed and +experimented with. More measurement is required to understand the associated +tradeoffs. + + * After an out-of-bounds access, the instance can no longer execute code and + any outstanding JavaScript [ArrayBuffer][] aliasing the linear memory are + detached. + * This would primarily allow hoisting bounds checks above effectful + operations. + * This can be viewed as a mild security measure under the assumption that + while the sandbox is still ensuring safety, the instance's internal state + is incoherent and further execution could lead to Bad Things (e.g., XSS + attacks). + * To allow for potentially more-efficient memory sandboxing, the semantics + could allow for a nondeterministic choice between one of the following when + an out-of-bounds access occurred. + * The ideal trap semantics. + * Loads return an unspecified value. + * Stores are either ignored or store to an unspecified location in the + linear memory. + * Either tooling or an explicit opt-in "debug mode" in the spec should allow + execution of a module in a mode that threw exceptions on out-of-bounds + access. + + [ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer + + +## Resizing + +Implementations provide a `page_size` operation which allows them to efficiently +map the underlying OS's capabilities to the WebAssembly application, as well as +to communicate their own implementation details in a useful manner to the +developer. + +Note that the `page_size` value need not reflect the actual internal page size +of the implementation; it just needs to be a value suitable for use with +`resize_memory`. + + +## Control Flow + +See [#299](https://github.com/WebAssembly/design/pull/299). + + +## Locals + +Address-taken variables in the call stack are expected to be in linear memory, +since they cannot be represented as locals. This prevents WebAssembly from +performing clever optimizations on the stack and liveness of such variables, but +this loss isn't expected to be consequential. + +Conversely, non-address taken values which are usually on the stack are instead +represented as locals inside functions. This effectively means that WebAssembly +has an infinite set of registers, and can choose to spill values as it sees fit +in a manner unobservable to the hosted code. This implies that there's a shadow +stack, which is also used to spill return values. This allows strong security +properties to be enforced, but does mean that two stacks are maintained (one by +the VM, the other by the compiler which targets WebAssembly) which can lead to +some inefficiencies. + +Local values can also be pre-colored, meaning that multiple incoming SSA values +which have separate liveness can "share" the storage represented by a +local. This offloads some of the optimization work from the WebAssembly VM. + + +## Variable-Length Argument Lists + +C and C++ compilers are expected to implement variable-length argument lists by +storing arguments in a buffer in linear memory and passing a pointer to the +buffer. This greatly simplifies WebAssembly VM implementations by punting this +ABI consideration to the front-end compiler. It does negatively impact +performance, but variable-length calls are already somewhat slow. + + +## Multiple Return Values + +TODO + + +## Indirect Calls + +The exact semantics of indirect function calls, function pointers, and what +happens when calling the wrong function, are still being discussed. + +Fundamentally linear memory is a simple collection of bytes, which means that +some integral representation of function pointers must exist. It's desirable to +hide the actual address of generated code from untrusted code because that would +be an unfortunate information leak which could have negative security +implications. Indirection is therefore desired. + +One extra concern is that existing C++ code sometimes stores data inside of what +is usually a function pointer. This is expected to keep working. + +Dynamic linking further complicates this: WebAssembly cannot simply standardize +on fixed-size function tables since dynamically linked code can add new +functions, as well as remove them. + + +## Expressions with Control Flow + +Expression trees offer significant size reduction by avoiding the need for +`set_local`/`get_local` pairs in the common case of an expression with only one, +immediate use. The following primitives provide AST nodes that express control +flow and thus allow more opportunities to build bigger expression trees and +further reduce `set_local`/`get_local` usage (which constitute 30-40% of total +bytes in the +[polyfill prototype](https://github.com/WebAssembly/polyfill-prototype-1)). +Additionally, these primitives are useful building blocks for +WebAssembly-generators (including the JavaScript polyfill prototype). + + +## Limited Local Nondeterminism + +The current specification is fairly strict when it comes to +[limited local nondeterminism](Nondeterminism.md) of operations: it tries to +specify all possible corner cases. + +As WebAssembly gets implemented and tested with multiple languages on multiple +architectures there may be a need to revisit some of the decisions: + +* When all relevant hardware implement features the same way then there's no + need to get creative. +* When different languages have different expectations then it's unfortunate if + WebAssembly measurably penalizes one's performance by enforcing determinism + which that language doesn't care about, but which another language may want. From 71e29a94727e15e8aba1ccf83478f2eda260c52d Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:00:10 -0700 Subject: [PATCH 02/53] Link to types. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index ca71b804..49a807a1 100644 --- a/Rationale.md +++ b/Rationale.md @@ -31,7 +31,7 @@ Why not a stack-, register- or SSA-based bytecode? ## Basic Types Only -WebAssembly only represents [a few types](AstSemantics.md). +WebAssembly only represents [a few types](AstSemantics.md#Types). * More complex types can be formed from these basic types. It's up to the source language compiler to express its own types in terms of the basic machine types. This allows WebAssembly to present itself as a virtual ISA, and lets From 233619533ae3bc57aebb171461cc465c9e83a27c Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:00:44 -0700 Subject: [PATCH 03/53] Typo. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 49a807a1..9b966399 100644 --- a/Rationale.md +++ b/Rationale.md @@ -42,7 +42,7 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). * Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing hardware and can be supported by runtime libraries if developers wish to use them. They can be added to WebAssembly later without compromising MVP. -* More complex object types aren't sematcically useful for MVP. They may become +* More complex object types aren't semantically useful for MVP. They may become useful to support other languages, especially when considering [garbage collection](GC.md). From 661ac0a3ec85a3837ff07d8b128ab37f0148d3ec Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:02:51 -0700 Subject: [PATCH 04/53] Leave some alignment docs in AST semantics. --- AstSemantics.md | 15 +++++++++++++++ Rationale.md | 19 +++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 1d16958d..c76a6b9e 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -137,6 +137,7 @@ offsets have type `i64`. The MVP only includes wasm32; subsequent versions will add support for wasm64 and thus [>4 GiB linear memory](FutureFeatures.md#linear-memory-bigger-than-4-gib). + ### Alignment Each linear memory access operation also has an immediate positive integer power @@ -151,6 +152,20 @@ attribute value of the memory access, the memory access is considered *aligned*, otherwise it is considered *misaligned*. Aligned and misaligned accesses have the same behavior. +Alignment affects performance as follows: + + * Aligned accesses with at least natural alignment are fast. + * Aligned accesses with less than natural alignment may be somewhat slower + (think: implementation makes multiple accesses, either in software or in + hardware). + * Misaligned access of any kind may be *massively* slower (think: + implementation takes a signal and fixes things up). + +Thus, it is recommend that WebAssembly producers align frequently-used data to +permit the use of natural alignment access, and use loads and stores with the +greatest alignment values practical, while always avoiding misaligned accesses. + + ### Out of Bounds Out of bounds accesses trap. diff --git a/Rationale.md b/Rationale.md index 9b966399..1f49eb6c 100644 --- a/Rationale.md +++ b/Rationale.md @@ -63,23 +63,10 @@ Load/store instructions contain [alignment hints](AstSemantics.md#Alignment). This makes it easier to generate efficient code on certain hardware architectures. -Alignment affects performance as follows: - - * Aligned accesses with at least natural alignment are fast. - * Aligned accesses with less than natural alignment may be somewhat slower - (think: implementation makes multiple accesses, either in software or in - hardware). - * Misaligned access of any kind may be *massively* slower (think: - implementation takes a signal and fixes things up). - -Thus, it is recommend that WebAssembly producers align frequently-used data to -permit the use of natural alignment access, and use loads and stores with the -greatest alignment values practical, while always avoiding misaligned accesses. - -Either tooling or an explicit opt-in "debug mode" in the spec should allow +Either tooling or an explicit opt-in "debug mode" in the spec could allow execution of a module in a mode that threw exceptions on misaligned access. -(This mode would incur some runtime cost for branching on most platforms which -is why it isn't the specified default.) +This mode would incur some runtime cost for branching on most platforms which is +why it isn't the specified default. ## Out of Bounds From 3eb06ed9ed9281549b62e11f6428e4710b095311 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:04:27 -0700 Subject: [PATCH 05/53] Leave some page_size docs in AST semantics. --- AstSemantics.md | 4 ++++ Rationale.md | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index c76a6b9e..2be7dc2b 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -190,6 +190,10 @@ contiguous linear memory. The result type of `page_size` is `i32` for wasm32 and `i64` for wasm64. The result value of `page_size` is an unsigned integer which is a power of 2. +The `page_size` value need not reflect the actual internal page size of the +implementation; it just needs to be a value suitable for use with +`resize_memory`. + ## Local variables diff --git a/Rationale.md b/Rationale.md index 1f49eb6c..15016d67 100644 --- a/Rationale.md +++ b/Rationale.md @@ -109,10 +109,6 @@ map the underlying OS's capabilities to the WebAssembly application, as well as to communicate their own implementation details in a useful manner to the developer. -Note that the `page_size` value need not reflect the actual internal page size -of the implementation; it just needs to be a value suitable for use with -`resize_memory`. - ## Control Flow From 898721a8ae6e2578e22b45cefb03136be1d279e4 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:05:57 -0700 Subject: [PATCH 06/53] Keep details on varargs. --- AstSemantics.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AstSemantics.md b/AstSemantics.md index 2be7dc2b..6aaf4f12 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -255,7 +255,8 @@ Each function has a *signature*, which consists of: * Return types, which are a sequence of local types * Argument types, which are a sequence of local types -WebAssembly doesn't support variable-length argument lists (aka varargs). +WebAssembly doesn't support variable-length argument lists (aka varargs), they +are instead supported through explicit accesses to linear memory. In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. From 13dd5a00457395268469cca86975818281ba5f66 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:06:37 -0700 Subject: [PATCH 07/53] Typo. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 15016d67..8775a4ed 100644 --- a/Rationale.md +++ b/Rationale.md @@ -14,7 +14,7 @@ This rationale document tries to list how decisions were made, and where tradeoffs were made for the sake of language ergonomics, portability, performance, security, and Getting Things Done. -The rationale for [limited local nondeterminism](Nondeterminism.md) is details +The rationale for [limited local nondeterminism](Nondeterminism.md) is detailed in its own document. From c44752335d6badada1be5f842dbf0bf102740660 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:10:16 -0700 Subject: [PATCH 08/53] Clarify that nondeterminism is also rationalized. --- Nondeterminism.md | 3 +++ Rationale.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Nondeterminism.md b/Nondeterminism.md index 8bb7d363..931eada8 100644 --- a/Nondeterminism.md +++ b/Nondeterminism.md @@ -8,6 +8,9 @@ local, nondeterminism. * *Local*: when nondeterministic execution occurs, the effect is local, there is no "spooky action at a distance". +The [rationale](Rationale.md) document details why WebAssembly is designed as +detailed in this document. + The limited, local, nondeterministic model implies: * Applications can't access data outside the sandbox without going through appropriate APIs, or otherwise escape the sandbox. diff --git a/Rationale.md b/Rationale.md index 8775a4ed..d7420fe5 100644 --- a/Rationale.md +++ b/Rationale.md @@ -14,8 +14,8 @@ This rationale document tries to list how decisions were made, and where tradeoffs were made for the sake of language ergonomics, portability, performance, security, and Getting Things Done. -The rationale for [limited local nondeterminism](Nondeterminism.md) is detailed -in its own document. +[Limited local nondeterminism](Nondeterminism.md) is detailed in its own +document, its rationale is also explained here. ## Why AST? From 753724d626215e2162071d5b50853dce10239f20 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:12:54 -0700 Subject: [PATCH 09/53] Typo. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index d7420fe5..3b76bab2 100644 --- a/Rationale.md +++ b/Rationale.md @@ -49,7 +49,7 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). ## Load/Store Addressing -Load/store instructions include and immediate offset used for +Load/store instructions include an immediate offset used for [addressing](AstSemantics.md#Addressing). This is intended to simplify folding of offsets into complex address modes in hardware, and to simplify bounds checking optimizations. It offloads some of the optimization work to the From 42bf4a37677e10d18b21ad922b54592156a881ad Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:15:35 -0700 Subject: [PATCH 10/53] Clarification on being creative. --- Rationale.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 3b76bab2..a9c89379 100644 --- a/Rationale.md +++ b/Rationale.md @@ -192,7 +192,10 @@ As WebAssembly gets implemented and tested with multiple languages on multiple architectures there may be a need to revisit some of the decisions: * When all relevant hardware implement features the same way then there's no - need to get creative. + need to get creative and add nondeterminism to WebAssembly when realistically + there's only one mapping from WebAssenbly expression to ISA-specific + operations. One such example is floating-point, where at a high-level most + basic instructions follow IEEE-754 semantics. * When different languages have different expectations then it's unfortunate if WebAssembly measurably penalizes one's performance by enforcing determinism which that language doesn't care about, but which another language may want. From 26b5815dde448a893e15b5b93f3e777d6b9c4ab3 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 16:18:04 -0700 Subject: [PATCH 11/53] C/C++ for address-taken. --- Rationale.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Rationale.md b/Rationale.md index a9c89379..259353ca 100644 --- a/Rationale.md +++ b/Rationale.md @@ -117,10 +117,12 @@ See [#299](https://github.com/WebAssembly/design/pull/299). ## Locals -Address-taken variables in the call stack are expected to be in linear memory, -since they cannot be represented as locals. This prevents WebAssembly from -performing clever optimizations on the stack and liveness of such variables, but -this loss isn't expected to be consequential. +C/C++ makes it possible to take the address of a function's local values and +pass this pointer to callees or other threads. Such address-taken variables in +the call stack are expected to be in the WebAssembly linear memory, since they +cannot be represented as locals. This prevents WebAssembly from performing +clever optimizations on the stack and liveness of such variables, but this loss +isn't expected to be consequential. Conversely, non-address taken values which are usually on the stack are instead represented as locals inside functions. This effectively means that WebAssembly From e2046162cecee21250612e792e07b248665c8345 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 16:27:15 -0700 Subject: [PATCH 12/53] Move some mentions of nondeterminism. --- Nondeterminism.md | 5 ----- Rationale.md | 13 +++++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Nondeterminism.md b/Nondeterminism.md index 931eada8..a2a19a88 100644 --- a/Nondeterminism.md +++ b/Nondeterminism.md @@ -20,11 +20,6 @@ The limited, local, nondeterministic model implies: [Control Flow Integrity](https://research.microsoft.com/apps/pubs/default.aspx?id=64250). * WebAssembly has no [nasal demons](https://en.wikipedia.org/w/index.php?title=Nasal_demons). -Ideally, WebAssembly would be fully deterministic (except where nondeterminism -was essential to the API, like random number generators, date/time functions or -input events). Nondeterminism is only specified as a compromise when there is no -other practical way to achieve [portable](Portability.md) native performance. - The following is a list of the places where the WebAssembly specification currently admits nondeterminism: diff --git a/Rationale.md b/Rationale.md index 259353ca..d672f468 100644 --- a/Rationale.md +++ b/Rationale.md @@ -190,6 +190,19 @@ The current specification is fairly strict when it comes to [limited local nondeterminism](Nondeterminism.md) of operations: it tries to specify all possible corner cases. +Ideally, WebAssembly would be fully deterministic because a fully deterministic +platform is easier to reason about, implement and test portably. There are a few +obvious exceptions where nondeterminism is essential to the API, like random +number generators, date/time functions or input events. Nondeterminism is only +specified as a compromise when there is no other practical way to achieve +[portable](Portability.md) native performance, or when implementation leeway is +desirable to allow usage of new hardware features or to security-harden certain +usecases. + +When nondeterminism is allowed into WebAssembly it is always done in a limited +and local manner. This prevents the entire program from being invalid, as would +be the case with C++ undefined behavior. + As WebAssembly gets implemented and tested with multiple languages on multiple architectures there may be a need to revisit some of the decisions: From 9357074551f2b74acb810f5933f010ec7548c287 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 16:29:22 -0700 Subject: [PATCH 13/53] Clarify varargs. --- AstSemantics.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 6aaf4f12..747284d9 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -255,8 +255,9 @@ Each function has a *signature*, which consists of: * Return types, which are a sequence of local types * Argument types, which are a sequence of local types -WebAssembly doesn't support variable-length argument lists (aka varargs), they -are instead supported through explicit accesses to linear memory. +WebAssembly doesn't support variable-length argument lists (aka +varargs). Compilers targetting WebAssembly can instead support them through +explicit accesses to linear memory. In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. From 05a7c611804e322156b3c5614ba75f7a16388515 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:07:05 -0700 Subject: [PATCH 14/53] Remove rationale intro, I'll add it back in a separate PR. --- Rationale.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Rationale.md b/Rationale.md index d672f468..42160abd 100644 --- a/Rationale.md +++ b/Rationale.md @@ -1,22 +1,5 @@ # Design Rationale -WebAssembly was designed incrementally, and desisions were made with a solid -rationale which may turn out to be incorrect in the future. Sometimes, decisions -were made without the luxury of data, and the designers would like to revisit -certain decisions once real end-to-end usecases are met and Science can be -performed. - -You, the reader, may also be interested in understanding why -[Abstract Syntax Tree (AST)](AstSemantics.md) semantics are the way they are, -and digging through github's issues and pull requests becomes difficult. - -This rationale document tries to list how decisions were made, and where -tradeoffs were made for the sake of language ergonomics, portability, -performance, security, and Getting Things Done. - -[Limited local nondeterminism](Nondeterminism.md) is detailed in its own -document, its rationale is also explained here. - ## Why AST? From e3f7bb56e46175128f235cc9ff131ee9f05597be Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:09:33 -0700 Subject: [PATCH 15/53] Less creative, more precise on floating-point. --- Rationale.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Rationale.md b/Rationale.md index 42160abd..c70b4b59 100644 --- a/Rationale.md +++ b/Rationale.md @@ -190,10 +190,11 @@ As WebAssembly gets implemented and tested with multiple languages on multiple architectures there may be a need to revisit some of the decisions: * When all relevant hardware implement features the same way then there's no - need to get creative and add nondeterminism to WebAssembly when realistically - there's only one mapping from WebAssenbly expression to ISA-specific - operations. One such example is floating-point, where at a high-level most - basic instructions follow IEEE-754 semantics. + need to add nondeterminism to WebAssembly when realistically there's only one + mapping from WebAssenbly expression to ISA-specific operations. One such + example is floating-point: at a high-level most basic instructions follow + IEEE-754 semantics, it is therefore not necessary to specify WebAssembly's + floating-point operations differently from IEEE-754. * When different languages have different expectations then it's unfortunate if WebAssembly measurably penalizes one's performance by enforcing determinism which that language doesn't care about, but which another language may want. From 537d6e8e6663766628379c4d9713517737e9670d Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:53:30 -0700 Subject: [PATCH 16/53] Refactor nondeterminism. --- Rationale.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Rationale.md b/Rationale.md index c70b4b59..775a53dd 100644 --- a/Rationale.md +++ b/Rationale.md @@ -169,18 +169,29 @@ WebAssembly-generators (including the JavaScript polyfill prototype). ## Limited Local Nondeterminism -The current specification is fairly strict when it comes to -[limited local nondeterminism](Nondeterminism.md) of operations: it tries to -specify all possible corner cases. +There are a few obvious cases where nondeterminism is essential to the API, such +as random number generators, date/time functions or input events. The +WebAssembly specification is fairly strict when it comes to other sources of +[limited local nondeterminism](Nondeterminism.md) of operations: it specifies +all possible corner cases, and specifies a single outcome when this can be done +reasonably. Ideally, WebAssembly would be fully deterministic because a fully deterministic -platform is easier to reason about, implement and test portably. There are a few -obvious exceptions where nondeterminism is essential to the API, like random -number generators, date/time functions or input events. Nondeterminism is only -specified as a compromise when there is no other practical way to achieve -[portable](Portability.md) native performance, or when implementation leeway is -desirable to allow usage of new hardware features or to security-harden certain -usecases. +platform is easier to: + +* Reason about. +* Implement. +* Test portably. + +Nondeterminism is only specified as a compromise when there is no other +practical way to: + +* Achieve [portable](Portability.md) native performance. +* Lowering resource usage. +* Reduce implementation complexity (both of WebAssembly VMs as well as compilers + generating WebAssembly binaries). +* Allow usage of new hardware features. +* Allows implementations to security-harden certain usecases. When nondeterminism is allowed into WebAssembly it is always done in a limited and local manner. This prevents the entire program from being invalid, as would From 0cf9b0e1d3b877a220875a9b94707f0a1fadc6bc Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:54:43 -0700 Subject: [PATCH 17/53] Drop 'fairly'. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 775a53dd..79614b71 100644 --- a/Rationale.md +++ b/Rationale.md @@ -171,7 +171,7 @@ WebAssembly-generators (including the JavaScript polyfill prototype). There are a few obvious cases where nondeterminism is essential to the API, such as random number generators, date/time functions or input events. The -WebAssembly specification is fairly strict when it comes to other sources of +WebAssembly specification is strict when it comes to other sources of [limited local nondeterminism](Nondeterminism.md) of operations: it specifies all possible corner cases, and specifies a single outcome when this can be done reasonably. From a8a981e6c6cdbbd63713f53fec414567bc088670 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:31:20 -0700 Subject: [PATCH 18/53] Expand on i8/i16. --- Rationale.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Rationale.md b/Rationale.md index 79614b71..c659d1e2 100644 --- a/Rationale.md +++ b/Rationale.md @@ -15,13 +15,16 @@ Why not a stack-, register- or SSA-based bytecode? ## Basic Types Only WebAssembly only represents [a few types](AstSemantics.md#Types). + * More complex types can be formed from these basic types. It's up to the source language compiler to express its own types in terms of the basic machine types. This allows WebAssembly to present itself as a virtual ISA, and lets compilers target it as they would any other ISA. * These types are efficiently executed by all modern architectures. -* Smaller types (such as `i8` and `i16`) are no more efficient and are only - semantically meaningful for memory accesses. +* Smaller types (such as `i8` and `i16`) are usually no more efficient and in + languages like C/C++ are only semantically meaningful for memory accesses + since arithmetic get widened to `i32` or `i64`. Avoiding them at least for MVP + makes it easier to implement a WebAssembly VM. * Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing hardware and can be supported by runtime libraries if developers wish to use them. They can be added to WebAssembly later without compromising MVP. From 02c0c2bee2e4ca98dbabdfdc2c8c41969ddd860a Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:34:28 -0700 Subject: [PATCH 19/53] Drop f80, expand on HW support. --- Rationale.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Rationale.md b/Rationale.md index c659d1e2..46436e06 100644 --- a/Rationale.md +++ b/Rationale.md @@ -25,9 +25,12 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). languages like C/C++ are only semantically meaningful for memory accesses since arithmetic get widened to `i32` or `i64`. Avoiding them at least for MVP makes it easier to implement a WebAssembly VM. -* Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing +* Other types (such as `f16`, `i128`) aren't widely supported by existing hardware and can be supported by runtime libraries if developers wish to use - them. They can be added to WebAssembly later without compromising MVP. + them. Hardware support is sometimes uneven, e.g. some support load/store of + `f16` only whereas other hardware also supports scalar arithmetic on `f16`, + and yet other hardware only supports SIMD arithmetic on `f16`. They can be + added to WebAssembly later without compromising MVP. * More complex object types aren't semantically useful for MVP. They may become useful to support other languages, especially when considering [garbage collection](GC.md). From b92f59047e55ad54e11cdb1f866fbe05bbe46acc Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:35:57 -0700 Subject: [PATCH 20/53] Sematically useful. --- Rationale.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 46436e06..8dbaa915 100644 --- a/Rationale.md +++ b/Rationale.md @@ -31,9 +31,10 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). `f16` only whereas other hardware also supports scalar arithmetic on `f16`, and yet other hardware only supports SIMD arithmetic on `f16`. They can be added to WebAssembly later without compromising MVP. -* More complex object types aren't semantically useful for MVP. They may become - useful to support other languages, especially when considering - [garbage collection](GC.md). +* More complex object types aren't semantically useful for MVP: WebAssembly + seeks to provide the primitive building blocks upon which higher-level + constructs can be built. They may become useful to support other languages, + especially when considering [garbage collection](GC.md). ## Load/Store Addressing From f488d997187186d65c2f41547ddc7920f7fbe3e1 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:36:56 -0700 Subject: [PATCH 21/53] Nit. --- Rationale.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 8dbaa915..d452d2ff 100644 --- a/Rationale.md +++ b/Rationale.md @@ -108,9 +108,9 @@ See [#299](https://github.com/WebAssembly/design/pull/299). ## Locals C/C++ makes it possible to take the address of a function's local values and -pass this pointer to callees or other threads. Such address-taken variables in -the call stack are expected to be in the WebAssembly linear memory, since they -cannot be represented as locals. This prevents WebAssembly from performing +pass this pointer to callees or to other threads. Such address-taken variables +in the call stack are expected to be in the WebAssembly linear memory, since +they cannot be represented as locals. This prevents WebAssembly from performing clever optimizations on the stack and liveness of such variables, but this loss isn't expected to be consequential. From b06176c2082a649185911dbd22f79c50352a136a Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:54:27 -0700 Subject: [PATCH 22/53] Clarify address-taken locals. --- Rationale.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index d452d2ff..131a988a 100644 --- a/Rationale.md +++ b/Rationale.md @@ -108,9 +108,11 @@ See [#299](https://github.com/WebAssembly/design/pull/299). ## Locals C/C++ makes it possible to take the address of a function's local values and -pass this pointer to callees or to other threads. Such address-taken variables -in the call stack are expected to be in the WebAssembly linear memory, since -they cannot be represented as locals. This prevents WebAssembly from performing +pass this pointer to callees or to other threads. Since WebAssembly's local +variables are outside the address space, C/C++ compilers implement address-taken +variables by creating a separate stack data structure within linear memory. This +stack is sometimes called the "aliased" stack, since it is used for variables +which may be pointed to by pointers. This prevents WebAssembly from performing clever optimizations on the stack and liveness of such variables, but this loss isn't expected to be consequential. From 55d8757ab1da61c20c6e366210dc44d90a6d3190 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 11:02:42 -0700 Subject: [PATCH 23/53] GVN. --- Rationale.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 131a988a..6183b1d1 100644 --- a/Rationale.md +++ b/Rationale.md @@ -112,9 +112,14 @@ pass this pointer to callees or to other threads. Since WebAssembly's local variables are outside the address space, C/C++ compilers implement address-taken variables by creating a separate stack data structure within linear memory. This stack is sometimes called the "aliased" stack, since it is used for variables -which may be pointed to by pointers. This prevents WebAssembly from performing -clever optimizations on the stack and liveness of such variables, but this loss -isn't expected to be consequential. +which may be pointed to by pointers. + +This prevents WebAssembly from performing clever optimizations on the stack and +liveness of such variables, but this loss isn't expected to be +consequential. Common C compiler optimizations such as LLVM's global value +numbering effectively split address-taken variables into parts, shrinking the +range where they actually need to have their address taken, and creating new +ranges where they can be allocated as local variables. Conversely, non-address taken values which are usually on the stack are instead represented as locals inside functions. This effectively means that WebAssembly From 152cfc8a6bc5fee62f36d7ae1503124f4206394a Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 11:04:27 -0700 Subject: [PATCH 24/53] Drop shadow. --- Rationale.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Rationale.md b/Rationale.md index 6183b1d1..2ae1b583 100644 --- a/Rationale.md +++ b/Rationale.md @@ -124,11 +124,11 @@ ranges where they can be allocated as local variables. Conversely, non-address taken values which are usually on the stack are instead represented as locals inside functions. This effectively means that WebAssembly has an infinite set of registers, and can choose to spill values as it sees fit -in a manner unobservable to the hosted code. This implies that there's a shadow -stack, which is also used to spill return values. This allows strong security -properties to be enforced, but does mean that two stacks are maintained (one by -the VM, the other by the compiler which targets WebAssembly) which can lead to -some inefficiencies. +in a manner unobservable to the hosted code. This implies that there's a +separate stack, unaddressable from hosted code, which is also used to spill +return values. This allows strong security properties to be enforced, but does +mean that two stacks are maintained (one by the VM, the other by the compiler +which targets WebAssembly) which can lead to some inefficiencies. Local values can also be pre-colored, meaning that multiple incoming SSA values which have separate liveness can "share" the storage represented by a From 94638c4e0fb322ed6344bee1bdecc39452177107 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 11:10:14 -0700 Subject: [PATCH 25/53] Reword around coloring. --- Rationale.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 2ae1b583..54b835c3 100644 --- a/Rationale.md +++ b/Rationale.md @@ -130,9 +130,14 @@ return values. This allows strong security properties to be enforced, but does mean that two stacks are maintained (one by the VM, the other by the compiler which targets WebAssembly) which can lead to some inefficiencies. -Local values can also be pre-colored, meaning that multiple incoming SSA values -which have separate liveness can "share" the storage represented by a -local. This offloads some of the optimization work from the WebAssembly VM. +Local variables are not in Static Single Assignment (SSA) form, meaning that +multiple incoming SSA values which have separate liveness can "share" the +storage represented by a local through the `set_local` operation. From an SSA +perspective, this means that multiple independent values can share a local +variable in WebAssembly, which is effectively a kind of pre-coloring that clever +producers can use to pre-color variables and give hints to a WebAssembly VM's +register allocation algorithms, offloading some of the optimization work from +the WebAssembly VM. ## Variable-Length Argument Lists From 2781f71e5871176c276ed34fe641fef71d07a281 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 13:37:05 -0700 Subject: [PATCH 26/53] Add Rationale.md Move some of AstSemantics.md to that document. I left things pretty much as-is for now, and will defer more contended discussions to subsequent (and more targetted) edits. --- AstSemantics.md | 110 ++++++------------------- Rationale.md | 215 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 86 deletions(-) create mode 100644 Rationale.md diff --git a/AstSemantics.md b/AstSemantics.md index 38b91732..44c273bf 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -1,26 +1,20 @@ # Abstract Syntax Tree Semantics -WebAssembly code is represented as an abstract syntax tree -that has a basic division between statements and -expressions. Each function body consists of exactly one statement. -All expressions and operations are typed, with no implicit conversions or -overloading rules. +WebAssembly code is represented as an Abstract Syntax Tree (AST) that has a +basic division between statements and expressions. Each function body consists +of exactly one statement. All expressions and operations are typed, with no +implicit conversions or overloading rules. Verification of WebAssembly code requires only a single pass with constant-time type checking and well-formedness checking. -Why not a stack-, register- or SSA-based bytecode? -* Trees allow a smaller binary encoding: [JSZap][], [Slim Binaries][]. -* [Polyfill prototype][] shows simple and efficient translation to asm.js. - - [JSZap]: https://research.microsoft.com/en-us/projects/jszap/ - [Slim Binaries]: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.108.1711 - [Polyfill prototype]: https://github.com/WebAssembly/polyfill-prototype-1 - WebAssembly offers a set of operations that are language-independent but closely match operations in many programming languages and are efficiently implementable on all modern computers. +The [rationale](Rationale.md) document details why WebAssembly is designed as +detailed in this document. + ## Traps Some operations may *trap* under some conditions, as noted below. In the MVP, @@ -148,8 +142,7 @@ The semantics of out-of-bounds accesses are discussed The use of infinite-precision in the effective address computation means that the addition of the offset to the address never causes wrapping, so if the address for an access is out-of-bounds, the effective address will always also -be out-of-bounds. This is intended to simplify folding of offsets into complex -address modes in hardware, and to simplify bounds checking optimizations. +be out-of-bounds. In wasm32, address operands and offset attributes have type `i32`, and linear memory sizes are limited to 4 GiB (of course, actual sizes are further limited @@ -170,53 +163,12 @@ when considering alignment. If the effective address of a memory access is a multiple of the alignment attribute value of the memory access, the memory access is considered *aligned*, otherwise it is considered *misaligned*. Aligned and misaligned accesses have -the same behavior. Alignment affects performance as follows: - - * Aligned accesses with at least natural alignment are fast. - * Aligned accesses with less than natural alignment may be somewhat slower - (think: implementation makes multiple accesses, either in software or - in hardware). - * Misaligned access of any kind may be *massively* slower - (think: implementation takes a signal and fixes things up). - -Thus, it is recommend that WebAssembly producers align frequently-used data -to permit the use of natural alignment access, and use loads and stores with -the greatest alignment values practical, while always avoiding misaligned -accesses. - -Either tooling or an explicit opt-in "debug mode" in the spec should allow -execution of a module in a mode that threw exceptions on misaligned access. -(This mode would incur some runtime cost for branching on most platforms which -is why it isn't the specified default.) - -### Out of bounds - -The ideal semantics is for out-of-bounds accesses to trap, but the implications -are not yet fully clear. - -There are several possible variations on this design being discussed and -experimented with. More measurement is required to understand the associated -tradeoffs. - - * After an out-of-bounds access, the instance can no longer execute code and any - outstanding JavaScript [ArrayBuffer][] aliasing the linear memory are detached. - * This would primarily allow hoisting bounds checks above effectful - operations. - * This can be viewed as a mild security measure under the assumption that - while the sandbox is still ensuring safety, the instance's internal state - is incoherent and further execution could lead to Bad Things (e.g., XSS - attacks). - * To allow for potentially more-efficient memory sandboxing, the semantics could - allow for a nondeterministic choice between one of the following when an - out-of-bounds access occurred. - * The ideal trap semantics. - * Loads return an unspecified value. - * Stores are either ignored or store to an unspecified location in the linear memory. - * Either tooling or an explicit opt-in "debug mode" in the spec should allow - execution of a module in a mode that threw exceptions on out-of-bounds - access. - - [ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer +the same behavior. + +### Out of Bounds + +Out of bounds accesses trap. + ### Resizing @@ -234,9 +186,9 @@ MVP, there are [future features](FutureFeatures.md#finer-grained-control-over-me proposed to allow setting protection and creating mappings within the contiguous linear memory. -In the MVP, memory can only be grown. After the MVP, a memory shrinking operation -may be added. However, due to normal fragmentation, applications are instead -expected release unused physical pages from the working set using the +In the MVP, memory can only be grown. After the MVP, a memory shrinking +operation may be added. However, due to normal fragmentation, applications are +instead expected release unused physical pages from the working set using the [`discard`](FutureFeatures.md#finer-grained-control-over-memory) future feature. The result type of `page_size` is `int32` for wasm32 and `int64` for wasm64. @@ -246,6 +198,7 @@ The result value of `page_size` is an unsigned integer which is a power of 2. of the implementation; it just needs to be a value suitable for use with `grow_memory`) + ## Local variables Each function has a fixed, pre-declared number of local variables which occupy a single @@ -262,6 +215,7 @@ The details of index space for local variables and their types will be further c e.g. whether locals with type `i32` and `i64` must be contiguous and separate from others, etc. + ## Control flow structures WebAssembly offers basic structured control flow. All control flow structures @@ -305,10 +259,7 @@ Each function has a *signature*, which consists of: * Return types, which are a sequence of local types * Argument types, which are a sequence of local types -Note that WebAssembly itself does not support variable-length argument lists -(aka varargs). C and C++ compilers are expected to implement this functionality -by storing arguments in a buffer in linear memory and passing a pointer to the -buffer. +WebAssembly doesn't support variable-length argument lists (aka varargs). In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. @@ -335,14 +286,11 @@ in the function table. Function-pointer values are comparable for equality and the `addressof` operator is monomorphic. Function-pointer values can be explicitly coerced to and from -integers (which, in particular, is necessary when loading/storing to memory -since memory only provides integer types). For security and safety reasons, -the integer value of a coerced function-pointer value is an abstract index and -does not reveal the actual machine code address of the target function. +integers. In the MVP, function pointer values are local to a single module. The -[dynamic linking](DynamicLinking.md) feature is necessary for -two modules to pass function pointers back and forth. +[dynamic linking](DynamicLinking.md) feature is necessary for two modules to +pass function pointers back and forth. Multiple return value calls will be possible, though possibly not in the MVP. The details of multiple-return-value calls needs clarification. Calling a @@ -361,17 +309,7 @@ supported (including NaN values of all possible bit patterns). * `f32.const`: produce the value of an f32 immediate * `f64.const`: produce the value of an f64 immediate -## Expressions with control flow - -Expression trees offer significant size reduction by avoiding the need for -`set_local`/`get_local` pairs in the common case of an expression with only one, -immediate use. The following primitives provide AST nodes that express control -flow and thus allow more opportunities to build bigger expression trees and -further reduce `set_local`/`get_local` usage (which constitute 30-40% of total -bytes in the -[polyfill prototype](https://github.com/WebAssembly/polyfill-prototype-1)). -Additionally, these primitives are useful building blocks for -WebAssembly-generators (including the JavaScript polyfill prototype). +## Expressions with Control Flow * `comma`: evaluate and ignore the result of the first operand, evaluate and return the second operand diff --git a/Rationale.md b/Rationale.md new file mode 100644 index 00000000..ca71b804 --- /dev/null +++ b/Rationale.md @@ -0,0 +1,215 @@ +# Design Rationale + +WebAssembly was designed incrementally, and desisions were made with a solid +rationale which may turn out to be incorrect in the future. Sometimes, decisions +were made without the luxury of data, and the designers would like to revisit +certain decisions once real end-to-end usecases are met and Science can be +performed. + +You, the reader, may also be interested in understanding why +[Abstract Syntax Tree (AST)](AstSemantics.md) semantics are the way they are, +and digging through github's issues and pull requests becomes difficult. + +This rationale document tries to list how decisions were made, and where +tradeoffs were made for the sake of language ergonomics, portability, +performance, security, and Getting Things Done. + +The rationale for [limited local nondeterminism](Nondeterminism.md) is details +in its own document. + + +## Why AST? + +Why not a stack-, register- or SSA-based bytecode? +* Trees allow a smaller binary encoding: [JSZap][], [Slim Binaries][]. +* [Polyfill prototype][] shows simple and efficient translation to asm.js. + + [JSZap]: https://research.microsoft.com/en-us/projects/jszap/ + [Slim Binaries]: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.108.1711 + [Polyfill prototype]: https://github.com/WebAssembly/polyfill-prototype-1 + + +## Basic Types Only + +WebAssembly only represents [a few types](AstSemantics.md). +* More complex types can be formed from these basic types. It's up to the source + language compiler to express its own types in terms of the basic machine + types. This allows WebAssembly to present itself as a virtual ISA, and lets + compilers target it as they would any other ISA. +* These types are efficiently executed by all modern architectures. +* Smaller types (such as `i8` and `i16`) are no more efficient and are only + semantically meaningful for memory accesses. +* Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing + hardware and can be supported by runtime libraries if developers wish to use + them. They can be added to WebAssembly later without compromising MVP. +* More complex object types aren't sematcically useful for MVP. They may become + useful to support other languages, especially when considering + [garbage collection](GC.md). + + +## Load/Store Addressing + +Load/store instructions include and immediate offset used for +[addressing](AstSemantics.md#Addressing). This is intended to simplify folding +of offsets into complex address modes in hardware, and to simplify bounds +checking optimizations. It offloads some of the optimization work to the +compiler that targets WebAssembly, executing on the developer's machine, instead +of performing that work in the WebAssembly compiler on the user's machine. + + +## Alignment Hints + +Load/store instructions contain +[alignment hints](AstSemantics.md#Alignment). This makes it easier to generate +efficient code on certain hardware architectures. + +Alignment affects performance as follows: + + * Aligned accesses with at least natural alignment are fast. + * Aligned accesses with less than natural alignment may be somewhat slower + (think: implementation makes multiple accesses, either in software or in + hardware). + * Misaligned access of any kind may be *massively* slower (think: + implementation takes a signal and fixes things up). + +Thus, it is recommend that WebAssembly producers align frequently-used data to +permit the use of natural alignment access, and use loads and stores with the +greatest alignment values practical, while always avoiding misaligned accesses. + +Either tooling or an explicit opt-in "debug mode" in the spec should allow +execution of a module in a mode that threw exceptions on misaligned access. +(This mode would incur some runtime cost for branching on most platforms which +is why it isn't the specified default.) + + +## Out of Bounds + +The ideal semantics is for +[out-of-bounds accesses](AstSemantics.md#Out-of-Bounds) to trap, but the +implications are not yet fully clear. + +There are several possible variations on this design being discussed and +experimented with. More measurement is required to understand the associated +tradeoffs. + + * After an out-of-bounds access, the instance can no longer execute code and + any outstanding JavaScript [ArrayBuffer][] aliasing the linear memory are + detached. + * This would primarily allow hoisting bounds checks above effectful + operations. + * This can be viewed as a mild security measure under the assumption that + while the sandbox is still ensuring safety, the instance's internal state + is incoherent and further execution could lead to Bad Things (e.g., XSS + attacks). + * To allow for potentially more-efficient memory sandboxing, the semantics + could allow for a nondeterministic choice between one of the following when + an out-of-bounds access occurred. + * The ideal trap semantics. + * Loads return an unspecified value. + * Stores are either ignored or store to an unspecified location in the + linear memory. + * Either tooling or an explicit opt-in "debug mode" in the spec should allow + execution of a module in a mode that threw exceptions on out-of-bounds + access. + + [ArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer + + +## Resizing + +Implementations provide a `page_size` operation which allows them to efficiently +map the underlying OS's capabilities to the WebAssembly application, as well as +to communicate their own implementation details in a useful manner to the +developer. + +Note that the `page_size` value need not reflect the actual internal page size +of the implementation; it just needs to be a value suitable for use with +`resize_memory`. + + +## Control Flow + +See [#299](https://github.com/WebAssembly/design/pull/299). + + +## Locals + +Address-taken variables in the call stack are expected to be in linear memory, +since they cannot be represented as locals. This prevents WebAssembly from +performing clever optimizations on the stack and liveness of such variables, but +this loss isn't expected to be consequential. + +Conversely, non-address taken values which are usually on the stack are instead +represented as locals inside functions. This effectively means that WebAssembly +has an infinite set of registers, and can choose to spill values as it sees fit +in a manner unobservable to the hosted code. This implies that there's a shadow +stack, which is also used to spill return values. This allows strong security +properties to be enforced, but does mean that two stacks are maintained (one by +the VM, the other by the compiler which targets WebAssembly) which can lead to +some inefficiencies. + +Local values can also be pre-colored, meaning that multiple incoming SSA values +which have separate liveness can "share" the storage represented by a +local. This offloads some of the optimization work from the WebAssembly VM. + + +## Variable-Length Argument Lists + +C and C++ compilers are expected to implement variable-length argument lists by +storing arguments in a buffer in linear memory and passing a pointer to the +buffer. This greatly simplifies WebAssembly VM implementations by punting this +ABI consideration to the front-end compiler. It does negatively impact +performance, but variable-length calls are already somewhat slow. + + +## Multiple Return Values + +TODO + + +## Indirect Calls + +The exact semantics of indirect function calls, function pointers, and what +happens when calling the wrong function, are still being discussed. + +Fundamentally linear memory is a simple collection of bytes, which means that +some integral representation of function pointers must exist. It's desirable to +hide the actual address of generated code from untrusted code because that would +be an unfortunate information leak which could have negative security +implications. Indirection is therefore desired. + +One extra concern is that existing C++ code sometimes stores data inside of what +is usually a function pointer. This is expected to keep working. + +Dynamic linking further complicates this: WebAssembly cannot simply standardize +on fixed-size function tables since dynamically linked code can add new +functions, as well as remove them. + + +## Expressions with Control Flow + +Expression trees offer significant size reduction by avoiding the need for +`set_local`/`get_local` pairs in the common case of an expression with only one, +immediate use. The following primitives provide AST nodes that express control +flow and thus allow more opportunities to build bigger expression trees and +further reduce `set_local`/`get_local` usage (which constitute 30-40% of total +bytes in the +[polyfill prototype](https://github.com/WebAssembly/polyfill-prototype-1)). +Additionally, these primitives are useful building blocks for +WebAssembly-generators (including the JavaScript polyfill prototype). + + +## Limited Local Nondeterminism + +The current specification is fairly strict when it comes to +[limited local nondeterminism](Nondeterminism.md) of operations: it tries to +specify all possible corner cases. + +As WebAssembly gets implemented and tested with multiple languages on multiple +architectures there may be a need to revisit some of the decisions: + +* When all relevant hardware implement features the same way then there's no + need to get creative. +* When different languages have different expectations then it's unfortunate if + WebAssembly measurably penalizes one's performance by enforcing determinism + which that language doesn't care about, but which another language may want. From 0e53992bf008b3134b92ce1c30acc3ea3114c075 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:00:10 -0700 Subject: [PATCH 27/53] Link to types. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index ca71b804..49a807a1 100644 --- a/Rationale.md +++ b/Rationale.md @@ -31,7 +31,7 @@ Why not a stack-, register- or SSA-based bytecode? ## Basic Types Only -WebAssembly only represents [a few types](AstSemantics.md). +WebAssembly only represents [a few types](AstSemantics.md#Types). * More complex types can be formed from these basic types. It's up to the source language compiler to express its own types in terms of the basic machine types. This allows WebAssembly to present itself as a virtual ISA, and lets From 881a6cd34ff82dfbfd5935c581696d98c44ec4b3 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:00:44 -0700 Subject: [PATCH 28/53] Typo. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 49a807a1..9b966399 100644 --- a/Rationale.md +++ b/Rationale.md @@ -42,7 +42,7 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). * Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing hardware and can be supported by runtime libraries if developers wish to use them. They can be added to WebAssembly later without compromising MVP. -* More complex object types aren't sematcically useful for MVP. They may become +* More complex object types aren't semantically useful for MVP. They may become useful to support other languages, especially when considering [garbage collection](GC.md). From 18b1dbb1f8c252cc2d5467028011b2452edbef1f Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:02:51 -0700 Subject: [PATCH 29/53] Leave some alignment docs in AST semantics. --- AstSemantics.md | 15 +++++++++++++++ Rationale.md | 19 +++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 44c273bf..f7978f2d 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -151,6 +151,7 @@ offsets have type `i64`. The MVP only includes wasm32; subsequent versions will add support for wasm64 and thus [>4 GiB linear memory](FutureFeatures.md#linear-memory-bigger-than-4-gib). + ### Alignment Each linear memory access operation also has an immediate positive integer power @@ -165,6 +166,20 @@ attribute value of the memory access, the memory access is considered *aligned*, otherwise it is considered *misaligned*. Aligned and misaligned accesses have the same behavior. +Alignment affects performance as follows: + + * Aligned accesses with at least natural alignment are fast. + * Aligned accesses with less than natural alignment may be somewhat slower + (think: implementation makes multiple accesses, either in software or in + hardware). + * Misaligned access of any kind may be *massively* slower (think: + implementation takes a signal and fixes things up). + +Thus, it is recommend that WebAssembly producers align frequently-used data to +permit the use of natural alignment access, and use loads and stores with the +greatest alignment values practical, while always avoiding misaligned accesses. + + ### Out of Bounds Out of bounds accesses trap. diff --git a/Rationale.md b/Rationale.md index 9b966399..1f49eb6c 100644 --- a/Rationale.md +++ b/Rationale.md @@ -63,23 +63,10 @@ Load/store instructions contain [alignment hints](AstSemantics.md#Alignment). This makes it easier to generate efficient code on certain hardware architectures. -Alignment affects performance as follows: - - * Aligned accesses with at least natural alignment are fast. - * Aligned accesses with less than natural alignment may be somewhat slower - (think: implementation makes multiple accesses, either in software or in - hardware). - * Misaligned access of any kind may be *massively* slower (think: - implementation takes a signal and fixes things up). - -Thus, it is recommend that WebAssembly producers align frequently-used data to -permit the use of natural alignment access, and use loads and stores with the -greatest alignment values practical, while always avoiding misaligned accesses. - -Either tooling or an explicit opt-in "debug mode" in the spec should allow +Either tooling or an explicit opt-in "debug mode" in the spec could allow execution of a module in a mode that threw exceptions on misaligned access. -(This mode would incur some runtime cost for branching on most platforms which -is why it isn't the specified default.) +This mode would incur some runtime cost for branching on most platforms which is +why it isn't the specified default. ## Out of Bounds From bf97af20673170ac1fd3be6b46fce640975de559 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:04:27 -0700 Subject: [PATCH 30/53] Leave some page_size docs in AST semantics. --- AstSemantics.md | 4 ++++ Rationale.md | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index f7978f2d..157f51b8 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -213,6 +213,10 @@ The result value of `page_size` is an unsigned integer which is a power of 2. of the implementation; it just needs to be a value suitable for use with `grow_memory`) +The `page_size` value need not reflect the actual internal page size of the +implementation; it just needs to be a value suitable for use with +`resize_memory`. + ## Local variables diff --git a/Rationale.md b/Rationale.md index 1f49eb6c..15016d67 100644 --- a/Rationale.md +++ b/Rationale.md @@ -109,10 +109,6 @@ map the underlying OS's capabilities to the WebAssembly application, as well as to communicate their own implementation details in a useful manner to the developer. -Note that the `page_size` value need not reflect the actual internal page size -of the implementation; it just needs to be a value suitable for use with -`resize_memory`. - ## Control Flow From 31a42501e452557b4c018e0673f8cbed5d90cffc Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:05:57 -0700 Subject: [PATCH 31/53] Keep details on varargs. --- AstSemantics.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AstSemantics.md b/AstSemantics.md index 157f51b8..75916233 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -278,7 +278,8 @@ Each function has a *signature*, which consists of: * Return types, which are a sequence of local types * Argument types, which are a sequence of local types -WebAssembly doesn't support variable-length argument lists (aka varargs). +WebAssembly doesn't support variable-length argument lists (aka varargs), they +are instead supported through explicit accesses to linear memory. In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. From 694bcc5c63eb536189782b2632510ace9f649687 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:06:37 -0700 Subject: [PATCH 32/53] Typo. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 15016d67..8775a4ed 100644 --- a/Rationale.md +++ b/Rationale.md @@ -14,7 +14,7 @@ This rationale document tries to list how decisions were made, and where tradeoffs were made for the sake of language ergonomics, portability, performance, security, and Getting Things Done. -The rationale for [limited local nondeterminism](Nondeterminism.md) is details +The rationale for [limited local nondeterminism](Nondeterminism.md) is detailed in its own document. From 7fdd2f289b79c27bc61f81d1364bb0430ced2961 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:10:16 -0700 Subject: [PATCH 33/53] Clarify that nondeterminism is also rationalized. --- Nondeterminism.md | 3 +++ Rationale.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Nondeterminism.md b/Nondeterminism.md index 65567dd2..d71115ed 100644 --- a/Nondeterminism.md +++ b/Nondeterminism.md @@ -8,6 +8,9 @@ local, nondeterminism. * *Local*: when nondeterministic execution occurs, the effect is local, there is no "spooky action at a distance". +The [rationale](Rationale.md) document details why WebAssembly is designed as +detailed in this document. + The limited, local, nondeterministic model implies: * Applications can't access data outside the sandbox without going through appropriate APIs, or otherwise escape the sandbox. diff --git a/Rationale.md b/Rationale.md index 8775a4ed..d7420fe5 100644 --- a/Rationale.md +++ b/Rationale.md @@ -14,8 +14,8 @@ This rationale document tries to list how decisions were made, and where tradeoffs were made for the sake of language ergonomics, portability, performance, security, and Getting Things Done. -The rationale for [limited local nondeterminism](Nondeterminism.md) is detailed -in its own document. +[Limited local nondeterminism](Nondeterminism.md) is detailed in its own +document, its rationale is also explained here. ## Why AST? From 659d9c9445c4baeffe6ae4330fd31589d320874f Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:12:54 -0700 Subject: [PATCH 34/53] Typo. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index d7420fe5..3b76bab2 100644 --- a/Rationale.md +++ b/Rationale.md @@ -49,7 +49,7 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). ## Load/Store Addressing -Load/store instructions include and immediate offset used for +Load/store instructions include an immediate offset used for [addressing](AstSemantics.md#Addressing). This is intended to simplify folding of offsets into complex address modes in hardware, and to simplify bounds checking optimizations. It offloads some of the optimization work to the From 0e698ef13a2e7ca757ae609964b2d04da0017cc3 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 14:15:35 -0700 Subject: [PATCH 35/53] Clarification on being creative. --- Rationale.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 3b76bab2..a9c89379 100644 --- a/Rationale.md +++ b/Rationale.md @@ -192,7 +192,10 @@ As WebAssembly gets implemented and tested with multiple languages on multiple architectures there may be a need to revisit some of the decisions: * When all relevant hardware implement features the same way then there's no - need to get creative. + need to get creative and add nondeterminism to WebAssembly when realistically + there's only one mapping from WebAssenbly expression to ISA-specific + operations. One such example is floating-point, where at a high-level most + basic instructions follow IEEE-754 semantics. * When different languages have different expectations then it's unfortunate if WebAssembly measurably penalizes one's performance by enforcing determinism which that language doesn't care about, but which another language may want. From 14712ccd008e1d0f93cc6e1b0b4993576a6e91e4 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 16:18:04 -0700 Subject: [PATCH 36/53] C/C++ for address-taken. --- Rationale.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Rationale.md b/Rationale.md index a9c89379..259353ca 100644 --- a/Rationale.md +++ b/Rationale.md @@ -117,10 +117,12 @@ See [#299](https://github.com/WebAssembly/design/pull/299). ## Locals -Address-taken variables in the call stack are expected to be in linear memory, -since they cannot be represented as locals. This prevents WebAssembly from -performing clever optimizations on the stack and liveness of such variables, but -this loss isn't expected to be consequential. +C/C++ makes it possible to take the address of a function's local values and +pass this pointer to callees or other threads. Such address-taken variables in +the call stack are expected to be in the WebAssembly linear memory, since they +cannot be represented as locals. This prevents WebAssembly from performing +clever optimizations on the stack and liveness of such variables, but this loss +isn't expected to be consequential. Conversely, non-address taken values which are usually on the stack are instead represented as locals inside functions. This effectively means that WebAssembly From 6e91da7bdccad41cca45b2c3e4447ff18acd0f6b Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 16:27:15 -0700 Subject: [PATCH 37/53] Move some mentions of nondeterminism. --- Nondeterminism.md | 5 ----- Rationale.md | 13 +++++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Nondeterminism.md b/Nondeterminism.md index d71115ed..36dfda12 100644 --- a/Nondeterminism.md +++ b/Nondeterminism.md @@ -20,11 +20,6 @@ The limited, local, nondeterministic model implies: [Control Flow Integrity](https://research.microsoft.com/apps/pubs/default.aspx?id=64250). * WebAssembly has no [nasal demons](https://en.wikipedia.org/w/index.php?title=Nasal_demons). -Ideally, WebAssembly would be fully deterministic (except where nondeterminism -was essential to the API, like random number generators, date/time functions or -input events). Nondeterminism is only specified as a compromise when there is no -other practical way to achieve [portable](Portability.md) native performance. - The following is a list of the places where the WebAssembly specification currently admits nondeterminism: diff --git a/Rationale.md b/Rationale.md index 259353ca..d672f468 100644 --- a/Rationale.md +++ b/Rationale.md @@ -190,6 +190,19 @@ The current specification is fairly strict when it comes to [limited local nondeterminism](Nondeterminism.md) of operations: it tries to specify all possible corner cases. +Ideally, WebAssembly would be fully deterministic because a fully deterministic +platform is easier to reason about, implement and test portably. There are a few +obvious exceptions where nondeterminism is essential to the API, like random +number generators, date/time functions or input events. Nondeterminism is only +specified as a compromise when there is no other practical way to achieve +[portable](Portability.md) native performance, or when implementation leeway is +desirable to allow usage of new hardware features or to security-harden certain +usecases. + +When nondeterminism is allowed into WebAssembly it is always done in a limited +and local manner. This prevents the entire program from being invalid, as would +be the case with C++ undefined behavior. + As WebAssembly gets implemented and tested with multiple languages on multiple architectures there may be a need to revisit some of the decisions: From f1a34fbf3eee75e6bd8648a317208674d92ed0e9 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Tue, 6 Oct 2015 16:29:22 -0700 Subject: [PATCH 38/53] Clarify varargs. --- AstSemantics.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 75916233..ec014fa5 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -278,8 +278,9 @@ Each function has a *signature*, which consists of: * Return types, which are a sequence of local types * Argument types, which are a sequence of local types -WebAssembly doesn't support variable-length argument lists (aka varargs), they -are instead supported through explicit accesses to linear memory. +WebAssembly doesn't support variable-length argument lists (aka +varargs). Compilers targetting WebAssembly can instead support them through +explicit accesses to linear memory. In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. From de90fca2254ac949130faddee3ec08acc1f64c1c Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:07:05 -0700 Subject: [PATCH 39/53] Remove rationale intro, I'll add it back in a separate PR. --- Rationale.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Rationale.md b/Rationale.md index d672f468..42160abd 100644 --- a/Rationale.md +++ b/Rationale.md @@ -1,22 +1,5 @@ # Design Rationale -WebAssembly was designed incrementally, and desisions were made with a solid -rationale which may turn out to be incorrect in the future. Sometimes, decisions -were made without the luxury of data, and the designers would like to revisit -certain decisions once real end-to-end usecases are met and Science can be -performed. - -You, the reader, may also be interested in understanding why -[Abstract Syntax Tree (AST)](AstSemantics.md) semantics are the way they are, -and digging through github's issues and pull requests becomes difficult. - -This rationale document tries to list how decisions were made, and where -tradeoffs were made for the sake of language ergonomics, portability, -performance, security, and Getting Things Done. - -[Limited local nondeterminism](Nondeterminism.md) is detailed in its own -document, its rationale is also explained here. - ## Why AST? From 68006a2e7459335e1cdd9f7418bb9e874011fc30 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:09:33 -0700 Subject: [PATCH 40/53] Less creative, more precise on floating-point. --- Rationale.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Rationale.md b/Rationale.md index 42160abd..c70b4b59 100644 --- a/Rationale.md +++ b/Rationale.md @@ -190,10 +190,11 @@ As WebAssembly gets implemented and tested with multiple languages on multiple architectures there may be a need to revisit some of the decisions: * When all relevant hardware implement features the same way then there's no - need to get creative and add nondeterminism to WebAssembly when realistically - there's only one mapping from WebAssenbly expression to ISA-specific - operations. One such example is floating-point, where at a high-level most - basic instructions follow IEEE-754 semantics. + need to add nondeterminism to WebAssembly when realistically there's only one + mapping from WebAssenbly expression to ISA-specific operations. One such + example is floating-point: at a high-level most basic instructions follow + IEEE-754 semantics, it is therefore not necessary to specify WebAssembly's + floating-point operations differently from IEEE-754. * When different languages have different expectations then it's unfortunate if WebAssembly measurably penalizes one's performance by enforcing determinism which that language doesn't care about, but which another language may want. From 935ca4ae7ba69e504fb2fa2bcd721fb895a61dcd Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:53:30 -0700 Subject: [PATCH 41/53] Refactor nondeterminism. --- Rationale.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Rationale.md b/Rationale.md index c70b4b59..775a53dd 100644 --- a/Rationale.md +++ b/Rationale.md @@ -169,18 +169,29 @@ WebAssembly-generators (including the JavaScript polyfill prototype). ## Limited Local Nondeterminism -The current specification is fairly strict when it comes to -[limited local nondeterminism](Nondeterminism.md) of operations: it tries to -specify all possible corner cases. +There are a few obvious cases where nondeterminism is essential to the API, such +as random number generators, date/time functions or input events. The +WebAssembly specification is fairly strict when it comes to other sources of +[limited local nondeterminism](Nondeterminism.md) of operations: it specifies +all possible corner cases, and specifies a single outcome when this can be done +reasonably. Ideally, WebAssembly would be fully deterministic because a fully deterministic -platform is easier to reason about, implement and test portably. There are a few -obvious exceptions where nondeterminism is essential to the API, like random -number generators, date/time functions or input events. Nondeterminism is only -specified as a compromise when there is no other practical way to achieve -[portable](Portability.md) native performance, or when implementation leeway is -desirable to allow usage of new hardware features or to security-harden certain -usecases. +platform is easier to: + +* Reason about. +* Implement. +* Test portably. + +Nondeterminism is only specified as a compromise when there is no other +practical way to: + +* Achieve [portable](Portability.md) native performance. +* Lowering resource usage. +* Reduce implementation complexity (both of WebAssembly VMs as well as compilers + generating WebAssembly binaries). +* Allow usage of new hardware features. +* Allows implementations to security-harden certain usecases. When nondeterminism is allowed into WebAssembly it is always done in a limited and local manner. This prevents the entire program from being invalid, as would From 38348f3ce1929c3dee15a96ab2a31a187105d8e1 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 09:54:43 -0700 Subject: [PATCH 42/53] Drop 'fairly'. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 775a53dd..79614b71 100644 --- a/Rationale.md +++ b/Rationale.md @@ -171,7 +171,7 @@ WebAssembly-generators (including the JavaScript polyfill prototype). There are a few obvious cases where nondeterminism is essential to the API, such as random number generators, date/time functions or input events. The -WebAssembly specification is fairly strict when it comes to other sources of +WebAssembly specification is strict when it comes to other sources of [limited local nondeterminism](Nondeterminism.md) of operations: it specifies all possible corner cases, and specifies a single outcome when this can be done reasonably. From 38dbd0f1c12ab7be136fe76012da212781773333 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:31:20 -0700 Subject: [PATCH 43/53] Expand on i8/i16. --- Rationale.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Rationale.md b/Rationale.md index 79614b71..c659d1e2 100644 --- a/Rationale.md +++ b/Rationale.md @@ -15,13 +15,16 @@ Why not a stack-, register- or SSA-based bytecode? ## Basic Types Only WebAssembly only represents [a few types](AstSemantics.md#Types). + * More complex types can be formed from these basic types. It's up to the source language compiler to express its own types in terms of the basic machine types. This allows WebAssembly to present itself as a virtual ISA, and lets compilers target it as they would any other ISA. * These types are efficiently executed by all modern architectures. -* Smaller types (such as `i8` and `i16`) are no more efficient and are only - semantically meaningful for memory accesses. +* Smaller types (such as `i8` and `i16`) are usually no more efficient and in + languages like C/C++ are only semantically meaningful for memory accesses + since arithmetic get widened to `i32` or `i64`. Avoiding them at least for MVP + makes it easier to implement a WebAssembly VM. * Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing hardware and can be supported by runtime libraries if developers wish to use them. They can be added to WebAssembly later without compromising MVP. From df346221d12694e5bb76680cabf0073e8859440b Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:34:28 -0700 Subject: [PATCH 44/53] Drop f80, expand on HW support. --- Rationale.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Rationale.md b/Rationale.md index c659d1e2..46436e06 100644 --- a/Rationale.md +++ b/Rationale.md @@ -25,9 +25,12 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). languages like C/C++ are only semantically meaningful for memory accesses since arithmetic get widened to `i32` or `i64`. Avoiding them at least for MVP makes it easier to implement a WebAssembly VM. -* Other types (such as `f16`, `f80`, `i128`) aren't widely supported by existing +* Other types (such as `f16`, `i128`) aren't widely supported by existing hardware and can be supported by runtime libraries if developers wish to use - them. They can be added to WebAssembly later without compromising MVP. + them. Hardware support is sometimes uneven, e.g. some support load/store of + `f16` only whereas other hardware also supports scalar arithmetic on `f16`, + and yet other hardware only supports SIMD arithmetic on `f16`. They can be + added to WebAssembly later without compromising MVP. * More complex object types aren't semantically useful for MVP. They may become useful to support other languages, especially when considering [garbage collection](GC.md). From 1ab46f4f45b861b15b619738040c5e103381f25a Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:35:57 -0700 Subject: [PATCH 45/53] Sematically useful. --- Rationale.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 46436e06..8dbaa915 100644 --- a/Rationale.md +++ b/Rationale.md @@ -31,9 +31,10 @@ WebAssembly only represents [a few types](AstSemantics.md#Types). `f16` only whereas other hardware also supports scalar arithmetic on `f16`, and yet other hardware only supports SIMD arithmetic on `f16`. They can be added to WebAssembly later without compromising MVP. -* More complex object types aren't semantically useful for MVP. They may become - useful to support other languages, especially when considering - [garbage collection](GC.md). +* More complex object types aren't semantically useful for MVP: WebAssembly + seeks to provide the primitive building blocks upon which higher-level + constructs can be built. They may become useful to support other languages, + especially when considering [garbage collection](GC.md). ## Load/Store Addressing From 70daaa1877aaf15fbfd7930742774a9162811c16 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:36:56 -0700 Subject: [PATCH 46/53] Nit. --- Rationale.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 8dbaa915..d452d2ff 100644 --- a/Rationale.md +++ b/Rationale.md @@ -108,9 +108,9 @@ See [#299](https://github.com/WebAssembly/design/pull/299). ## Locals C/C++ makes it possible to take the address of a function's local values and -pass this pointer to callees or other threads. Such address-taken variables in -the call stack are expected to be in the WebAssembly linear memory, since they -cannot be represented as locals. This prevents WebAssembly from performing +pass this pointer to callees or to other threads. Such address-taken variables +in the call stack are expected to be in the WebAssembly linear memory, since +they cannot be represented as locals. This prevents WebAssembly from performing clever optimizations on the stack and liveness of such variables, but this loss isn't expected to be consequential. From 370e16158a4b5ce06eaa38532de28714f124d918 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 10:54:27 -0700 Subject: [PATCH 47/53] Clarify address-taken locals. --- Rationale.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index d452d2ff..131a988a 100644 --- a/Rationale.md +++ b/Rationale.md @@ -108,9 +108,11 @@ See [#299](https://github.com/WebAssembly/design/pull/299). ## Locals C/C++ makes it possible to take the address of a function's local values and -pass this pointer to callees or to other threads. Such address-taken variables -in the call stack are expected to be in the WebAssembly linear memory, since -they cannot be represented as locals. This prevents WebAssembly from performing +pass this pointer to callees or to other threads. Since WebAssembly's local +variables are outside the address space, C/C++ compilers implement address-taken +variables by creating a separate stack data structure within linear memory. This +stack is sometimes called the "aliased" stack, since it is used for variables +which may be pointed to by pointers. This prevents WebAssembly from performing clever optimizations on the stack and liveness of such variables, but this loss isn't expected to be consequential. From 62a8bb12d53f2ac2377c90efa9f76ba569a4632a Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 11:02:42 -0700 Subject: [PATCH 48/53] GVN. --- Rationale.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 131a988a..6183b1d1 100644 --- a/Rationale.md +++ b/Rationale.md @@ -112,9 +112,14 @@ pass this pointer to callees or to other threads. Since WebAssembly's local variables are outside the address space, C/C++ compilers implement address-taken variables by creating a separate stack data structure within linear memory. This stack is sometimes called the "aliased" stack, since it is used for variables -which may be pointed to by pointers. This prevents WebAssembly from performing -clever optimizations on the stack and liveness of such variables, but this loss -isn't expected to be consequential. +which may be pointed to by pointers. + +This prevents WebAssembly from performing clever optimizations on the stack and +liveness of such variables, but this loss isn't expected to be +consequential. Common C compiler optimizations such as LLVM's global value +numbering effectively split address-taken variables into parts, shrinking the +range where they actually need to have their address taken, and creating new +ranges where they can be allocated as local variables. Conversely, non-address taken values which are usually on the stack are instead represented as locals inside functions. This effectively means that WebAssembly From 5152df1ec8cd06c1fab6c163523419b44fb9c65a Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 11:04:27 -0700 Subject: [PATCH 49/53] Drop shadow. --- Rationale.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Rationale.md b/Rationale.md index 6183b1d1..2ae1b583 100644 --- a/Rationale.md +++ b/Rationale.md @@ -124,11 +124,11 @@ ranges where they can be allocated as local variables. Conversely, non-address taken values which are usually on the stack are instead represented as locals inside functions. This effectively means that WebAssembly has an infinite set of registers, and can choose to spill values as it sees fit -in a manner unobservable to the hosted code. This implies that there's a shadow -stack, which is also used to spill return values. This allows strong security -properties to be enforced, but does mean that two stacks are maintained (one by -the VM, the other by the compiler which targets WebAssembly) which can lead to -some inefficiencies. +in a manner unobservable to the hosted code. This implies that there's a +separate stack, unaddressable from hosted code, which is also used to spill +return values. This allows strong security properties to be enforced, but does +mean that two stacks are maintained (one by the VM, the other by the compiler +which targets WebAssembly) which can lead to some inefficiencies. Local values can also be pre-colored, meaning that multiple incoming SSA values which have separate liveness can "share" the storage represented by a From b62e31139bfce0faf88a1b1bf46c9e1265ac953a Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 11:10:14 -0700 Subject: [PATCH 50/53] Reword around coloring. --- Rationale.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Rationale.md b/Rationale.md index 2ae1b583..54b835c3 100644 --- a/Rationale.md +++ b/Rationale.md @@ -130,9 +130,14 @@ return values. This allows strong security properties to be enforced, but does mean that two stacks are maintained (one by the VM, the other by the compiler which targets WebAssembly) which can lead to some inefficiencies. -Local values can also be pre-colored, meaning that multiple incoming SSA values -which have separate liveness can "share" the storage represented by a -local. This offloads some of the optimization work from the WebAssembly VM. +Local variables are not in Static Single Assignment (SSA) form, meaning that +multiple incoming SSA values which have separate liveness can "share" the +storage represented by a local through the `set_local` operation. From an SSA +perspective, this means that multiple independent values can share a local +variable in WebAssembly, which is effectively a kind of pre-coloring that clever +producers can use to pre-color variables and give hints to a WebAssembly VM's +register allocation algorithms, offloading some of the optimization work from +the WebAssembly VM. ## Variable-Length Argument Lists From 4d9c8a72d3cbcb1c51795693a136e0bc3c4026be Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 11:13:41 -0700 Subject: [PATCH 51/53] Rebase. --- AstSemantics.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index ec014fa5..67bd1459 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -209,10 +209,6 @@ instead expected release unused physical pages from the working set using the The result type of `page_size` is `int32` for wasm32 and `int64` for wasm64. The result value of `page_size` is an unsigned integer which is a power of 2. -(Note that the `page_size` value need not reflect the actual internal page size -of the implementation; it just needs to be a value suitable for use with -`grow_memory`) - The `page_size` value need not reflect the actual internal page size of the implementation; it just needs to be a value suitable for use with `resize_memory`. From 7221b7b72386876bd529a5532235e416a1331e85 Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 12:29:04 -0700 Subject: [PATCH 52/53] Fix copy/paste context bug. --- Rationale.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rationale.md b/Rationale.md index 54b835c3..1064c4bb 100644 --- a/Rationale.md +++ b/Rationale.md @@ -177,10 +177,10 @@ functions, as well as remove them. Expression trees offer significant size reduction by avoiding the need for `set_local`/`get_local` pairs in the common case of an expression with only one, -immediate use. The following primitives provide AST nodes that express control -flow and thus allow more opportunities to build bigger expression trees and -further reduce `set_local`/`get_local` usage (which constitute 30-40% of total -bytes in the +immediate use. The `comma` and `conditional` primitives provide AST nodes that +express control flow and thus allow more opportunities to build bigger +expression trees and further reduce `set_local`/`get_local` usage (which +constitute 30-40% of total bytes in the [polyfill prototype](https://github.com/WebAssembly/polyfill-prototype-1)). Additionally, these primitives are useful building blocks for WebAssembly-generators (including the JavaScript polyfill prototype). From 1d96a85ae9e85fa6429c9cb3e57536ccafa8695c Mon Sep 17 00:00:00 2001 From: JF Bastien Date: Thu, 8 Oct 2015 12:29:30 -0700 Subject: [PATCH 53/53] Lower. --- Rationale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rationale.md b/Rationale.md index 1064c4bb..2065ff6a 100644 --- a/Rationale.md +++ b/Rationale.md @@ -206,7 +206,7 @@ Nondeterminism is only specified as a compromise when there is no other practical way to: * Achieve [portable](Portability.md) native performance. -* Lowering resource usage. +* Lower resource usage. * Reduce implementation complexity (both of WebAssembly VMs as well as compilers generating WebAssembly binaries). * Allow usage of new hardware features.