From 453320eb21f5e7476fb27db7874c45aa855927b7 Mon Sep 17 00:00:00 2001 From: titzer Date: Thu, 29 Sep 2016 20:04:23 +0200 Subject: Binary 0xc (#811) * Clarify that wasm may be viewed as either an AST or a stack machine. (#686) * Clarify that wasm may be viewed as either an AST or a stack machine. * Reword the introductory paragraph. * Add parens, remove "typed". * Make opcode 0x00 `unreachable`. (#684) Make opcode 0x00 `unreachable`, and move `nop` to a non-zero opcode. All-zeros is one of the more common patterns of corrupted data. This change makes it more likely that code that is accidentally zeroed, in whole or in part, will be noticed when executed rather than silently running through a nop slide. Obviously, this doesn't matter when an opcode table is present, but if there is a default opcode table, it would presumably use the opcodes defined here. * BinaryEncoding.md changes implied by #682 * Fix thinko in import section * Rename definition_kind to external_kind for precision * Rename resizable_definition to resizable_limits * Add opcode delimiter to init_expr * Add Elem section to ToC and move it before Data section to reflect Table going before Memory * Add missing init_expr to global variables and undo the grouped representation of globals * Note that only immutable globals can be exported * Change the other 'mutability' flag to 'varuint1' * Give 'anyfunc' its own opcode * Add note about immutable global import requirement * Remove explicit 'default' flag; make memory/table default by default * Change (get|set)_global opcodes * Add end opcode to functions * Use section codes instead of section names (rebasing onto 0xC instead of master) This PR proposes uses section codes for known sections, which is more compact and easier to check in a decoder. It allows for user-defined sections that have string names to be encoded in the same manner as before. The scheme of using negative numbers proposed here also has the advantage of allowing a single decoder to accept the old (0xB) format and the new (0xC) format for the time being. * Use LEB for br_table (#738) * Describe operand order of call_indirect (#758) * Remove arities from call/return (#748) * Limit varint sizes in Binary Encoding. (#764) * Global section (#771) global-variable was a broken anchor and the type of count was an undefined reference and inconsistent with all the rest of the sections. * Make name section a user-string section. * Update BinaryEncoding.md * Update BinaryEncoding.md * Use positive section code byte * Remove specification of name strings for unknown sections * Update BinaryEncoding.md * Remove repetition in definition of var(u)int types (#768) * Fix typo (#781) * Move the element section before the code section (#779) * Binary format identifier is out of date (#785) * Update BinaryEncoding.md to reflect the ml-proto encoding of the memory and table sections. (#800) * Add string back * Block signatures (#765) * Replace branch arities with block and if signatures. Moving arities to blocks has the nice property of giving implementations useful information up front, however some anticipated uses of this information would really want to know the types up front too. This patch proposes replacing block arities with function signature indices, which would provide full type information about a block up front. * Remove the arity operand from br_table too. * Remove mentions of "arguments". * Make string part of the payload * Remove references to post-order AST in BinaryEncoding.md (#801) * Simplify loop by removing its exit label. This removes loop's bottom label. * Move description of `return` to correct column (#804) * type correction and missing close quote (#805) * Remove more references to AST (#806) * Remove reference to AST in JS.md Remove a reference to AST in JS.md. Note that the ml-proto spec still uses the name `Ast.Module` and has files named `ast.ml`, etc, so leaving those references intact for now. * Use "instruction" instead of "AST operator" * Update rationale for stack machine * Update Rationale.md * Update discussion of expression trees * Update MVP.md * Update Rationale.md * Update Rationale.md * Remove references to expressions * Update Rationale.md * Update Rationale.md * Address review comments * Address review comments * Address review comments * Delete h --- BinaryEncoding.md | 352 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 222 insertions(+), 130 deletions(-) (limited to 'BinaryEncoding.md') diff --git a/BinaryEncoding.md b/BinaryEncoding.md index 90e6638..d6fdbdc 100644 --- a/BinaryEncoding.md +++ b/BinaryEncoding.md @@ -8,7 +8,7 @@ See the [rationale document](Rationale.md#why-a-binary-encoding) for more detail The encoding is split into three layers: -* **Layer 0** is a simple post-order encoding of the AST and related data structures. +* **Layer 0** is a simple binary encoding of the bytecode instructions and related data structures. The encoding is dense and trivial to interact with, making it suitable for scenarios like JIT, instrumentation tools, and debugging. * **Layer 1** provides structural compression on top of layer 0, exploiting @@ -31,50 +31,67 @@ for a proposal for layer 1 structural compression. # Data types -### uint8 -A single-byte unsigned integer. +### `uintN` +An unsigned integer of _N_ bits, +represented in _N_/8 bytes in [little endian](https://en.wikipedia.org/wiki/Endianness#Little-endian) order. +_N_ is either 8, 16, or 32. -### uint32 -A four-byte little endian unsigned integer. +### `varuintN` +A [LEB128](https://en.wikipedia.org/wiki/LEB128) variable-length integer, limited to _N_ bits (i.e., the values [0, 2^_N_-1]), +represented by _at most_ ceil(_N_/7) bytes that may contain padding `0x80` bytes. -### varint32 -A [Signed LEB128](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) variable-length integer, limited to int32 values. +Note: Currently, the only sizes used are `varuint1`, `varuint7`, and `varuint32`, +where the former two are used for compatibility with potential future extensions. -### varuint1 -A [LEB128](https://en.wikipedia.org/wiki/LEB128) variable-length integer, limited to the values 0 or 1. `varuint1` values may contain leading zeros. (This type is mainly used for compatibility with potential future extensions.) +### `varintN` +A [Signed LEB128](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) variable-length integer, limited to _N_ bits (i.e., the values [-2^(_N_-1), +2^(_N_-1)-1]), +represented by _at most_ ceil(_N_/7) bytes that may contain padding `0x80` or `0xFF` bytes. -### varuint7 -A [LEB128](https://en.wikipedia.org/wiki/LEB128) variable-length integer, limited to the values [0, 127]. `varuint7` values may contain leading zeros. (This type is mainly used for compatibility with potential future extensions.) +Note: Currently, the only sizes used are `varint32` and `varint64`. -### varuint32 -A [LEB128](https://en.wikipedia.org/wiki/LEB128) variable-length integer, limited to uint32 values. `varuint32` values may contain leading zeros. - -### varint64 -A [Signed LEB128](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) variable-length integer, limited to int64 values. - -### value_type +### `value_type` A single-byte unsigned integer indicating a [value type](AstSemantics.md#types). These types are encoded as: * `1` indicating type `i32` * `2` indicating type `i64` * `3` indicating type `f32` * `4` indicating type `f64` +### `inline_signature_type` +A single-byte unsigned integer indicating a signature. These types are encoded as: +* `0` indicating a signature with 0 results. +* `1` indicating a signature with 1 result of type `i32`. +* `2` indicating a signature with 1 result of type `i64`. +* `3` indicating a signature with 1 result of type `f32`. +* `4` indicating a signature with 1 result of type `f64`. + +### `external_kind` +A single-byte unsigned integer indicating the kind of definition being imported or defined: +* `0` indicating a `Function` [import](Modules.md#imports) or [definition](Modules.md#function-and-code-sections) +* `1` indicating a `Table` [import](Modules.md#imports) or [definition](Modules.md#table-section) +* `2` indicating a `Memory` [import](Modules.md#imports) or [definition](Modules.md#linear-memory-section) +* `3` indicating a `Global` [import](Modules.md#imports) or [definition](Modules.md#global-section) + +### `resizable_limits` +A packed tuple that describes the limits of a +[table](AstSemantics.md#table) or [memory](AstSemantics.md#resizing): -# Definitions +| Field | Type | Description | +| ----- | ----- | ----- | +| flags | `varuint32` | bit `0x1` is set if the maximum field is present | +| initial | `varuint32` | initial length (in units of table elements or wasm pages) | +| maximum | `varuint32`? | only present if specified by `flags` | -### Post-order encoding -Refers to an approach for encoding syntax trees, where each node begins with an identifying binary -sequence, then followed recursively by any child nodes. +The "flags" field may later be extended to include a flag for sharing (between +threads). -* Examples - * Given a simple AST node: `i32.add(left: AstNode, right: AstNode)` - * First recursively write the left and right child nodes. - * Then write the opcode for `i32.add` (uint8) +### `init_expr` +The encoding of an [initializer expression](Modules.md#initializer-expression) +is the normal encoding of the expression followed by the `end` opcode as a +delimiter. - * Given a call AST node: `call(args: AstNode[], callee_index: varuint32)` - * First recursively write each argument node. - * Then write the (variable-length) integer `callee_index` (varuint32) - * Finally write the opcode of `Call` (uint8) +Note that `get_global` in an initializer expression can only refer to immutable +imported globals and all uses of `init_expr` can only appear after the Imports +section. # Module structure @@ -87,34 +104,41 @@ The module starts with a preamble of two fields: | Field | Type | Description | | ----- | ----- | ----- | | magic number | `uint32` | Magic number `0x6d736100` (i.e., '\0asm') | -| version | `uint32` | Version number, currently 10. The version for MVP will be reset to 1. | +| version | `uint32` | Version number, currently 12. The version for MVP will be reset to 1. | -This preamble is followed by a sequence of sections. Each section is identified by an -immediate string. Sections whose identity is unknown to the WebAssembly -implementation are ignored and this is supported by including the size in bytes -for all sections. The encoding of sections is structured as follows: +The module preamble is followed by a sequence of sections. +Each section is identified by a 1-byte *section code* that encodes either a known section or a user-defined section. +The section length and payload data then follow. +Known sections have non-zero ids, while unknown sections have a `0` id followed by an identifying string as +part of the payload. +Unknown sections are ignored by the WebAssembly implementation, and thus validation errors within them do not +invalidate a module. | Field | Type | Description | | ----- | ----- | ----- | -| id_len | `varuint32` | section identifier string length | -| id_str | `bytes` | section identifier string of id_len bytes | +| id | `varuint7` | section code | | payload_len | `varuint32` | size of this section in bytes | -| payload_str | `bytes` | content of this section, of length payload_len | +| name_len | `varuint32` ? | length of the section name in bytes, present if `id == 0` | +| name | `bytes` ? | section name string, present if `id == 0` | +| payload_data | `bytes` | content of this section, of length `payload_len - sizeof(name) - sizeof(name_len)` | Each section is optional and may appear at most once. -Known sections (from this list) may not appear out of order. -The content of each section is encoded in its `payload_str`. - -* [Type](#type-section) section -* [Import](#import-section) section -* [Function](#function-section) section -* [Table](#table-section) section -* [Memory](#memory-section) section -* [Export](#export-section) section -* [Start](#start-section) section -* [Code](#code-section) section -* [Data](#data-section) section -* [Name](#name-section) section +Known sections from this list may not appear out of order. +The content of each section is encoded in its `payload_data`. + +| Section Name | Code | Description | +| ------------ | ---- | ----------- | +| [Type](#type-section) | `1` | Function signature declarations | +| [Import](#import-section) | `2` | Import declarations | +| [Function](#function-section) | `3` | Function declarations | +| [Table](#table-section) | `4` | Indirect function table and other tables | +| [Memory](#memory-section) | `5` | Memory attributes | +| [Global](#global-section) | `6` | Global declarations | +| [Export](#export-section) | `7` | Exports | +| [Start](#start-section) | `8` | Start function declaration | +| [Element](#element-section) | `9` | Elements section | +| [Code](#code-section) | `10` | Function bodies (code) | +| [Data](#data-section) | `11` | Data segments | The end of the last present section must coincide with the last byte of the module. The shortest valid module is 8 bytes (`magic number`, `version`, @@ -122,18 +146,16 @@ followed by zero sections). ### Type section -ID: `type` - The type section declares all function signatures that will be used in the module. | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | count | `varuint32` | count of type entries to follow | | entries | `type_entry*` | repeated type entries as described below | #### Type entry | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | form | `varuint7` | `0x40`, indicating a function type | | param_count | `varuint32` | the number of parameters to the function | | param_types | `value_type*` | the parameter types of the function | @@ -144,89 +166,163 @@ The type section declares all function signatures that will be used in the modul ### Import section -ID: `import` - The import section declares all imports that will be used in the module. | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | count | `varuint32` | count of import entries to follow | | entries | `import_entry*` | repeated import entries as described below | #### Import entry | Field | Type | Description | -| ----- | ----- | ----- | -| sig_index | `varuint32` | signature index of the import | +| ----- | ---- | ----------- | | module_len | `varuint32` | module string length | | module_str | `bytes` | module string of `module_len` bytes | -| function_len | `varuint32` | function string length | -| function_str | `bytes` | function string of `function_len` bytes | +| field_len | `varuint32` | field name length | +| field_str | `bytes` | field name string of `field_len` bytes | +| kind | `external_kind` | the kind of definition being imported | -### Function section +Followed by, if the `kind` is `Function`: + +| Field | Type | Description | +| ----- | ---- | ----------- | +| sig_index | `varuint32` | signature index of the import | + +or, if the `kind` is `Table`: + +| Field | Type | Description | +| ----- | ---- | ----------- | +| element_type | `varuint7` | `0x20`, indicating [`anyfunc`](AstSemantics.md#table) | +| | `resizable_limits` | see [above](#resizable_limits) | + +or, if the `kind` is `Memory`: -ID: `function` +| Field | Type | Description | +| ----- | ---- | ----------- | +| | `resizable_limits` | see [above](#resizable_limits) | + +or, if the `kind` is `Global`: + +| Field | Type | Description | +| ----- | ---- | ----------- | +| type | `value_type` | type of the imported global | +| mutability | `varuint1` | `0` if immutable, `1` if mutable; must be `0` in the MVP | + +### Function section The function section _declares_ the signatures of all functions in the module (their definitions appear in the [code section](#code-section)). | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | count | `varuint32` | count of signature indices to follow | | types | `varuint32*` | sequence of indices into the type section | ### Table section -ID: `table` - -The table section defines the module's -[indirect function table](AstSemantics.md#calls). +The encoding of a [Table section](Modules.md#table-section): | Field | Type | Description | | ----- | ----- | ----- | -| count | `varuint32` | count of entries to follow | -| entries | `varuint32*` | repeated indexes into the function section | +| count | `varuint32` | indicating the number of tables defined by the module | +| entries | `table_type*` | repeated `table_type` entries as described below | + +| Field | Type | Description | +| ----- | ---- | ----------- | +| element_type | `varuint7` | `0x20`, indicating [`anyfunc`](AstSemantics.md#table) | +| | `resizable_limits` | see [above](#resizable_limits) | + +In the MVP, the number of tables must be no more than 1. ### Memory section ID: `memory` -The memory section declares the size and characteristics of the memory -associated with the module. +The encoding of a [Memory section](Modules.md#linear-memory-section): | Field | Type | Description | | ----- | ----- | ----- | -| initial | `varuint32` | initial memory size in 64KiB pages | -| maximum | `varuint32` | maximum memory size in 64KiB pages | -| exported | `uint8` | `1` if the memory is visible outside the module | +| count | `varuint32` | indicating the number of memories defined by the module | +| entries | `memory_type*` | repeated `memory_type` entries as described below | -### Export section +| Field | Type | Description | +| ----- | ---- | ----------- | +| | `resizable_limits` | see [above](#resizable_limits) | + +Note that the initial/maximum fields are specified in units of +[WebAssembly pages](AstSemantics.md#linear-memory). -ID: `export` +In the MVP, the number of memories must be no more than 1. -The export section declares all exports from the module. +### Global section + +The encoding of the [Global section](Modules.md#global-section): | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | +| count | `varuint32` | count of global variable entries | +| globals | `global_variable*` | global variables, as described below | + +#### Global Entry + +Each `global_variable` declares a single global variable of a given type, mutability +and with the given initializer. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| type | `value_type` | type of the variables | +| mutability | `varuint1` | `0` if immutable, `1` if mutable | +| init | `init_expr` | the initial value of the global | + +Note that, in the MVP, only immutable global variables can be exported. + +### Export section + +The encoding of the [Export section](Modules.md#exports): + +| Field | Type | Description | +| ----- | ---- | ----------- | | count | `varuint32` | count of export entries to follow | | entries | `export_entry*` | repeated export entries as described below | #### Export entry | Field | Type | Description | -| ----- | ----- | ----- | -| func_index | `varuint32` | index into the function table | -| function_len | `varuint32` | function string length | -| function_str | `bytes` | function string of `function_len` bytes | +| ----- | ---- | ----------- | +| field_len | `varuint32` | field name string length | +| field_str | `bytes` | field name string of `field_len` bytes | +| kind | `external_kind` | the kind of definition being exported | +| index | `varuint32` | the index into the corresponding [index space](Modules.md) | -### Start section +For example, if the "kind" is `Function`, then "index" is a +[function index](Modules.md#function-index-space). Note that, in the MVP, the +only valid index value for a memory or table export is 0. -ID: `start` +### Start section The start section declares the [start function](Modules.md#module-start-function). | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | index | `varuint32` | start function index | +### Element section + +The encoding of the [Elements section](Modules.md#elements-section): + +| Field | Type | Description | +| ----- | ---- | ----------- | +| count | `varuint32` | count of element segments to follow | +| entries | `elem_segment*` | repeated element segments as described below | + +a `elem_segment` is: + +| Field | Type | Description | +| ----- | ---- | ----------- | +| index | `varuint32` | the [table index](Modules.md#table-index-space) (0 in the MVP) | +| offset | `init_expr` | an `i32` initializer expression that computes the offset at which to place the elements | +| num_elem | `varuint32` | number of elements to follow | +| elems | `varuint32*` | sequence of [function indices](Modules.md#function-index-space) | + ### Code section ID: `code` @@ -236,44 +332,43 @@ The count of function declared in the [function section](#function-section) and function bodies defined in this section must be the same and the `i`th declaration corresponds to the `i`th function body. -| Field | Type | Description | -| ----- | ----- | ----- | ----- | +| Field | Type | Description | +| ----- | ---- | ----------- | | count | `varuint32` | count of function bodies to follow | | bodies | `function_body*` | sequence of [Function Bodies](#function-bodies) | ### Data section -ID: `data` - The data section declares the initialized data that is loaded into the linear memory. | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | count | `varuint32` | count of data segments to follow | | entries | `data_segment*` | repeated data segments as described below | a `data_segment` is: | Field | Type | Description | -| ----- | ----- | ----- | -| offset | `varuint32` | the offset in linear memory at which to store the data | +| ----- | ---- | ----------- | +| index | `varuint32` | the [linear memory index](Modules.md#linear-memory-index-space) (0 in the MVP) | +| offset | `init_expr` | an `i32` initializer expression that computes the offset at which to place the data | | size | `varuint32` | size of `data` (in bytes) | | data | `bytes` | sequence of `size` bytes | ### Name section -ID: `name` +User-defined section string: `"name"` -The names section does not change execution semantics and a validation error in -this section does not cause validation for the whole module to fail and is -instead treated as if the section was absent. The expectation is that, when a -binary WebAssembly module is viewed in a browser or other development +The names section does not change execution semantics, and thus is not allocated a section code. +It is encoded as an unknown section (id `0`) followed by the identification string `"name"`. +Like all unknown sections, a validation error in this section does not cause validation of the module to fail. +The expectation is that, when a binary WebAssembly module is viewed in a browser or other development environment, the names in this section will be used as the names of functions and locals in the [text format](TextFormat.md). | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | count | `varuint32` | count of entries to follow | | entries | `function_names*` | sequence of names | @@ -284,7 +379,7 @@ functions. #### Function names | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | fun_name_len | `varuint32` | string length, in bytes | | fun_name_str | `bytes` | valid utf8 encoding | | local_count | `varuint32` | count of local names to follow | @@ -296,25 +391,23 @@ count may be greater or less than the actual number of locals. #### Local name | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | local_name_len | `varuint32` | string length, in bytes | | local_name_str | `bytes` | valid utf8 encoding | # Function Bodies -Function bodies consist of a sequence of local variable declarations followed by a -dense post-order encoding of an [Abstract Syntax Tree](AstSemantics.md). -Each node in the abstract syntax tree corresponds to an operator, such as `i32.add` or `if` or `block`. -Operators are encoding by an opcode byte followed by immediate bytes (if any), followed by children -nodes (if any). +Function bodies consist of a sequence of local variable declarations followed by +[bytecode instructions](AstSemantics.md). Each function body must end with the `end` opcode. -| Field | Type |Description | -| ----- | ----- | ----- | +| Field | Type | Description | +| ----- | ---- | ----------- | | body_size | `varuint32` | size of function body to follow, in bytes | | local_count | `varuint32` | number of local entries | | locals | `local_entry*` | local variables | -| ast | `byte*` | post-order encoded AST | +| code | `byte*` | bytecode of the function | +| end | `byte` | `0x0f`, indicating the end of the body | #### Local Entry @@ -322,7 +415,7 @@ Each local entry declares a number of local variables of a given type. It is legal to have several entries with the same type. | Field | Type | Description | -| ----- | ----- | ----- | +| ----- | ---- | ----------- | | count | `varuint32` | number of local variables of the following type | | type | `value_type` | type of the variables | @@ -331,32 +424,30 @@ It is legal to have several entries with the same type. | Name | Opcode | Immediates | Description | | ---- | ---- | ---- | ---- | -| `nop` | `0x00` | | no operation | -| `block` | `0x01` | | begin a sequence of expressions, the last of which yields a value | -| `loop` | `0x02` | | begin a block which can also form control flow loops | -| `if` | `0x03` | | begin if expression | +| `unreachable` | `0x00` | | trap immediately | +| `block` | `0x01` | sig : `inline_signature_type` | begin a sequence of expressions, yielding 0 or 1 values | +| `loop` | `0x02` | sig : `inline_signature_type` | begin a block which can also form control flow loops | +| `if` | `0x03` | sig : `inline_signature_type` | begin if expression | | `else` | `0x04` | | begin else expression of if | | `select` | `0x05` | | select one of two values based on condition | -| `br` | `0x06` | argument_count : `varuint1`, relative_depth : `varuint32` | break that targets an outer nested block | -| `br_if` | `0x07` | argument_count : `varuint1`, relative_depth : `varuint32` | conditional break that targets an outer nested block | +| `br` | `0x06` | relative_depth : `varuint32` | break that targets an outer nested block | +| `br_if` | `0x07` | relative_depth : `varuint32` | conditional break that targets an outer nested block | | `br_table` | `0x08` | see below | branch table control flow construct | -| `return` | `0x09` | argument_count : `varuint1` | return zero or one value from this function | -| `unreachable` | `0x0a` | | trap immediately | +| `return` | `0x09` | | return zero or one value from this function | | `drop` | `0x0b` | | ignore value | +| `nop` | `0x0a` | | no operation | | `end` | `0x0f` | | end a block, loop, or if | -Note that there is no explicit `if_else` opcode, as the else clause is encoded with the `else` bytecode. - -The counts following the break and return operators specify how many preceding operands are taken as transfer arguments; in the MVP, all these values must be either 0 or 1. +The _sig_ fields of `block` and `if` operators specify function signatures +which describe their use of the operand stack. The `br_table` operator has an immediate operand which is encoded as follows: | Field | Type | Description | | ---- | ---- | ---- | -| arity | `varuint1` | number of arguments | -| target_count | `varuint32` | number of targets in the target_table | -| target_table | `uint32*` | target entries that indicate an outer block or loop to which to break | -| default_target | `uint32` | an outer block or loop to which to break in the default case | +| target_count | `varuint32` | number of entries in the target_table | +| target_table | `varuint32*` | target entries that indicate an outer block or loop to which to break | +| default_target | `varuint32` | an outer block or loop to which to break in the default case | The `br_table` operator implements an indirect branch. It accepts an optional value argument (like other branches) and an additional `i32` expression as input, and @@ -374,11 +465,12 @@ out of range, `br_table` branches to the default target. | `get_local` | `0x14` | local_index : `varuint32` | read a local variable or parameter | | `set_local` | `0x15` | local_index : `varuint32` | write a local variable or parameter | | `tee_local` | `0x19` | local_index : `varuint32` | write a local variable or parameter and return the same value | -| `call` | `0x16` | argument_count : `varuint1`, function_index : `varuint32` | call a function by its index | -| `call_indirect` | `0x17` | argument_count : `varuint1`, type_index : `varuint32` | call a function indirect with an expected signature | -| `call_import` | `0x18` | argument_count : `varuint1`, import_index : `varuint32` | call an imported function by its index | +| `get_global` | `0xbb` | global_index : `varuint32` | read a global variable | +| `set_global` | `0xbc` | global_index : `varuint32` | write a global variable | +| `call` | `0x16` | function_index : `varuint32` | call a function by its [index](Modules.md#function-index-space) | +| `call_indirect` | `0x17` | type_index : `varuint32` | call a function indirect with an expected signature | -The counts following the different call opcodes specify the number of preceding operands taken as arguments. +The `call_indirect` operator takes a list of function arguments and as the last operand the index into the table. ## Memory-related operators ([described here](AstSemantics.md#linear-memory-accesses)) -- cgit v1.2.3