diff --git a/AstSemantics.md b/AstSemantics.md index 94b2f2cc..754f1f41 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -80,29 +80,37 @@ operators. ## Linear Memory -The main storage of a WebAssembly instance, called the *linear memory*, is a -contiguous, byte-addressable range of memory spanning from offset `0` and -extending up to a varying *memory size*. -This size always is a multiple of the WebAssembly page size, -which is 64KiB on all engines (though large page support may be added in the -[future](FutureFeatures.md#large-page-support)). -The initial state of linear memory is specified by the -[module](Modules.md#linear-memory-section), and it can be dynamically grown by -the [`grow_memory`](AstSemantics.md#resizing) operator. - -The linear memory can be considered -to be an untyped array of bytes, and it is unspecified how embedders map this -array into their process' own [virtual memory][]. The linear memory is -sandboxed; it does not alias the execution engine's internal data structures, -the execution stack, local variables, or other process memory. +A *linear memory* is a contiguous, byte-addressable range of memory spanning +from offset `0` and extending up to a varying *memory size*. This size is always +a multiple of the WebAssembly page size, which is fixed to 64KiB (though large +page support may be added in an opt-in manner in the +[future](FutureFeatures.md#large-page-support)). The initial state of a linear +memory is defined by the module's [linear memory](Modules.md#linear-memory-section) and +[data](Modules.md#data-section) sections. The memory size can be dynamically +increased by the [`grow_memory`](AstSemantics.md#resizing) operator. + +A linear memory can be considered to be an untyped array of bytes, and it is +unspecified how embedders map this array into their process' own [virtual +memory][]. Linear memory is sandboxed; it does not alias other linear memories, +the execution engine's internal data structures, the execution stack, local +variables, or other process memory. [virtual memory]: https://en.wikipedia.org/wiki/Virtual_memory -In the MVP, linear memory is not shared between threads of execution. Separate -instances can execute in separate threads but have their own linear memory and can -only communicate through messaging, e.g. in browsers using `postMessage`. It -will be possible to share linear memory between threads of execution when -[threads](PostMVP.md#threads) are added. +Every WebAssembly [instance](Modules.md) has one specially-designated *default* +linear memory which is the linear memory accessed by all the +[memory operators below](#linear-memory-access). In the MVP, there are *only* +default linear memories but [new memory operators](FutureFeatures.md#multiple-tables-and-memories) +may be added after the MVP which can also access non-default memories. + +Linear memories (default or otherwise) can either be [imported](Modules.md#imports) +or [defined inside the module](Modules.md#linear-memory-section), with defaultness +indicated by a flag on the import or definition. After import or definition, +there is no difference when accessing a linear memory whether it was imported or +defined internally. + +In the MVP, linear memory cannot be shared between threads of execution. +The addition of [threads](PostMVP.md#threads) will allow this. ### Linear Memory Accesses @@ -143,6 +151,8 @@ size in which case integer wrapping is implied. In addition to storing to memory, store instructions produce a value which is their `value` input operand before wrapping. +The above operators operate on the [default linear memory](#linear-memory). + ### Addressing Each linear memory access operator has an address operand and an unsigned @@ -210,7 +220,7 @@ the [future](FutureFeatures.md#large-page-support)). * `grow_memory` : grow linear memory by a given unsigned delta of pages. Return the previous memory size in units of pages or -1 on failure. -When a maximum memory size is declared in the [memory section](Module.md#linear-memory-section), +When a linear memory has a declared [maximum memory size](Modules.md#linear-memory-section), `grow_memory` must fail if it would grow past the maximum. However, `grow_memory` may still fail before the maximum if it was not possible to reserve the space up front or if enabling the reserved memory fails. @@ -232,12 +242,53 @@ operator 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 above operators operate on the [default linear memory](#linear-memory). + +## Table + +A *table* is similar to a linear memory whose elements, instead of being bytes, +are opaque values of a particular *table element type*. This allows the table to +contain values—like GC references, raw OS handles, or native pointers—that are +accessed by WebAssembly code indirectly through an integer index. This feature +bridges the gap between low-level, untrusted linear memory and high-level +opaque handles/references at the cost of a bounds-checked table indirection. + +The table's element type constrains the type of elements stored +in the table and allows engines to avoid some type checks on table use. +When a WebAssembly value is stored in a table, the value's type must precisely +match the element type. Just like linear memory, updates to a table are +observed immediately by all instances that reference the table. Depending on the +operator/API used to store the value, this check may be static or dynamic. Host +environments may also allow storing non-WebAssembly values in tables in which +case, as with [imports](Modules.md#imports), the meaning of using the value is +defined by the host environment. + +Every WebAssembly [instance](Modules.md) has one specially-designated *default* +table which is indexed by [`call_indirect`](#calls) and other future +table operators. Tables can either be [imported](Modules.md#imports) or +[defined inside the module](Modules.md#table-section), with defaultness +indicated by a flag on the import or definition. After import or definition, +there is no difference when calling into a table whether it was imported or +defined internally. + +In the MVP, the primary purpose of tables is to implement indirect function +calls in C/C++ using an integer index as the pointer-to-function and the table +to hold the array of indirectly-callable functions. Thus, in the MVP: +* tables may only be accessed from WebAssembly code via [`call_indirect`](#calls); +* the only allowed table element type is `anyfunc` (function with any signature); +* tables may not be directly mutated or resized from WebAssembly code; + this can only be done through the host environment (e.g., the + the `WebAssembly` [JavaScript API](JS.md#webassemblytable-objects)). + +These restrictions may be relaxed in the +[future](FutureFeatures.md#more-table-operators-and-types). + ## Local variables -Each function has a fixed, pre-declared number of local variables which occupy a single +Each function has a fixed, pre-declared number of *local variables* which occupy a single index space local to the function. Parameters are addressed as local variables. Local variables do not have addresses and are not aliased by linear memory. Local -variables have value types and are initialized to the appropriate zero value for their +variables have [value types](#types) and are initialized to the appropriate zero value for their type at the beginning of the function, except parameters which are initialized to the values of the arguments passed to the function. @@ -248,6 +299,32 @@ 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. +## Global variables + +A *global variable* stores a single value of a fixed [value type](#types) and may be +declared either *mutable* or *immutable*. This provides WebAssembly with memory +locations that are disjoint from any [linear memory](#linear-memory) and thus +cannot be arbitrarily aliased as bits. + +Global variables are accessed via an integer index into the module-defined +[global index space](Modules.md#global-index-space). Global variables can +either be [imported](Modules.md#imports) or [defined inside the module](Modules.md#global-section). +After import or definition, there is no difference when accessing a global. + + * `get_global`: get the current value of a global variable + * `set_global`: set the current value of a global variable + +It is a validation error for a `set_global` to index an immutable global variable. + +In the MVP, the primary use case of global variables is to represent +instantiation-time immutable values as a useful building block for +[dynamic linking](DynamicLinking.md). + +After the MVP, when [reference types](GC.md) are added to the set of [value types](#types), +global variables will be necessary to allow sharing reference types between +[threads](PostMVP.md#threads) since shared linear memory cannot load or store +references. + ## Control flow structures WebAssembly offers basic structured control flow with the following constructs. @@ -314,43 +391,32 @@ 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. -Direct calls to a function specify the callee by index into a *main function table*. +Direct calls to a function specify the callee by an index into the +[function index space](Modules.md#function-index-space). * `call`: call function directly A direct call to a function with a mismatched signature is a module verification error. -Like direct calls, calls to [imports](Modules.md#imports-and-exports) specify -the callee by index into an *imported function table* defined by the sequence of import -declarations in the module import section. A direct call to an imported function with a -mismatched signature is a module verification error. - - * `call_import` : call imported function directly - -Indirect calls allow calling target functions that are unknown at compile time. -The target function is an expression of value type `i32` and is always the first -input into the indirect call. - -A `call_indirect` specifies the *expected* signature of the target function with -an index into a *signature table* defined by the module. An indirect call to a -function with a mismatched signature causes a trap. +Indirect calls to a function indicate the callee with an `i32` index into +a [table](#table). The *expected* signature of the target function (specified +by its index in the [types section](BinaryEncoding.md#type-section)) is given as +a second immediate. * `call_indirect`: call function indirectly -Functions from the main function table are made addressable by defining an -*indirect function table* that consists of a sequence of indices into the -module's main function table. A function from the main table may appear more -than once in the indirect function table. Functions not appearing in the -indirect function table cannot be called indirectly. - -In the MVP, indices into the indirect function table are local to a single -module, so wasm modules may use `i32` constants to refer to entries in their own -indirect function table. The [dynamic linking](DynamicLinking.md) feature is -necessary for two modules to pass function pointers back and forth. This will -mean concatenating indirect function tables and adding an operator `address_of` -that computes the absolute index into the concatenated table from an index in a -module's local indirect table. JITing may also mean appending more functions to -the end of the indirect function table. +Unlike `call`, which checks that the caller and callee signatures match +statically as part of validation, `call_indirect` checks for signature match +*dynamically*, comparing the caller's expected signature with the callee function's +signature and and trapping if there is a mismatch. Since the callee may be in a +different module which necessarily has a separate [types section](BinaryEncoding.md#type-section), +and thus index space of types, the signature match must compare the underlying +[`func_type`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5). +As noted [above](#table), table elements may also be host-environment-defined +values in which case the meaning of a call (and how the signature is checked) +is defined by the host-environment, much like calling an import. + +In the MVP, the single `call_indirect` operator accesses the [default table](#table). Multiple return value calls will be possible, though possibly not in the MVP. The details of multiple-return-value calls needs clarification. Calling a diff --git a/DynamicLinking.md b/DynamicLinking.md index 5ef24126..aec31e9a 100644 --- a/DynamicLinking.md +++ b/DynamicLinking.md @@ -1,49 +1,87 @@ # Dynamic linking -Dynamic loading of code is in [the MVP](MVP.md) in the form of -[modules](Modules.md), but all loaded modules have their own separate -[linear memory](AstSemantics.md#linear-memory) by default and cannot share -[function pointers](AstSemantics.md#calls). Limited collaboration between -modules is possible in the MVP by having two modules share the same linear -memory and invoke each other through the host environment. - -True dynamic linking will allow developers to share memory, function pointers, -and future non-memory state such as global variables and thread-local variables -between WebAssembly dynamic libraries. - -WebAssembly will support both load-time and run-time (`dlopen`) dynamic linking -of libraries. - -One important requirement of dynamic linking is to allow the linked module -to have its own position-independent global data segment. This could be achieved -by specifying a new kind of link-time-initialized immutable global variable -which would be initialized with the address (in linear memory) of the modules' -global data segment. These immutable globals could also be used to provide -a linked module with the offsets of its function pointers in the instance's -function pointer tables. An important aspect of immutable globals is that they -could either be patched directly as constant values or implemented through a -[Global Offset Table](https://en.wikipedia.org/wiki/Position-independent_code) -in position-independent code. - -Dynamic linking is especially useful when combined with a Content Distribution -Network (CDN) such as [hosted libraries][] because the library is only ever -downloaded and compiled once per user device. It can also allow for smaller -differential updates, which could be implemented in collaboration with -[service workers][]. - -We would like to standardize a single [ABI][] per source language, allowing for -WebAssembly libraries to interface with each other regardless of compiler. While -it is highly recommended for compilers targeting WebAssembly to adhere to the -specified ABI for interoperability, WebAssembly runtimes will be ABI agnostic, -so it will be possible to use a non-standard ABI for specialized purposes. - -Although dynamic linking is not part of the MVP, it has significant implications -on many aspects of the design that do impact the MVP, such as the way linear -memory is managed, how module imports and exports are specified, and how globals -and function pointers work. Therefore we want to have some viable ideas to -ensure we don't standardize a design that unnecessarily complicates the design -or implementation of dynamic linking. - - [hosted libraries]: https://developers.google.com/speed/libraries/ - [service workers]: https://www.w3.org/TR/service-workers/ +WebAssembly enables load-time and run-time (`dlopen`) dynamic linking in the +MVP by having multiple [instantiated modules](Modules.md) +share functions, [linear memories](AstSemantics.md#linear-memory), +[tables](AstSemantics.md#table) and [constants](AstSemantics.md#constants) +using module [imports](Modules.md#imports) and [exports](Modules.md#exports). In +particular, since all (non-local) state that a module can access can be imported +and exported and thus shared between separate modules' instances, toolchains +have the building blocks to implement dynamic loaders. + +Since the manner in which modules are loaded and instantiated is defined by the +host environment (e.g., the [JavaScript API](JS.md)), dynamic linking requires +use of host-specific functionality to link two modules. At a minimum, the host +environment must provide a way to dynamically instantiate modules while +connecting exports to imports. + +The simplest load-time dynamic linking scheme between modules A and B can be +achieved by having module A export functions, tables and memories that are +imported by B. A C++ toolchain can expose this functionality by using the +same function attributes currently used to export/import symbols from +native DSOs/DLLs: +``` +#ifdef _WIN32 +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT __attribute__ ((visibility ("default"))) +# define IMPORT __attribute__ ((visibility ("default"))) +#endif + +typedef void (**PF)(); + +IMPORT PF imp(); +EXPORT void exp() { (*imp())(); } +``` +This code would, at a minimum, generate a WebAssembly module with imports for: +* the function `imp` +* the heap used to perfom the load, when dereferencing the return value of `imp` +* the table used to perform the pointer-to-function call + +and exports for: +* the function `exp` + +A more realistic module using libc would have more imports including: +* an immutable `i32` global import for the offset in linear memory to place + global [data segments](Modules.md#data-section) and later use as a constant + base address when loading and storing from globals +* an immutable `i32` global import for the offset into the indirect function + table at which to place the modules' indirectly called functions and later + compute their indices for address-of + +One extra detail is what to use as the [module name](Modules.md#imports) for +imports (since WebAssembly has a two-level namespace). One option is to have a +single default module name for all C/C++ imports/exports (which then allows the +toolchain to put implementation-internal names in a separate namespace, avoiding +the need for `__`-prefix conventions). + +To implement run-time dynamic linking (e.g., `dlopen` and `dlsym`): +* `dlopen` would compile and instantiate a new module, storing the compiled + instance in a host-environment table, returning the index to the caller. +* `dlsym` would be given this index, pull the instance out of the table, + search the instances's exports, append the found function to the function + table (using host-defined functionality in the MVP, but directly from + WebAssembly code in the + [future](FutureFeatures.md#more-table-operators-and-types)) and return the + table index of the appended element to the caller. + +Note that the representation of a C function-pointer in WebAssembly is an index +into a function table, so the above scheme lines up perfectly with the +function-pointer return value of `dlsym`. + +More complicated dynamic linking functionality (e.g., interposition, weak +symbols, etc) can be simulated efficiently by assigning a function table +index to each weak/mutable symbol, calling the symbol via `call_indirect` on that +index, and mutating the underlying element as needed. + +After the MVP, we would like to standardize a single [ABI][] per source +language, allowing for WebAssembly libraries to interface with each other +regardless of compiler. Specifying an ABI requires that all ABI-related +future features (like SIMD, multiple return values and exception handling) +have been implemented. While it is highly recommended for compilers targeting +WebAssembly to adhere to the specified ABI for interoperability, WebAssembly +runtimes will be ABI agnostic, so it will be possible to use a non-standard ABI +for specialized purposes. + [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface diff --git a/FeatureTest.md b/FeatureTest.md index 705be528..fea93281 100644 --- a/FeatureTest.md +++ b/FeatureTest.md @@ -3,7 +3,7 @@ See [rationale](Rationale.md#feature-testing---motivating-scenarios) for motivat # Feature Test [PostMVP](PostMVP.md), applications will be able to query which features are -supported via [`has_feature` or a similar API](PostMVP#Feature-Testing). This +supported via [`has_feature` or a similar API](PostMVP.md#feature-testing). This accounts for the pragmatic reality that features are shipped in different orders at different times by different engines. diff --git a/FutureFeatures.md b/FutureFeatures.md index 335bd9ff..724f4bbe 100644 --- a/FutureFeatures.md +++ b/FutureFeatures.md @@ -393,16 +393,6 @@ reduce fragmentation issues. Languages like Fortran which limit aliasing would b one use case. C/C++ compilers could also determine that some global variables never have their address taken. -## Importing linear memory - -In the MVP, functions and [linear memory](Modules.md#linear-memory-section) can -be exported, but only functions can be imported. This feature would additionally -allow importing linear memory. One use case is sharing linear memories between -separate WebAssembly [instances](Modules.md). Another use case is allowing, on -the Web platform, importing a JS `ArrayBuffer` as a linear memory. This would -allow highly efficient, specialized code to be generated for accessing the -`ArrayBuffer`. - ## Streaming Compilation The WebAssembly binary format is designed to allow streaming decoding, @@ -425,3 +415,47 @@ of WebAssembly in browsers: would enable Web apps to perform their own (["layer 1"](BinaryEncoding.md)) custom compression (on top of the spec-defined binary format, under generic HTTP `Content-Encoding` compression). + +## Multiple Tables and Memories + +The MVP limits modules to at most one memory and at most one table (the default +ones) and there are only operators for accessing the default table and memory. + +After the MVP and after [GC reference types](GC.md) have been added, the default +limitation can be relaxed so that any number of tables and memories could be +imported or internally defined and memories/tables could be passed around as +parameters, return values and locals. New variants of `load`, `store` +and `call_indirect` would then be added which took an additional memory/table +reference operand. + +To access an imported or internally-defined non-default table or memory, a +new `address_of` operator could be added which, given an index immediate, +would return a first-class reference. Beyond tables and memories, this could +also be used for function definitions to get a reference to a function (which, +since opaque, could be implemented as a raw function pointer). + +## More Table Operators and Types + +In the MVP, WebAssembly has limited functionality for operating on +[tables](AstSemantics.md#table) and the host-environment can do much more (e.g., +see [JavaScript's `WebAssembly.Table` API](JS.md#webassemblytable-objects)). +It would be useful to be able to do everything from within WebAssembly so, e.g., +it was possible to write a WebAssembly dynamic loader in WebAssembly. As a +prerequisite, WebAssembly would need first-class support for +[GC references](GC.md) in expressions and locals. Given that, the following +could be added: +* `get_table`/`set_table`: get or set the table element at a given dynamic + index; the got/set value would have a GC reference type +* `grow_table`: grow the current table (up to the optional maximum), similar to + `grow_memory` +* `current_table_length`: like `current_memory`. + +Additionally, in the MVP, the only allowed element type of tables is a generic +"function" type which simply means the element can be called but there is no +static signature validation check. This could be improved by allowing: +* functions with a particular signature, allowing wasm generators to use + multiple homogeneously-typed function tables (instead of a single + heterogeneous function table) which eliminates the implied dynamic signature + check of a call to a heterogeneous table; +* any other specific GC reference type, effectively allowing WebAssembly code + to implement a variety of rooting API schemes. diff --git a/JS.md b/JS.md index 71713922..e78dfd05 100644 --- a/JS.md +++ b/JS.md @@ -24,8 +24,10 @@ and has the following properties: The following intrinsic objects are added: -* `WebAssembly.Module` : the [`WebAssembly.Module` constructor](#wasmmodule-constructor) -* `WebAssembly.Instance` : the [`WebAssembly.Instance` constructor](#wasminstance-constructor) +* `WebAssembly.Module` : the [`WebAssembly.Module` constructor](#webassemblymodule-constructor) +* `WebAssembly.Instance` : the [`WebAssembly.Instance` constructor](#webassemblyinstance-constructor) +* `WebAssembly.Memory` : the [`WebAssembly.Memory` constructor](#webassemblymemory-constructor) +* `WebAssembly.Table` : the [`WebAssembly.Table` constructor](#webassemblytable-constructor) * `WebAssembly.CompileError` : a [NativeError](http://tc39.github.io/ecma262/#sec-nativeerror-object-structure) which indicates an error during WebAssembly decoding or validation * `WebAssembly.RuntimeError` : a [NativeError](http://tc39.github.io/ecma262/#sec-nativeerror-object-structure) @@ -45,7 +47,7 @@ the returned `Promise` is [rejected](http://tc39.github.io/ecma262/#sec-rejectpr with a `TypeError`. Otherwise, this function starts an asychronous task to compile a `WebAssembly.Module` -as described in the [`WebAssembly.Module` constructor](#wasmmodule-constructor). +as described in the [`WebAssembly.Module` constructor](#webassemblymodule-constructor). On success, the `Promise` is [fulfilled](http://tc39.github.io/ecma262/#sec-fulfillpromise) with the resulting `WebAssembly.Module` instance. On failure, the `Promise` is [rejected](http://tc39.github.io/ecma262/#sec-rejectpromise) with a @@ -84,7 +86,7 @@ Otherwise, this function performs synchronous compilation of the `BufferSource`: an AST according to [BinaryEncoding.md](BinaryEncoding.md) and then validated according to the rules in [spec/check.ml](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/check.ml#L325). * The spec `string` values inside `Ast.module` are decoded as UTF8 as described in - [Web.md](Web.md#function-names). + [Web.md](Web.md#names). * On success, a new `WebAssembly.Module` instance is returned with [[Module]] set to the validated `Ast.module`. * On failure, a new `WebAssembly.CompileError` is thrown. @@ -137,45 +139,87 @@ not Object, a `TypeError` is thrown. If the list of [`module.imports`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L215) is not empty and `Type(importObject)` is not Object, a `TypeError` is thrown. -Let `imports` by an initially-empty list of JS functions. +Let `imports` be an initially-empty [`import list`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.mli#L3) +(assuming the ML spec `Eval.import` type has been extended to be a union of: +* a function [`value list -> value option`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.mli#L3) +* a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli#L1) +* a [`Table.table`](#webassemblytable-objects) +* a [`Values.value`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/values.ml#L9) For each [`import`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L135) -`i` in `module.imports`: +`i` in `module.imports` (assuming the ML spec `import` has been extended to have +function, global, memory and table imports): * Let `v` be the resultant value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`importObject`, [`i.module_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L139)). -* If [`i.func_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L140) +* If [`i.export_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L140) is not the empty string: * If `Type(v)` is not Object, throw a `TypeError`. - * Let `v` instead be the value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`v`, `i.func_name`) -* If `IsCallable(v)` is `false`, throw a `TypeError`. -* Append `v` to `imports`. + * Let `v` instead be the value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`v`, `i.export_name`) +* If `i` is a function import: + * If `IsCallable(v)` is `false`, throw a `TypeError`. + * Otherwise, append an anonymous function to `imports` + which calls `v` by coercing WebAssembly arguments to JavaScript arguments + via [`ToJSValue`](#tojsvalue) and returns the result by coercing + via [`ToWebAssemblyValue`](#towebassemblyvalue). + * Otherwise, append the function `v` to `imports` +* If `i` is a global import: + * If `i` is not an immutable global, throw a `TypeError`. + * Append [`ToWebAssemblyValue`](#towebassemblyvalue)`(v)` to `imports`. +* If `i` is a memory import: + * If `v` is not a [`WebAssembly.Memory` object](#webassemblymemory-objects), + throw a `TypeError`. + * Otherwise, append `v.[[Memory]]` to `imports`. +* Otherwise (`i` is a table import): + * If `v` is not a [`WebAssembly.Table` object](#webassemblytable-objects), + throw a `TypeError`. + * Otherwise, append `v.[[Table]]` to `imports`. Let `instance` be the result of evaluating -[`Eval.init`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L314) +[`Eval.init`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L319) with arguments `module` and `imports`. Note: this synchronously executes the [`start`](Modules.md#module-start-function) function, if present. -Let `exports` be an initially-empty list of (string, JS function) pairs. -Let `exportedFunctions` be an initially-empty map from function indices (integers) to -JS functions. - -For each [exported function](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128) -`f` in `module.exports`: -* Let `index` be the exported function index of `f`. -* If `index` is not already present in `exportedFunctions`, add a mapping - from `index` to the result of creating a new - [Exported Function](#exported-function-exotic-objects) Exotic Object (given `instance` and `index`). -* Append the pair (`f.name`, `exportedFunctions[index]`) to `exports` - -Let `moduleRecord` be a new [WebAssembly Module Record](#webassembly-module-record) (given `exports`). - -Let `exportStrings` be the projected list of only the first (string) components of `exports`. -Let `moduleNamespace` be the result of calling +Let `exports` be a list of (string, JS value) pairs that is mapped from +`module.exports` as follows (assuming the ML spec +[`export`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128) +has been modified so that each export simply has a `name`, `type` and `index`: +* If `index` refers to an imported `type` definition, then simply re-export the + imported JS value. +* Otherwise (an internal definition): + * If the `type` is function, then export an + [Exported Function Exotic Object](#exported-function-exotic-objects), + reusing an existing object if one exists for the given function definition, + otherwise creating a new object. + * If the `type` is global: + * If the global is not immutable, then throw a `TypeError`. + * Let `v` be the global variable's initialized value. + * Otherwise, export [`ToJSValue`](#tojsvalue)`(v)`. + * If the `type` is memory, then export a `WebAssembly.Memory` object, reusing + an existing object if one exists for the given memory definition, otherwise + creating a new object via [`CreateMemoryObject`](#creatememoryobject). + * Otherwise the `type` must be a table so export a `WebAssembly.Table` object, + reusing an existing object if one exists for the given table definition, + otherwise creating a new object via: + * Let `values` be a list of JS values that is mapped from the table's + elements as follows: + * sentinel values (which throw if called) are given the value `null` + * non-sentinel values are given an [Exported Function Exotic Objects](#exported-function-exotic-objects), + reusing an existing object if one exists for the given function, + otherwise creating a new one. + * Create a new `WebAssembly.Table` instance with [[Table]] + set to `table` and [[Values]] set to `values`. + +Let `moduleRecord` be a new [WebAssembly Module Record](#webassembly-module-record) +(given `exports`). + +Let `exportStrings` be the projected list of only the first (string) components +of `exports`. Let `moduleNamespace` be the result of calling [`ModuleNamespaceCreate(moduleRecord, exportStrings)`](http://tc39.github.io/ecma262/#sec-modulenamespacecreate). Set `moduleRecord.[[Namespace]]` to `moduleNamespace`. -Return a new `WebAssembly.Instance` object initializing `[[Instance]]` = `instance` and `exports` = `moduleNamespace`. +Return a new `WebAssembly.Instance` object setting `[[Instance]]` to `instance` +and `exports` to `moduleNamespace`. ### WebAssembly Module Record @@ -185,7 +229,7 @@ has one concrete subclass, [Source Text Module Record](http://tc39.github.io/ecm which corresponds to a normal ES6 module. These interfaces are used to define the [process of loading a module on the Web](https://html.spec.whatwg.org/multipage/webappapis.html#integration-with-the-javascript-module-system). -When WebAssembly gets [ES6 Module integration](Modules.md#integration-with-es6-modules), +When WebAssembly gets [ES6 Module integration](Modules.md#integration-with-es6-modules), a new *WebAssembly Module Record* subclass would be added which would specify the right thing to do for WebAssembly modules as part of the overall loading process. @@ -208,7 +252,7 @@ Like [Bound Function](http://tc39.github.io/ecma262/#sec-bound-function-exotic-o Exported Functions do not have the normal function internal slots but instead have: * [[Instance]] : the [`Eval.instance`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L15) containing the exported function - * [[FunctionIndex]] : the index of the function inside the module + * [[FunctionIndex]] : an index into the module's [function index space](Modules.md#function-index-space) as well as the internal slots required of all builtin functions: * [[Prototype]] : [%FunctionPrototype%](http://tc39.github.io/ecma262/#sec-well-known-intrinsic-objects) @@ -224,24 +268,217 @@ WebAssembly Exported Functions have a `[[Call]](this, argValues)` method defined * Let `argTypes` be the list of value types defined by the signature of [[FunctionIndex]]. * Let `args` be an empty list of coerced values. * For each value type `t` in `argTypes` and value `v` in `argValues`: - * Append to `args` `v` coerced to `t` as follows: - * coerce `v` to `i32` via [`ToInt32(v)`](http://tc39.github.io/ecma262/#sec-toint32) - * throw a `TypeError` if `t` is `i64` - * coerce `v` to `f32` by first applying [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) - and then converting the resulting IEEE754 64-bit double to a 32-bit float using `roundTiesToEven` - * coerce `v` to `f64` via [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) - * Perform [`Eval.invoke`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L327) + * Append [`ToWebAssemblyValue`](#towebassemblyvalue)`(v)` to `args`. + * Let `ret` be the result of calling [`Eval.invoke`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L327) passing [[Instance]], [[FunctionIndex]], and `args`. - * Coerce the result of `Eval.invoke` as follows: - * if the return value is `None`, return `undefined` - * interpret `i32` as a signed integer and convert that integer to a Number value - * throw a `TypeError` if returning an `i64` - * return `f32`/`f64` as Number values, possibly performing - [canonicalization of NaNs](http://tc39.github.io/ecma262/#sec-setvalueinbuffer) + * Return [`ToJSValue`](#tojsvalue)`(ret)`. Exported Functions do not have a [[Construct]] method and thus it is not possible to call one with the `new` operator. +## `WebAssembly.Memory` Objects + +A `WebAssembly.Memory` object contains a single [linear memory](AstSemantics.md#linear-memory) +which can be simultaneously referenced by multiple `Instance` objects. Each +`Memory` object has two internal slots: + * [[Memory]] : a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli) + * [[BufferObject]] : the current `ArrayBuffer` whose [[ArrayBufferByteLength]] + matches the current byte length of [[Memory]] + +### `WebAssembly.Memory` Constructor + +The `WebAssembly.Memory` constructor has the signature: +``` +new Memory(memoryDescriptor) +``` +If the NewTarget is `undefined`, a `TypeError` exception is thrown (i.e., this +constructor cannot be called as a function without `new`). + +If `Type(memoryDescriptor)` is not Object, a `TypeError` is thrown. + +Let `initial` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`memoryDescriptor`, `"initial"`)). + +If [`HasProperty`](http://tc39.github.io/ecma262/#sec-hasproperty)(`"maximum"`), +then let `maximum` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`memoryDescriptor`, `"maximum"`)). +Otherwise, let `maximum` be `None`. + +Let `memory` be the result of calling +[`Memory.create`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli#L18) +given arguments `initial` and `maximum`. Note that `initial` and `maximum` are +specified in units of WebAssembly pages (64KiB). + +(Note: the ML spec currently doesn't implement the maximum memory limit; we +assume here it will be extended in the future.) + +Return the result of [`CreateMemoryObject`](#creatememoryobject)(`memory`). + +### CreateMemoryObject + +Given a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli) +`m`, to create a `WebAssembly.Memory`: + +Let `buffer` be a new `ArrayBuffer` whose +[[[ArrayBufferData]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +aliases `m` and whose +[[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +is set to the byte length of `m`. + +Return a new `WebAssembly.Memory` instance with [[Memory]] set to `m` and +[[BufferObject]] set to `buffer`. + +### `WebAssembly.Memory.prototype.grow` + +Let `M` be the `this` value. If `M` is not a `WebAssembly.Memory`, +a `TypeError` is thrown. + +This method performs a [`grow_memory`](AstSemantics.md#resizing) on +`M.[[Memory]]`, having first performed [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger) +on the first argument. On failure, a `WebAssembly.RuntimeError` is thrown. + +If `M.[[Memory]].maximum` is `None`, perform +[`DetachArrayBuffer`](http://tc39.github.io/ecma262/#sec-detacharraybuffer)(`M.[[BufferObject]]`). + +In either case, assign to `M.[[BufferObject]]` a new `ArrayBuffer` whose +[[[ArrayBufferData]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +aliases `M.[[Memory]]` and whose +[[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +is set to the new byte length of `M.[[Memory]]`. + +### `WebAssembly.Memory.prototype.buffer` + +This is an accessor property whose [[Set]] is Undefined and whose [[Get]] +accessor function performs the following steps: + +If `this` is not a `WebAssembly.Memory`, a `TypeError` is thrown. Otherwise +return `M.[[BufferObject]]`. + +## `WebAssembly.Table` Objects + +A `WebAssembly.Table` object contains a single [table](AstSemantics.md#table) +which can be simultaneously referenced by multiple `Instance` objects. Each +`Table` object has two internal slots: + * [[Table]] : a [`Table.table`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/table.mli) + * [[Values]] : an array whose elements are either `null` or Function Objects + +(Note: the ML spec currently represents tables as a single `int list` of +function indices; we assume here it will be extended in the future with +a more general `Table` similar to +[`Memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli).) + +### `WebAssembly.Table` Constructor + +The `WebAssembly.Table` constructor has the signature: +``` +new Table(tableDescriptor) +``` +If the NewTarget is `undefined`, a `TypeError` exception is thrown (i.e., this +constructor cannot be called as a function without `new`). + +If `Type(tableDescriptor)` is not Object, a `TypeError` is thrown. + +Let `element` be the result of calling [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"element"`). +If `element` is not the string `"anyfunc"`, a `TypeError` is thrown. +(Note: this check is intended to be relaxed in the +[future](FutureFeatures.md#more-table-operators-and-types) to allow different +elemtn types.) + +Let `initial` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"initial"`)). + +If [`HasProperty`](http://tc39.github.io/ecma262/#sec-hasproperty)(`"maximum"`), +then let `maximum` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"maximum"`)). +Otherwise, let `maximum` be None. + +Let `table` be the result of calling `Table.create` given arguments `initial` +and `maximum`. + +Let `values` be a new empty array of `initial` elements, all with value +`null`. + +Return a new `WebAssemby.Table` instance with [[Table]] set to `table` and +[[Values]] set to `values`. + +### `WebAssembly.Table.prototype.grow` + +This method calls `Table.grow`, having performed +[`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger) on the first argument. +On failure, a `WebAssembly.RuntimeError` is thrown. + +(Note: the ML spec currently doesn't support resizing tables; we assume here it +will be extended in the future to have a `grow` operation similar to +[`Memory.grow`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli#L21).) + +### `WebAssembly.Table.prototype.get` + +This method has the following signature +``` +get(index) +``` +Let `T` be the `this` value. If `T` is not a `WebAssembly.Table`, a `TypeError` +is thrown. + +Let `i` be the result of [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)(`index`). + +Return `T.[[Values]][i]`. + +### `WebAssembly.Table.prototype.set` + +This method has the following signature +``` +set(index, value) +``` + +Let `T` be the `this` value. If `T` is not a `WebAssembly.Table`, a `TypeError` +is thrown. + +If [`IsCallable`](http://tc39.github.io/ecma262/#sec-iscallable)(`value`) is +false and `Type(value)` is not Null, throw a type error. + +Let `i` be the result of [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)(`index`). + +If `v` is an [Exported Function Exotic Object](#exported-function-exotic-objects): +* Set the `i`th element of `T.[[Table]]` to the `v.[[FunctionIndex]]`th function + in the module's [function index space](Modules.md#function-index-space). + +Otherwise: +* Set the `i`th element of `T.[[Table]]` to a host-defined value that can be + called with *any* signature by coercing its WebAssembly arguments to JavaScript + arguments via [ToJSValue](#tojsvalue) and coercing its JavaScript return value + to a WebAssembly return value via [ToWebAssemblyValue](#towebassemblyvalue). + +Set `T.[[Values]][i]` to `value`. + +Return Undefined. + +## ToJSValue + +To coerce a [WebAssembly value](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/values.ml#L9) +to a JavaScript value: + +* given a WebAssembly `i32` is interpreted as a signed integer, converted (losslessly) to an + IEEE754 double and then returned as a JavaScript Number +* given a WebAssembly `i64`, throw a `TypeError` +* given a WebAssembly `f32` (single-precision IEEE754), convert (losslessly) to + a IEEE754 double, [possibly canonicalize NaN](http://tc39.github.io/ecma262/#sec-setvalueinbuffer), + and return as a JavaScript Number +* given a WebAssembly `f64`, [possibly canonicalize NaN](http://tc39.github.io/ecma262/#sec-setvalueinbuffer) + and return as a JavaScript Number + +If the WebAssembly value is optional, then given `None`, return JavaScript value +`undefined`. + +## ToWebAssemblyValue + +To coerce a JavaScript value to a given [WebAssembly value type](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L3), + +* coerce to `i32` via [`ToInt32(v)`](http://tc39.github.io/ecma262/#sec-toint32) +* for `i64`, throw a `TypeError` +* coerce to `f32` by first applying [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) + and then converting the resulting IEEE754 64-bit double to a 32-bit float using `roundTiesToEven` +* coerce to `f64` via [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) + +If the value type is optional, then given `None`, the JavaScript value is +ignored. + ## Sample API Usage Given `demo.was` (encoded to `demo.wasm`): @@ -272,7 +509,5 @@ fetch('demo.wasm').then(response => ``` ## TODO - -* `WebAssembly.Memory`: imports, exports -* `WebAssembly.Table`: imports, exports * `WebAssembly.Module` `exports`/`imports` properties (reflection) +* JS API for cyclic imports (perhaps a Promise-returning `WebAssembly.instantiate`?) diff --git a/Modules.md b/Modules.md index 5c52792e..244722a0 100644 --- a/Modules.md +++ b/Modules.md @@ -1,58 +1,94 @@ # Modules The distributable, loadable, and executable unit of code in WebAssembly -is called a **module**. At runtime, a module can be loaded by a runtime -to produce an **instance** which encapsulates all the state directly -manipulated by running WebAssembly code. A WebAssembly instance's initial state -is determined by the module it was loaded from. - -A module contains: -* a set of [imports and exports](Modules.md#imports-and-exports); -* an optional start method name or index; -* a section defining [linear memory](Modules.md#linear-memory-section); -* a section containing [code](Modules.md#code-section); -* after the MVP, sections containing [debugging/symbol information](Tooling.md) or - a reference to separate files containing them; and -* possibly other sections in the future. -Sections declare their type and byte-length. Sections with unknown types are -silently ignored. - -An instance contains: -* the code of the module from which the instance was loaded; -* a [linear memory](AstSemantics.md#linear-memory); -* fully resolved imports; -* host-specific state (for example, the JavaScript function objects that reflect - exported functions to JavaScript); -* (when [threading](PostMVP.md#threads) is added) TLS variable state; -* (when [dynamic linking](DynamicLinking.md) is added) the code of multiple modules - that have been dynamically linked into the same instance; -* and other semantically-visible state added by other future features. - -While WebAssembly modules are designed to interoperate with ES6 modules -in a Web environment (more details [below](Modules.md#integration-with-es6-modules)), -WebAssembly modules are defined independently of JavaScript and do not require -the host environment to include a JavaScript VM. - -## Imports and Exports - -A module defines a set of functions in its -[code section](Modules.md#code-section) and can declare and name a subset of -these functions to be **exports**. The meaning of exports (how and when they are -called) is defined by the host environment. For example, a minimal shell -environment might only probe for and call the start function defined by the start -node of the module when given a module to execute. Exports are exported by name, -where the name is an arbitrary byte string of a given length. The host may need -to mangle these names. - -A module can declare a set of **imports**. An import is a tuple containing a -module name, the name of an exported function to import from the named module, -and the signature to use for that import within the importing module. Within a -module, the import can be [directly called](AstSemantics.md#calls) like a -function (according to the signature of the import). When the imported -module is also WebAssembly, it would be an error if the signature of the import -doesn't match the signature of the export. - -The WebAssembly spec does not define how imports are interpreted: +is called a **module**. At runtime, a module can be **instantiated** +with a set of import values to produce an **instance**, which is an immutable +tuple referencing all the state accessible to the running module. Multiple +module instances can access the same shared state which is the basis for +[dynamic linking](DynamicLinking.md) in WebAssembly. WebAssembly modules +are also designed to [integrate with ES6 modules](#integration-with-es6-modules). + +A module contains the following sections: +* [import](#imports) +* [export](#exports) +* [start](#module-start-function) +* [global](#global-section) +* [memory](#linear-memory-section) +* [data](#data-section) +* [table](#table-section) +* [elements](#elements-section) +* [function and code](#function-and-code-sections) + +A module also defines several *index spaces* which are statically indexed by +various operators and section fields in the module: +* the [function index space](#function-index-space) +* the [global index space](#global-index-space) +* the [linear memory index space](#linear-memory-index-space) +* the [table index space](#table-index-space) + +## Imports + +A module can declare a sequence of **imports** which are provided, at +instantiation time, by the host environment. There are several kinds of imports: +* **function imports**, which can be called inside the module by the + [`call`](AstSemantics.md#calls) operator; +* **global imports**, which can be accessed inside the module by the + [global operators](AstSemantics.md#global-variables); +* **linear memory imports**, which can be accessed inside the module by the + [memory operators](AstSemantics.md#linear-memory); and +* **table imports**, which can be accessed inside the module by + [call_indirect](AstSemantics.md#calls) and other + table operators in the + [future](FutureFeatures.md#more-table-operators-and-types). + +In the future, other kinds of imports may be added. Imports are designed to +allow modules to share code and data while still allowing separate compilation +and caching. + +All imports include two opaque names: a *module name* and an *export name*. The +interpretation of these names is up to the host environment but designed to +allow a host environments, like the [Web](Web.md), to support a two-level +namespace. + +Each specific kind of import defines additional fields: + +A *function import* includes a signature to use for the imported +function *inside* the module. The checking of the signature against the +imported function *outside* the module is defined by the host environment. +However, if the imported function is a WebAssembly function, the host +environment must raise an instantiation-time error if there is a signature +mismatch. + +A *global variable import* includes the *value type* and *mutability* +of the global variable. These fields have the same meaning as in the +[Global section](#global-section). + +A *linear memory import* includes the same set of fields defined in the +[Linear Memory section](#linear-memory-section): *default flag*, *initial +length* and optional *maximum length*. The host environment must only allow +imports of WebAssembly linear memories that have initial length +*greater-or-equal* than the initial length declared in the import and that have +maximum length *less-or-equal* than the maximum length declared in the import. +This ensures that separate compilation can assume: memory accesses below the +declared initial length are always in-bounds, accesses above the declared +maximum length are always out-of-bounds and if initial equals maximum, the +length is fixed. If the default flag is set, the imported memory is used as +the [default memory](AstSemantics.md#linear-memory) and at most one linear +memory definition (import or internal) may have the default flag set. In the +MVP, it is a validation error not to set the default flag. + +A *table import* includes the same set of fields defined in the +[Table section](#table-section): *default flag*, *element type*, *initial +length* and optional *maximum length*. As with the linear memory section, the +host environment must ensure only WebAssembly tables are imported with +exactly-matching element type, greater-or-equal initial length, and +less-or-equal maximum length. If the default flag is set, the imported table +is used as the [default table](AstSemantics.md#table) and at most one table +definition (import or internal) may have the default flag set. In the MVP, it is +a validation error not to set the default flag. + +Since the WebAssembly spec does not define how import names are interpreted: +* the [Web environment](Web.md#names) defines names to be UTF8-encoded strings; * the host environment can interpret the module name as a file path, a URL, a key in a fixed set of builtin modules or the host environment may invoke a user-defined hook to resolve the module name to one of these; @@ -66,14 +102,21 @@ arbitrary host environment functionality to WebAssembly code, similar to a native `syscall`. For example, a shell environment could define a builtin `stdio` module with an export `puts`. -In C/C++, an undefined `extern` declaration (perhaps only when given the -magic `__attribute__` or declared in a separate list of imports) could be -compiled to an import and C/C++ calls to this `extern` would then be compiled -to calls to this import. This is one way low-level C/C++ libraries could call -out of WebAssembly in order to implement portable source-level interfaces -(e.g., POSIX, OpenGL or SDL) in terms of host-specific functionality. +## Exports -### Integration with ES6 modules +A module can declare a sequence of **exports** which are returned at +instantiation time to the host environment. Each export has three fields: +a *name*, whose meaning is defined by the host environment, a *type*, +indicating whether the export is a function, global, memory or table, and +an *index* into the type's corresponding [index space](Modules.md). + +All definitions are exportable: functions, globals, linear memories and tables. +The meaning an exported definition is defined by the host environment. However, +if another WebAssembly instance imports the definition, then both instances +will share the same definition and the associated state (global variable value, +linear memory bytes, table elements) is shared. + +## Integration with ES6 modules While ES6 defines how to parse, link and execute a module, ES6 does not define when this parsing/linking/execution occurs. An additional extension @@ -122,7 +165,7 @@ independent libraries would have to hope that all the WebAssembly modules transitively used by those libraries "played well" together (e.g., explicitly shared `malloc` and coordinated global address ranges). Instead, the [dynamic linking future feature](DynamicLinking.md) is intended -to allow *explicitly* injecting multiple modules into the same instance. +to allow *explicitly* sharing state between module instances. ## Module start function @@ -131,7 +174,8 @@ by the loader after the instance is initialized and before the exported function are called. * The start function must not take any arguments or return anything -* The function can also be exported +* The function is identified by [function index](#function-index-space) and can also be + exported * There can only be at most one start node per module For example, a start node in a module will be: @@ -152,16 +196,32 @@ A module can: * The start function will be called after module loading and before any call to the module function is done +## Global section + +The *global section* provides an internal definition of zero or more +[global variables](AstSemantics.md#global-variables). + +Each global variable internal definition declares its *type* +(a [value type](AstSemantics.md#types)), *mutability* (boolean flag) and +*initializer* (an [initializer expression](#initializer-expression)). + ## Linear memory section -A module may contain an optional section declaring the use of linear memory -by the module. If the section is absent, the linear memory operators -`load`, `store`, `memory_size`, and `grow_memory` may not be used in the module. +The *linear memory section* provides an internal definition of zero or more +[linear memories](AstSemantics.md#linear-memory). In the MVP, the total number +of linear memory definitions is limited to 1, but this may be relaxed in the +[future](FutureFeatures.md#multiple-tables-and-memories). + +A linear memory definition may declare itself to be the +[default](AstSemantics.md#linear-memory) linear memory of the module. At most +one linear memory definition may declare itself to be the default. In the MVP, +if there is a linear memory definition, it *must* declare itself the default +(there is no way to access non-default linear memories anyhow). -The linear memory section declares the initial [memory size](AstSemantics.md#linear-memory) -(which may be subsequently increased by [`grow_memory`](AstSemantics.md#resizing)). +Each linear memory section declares an *initial* [memory size](AstSemantics.md#linear-memory) +(which may be subsequently increased by [`grow_memory`](AstSemantics.md#resizing)) and an +optional *maximum memory size*. -The linear memory section may optionally declare a maximum memory size. [`grow_memory`](AstSemantics.md#resizing) is guaranteed to fail if attempting to grow past the declared maximum. When declared, implementations *should* (non-normative) attempt to reserve virtual memory up to the maximum size. While @@ -170,30 +230,140 @@ reserve up to the *maximum* is not. When a maximum memory size is *not* declared on architectures with limited virtual address space, engines should allocate only the initial size and reallocate on demand. -The initial contents of linear memory are zero by default. However, the memory -section contains a possibly-empty array of *segments* (analogous to `.data`) -which can specify the initial contents of fixed `(offset, length)` ranges of -memory. - -The linear memory section may optionally declare that the instance's -linear memory is *externally aliasable*. How linear memory is aliased is up -to the host environment (as with all module exports). The -[Web](Web.md#aliasing-linear-memory-from-JS) would reflect exported linear -memory to JS as an `ArrayBuffer`. The MVP does not currently provide for -*importing* linear memory though this may be added -[in the future](FutureFeatures.md#importing-linear-memory). - -## Code section - -The WebAssembly spec defines the code section of a module in terms of an -[Abstract Syntax Tree](AstSemantics.md) (AST). Additionally, the spec defines -two concrete representations of the AST: a [binary format](BinaryEncoding.md) -which is natively decoded by the browser and a [text format](TextFormat.md) -which is intended to be read and written by humans. A WebAssembly environment -is only required to understand the binary format; the text format is defined so -that WebAssembly modules can be written by hand (and then converted to binary -with an offline tool) and so that developer tools have a well-defined text -projection of a binary WebAssembly module. This design separates the concerns -of specifying and reasoning about behavior, over-the-wire size and compilation -speed, and ergonomic syntax. - +## Data section + +The initial contents of linear memory are zero. The *data section* contains a +possibly-empty array of *data segments* which specify the initial contents +of fixed `(offset, length)` ranges of a given memory, specified by its +[linear memory index](#linear-memory-index-space). This section is analogous to +the `.data` section of native executables. The `length` is an integer constant +value (defining the length of the given segment). The `offset` is an +[initializer expression](#initializer-expression). + +## Table section + +The *table section* contains zero or more definitions of distinct +[tables](AstSemantics.md#table). In the MVP, the total number +of table definitions is limited to 1, but this may be relaxed in the +[future](FutureFeatures.md#multiple-tables-and-memories). + +A table definition may declare itself to be the +[default](AstSemantics.md#table) table of the module. At most +one table definition may declare itself to be the default. In the MVP, +if there is a table definition, it *must* declare itself the default +(there is no way to access non-default tables anyhow). + +Each table definition also includes an *element type*, *initial length*, and +optional *maximum length*. + +In the MVP, the only valid element type is `"function"`, but in the +[future](FutureFeatures.md#more-table-operators-and-types), more element +types may be added. + +In the MVP, tables can only be resized via host-defined APIs (such as +the JavaScript [`WebAssembly.Table.prototype.grow`](JS.md#webassemblytableprototypegrow)). +A `grow_table` may be added in the [future](FutureFeatures.md#more-table-operators-and-types). +In either case, table growth is guaranteed to fail if attempting to grow past +the declared maximum. As with linear memory, when a maximum is declared, +implementations *should* (non-normative) attempt to reserve virtual memory up to +the maximum size. While failure to allocate the *initial* memory size is a +runtime error, failure to reserve up to the *maximum* is not. When a maximum +memory size is *not* declared, on architectures with limited virtual address +space, engines should allocate only the initial size and reallocate on demand. + +## Elements section + +The intial contents of a tables' elements are sentinel values (that would trap +if called). The *elements section* allows a module to initialize (at +instantiation time) the elements of any imported or internally-defined table +with any other definition in the module. This is symmetric to how the +[Data section](#data-section) allows a module to initialize the bytes +of any imported or defined memory. + +The elements section contains a possibly-empty array of *element segments* which +specify the initial contents of fixed `(offset, length)` ranges of a given +table, specified by its [table index](#table-index-space). The `length` is an +integer constant value (defining the length of the given segment). The `offset` +is an [initializer expression](#initializer-expression). Elements are specified +with a `(type, index)` pair where `type` is the type of an +[index spaces](Modules.md) that is compatible with the table's element type and +`index` is an integer immediate into `type`s index space. + +## Function and Code sections + +A single logical function definition is defined in two sections: + * the *function* section declares the signatures of each internal function + definition in the module; + * the *code* section contains the [function body](BinaryEncoding.md#function-bodies) + of each function declared by the function section. + +This split aids in streaming compilation by putting the function bodies, +which constitute most of the byte size of the module, near the end so that all +metadata necessary for recursive module loading and parallel compilation is +available before compilation begins. + +## Function Index Space + +The *function index space* indexes all imported and internally-defined +function definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). + +The function index space is used by: +* [calls](AstSemantics.md#calls), to identify the callee of a direct call + +## Global Index Space + +The *global index space* indexes all imported and internally-defined +global definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). + +The global index space is used by: +* [global variable access operators](AstSemantics.md#global-variables), to + identify the global variable to read/write +* [data segments](#data-section), to define the offset of a data segment + (in linear memory) as the value of a global variable + +## Linear Memory Index Space + +The *linear memory index space* indexes all imported and internally-defined +linear memory definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). + +The linear memory index space is only used by the +[data section](#data-section). In the MVP, there is at most one linear memory so +this index space is just a placeholder for when there can be +[multiple memories](FutureFeatures.md#multiple-tables-and-memories). + +## Table Index Space + +The *table index space* indexes all imported and internally-defined +table definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). + +The table index space is only used by the [elements section](#elements-section). +In the MVP, there is at most one table so this index space is just +a placeholder for when there can be +[multiple tables](FutureFeatures.md#multiple-tables-and-memories). + +## Initializer Expression + +Initializer expressions are evaluated at instantiation time and are currently +used to: + * define the initial value of [global variables](#global-section) + * define the offset of a [data segment](#data-section) or + [elements segment](#elements-section) + +An initializer expression is a pure WebAssembly expression that is encoded with +the same [binary encoding](BinaryEncoding.md) as WebAssembly expressions. Not +all WebAssembly operators can or should be supported in initializer expressions; +initializer expressions represent a minimal pure subset of WebAssembly +expressions. + +In the MVP, to keep things simple while still supporting the basic needs +of [dynamic linking](DynamicLinking.md), initializer expressions are restricted +to the following nullary operators: + * the four [constant operators](AstSemantics.md#constants); and + * `get_global`, where the global index must refer to an immutable import. + +In the future, operators like `i32.add` could be added to allow more expressive +`base + offset` load-time calculations. diff --git a/Nondeterminism.md b/Nondeterminism.md index db738c97..68af0eb9 100644 --- a/Nondeterminism.md +++ b/Nondeterminism.md @@ -48,7 +48,7 @@ currently admits nondeterminism: - Memory allocation may fail. - The runtime can fail to allocate a physical page when a memory location is first accessed (e.g. through a load or store), even if that memory was virtually reserved - by the maximum size property of the [memory section](Module.md#linear-memory-section). + by the maximum size property of the [memory section](Modules.md#linear-memory-section). - Program stack may get exhausted (e.g., because function call depth is too big, or functions have too many locals, or infinite recursion). Note that this stack isn't located in the program-accessible linear memory. diff --git a/PostMVP.md b/PostMVP.md index 9d8ffc1a..2382fe7d 100644 --- a/PostMVP.md +++ b/PostMVP.md @@ -32,10 +32,6 @@ and can thus be represented efficiently by the engine. [PNaCl atomic support]: https://developer.chrome.com/native-client/reference/pnacl-c-cpp-language-support#memory-model-and-atomics [SharedArrayBuffer]: https://github.com/tc39/ecmascript_sharedmem -## Dynamic linking - -This is covered in the [dynamic linking](DynamicLinking.md) section. - ## Fixed-width SIMD Support fixed-width SIMD vectors, initially only for 128-bit wide vectors as diff --git a/Web.md b/Web.md index 737558a1..9b4f34c6 100644 --- a/Web.md +++ b/Web.md @@ -27,7 +27,7 @@ alias the exported memory of instantiated modules, etc. WebAssembly's [modules](Modules.md) allow for natural [integration with the ES6 module system](Modules.md#integration-with-es6-modules). -### Function Names +### Names A WebAssembly module imports and exports functions. WebAssembly names functions using arbitrary-length byte sequences. Any 8-bit values are permitted in a @@ -56,35 +56,6 @@ Transcoding failure is detected by `decodeURIComponent`, which may throw `URIError`. If it does, the WebAssembly module will not validate. This validation rule is only mandatory for Web embedding. -## Aliasing linear memory from JS - -If [allowed by the module](Modules.md#linear-memory-section), JavaScript can -alias a loaded module's linear memory through an exported `ArrayBuffer`. -Module instances would additionally expose methods to JS to copy ranges of -bytes into and out of linear memory as separate (unaliased) `ArrayBuffer`s. - -Since JS semantics and implementations require the `byteLength` of an -`ArrayBuffer` to be constant, [resizing](AstSemantics.md#resizing) the -linear memory cannot simply resize the exported `ArrayBuffer`. Instead, -the `ArrayBuffer` would be [detached](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-detacharraybuffer) -and a new `ArrayBuffer` (with a new `byteLength`) would be exported in -its place. - -When [threads](PostMVP.md#threads) are added, a -[`SharedArrayBuffer`](https://github.com/lars-t-hansen/ecmascript_sharedmem) -would need to be exported instead of an `ArrayBuffer`. However, the -detach-on-resize strategy would pose significant usability and implementation -hazards, since resizing can happen concurrently. One solution would be -to simply not export a `SharedArrayBuffer` when a module declared use of -threads and resizable memory (the copy in/out methods would need to be used -instead). - -Similarly, various [linear memory operations](FutureFeatures.md#finer-grained-control-over-memory) -like `mprotect` conflict with the JS semantics of `ArrayBuffer` and -would inhibit export. In general, `ArrayBuffer` could be viewed as an -optimization of copy in/out that was only available when linear memory -behaved like an `ArrayBuffer` (or `SharedArrayBuffer`). - ## Security WebAssembly's security model should depend on [CORS][] and