WebAssembly 3.0 Specification Guide
WebAssembly 3.0 Specification Guide
1 Introduction 1
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Structure 5
2.1 Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3 Validation 29
3.1 Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.2 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.3 Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.4 Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.5 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4 Execution 79
4.1 Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.2 Runtime Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
4.3 Numerics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.4 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
4.5 Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
4.6 Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.7 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
i
7 Appendix 239
7.1 Embedding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
7.2 Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
7.3 Implementation Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
7.4 Type Soundness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
7.5 Type System Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
7.6 Validation Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
7.7 Custom Sections and Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
7.8 Change History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
7.9 Index of Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
7.10 Index of Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
7.11 Index of Semantic Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
Index 307
ii
CHAPTER 1
Introduction
1.1 Introduction
WebAssembly (abbreviated Wasm2 ) is a safe, portable, low-level code format designed for efficient execution and
compact representation. Its main goal is to enable high performance applications on the Web, but it does not make
any Web-specific assumptions or provide Web-specific features, so it can be employed in other environments as
well.
WebAssembly is an open standard developed by a W3C Community Group1 .
This document describes version 3.0 (2025-11-14) of the core WebAssembly standard. It is intended that it will
be superseded by new incremental releases with additional features in the future.
does not corrupt its own memory layout, e.g. inside WebAssembly’s linear memory.
1
WebAssembly Specification, Release 3.0 (2025-11-14)
1.1.2 Scope
At its core, WebAssembly is a virtual instruction set architecture (virtual ISA). As such, it has many use cases
and can be embedded in many different environments. To encompass their variety and enable maximum reuse, the
WebAssembly specification is split and layered into several documents.
This document is concerned with the core ISA layer of WebAssembly. It defines the instruction set, binary en-
coding, validation, and execution semantics, as well as a textual representation. It does not, however, define how
WebAssembly programs can interact with a specific environment they execute in, nor how they are invoked from
such an environment.
Instead, this specification is complemented by additional documents defining interfaces to specific embedding
environments such as the Web. These will each define a WebAssembly application programming interface (API)
suitable for a given environment.
1.1.4 Dependencies
WebAssembly depends on two existing standards:
• IEEE 7544 , for the representation of floating-point data and the semantics of respective numeric operations.
• Unicode5 , for the representation of import/export names and the text format.
However, to make this specification self-contained, relevant aspects of the aforementioned standards are defined
and formalized as part of this specification, such as the binary representation and rounding of floating-point values,
and the value range and UTF-8 encoding of Unicode characters.
4 [Link]
5 [Link]
2 Chapter 1. Introduction
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
The aforementioned standards are the authoritative source of all respective definitions. Formalizations given in
this specification are intended to match these definitions. Any discrepancy in the syntax or semantics described
is to be considered an error.
1.2 Overview
1.2.1 Concepts
WebAssembly encodes a low-level, assembly-like programming language. This language is structured around the
following concepts.
Values
WebAssembly provides only four basic number types. These are integers and IEEE 7546 numbers, each in
32 and 64 bit width. 32-bit integers also serve as Booleans and as memory addresses. The usual operations
on these types are available, including the full matrix of conversions between them. There is no distinction
between signed and unsigned integer types. Instead, integers are interpreted by respective operations as
either unsigned or signed in two’s complement representation.
In addition to these basic number types, there is a single 128 bit wide vector type representing different types
of packed data. The supported representations are four 32-bit, or two 64-bit IEEE 7547 numbers, or different
widths of packed integer values, specifically two 64-bit integers, four 32-bit integers, eight 16-bit integers,
or sixteen 8-bit integers.
Finally, values can consist of opaque references that represent pointers towards different sorts of entities.
Unlike with other types, their size or representation is not observable.
Instructions
The computational model of WebAssembly is based on a stack machine. Code consists of sequences of
instructions that are executed in order. Instructions manipulate values on an implicit operand stack 8 and
fall into two main categories. Simple instructions perform basic operations on data. They pop arguments
from the operand stack and push results back to it. Control instructions alter control flow. Control flow
is structured, meaning it is expressed with well-nested constructs such as blocks, loops, and conditionals.
Branches can only target such constructs.
Traps
Under some conditions, certain instructions may produce a trap, which immediately aborts execution. Traps
cannot be handled by WebAssembly code, but are reported to the outside environment, where they typically
can be caught.
Functions
Code is organized into separate functions. Each function takes a sequence of values as parameters and returns
a sequence of values as results. Functions can call each other, including recursively, resulting in an implicit
call stack that cannot be accessed directly. Functions may also declare mutable local variables that are usable
as virtual registers.
Tables
A table is an array of opaque values of a particular reference type. It allows programs to select such values
indirectly through a dynamic index operand. Thereby, for example, a program can call functions indirectly
through a dynamic index into a table. This allows emulating function pointers by way of table indices.
Linear Memory
A linear memory is a contiguous, mutable array of raw bytes. Such a memory is created with an initial size
but can be grown dynamically. A program can load and store values from/to a linear memory at any byte
address (including unaligned). Integer loads and stores can specify a storage size which is smaller than the
6 [Link]
7 [Link]
8 In practice, implementations need not maintain an actual operand stack. Instead, the stack can be viewed as a set of anonymous registers
that are implicitly referenced by instructions. The type system ensures that the stack height, and thus any referenced register, is always known
statically.
1.2. Overview 3
WebAssembly Specification, Release 3.0 (2025-11-14)
size of the respective value type. A trap occurs if an access is not within the bounds of the current memory
size.
Modules
A WebAssembly binary takes the form of a module that contains definitions for functions, tables, and linear
memories, as well as mutable or immutable global variables. Definitions can also be imported, specifying a
module/name pair and a suitable type. Each definition can optionally be exported under one or more names.
In addition to definitions, modules can define initialization data for their memories or tables that takes the
form of segments copied to given offsets. They can also define a start function that is automatically executed.
Embedder
A WebAssembly implementation will typically be embedded into a host environment. This environment
defines how loading of modules is initiated, how imports are provided (including host-side definitions), and
how exports can be accessed. However, the details of any particular embedding are beyond the scope of this
specification, and will instead be provided by complementary, environment-specific API definitions.
4 Chapter 1. Introduction
CHAPTER 2
Structure
2.1 Conventions
WebAssembly is a programming language that has multiple concrete representations (its binary format and the text
format). Both map to a common structure. For conciseness, this structure is described in the form of an abstract
syntax. All parts of this specification are defined in terms of this abstract syntax.
5
WebAssembly Specification, Release 3.0 (2025-11-14)
• 𝐴 ∈ 𝑠 denotes that 𝐴 is a member of the sequence 𝑠, that is, 𝑠 is of the form 𝑠1 𝐴 𝑠2 for some sequences 𝑠1 ,
𝑠2 .
Moreover, the following conventions are employed:
• The notation 𝑥𝑛 , where 𝑥 is a non-terminal symbol, is treated as a meta variable ranging over respective
sequences of 𝑥 (similarly for 𝑥* , 𝑥+ , 𝑥? ).
• When given a sequence 𝑥𝑛 , then the occurrences of 𝑥 in an iterated sequence (. . . 𝑥 . . .)𝑛 are assumed to
denote the individual elements of 𝑥𝑛 , respectively (similarly for 𝑥* , 𝑥+ , 𝑥? ). This implicitly expresses a
form of mapping syntactic constructions over a sequence.
• 𝑒𝑖<𝑛 denotes the same sequence as 𝑒𝑛 , but implicitly also defines 𝑖𝑛 to be the sequence of values 0 to (𝑛−1).
Note
For example, if 𝑥𝑛 is the sequence 𝑎 𝑏 𝑐, then (f(𝑥) + 1)𝑛 denotes the sequence (f(𝑎) + 1) (f(𝑏) + 1) (f(𝑐) + 1).
The form 𝑒𝑖<𝑛 additionally gives access to an index variable inside the iteration. For example, (f(𝑥) + 𝑖)𝑖<𝑛
denotes the sequence (f(𝑎) + 0) (f(𝑏) + 1) (f(𝑐) + 2).
Productions of the following form are interpreted as records that map a fixed set of fields field𝑖 to “values” 𝐴𝑖 ,
respectively:
{field1 𝐴*1 , field2 𝐴*2 , . . .} ⊕ {field1 𝐵1* , field2 𝐵2* , . . .} = {field1 (𝐴*1 ⊕ 𝐵1* ), field2 (𝐴*2 ⊕ 𝐵2* ), . . .}
• 𝑟* denotes the composition of a sequence of records, respectively; if the sequence is empty, then all fields
⨁︀
of the resulting record are empty.
The update notation for sequences and records generalizes recursively to nested components accessed by “paths”
pth ::= ([𝑖] | .field)+ :
• 𝑠[[𝑖]pth = 𝐴] is short for 𝑠[[𝑖] = 𝑠[𝑖][pth = 𝐴]],
• 𝑟[.field pth = 𝐴] is short for 𝑟[.field = 𝑟.field[pth = 𝐴]].
6 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
2.1.3 Lists
Lists are bounded sequences of the form 𝐴𝑛 (or 𝐴* ), where the 𝐴 can either be values or complex constructions.
A list can have at most 232 − 1 elements.
2.2 Values
WebAssembly programs operate on primitive numeric values. Moreover, in the definition of programs, immutable
sequences of values occur to represent more complex data, such as text strings or other vectors.
2.2.1 Bytes
The simplest form of value are raw uninterpreted bytes. In the abstract syntax they are represented as hexadecimal
literals.
Conventions
• The meta variable 𝑏 ranges over bytes.
• Bytes are sometimes interpreted as natural numbers 𝑛 < 256.
2.2.2 Integers
Different classes of integers with different value ranges are distinguished by their bit width 𝑁 and by whether they
are unsigned or signed.
u𝑁 ::= 0 | . . . | 2𝑁 − 1
s𝑁 ::= −2𝑁 −1 | . . . | −1 | 0 | +1 | . . . | +2𝑁 −1 − 1
i𝑁 ::= u𝑁
The class i defines uninterpreted integers, whose signedness interpretation can vary depending on context. In the
abstract syntax, they are represented as unsigned values. However, some operations convert them to signed based
on a two’s complement interpretation.
Note
The main integer types occurring in this specification are u8 , u32 , u64 , and u128 . However, other sizes occur
as auxiliary constructions, e.g., in the definition of floating-point numbers.
Conventions
• The meta variables 𝑚, 𝑛, 𝑖, 𝑗 range over integers.
• Numbers may be denoted by simple arithmetics, as in the grammar above. In order to distinguish arithmetics
like 2𝑁 from sequences like (1)𝑁 , the latter is distinguished with parentheses.
2.2.3 Floating-Point
Floating-point data represents 32 or 64 bit values that correspond to the respective binary formats of the IEEE
7549 standard (Section 3.3).
Every value has a sign and a magnitude. Magnitudes can either be expressed as normal numbers of the form
𝑚0 . 𝑚1 𝑚2 . . . 𝑚m · 2𝑒 , where 𝑒 is the exponent and 𝑚 is the significand whose most significant bit 𝑚0 is 1, or as
a subnormal number where the exponent is fixed to the smallest possible value and 𝑚0 is 0; among the subnormals
9 [Link]
2.2. Values 7
WebAssembly Specification, Release 3.0 (2025-11-14)
are positive and negative zero values. Since the significands are binary values, normals are represented in the form
(1 + 𝑚 · 2−𝑀 ) · 2𝑒 in the abstract syntax, where 𝑀 is the bit width of 𝑚; similarly for subnormals.
Possible magnitudes also include the special values ∞ (infinity) and nan (NaN, not a number). NaN values have a
payload that describes the mantissa bits in the underlying binary representation. No distinction is made between
signalling and quiet NaNs.
signif(32) = 23
signif(64) = 52
expon(32) = 8
expon(64) = 11
A canonical NaN is a floating-point value ±nan(canon𝑁 ) where canon𝑁 is a payload whose most significant bit
is 1 while all others are 0:
An arithmetic NaN is a floating-point value ±nan(𝑚) with 𝑚 ≥ canon𝑁 , such that the most significant bit is 1
while all others are arbitrary.
Note
In the abstract syntax, subnormals are distinguished by the leading 0 of the significand. The exponent of
subnormals has the same value as the smallest possible exponent of a normal number. Only in the binary
representation the exponent of a subnormal is encoded differently than the exponent of any normal number.
The notion of canonical NaN defined here is unrelated to the notion of canonical NaN that the IEEE 75410
standard (Section 3.5.2) defines for decimal interchange formats.
Conventions
• The meta variable 𝑧 ranges over floating-point values where clear from context.
• Where clear from context, shorthands like +1 denote floating point values like +(1 + 0 · 2−𝑀 ) · 20 .
2.2.4 Vectors
Numeric vectors are 128-bit values that are processed by vector instructions (also known as SIMD instructions,
single instruction multiple data). They are represented in the abstract syntax using u128 . The interpretation of
lane types (integer or floating-point numbers) and lane sizes are determined by the specific instruction operating
on them.
2.2.5 Names
Names are sequences of characters, which are scalar values as defined by Unicode11 (Section 2.4).
Due to the limitations of the binary format, the length of a name is bounded by the length of its UTF-8 encoding.
10 [Link]
11 [Link]
8 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
Convention
• Characters (Unicode scalar values) are sometimes used interchangeably with natural numbers 𝑛 < 1114112.
2.3 Types
Various entities in WebAssembly are classified by types. Types are checked during validation, instantiation, and
possibly execution.
The types i32 and i64 classify 32 and 64 bit integers, respectively. Integers are not inherently signed or unsigned,
their interpretation is determined by individual operations.
The types f 32 and f 64 classify 32 and 64 bit floating-point data, respectively. They correspond to the respective
binary floating-point representations, also known as single and double precision, as defined by the IEEE 75412
standard (Section 3.3).
Number types are transparent, meaning that their bit patterns can be observed. Values of number type can be stored
in memories.
Conventions
• The notation |𝑡| denotes the bit width of a number type 𝑡. That is, |i32| = |f 32| = 32 and |i64| = |f 64| = 64.
The type v128 corresponds to a 128 bit vector of packed integer or floating-point data. The packed data can be
interpreted as signed or unsigned integers, single or double precision floating-point values, or a single 128 bit type.
The interpretation is determined by individual operations.
Vector types, like number types are transparent, meaning that their bit patterns can be observed. Values of vector
type can be stored in memories.
Conventions
• The notation |𝑡| for bit width extends to vector types as well, that is, |v128| = 128.
The syntax of type uses is extended with additional forms for the purpose of specifying validation and execution.
12 [Link]
2.3. Types 9
WebAssembly Specification, Release 3.0 (2025-11-14)
A heap type is either abstract or concrete. A concrete heap type consists of a type use that classifies an object of
the respective type defined in a module. Abstract types are denoted by individual keywords.
The type func denotes the common supertype of all function types, regardless of their concrete definition. Dually,
the type nofunc denotes the common subtype of all function types, regardless of their concrete definition. This
type has no values.
The type exn denotes the common supertype of all exception references. This type has no concrete subtypes.
Dually, the type noexn denotes the common subtype of all forms of exception references. This type has no values.
The type extern denotes the common supertype of all external references received through the embedder. This
type has no concrete subtypes. Dually, the type noextern denotes the common subtype of all forms of external
references. This type has no values.
The type any denotes the common supertype of all aggregate types, as well as possibly abstract values produced by
internalizing an external reference of type extern. Dually, the type none denotes the common subtype of all forms
of aggregate types. This type has no values.
The type eq is a subtype of any that includes all types for which references can be compared, i.e., aggregate values
and i31.
The types struct and array denote the common supertypes of all structure and array aggregates, respectively.
The type i31 denotes unboxed scalars, that is, integers injected into references. Their observable value range is
limited to 31 bits.
Note
Values of type i31 are not actually allocated in the store, but represented in a way that allows them to be mixed
with actual references into the store without ambiguity. Engines need to perform some form of pointer tagging
to achieve this, which is why one bit is reserved. Since this type is to be reliably unboxed on all hardware
platforms supported by WebAssembly, it cannot be wider than 32 bits minus the tag bit.
Although the types none, nofunc, noexn, and noextern are not inhabited by any values, they can be used to
form the types of all null references in their respective hierarchy. For example, (ref null nofunc) is the generic
type of a null reference compatible with all function reference types.
The syntax of abstract heap types is extended with additional forms for the purpose of specifying validation and
execution.
10 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
Conventions
• The reference type anyref is an abbreviation for (ref null any).
• The reference type eqref is an abbreviation for (ref null eq).
• The reference type i31ref is an abbreviation for (ref null i31).
• The reference type structref is an abbreviation for (ref null struct).
• The reference type arrayref is an abbreviation for (ref null array).
• The reference type funcref is an abbreviation for (ref null func).
• The reference type exnref is an abbreviation for (ref null exn).
• The reference type externref is an abbreviation for (ref null extern).
• The reference type nullref is an abbreviation for (ref null none).
• The reference type nullfuncref is an abbreviation for (ref null nofunc).
• The reference type nullexnref is an abbreviation for (ref null noexn).
• The reference type nullexternref is an abbreviation for (ref null noextern).
The syntax of value types is extended with additional forms for the purpose of specifying validation.
Conventions
• The meta variable 𝑡 ranges over value types or subclasses thereof where clear from context.
2.3. Types 11
WebAssembly Specification, Release 3.0 (2025-11-14)
Conventions
• The notation |𝑡| for the bit width of a value type 𝑡 extends to packed types as well, that is, |i8| = 8 and
|i16| = 16.
• The auxiliary function unpack maps a storage type to the value type obtained when accessing a field:
unpack(valtype) = valtype
unpack(packtype) = i32
Conventions
The minimum of two address types is defined as the address type whose bit width is the minimum of the two.
min(at 1 , at 2 ) = at 1 if |at 1 | ≤ |at 2 |
min(at 1 , at 2 ) = at 2 otherwise
12 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
2.3.12 Limits
Limits classify the size range of resizeable storage associated with memory types and table types.
If no maximum is present, then the respective storage can grow to any valid size.
The limits constrain the minimum and optionally the maximum size of a memory. The limits are given in units of
page size.
Like memories, tables are constrained by limits for their minimum and optionally maximum size. The limits are
given in numbers of entries.
datatype ::= ok
2.3. Types 13
WebAssembly Specification, Release 3.0 (2025-11-14)
externtype ::= tag tagtype | global globaltype | mem memtype | table tabletype | func typeuse
For functions, the type use has to refer to the definition of a function type.
Note
Future versions of WebAssembly may have additional uses for tags, and may allow non-empty result types in
the function types of tags.
Conventions
The following auxiliary notation is defined for sequences of external types. It filters out entries of a specific kind
in an order-preserving fashion:
funcs(𝜖) = 𝜖
funcs((func dt) xt * ) = dt funcs(xt * )
funcs(externtype xt * ) = funcs(xt * ) otherwise
tables(𝜖) = 𝜖
tables((table tt) xt * ) = tt tables(xt * )
tables(externtype xt * ) = tables(xt * ) otherwise
mems(𝜖) = 𝜖
mems((mem mt) xt * ) = mt mems(xt * )
mems(externtype xt * ) = mems(xt * ) otherwise
globals(𝜖) = 𝜖
globals((global gt) xt * ) = gt globals(xt * )
globals(externtype xt * ) = globals(xt * ) otherwise
tags(𝜖) = 𝜖
tags((tag jt) xt * ) = jt tags(xt * )
tags(externtype xt * ) = tags(xt * ) otherwise
2.4 Instructions
WebAssembly code consists of sequences of instructions. Its computational model is based on a stack machine
in that instructions manipulate values on an implicit operand stack, consuming (popping) argument values and
producing or returning (pushing) result values.
In addition to dynamic operands from the stack, some instructions also have static immediate arguments, typically
indices or type annotations, which are part of the instruction itself.
Some instructions are structured in that they contain nested sequences of instructions.
The following sections group instructions into a number of different categories.
The syntax of instruction is further extended with additional forms for the purpose of specifying execution.
14 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
In future versions of WebAssembly, the type annotation on select may allow for more than a single value being
selected at the same time.
The block, loop, if and try_table instructions are structured instructions. They bracket nested sequences of in-
structions, called blocks. As the grammar prescribes, they must be well-nested.
A structured instruction can consume input and produce output on the operand stack according to its annotated
block type.
Each structured control instruction introduces an implicit label. Labels are targets for branch instructions that
reference them with label indices. Unlike with other index spaces, indexing of labels is relative by nesting depth,
that is, label 0 refers to the innermost structured control instruction enclosing the referring branch instruction, while
increasing indices refer to those farther out. Consequently, labels can only be referenced from within the associated
structured control instruction. This also implies that branches can only be directed outwards, “breaking” from the
block of the control construct they target. The exact effect depends on that control construct. In case of block or
if it is a forward jump, resuming execution after the end of the block. In case of loop it is a backward jump to the
beginning of the loop.
2.4. Instructions 15
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
This enforces structured control flow. Intuitively, a branch targeting a block or if behaves like a break statement
in most C-like languages, while a branch targeting a loop behaves like a continue statement.
Branch instructions come in several flavors: br performs an unconditional branch, br_if performs a conditional
branch, and br_table performs an indirect branch through an operand indexing into the label list that is an immediate
to the instruction, or to a default target if the operand is out of bounds. The br_on_null and br_on_non_null
instructions check whether a reference operand is null and branch if that is the case or not the case, respectively.
Similarly, br_on_cast and br_on_cast_fail attempt a downcast on a reference operand and branch if that succeeds,
or fails, respectively.
The return instruction is a shortcut for an unconditional branch to the outermost block, which implicitly is the body
of the current function. Taking a branch unwinds the operand stack up to the height where the targeted structured
control instruction was entered. However, branches may additionally consume operands themselves, which they
push back on the operand stack after unwinding. Forward branches require operands according to the output of
the targeted block’s type, i.e., represent the values produced by the terminated block. Backward branches require
operands according to the input of the targeted block’s type, i.e., represent the values consumed by the restarted
block.
The call instruction invokes another function, consuming the necessary arguments from the stack and returning
the result values of the call. The call_ref instruction invokes a function indirectly through a function reference
operand. The call_indirect instruction calls a function indirectly through an operand indexing into a table that is
denoted by a table index and must contain function references. Since it may contain functions of heterogeneous
type, the callee is dynamically checked against the function type indexed by the instruction’s second immediate,
and the call is aborted with a trap if it does not match.
The return_call, return_call_ref, and return_call_indirect instructions are tail-call variants of the previous ones.
That is, they first return from the current function before actually performing the respective call. It is guaranteed
that no sequence of nested calls using only these instructions can cause resource exhaustion due to hitting an
implementation’s limit on the number of active calls.
The instructions throw, throw_ref, and try_table are concerned with exceptions. The throw and throw_ref instruc-
tions raise and reraise an exception, respectively, and transfers control to the innermost enclosing exception handler
that has a matching catch clause. The try_table instruction installs an exception handler that handles exceptions
as specified by its catch clauses.
These instructions get or set the values of respective variables. The [Link] instruction is like [Link] but also
returns its argument.
16 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
The [Link] and [Link] instructions load or store an element in a table, respectively.
The [Link] instruction returns the current size of a table. The [Link] instruction grows table by a given
delta and returns the previous size, or −1 if enough space cannot be allocated. It also takes an initialization value
for the newly allocated entries.
The [Link] instruction sets all entries in a range to a given value. The [Link] instruction copies elements
from a source table region to a possibly overlapping destination region; the first index denotes the destination. The
[Link] instruction copies elements from a passive element segment into a table.
The [Link] instruction prevents further use of a passive element segment. This instruction is intended to be
used as an optimization hint. After an element segment is dropped its elements can no longer be retrieved, so the
memory used by this segment may be freed.
Note
Memory is accessed with load and store instructions for the different number types and vector types. They all
take a memory index and a memory argument memarg that contains an address offset and the expected alignment
(expressed as the exponent of a power of 2).
2.4. Instructions 17
WebAssembly Specification, Release 3.0 (2025-11-14)
Integer loads and stores can optionally specify a storage size sz that is smaller than the bit width of the respective
value type. In the case of loads, a sign extension mode sx is then required to select appropriate behavior.
Vector loads can specify a shape that is half the bit width of v128. Each lane is half its usual size, and the sign
extension mode sx then specifies how the smaller lane is extended to the larger lane. Alternatively, vector loads
can perform a splat, such that only a single lane of the specified storage size is loaded, and the result is duplicated
to all lanes.
The static address offset is added to the dynamic address operand, yielding a 33-bit or 65-bit effective address that
is the zero-based index at which the memory is accessed. All values are read and written in little endian13 byte
order. A trap results if any of the accessed memory bytes lies outside the address range implied by the memory’s
current size.
The [Link] instruction returns the current size of a memory. The [Link] instruction grows a memory
by a given delta and returns the previous size, or −1 if enough memory cannot be allocated. Both instructions
operate in units of page size.
The [Link] instruction sets all values in a region of memory to a given byte. The [Link] instruction
copies data from a source memory region to a possibly overlapping destination region in another or the same
memory; the first index denotes the destination. The [Link] instruction copies data from a passive data
segment into a memory.
The [Link] instruction prevents further use of a passive data segment. This instruction is intended to be used
as an optimization hint. After a data segment is dropped its data can no longer be retrieved, so the memory used
by this segment may be freed.
The [Link] and [Link] instructions produce a null reference or a reference to a given function, respectively.
The instruction ref.is_null checks for null, while ref.as_non_null converts a nullable to a non-null one, and traps
if it encounters null.
The [Link] compares two references.
The instructions [Link] and [Link] test the dynamic type of a reference operand. The former merely returns the
result of the test, while the latter performs a downcast and traps if the operand’s type does not match.
Note
The br_on_null and br_on_non_null instructions provide versions of ref.as_null that branch depending on the
success of failure of a null test instead of trapping. Similarly, the br_on_cast and br_on_cast_fail instructions
provides versions of [Link] that branch depending on the success of the downcast instead of trapping.
An additional instruction operating on function references is the control instruction call_ref.
13 [Link]
18 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
The instructions [Link] and struct.new_default allocate a new structure, initializing them either with operands
or with default values. The remaining instructions on structs access individual fields, allowing for different sign
extension modes in the case of packed storage types.
Similarly, arrays can be allocated either with an explicit initialization operand or a default value. Furthermore,
array.new_fixed allocates an array with statically fixed size, and array.new_data and array.new_elem allocate an
array and initialize it from a data or element segment, respectively. The instructions [Link], [Link] sx , and
[Link] access individual slots, again allowing for different sign extension modes in the case of a packed storage
type; [Link] produces the length of an array; [Link] fills a specified slice of an array with a given value and
[Link], array.init_data, and array.init_elem copy elements to a specified slice of an array from a given array,
data segment, or element segment, respectively.
The instructions ref.i31 and [Link] sx convert between type i32 and an unboxed scalar.
The instructions any.convert_extern and extern.convert_any allow lossless conversion between references repre-
sented as type (ref null extern) and as (ref null any).
2.4. Instructions 19
WebAssembly Specification, Release 3.0 (2025-11-14)
sz ::= 8 | 16 | 32 | 64
sx ::= u | s
num i𝑁 ::= i𝑁
num f𝑁 ::= f𝑁
instr ::= ...
| [Link] num numtype
| [Link] numtype
| [Link] numtype
| [Link] numtype
| [Link] numtype
| numtype 1 .cvtop numtype 2 ,numtype 1 _numtype 2
unop i𝑁 ::= clz | ctz | popcnt | extendsz _s if sz < 𝑁
unop f𝑁 ::= abs | neg | sqrt | ceil | floor | trunc | nearest
binop i𝑁 ::= add | sub | mul | div_sx | rem_sx
| and | or | xor | shl | shr_sx | rotl | rotr
binop f𝑁 ::= add | sub | mul | div | min | max | copysign
testop i𝑁 ::= eqz
relop i𝑁 ::= eq | ne | lt_sx | gt_sx | le_sx | ge_sx
relop f𝑁 ::= eq | ne | lt | gt | le | ge
cvtop i𝑁 1 ,i𝑁 2 ::= extend_sx if 𝑁1 < 𝑁2
| wrap if 𝑁1 > 𝑁2
cvtop i𝑁 1 ,f𝑁 2 ::= convert_sx
| reinterpret if 𝑁1 = 𝑁2
cvtop f𝑁 1 ,i𝑁 2 ::= trunc_sx
| trunc_sat_sx
| reinterpret if 𝑁1 = 𝑁2
cvtop f𝑁 1 ,f𝑁 2 ::= promote if 𝑁1 < 𝑁2
| demote if 𝑁1 > 𝑁2
Numeric instructions are divided by number type. For each type, several subcategories can be distinguished:
• Constants: return a static constant.
• Unary operations: consume one operand and produce one result of the respective type.
• Binary operations: consume two operands and produce one result of the respective type.
• Tests: consume one operand of the respective type and produce a Boolean integer result.
• Comparisons: consume two operands of the respective type and produce a Boolean integer result.
• Conversions: consume a value of one type and produce a result of another (the source type of the conversion
is the one after the “_”).
Some integer instructions come in two flavors, where a signedness annotation sx distinguishes whether the operands
are to be interpreted as unsigned or signed integers. For the other integer instructions, the use of two’s complement
for the signed interpretation means that they behave the same regardless of signedness.
20 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
Vector instructions have a naming convention involving a shape prefix that determines how their operands will be
interpreted, written 𝑡x𝑁 , and consisting of a lane type 𝑡, a possibly packed numeric type, and its dimension 𝑁 ,
which denotes the number of lanes of that type. Operations are performed point-wise on the values of each lane.
Instructions prefixed with v128 do not involve a specific interpretation, and treat the v128 as either an i128 value or
a vector of 128 individual bits.
Note
For example, the shape i32x4 interprets the operand as four i32 values, packed into an i128 . The bit width of the
lane type 𝑡 times 𝑁 always is 128.
2.4. Instructions 21
WebAssembly Specification, Release 3.0 (2025-11-14)
22 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
• Splats: consume a value of numeric type and produce a v128 result of a specified shape.
• Extract lanes: consume a v128 operand and return the numeric value in a given lane.
• Replace lanes: consume a v128 operand and a numeric value for a given lane, and produce a v128 result.
Some vector instructions have a signedness annotation sx which distinguishes whether the elements in the operands
are to be interpreted as unsigned or signed integers. For the other vector instructions, the use of two’s complement
for the signed interpretation means that they behave the same regardless of signedness.
Conventions
• The function lanetype(shape) extracts the lane type of a shape.
• The function dim(shape) extracts the dimension of a shape.
• The function zeroop(vcvtop) extracts the zero flag from a vector conversion operator, or returns 𝜖 if it does
not contain any.
• The function halfop(vcvtop) extracts the half flag from a vector conversion operator, or returns 𝜖 if it does
not contain any.
2.4.10 Expressions
Function bodies, initialization values for globals, elements and offsets of element segments, and offsets of data
segments are given as expressions, which are sequences of instructions.
In some places, validation restricts expressions to be constant, which limits the set of allowable instructions.
2.5 Modules
WebAssembly programs are organized into modules, which are the unit of deployment, loading, and compilation.
A module collects definitions for types, tags, and globals, memories, tables, functions. In addition, it can declare
imports and exports and provide initialization in the form of data and element segments, or a start function.
module ::= module type * import * tag * global * mem * table * func * data * elem * start ? export *
Each of the lists — and thus the entire module — may be empty.
2.5.1 Indices
Definitions are referenced with zero-based indices. Each class of definition has its own index space, as distinguished
by the following classes.
The index space for tags, globals, memories, tables, and functions includes respective imports declared in the same
module. The indices of these imports precede the indices of other definitions in the same index space.
Data indices reference data segments and element indices reference element segments.
2.5. Modules 23
WebAssembly Specification, Release 3.0 (2025-11-14)
The index space for locals is only accessible inside a function and includes the parameters of that function, which
precede the local variables.
Label indices reference structured control instructions inside an instruction sequence.
Each aggregate type provides an index space for its fields.
Conventions
• The meta variable 𝑙 ranges over label indices.
• The meta variables 𝑥, 𝑦 range over indices in any of the other index spaces.
• For every index space abcidx , the notation abcidx(𝐴) denotes the set of indices from that index space
occurring free in 𝐴. Sometimes this set is reinterpreted as the list of its elements.
Note
For example, if instr * is ([Link] 1) ([Link] 2 3), then dataidxinstrs (instr * ) = 1 3, or equivalently,
the set {1, 3}.
2.5.2 Types
The type section of a module defines a list of recursive types, each consisting of a list of sub types referenced by
individual type indices. All function, structure, or array types used in a module must be defined in this section.
2.5.3 Tags
The tag section of a module defines a list of tags:
The type index of a tag must refer to a function type that declares its tag type.
Tags are referenced through tag indices, starting with the smallest index not referencing a tag import.
2.5.4 Globals
The global section of a module defines a list of global variables (or globals for short):
Each global stores a single value of the type specified in the global type. It also specifies whether a global is
immutable or mutable. Moreover, each global is initialized with a value given by a constant initializer expression.
Globals are referenced through global indices, starting with the smallest index not referencing a global import.
2.5.5 Memories
The mem section of a module defines a list of linear memories (or memories for short) as described by their
memory type:
A memory is a list of raw uninterpreted bytes. The minimum size in the limits of its memory type specifies the
initial size of that memory, while its maximum, if present, restricts the size to which it can grow later. Both are in
units of page size.
24 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
2.5.6 Tables
The table section of a module defines a list of tables described by their table type:
A table is an array of opaque values of a particular reference type that is specified by the table type. Each table
slot is initialized with a value given by a constant initializer expression. Tables can further be initialized through
element segments.
The minimum size in the limits of the table type specifies the initial size of that table, while its maximum restricts
the size to which it can grow later.
Tables are referenced through table indices, starting with the smallest index not referencing a table import. Most
constructs implicitly reference table index 0.
2.5.7 Functions
The func section of a module defines a list of functions with the following structure:
The type index of a function declares its signature by reference to a function type defined in the module. The
parameters of the function are referenced through 0-based local indices in the function’s body; they are mutable.
The locals declare a list of mutable local variables and their types. These variables are referenced through local
indices in the function’s body. The index of the first local is the smallest index not referencing a parameter.
A function’s expression is an instruction sequence that represents the body of the function. Upon termination it
must produce a stack matching the function type’s result type.
Functions are referenced through function indices, starting with the smallest index not referencing a function im-
port.
Similar to element segments, data segments have a mode that identifies them as either active or passive. A passive
data segment’s contents can be copied into a memory using the [Link] instruction. An active data segment
copies its contents into a memory during instantiation, as specified by a memory index and a constant expression
defining an offset into that memory.
Data segments are referenced through data indices.
Each element segment defines a reference type and a corresponding list of constant element expressions.
2.5. Modules 25
WebAssembly Specification, Release 3.0 (2025-11-14)
Element segments have a mode that identifies them as either active, passive, or declarative. A passive element
segment’s elements can be copied to a table using the [Link] instruction. An active element segment copies
its elements into a table during instantiation, as specified by a table index and a constant expression defining an
offset into that table. A declarative element segment is not available at runtime but merely serves to forward-
declare references that are formed in code with instructions like [Link]. The offset is given by another constant
expression.
Element segments are referenced through element indices.
Note
The start function is intended for initializing the state of a module. The module and its exports are not accessible
externally before this initialization has completed.
2.5.11 Imports
The import section of a module defines a set of imports that are required for instantiation.
Each import is labeled by a two-level name space, consisting of a module name and an item name for an entity
within that module. Importable definitions are tags, globals, memories, tables, and functions. Each import is
specified by a respective external type that a definition provided during instantiation is required to match.
Every import defines an index in the respective index space. In each index space, the indices of imports go before
the first index of any definition contained in the module itself.
Note
Unlike export names, import names are not necessarily unique. It is possible to import the same module/item
name pair multiple times; such imports may even have different type descriptions, including different kinds of
entities. A module with such imports can still be instantiated depending on the specifics of how an embedder
allows resolving and supplying imports. However, embedders are not required to support such overloading, and
a WebAssembly module itself cannot implement an overloaded name.
2.5.12 Exports
The export section of a module defines a set of exports that become accessible to the host environment once the
module has been instantiated.
export ::= export name externidx
externidx ::= func funcidx | global globalidx | table tableidx | memory memidx | tag tagidx
Each export is labeled by a unique name. Exportable definitions are tags, globals, memories, tables, and functions,
which are referenced through a respective index.
26 Chapter 2. Structure
WebAssembly Specification, Release 3.0 (2025-11-14)
Conventions
The following auxiliary notation is defined for sequences of exports, filtering out indices of a specific kind in an
order-preserving fashion:
funcs(𝜖) = 𝜖
funcs((func 𝑥) xx * ) = 𝑥 funcs(xx * )
funcs(externidx xx * ) = funcs(xx * ) otherwise
tables(𝜖) = 𝜖
tables((table 𝑥) xx * ) = 𝑥 tables(xx * )
tables(externidx xx * ) = tables(xx * ) otherwise
mems(𝜖) = 𝜖
mems((memory 𝑥) xx * ) = 𝑥 mems(xx * )
mems(externidx xx * ) = mems(xx * ) otherwise
globals(𝜖) = 𝜖
globals((global 𝑥) xx * ) = 𝑥 globals(xx * )
globals(externidx xx * ) = globals(xx * ) otherwise
tags(𝜖) = 𝜖
tags((tag 𝑥) xx * ) = 𝑥 tags(xx * )
tags(externidx xx * ) = tags(xx * ) otherwise
2.5. Modules 27
WebAssembly Specification, Release 3.0 (2025-11-14)
28 Chapter 2. Structure
CHAPTER 3
Validation
3.1 Conventions
Validation checks that a WebAssembly module is well-formed. Only valid modules can be instantiated.
Validity is defined by a type system over the abstract syntax of a module and its contents. For each piece of abstract
syntax, there is a typing rule that specifies the constraints that apply to it. All rules are given in two equivalent
forms:
1. In prose, describing the meaning in intuitive form.
2. In formal notation, describing the rule in mathematical form.14
Note
The prose and formal rules are equivalent, so that understanding of the formal notation is not required to read this
specification. The formalism offers a more concise description in notation that is used widely in programming
languages semantics and is readily amenable to mathematical proof.
In both cases, the rules are formulated in a declarative manner. That is, they only formulate the constraints, they do
not define an algorithm. The skeleton of a sound and complete algorithm for type-checking instruction sequences
according to this specification is provided in the appendix.
3.1.1 Types
To define the semantics, the definition of some sorts of types is extended to include additional forms. By virtue
of not being representable in either the binary format or the text format, these forms cannot be used in a program;
they only occur during validation or execution.
valtype ::= . . . | bot
absheaptype ::= . . . | bot
typeuse ::= . . . | deftype | rec.𝑛
The unique value type bot is a bottom type that matches all value types. Similarly, bot is also used as a bottom
type of all heap types.
14 The semantics is derived from the following article: Andreas Haas, Andreas Rossberg, Derek Schuff, Ben Titzer, Dan Gohman, Luke
Wagner, Alon Zakai, JF Bastien, Michael Holman. Bringing the Web up to Speed with WebAssembly15 . Proceedings of the 38th ACM
SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017). ACM 2017.
15 [Link]
29
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
No validation rule uses bottom types explicitly, but various rules can pick any value or heap type, including
bottom. This ensures the existence of principal types, and thus a validation algorithm without back tracking.
A type use can consist directly of a defined type. This occurs as the result of substituting a type index with its
definition.
A type use may also be a recursive type index. Such an index refers to the 𝑖-th component of a surrounding recursive
type. It occurs as the result of rolling up the definition of a recursive type.
Both extensions affect occurrences of type uses in concrete heap types, in sub types and in instructions.
A type of any form is closed when it does not contain a heap type that is a type index or a recursive type index
without a surrounding recursive type, i.e., all type indices have been substituted with their defined type and all free
recursive type indices have been unrolled.
Note
It is an invariant of the semantics that sub types occur only in one of two forms: either as “syntactic” types
as in a source module, where all supertypes are type indices, or as “semantic” types, where all supertypes are
resolved to either defined types or recursive type indices.
Recursive type indices are local to a recursive type. They are distinguished from regular type indices and rep-
resented such that two closed types are syntactically equal if and only if they have the same recursive structure.
Convention
• The difference rt 1 ∖ rt 2 between two reference types is defined as follows:
Note
This definition computes an approximation of the reference type that is inhabited by all values from rt 1 except
those from rt 2 . Since the type system does not have general union types, the definition only affects the presence
of null and cannot express the absence of other values.
Defined types do not occur in the binary or text format, but are formed by rolling up the recursive types defined in
a module.
Note
It is an invariant of the semantics that all recursive types occurring in defined types are rolled up.
30 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
Conventions
• 𝑡[𝑥* := dt * ] denotes the parallel substitution of type indices 𝑥* with corresponding defined types dt * in type
𝑡, provided |𝑥* | = |dt * |.
• 𝑡[(rec 𝑖)* := dt * ] denotes the parallel substitution of recursive type indices (rec 𝑖)* with defined types dt *
in type 𝑡, provided |(rec 𝑖)* | = |dt * |. This substitution does not proceed under recursive types, since they
are considered local binders for all recursive type indices.
• 𝑡[:= dt * ] is shorthand for the substitution 𝑡[𝑥* := dt * ], where 𝑥* = 0 . . . (|dt * | − 1).
Note
All recursive types formed by the semantics are closed with respect to recursive type indices that occur inside
them. Hence, substitution of recursive type indices never needs to modify the bodies of recursive types. In
addition, all types used for substitution are closed with respect to recursive type indices, such that name capture
of recursive type indices cannot occur.
Note
This representation is independent of the type index space, so that it is meaningful across module boundaries.
Moreover, this representation ensures that types with equivalent recursive structure are also syntactically equal,
hence allowing a simple equality check on (closed) types. It gives rise to an iso-recursive interpretation of
types.
The representation change is performed by two auxiliary operations on the syntax of recursive types:
• Rolling up a recursive type substitutes its internal type indices with corresponding recursive type indices.
• Unrolling a recursive type substitutes its recursive type indices with the corresponding defined types.
These operations are extended to defined types and defined as follows:
roll𝑥 (rectype) = rec (subtype[(𝑥 + 𝑖)𝑖<𝑛 := (rec.𝑖)𝑖<𝑛 ])𝑛 if rectype = rec subtype 𝑛
unroll(rectype) = rec (subtype[(rec.𝑖)𝑖<𝑛 := (rectype.𝑖)𝑖<𝑛 ])𝑛 if rectype = rec subtype 𝑛
roll*𝑥 (rectype) = ((rec subtype 𝑛 ).𝑖)𝑖<𝑛 if roll𝑥 (rectype) = rec subtype 𝑛
*
unroll(rectype.𝑖) = subtype [𝑖] if unroll(rectype) = rec subtype *
In addition, the following auxiliary relation denotes the expansion of a defined type or type use:
deftype ≈ comptype if expand(deftype) = comptype
deftype ≈𝐶 comptype if deftype ≈ comptype
typeidx ≈𝐶 comptype if 𝐶.types[typeidx ] ≈ comptype
An instruction type 𝑡*1 →𝑥* 𝑡*2 describes the required input stack with argument values of types 𝑡*1 that an instruction
pops off and the provided output stack with result values of types 𝑡*2 that it pushes back. Moreover, it enumerates
the indices 𝑥* of locals that have been set by the instruction or sequence.
3.1. Conventions 31
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
Instruction types are only used for validation, they do not occur in programs.
Note
Local types are only used for validation, they do not occur in programs.
3.1.6 Contexts
Validity of an individual definition is specified relative to a context, which collects relevant information about the
surrounding module and the definitions in scope:
• Types: the list of types defined in the current module.
• Recursive Types: the list of sub types in the current group of recursive types.
• Functions: the list of functions declared in the current module, represented by a defined type that expands
to their function type.
• Tables: the list of tables declared in the current module, represented by their table type.
• Memories: the list of memories declared in the current module, represented by their memory type.
• Globals: the list of globals declared in the current module, represented by their global type.
• Tags: the list of tags declared in the current module, represented by their tag type.
• Element Segments: the list of element segments declared in the current module, represented by the elements’
reference type.
• Data Segments: the list of data segments declared in the current module, each represented by an ok entry.
• Locals: the list of locals declared in the current function (including parameters), represented by their local
type.
• Labels: the stack of labels accessible from the current position, represented by their result type.
• Return: the return type of the current function, represented as an optional result type that is absent when no
return is allowed, as in free-standing expressions.
• References: the list of function indices that occur in the module outside functions and can hence be used to
form references inside them.
In other words, a context contains a sequence of suitable types for each index space, describing each defined entry
in that space. Locals, labels and return type are only used for validating instructions in function bodies, and are
left empty elsewhere. The label stack is the only part of the context that changes as validation of an instruction
sequence proceeds.
32 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
Convention
A type of any shape can be closed to bring it into closed form relative to a context it is valid in, by substituting
each type index 𝑥 occurring in it with its own corresponding defined type 𝐶.types[𝑥], after first closing the types
in 𝐶.types themselves.
Note
Free type indices referring to types within the same recursive type are handled separately by rolling up recursive
types before closing them.
Note
For example, if 𝐴 is a function, then 𝑇 is a defined function type; for an 𝐴 that is a global, 𝑇 is a global
type; and so on.
3.1. Conventions 33
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
This section gives a brief explanation of the notation for specifying typing rules formally. For the interested
reader, a more thorough introduction can be found in respective text books.16
The proposition that a phrase 𝐴 has a respective type 𝑇 is written 𝐴 : 𝑇 . In general, however, typing is dependent
on a context 𝐶. To express this explicitly, the complete form is a judgement 𝐶 ⊢ 𝐴 : 𝑇 , which says that 𝐴 : 𝑇
holds under the assumptions encoded in 𝐶.
The formal typing rules use a standard approach for specifying type systems, rendering them into deduction rules.
Every rule has the following general form:
premise 1 premise 2 ... premise 𝑛
conclusion
Such a rule is read as a big implication: if all premises hold, then the conclusion holds. Some rules have
no premises; they are axioms whose conclusion holds unconditionally. The conclusion always is a judgment
𝐶 ⊢ 𝐴 : 𝑇 , and there usually is one respective rule for each relevant construct 𝐴 of the abstract syntax.
Note
For example, the typing rule for the [Link] instruction can be given as an axiom:
𝐶.globals[𝑥] = mut 𝑡
𝐶 ⊢ [Link] 𝑥 : 𝜖 → 𝑡
Here, the premise enforces that the immediate global index 𝑥 exists in the context. The instruction produces a
value of its respective type 𝑡 (and does not consume any values). If 𝐶.globals[𝑥] does not exist then the premise
does not hold, and the instruction is ill-typed.
Finally, a structured instruction requires a recursive rule, where the premise is itself a typing judgement:
A block instruction is only valid when the instruction sequence in its body is. Moreover, the result type must
match the block’s annotation blocktype. If so, then the block instruction has the same type as the body. Inside
the body an additional label of the corresponding result type is available, which is expressed by extending the
context 𝐶 with the additional label information for the premise.
3.2 Types
Simple types, such as number types are universally valid. However, restrictions apply to most other types, such
as reference types, function types, as well as the limits of table types and memory types, which must be checked
during validation.
Moreover, block types are converted to instruction types for ease of processing.
16 For example: Benjamin Pierce. Types and Programming Languages17 . The MIT Press 2002
17 [Link]
34 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶 ⊢ numtype : ok
𝐶 ⊢ vectype : ok
𝐶 ⊢ absheaptype : ok
3.2. Types 35
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶 ⊢ bot : ok
36 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶 ⊢ packtype : ok
rec subtype *
The recursive type (rec subtype * ) is valid for the type index 𝑥 if:
• Either:
– The sub type sequence subtype * is empty.
• Or:
*
– The sub type sequence subtype * is of the form subtype 1 subtype ′ .
– The sub type subtype 1 is valid for the type index 𝑥.
*
– The recursive type (rec subtype ′ ) is valid for the type index 𝑥 + 1.
𝐶 ⊢ subtype 1 : ok(𝑥) 𝐶 ⊢ rec subtype * : ok(𝑥 + 1)
𝐶 ⊢ rec 𝜖 : ok(𝑥) 𝐶 ⊢ rec (subtype 1 subtype * ) : ok(𝑥)
3.2. Types 37
WebAssembly Specification, Release 3.0 (2025-11-14)
*
|𝑥* | ≤ 1 (𝑥 < 𝑥0 )* (unroll(𝐶.types[𝑥]) = sub 𝑥′ comptype ′ )*
𝐶 ⊢ comptype : ok (𝐶 ⊢ comptype ≤ comptype ′ )*
𝐶 ⊢ sub final? 𝑥* comptype : ok(𝑥0 )
Note
The side condition on the index ensures that a declared supertype is a previously defined types, preventing
cyclic subtype hierarchies.
Future versions of WebAssembly may allow more than one supertype.
3.2.12 Limits
Limits must have meaningful bounds that are within a given range.
The limits range [𝑛 .. 𝑚? ] is valid within 𝑘 if:
• 𝑛 is less than or equal to 𝑘.
• If 𝑚 is defined, then:
– 𝑛 is less than or equal to 𝑚.
– 𝑚 is less than or equal to 𝑘.
𝑛≤𝑘 (𝑛 ≤ 𝑚 ≤ 𝑘)?
𝐶 ⊢ [𝑛 .. 𝑚? ] : 𝑘
38 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
3.3 Matching
On most types, a notion of subtyping is defined that is applicable in validation rules, during module instantiation
when checking the types of imports, or during execution, when performing casts.
𝐶 ⊢ numtype ≤ numtype
3.3. Matching 39
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶 ⊢ vectype ≤ vectype
40 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
3.3. Matching 41
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶 ⊢ bot ≤ heaptype
42 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶 ⊢ bot ≤ valtype
Note
Instruction types are contravariant in their input and covariant in their output. Moreover, the supertype may
ignore variables from the init set 𝑥*1 . It may also add variables to the init set, provided these are already set in
the context, i.e., are vacuously initialized.
3.3. Matching 43
WebAssembly Specification, Release 3.0 (2025-11-14)
• Or:
– The composite type comptype 1 is of the form (func 𝑡*11 → 𝑡*12 ).
– The composite type comptype 2 is of the form (func 𝑡*21 → 𝑡*22 ).
– The result type 𝑡*21 matches the result type 𝑡*11 .
– The result type 𝑡*12 matches the result type 𝑡*22 .
(𝐶 ⊢ ft 1 ≤ ft 2 )* 𝐶 ⊢ ft 1 ≤ ft 2 𝐶 ⊢ 𝑡*21 ≤ 𝑡*11 𝐶 ⊢ 𝑡*12 ≤ 𝑡*22
*
𝐶 ⊢ struct (ft *1 ft ′ 1 ) ≤ struct ft *2 𝐶 ⊢ array ft 1 ≤ array ft 2 𝐶 ⊢ func 𝑡*11 → 𝑡*12 ≤ func 𝑡*21 → 𝑡*22
𝐶 ⊢ packtype ≤ packtype
44 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
Note that there is no explicit definition of type equivalence, since it coincides with syntactic equality, as used
in the premise of the former rule above.
3.3.11 Limits
The limits range [𝑛1 .. u64 ?1 ] matches the limits range [𝑛2 .. u64 ?2 ] if:
• 𝑛1 is greater than or equal to 𝑛2 .
• Either:
– u64 ?1 is of the form 𝑚1 .
– If u64 2 is defined, then:
∗ 𝑚1 is less than or equal to u64 2 .
• Or:
– u64 ?1 is absent.
– u64 ?2 is absent.
𝑛1 ≥ 𝑛2 (𝑚1 ≤ 𝑚2 )? 𝑛1 ≥ 𝑛2
𝐶 ⊢ [𝑛1 .. 𝑚1 ] ≤ [𝑛2 .. 𝑚?2 ] 𝐶 ⊢ [𝑛1 .. 𝜖] ≤ [𝑛2 .. 𝜖]
Note
Although the conclusion of this rule looks identical to its premise, they in fact describe different relations: the
premise invokes subtyping on defined types, while the conclusion defines it on tag types that happen to be
expressed as defined types.
3.3. Matching 45
WebAssembly Specification, Release 3.0 (2025-11-14)
– mut?1 is absent.
– mut?2 is absent.
• Or:
– mut?1 is of the form mut.
– mut?2 is of the form mut.
– The value type valtype 2 matches the value type valtype 1 .
𝐶 ⊢ valtype 1 ≤ valtype 2 𝐶 ⊢ valtype 1 ≤ valtype 2 𝐶 ⊢ valtype 2 ≤ valtype 1
𝐶 ⊢ valtype 1 ≤ valtype 2 𝐶 ⊢ mut valtype 1 ≤ mut valtype 2
46 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
3.4 Instructions
Instructions are classified by instruction types that describe how they manipulate the operand stack and initialize
locals: A type 𝑡*1 →𝑥* 𝑡*2 describes the required input stack with argument values of types 𝑡*1 that an instruction
pops off and the provided output stack with result values of types 𝑡*2 that it pushes back. Moreover, it enumerates
the indices 𝑥* of locals that have been set by the instruction. In most cases, this is empty.
Note
For example, the instruction [Link] has type i32 i32 → i32, consuming two i32 values and producing one. The
instruction ([Link] 𝑥) has type 𝑡 →𝑥 𝜖, provided 𝑡 is the type declared for the local 𝑥.
Typing extends to instruction sequences instr * . Such a sequence has an instruction type 𝑡*1 →𝑥* 𝑡*2 if the accumu-
lative effect of executing the instructions is consuming values of types 𝑡*1 off the operand stack, pushing new values
of types 𝑡*2 , and setting all locals 𝑥* .
For some instructions, the typing rules do not fully constrain the type, and therefore allow for multiple types. Such
instructions are called polymorphic. Two degrees of polymorphism can be distinguished:
• value-polymorphic: the value type 𝑡 of one or several individual operands is unconstrained. That is the case
for all parametric instructions like drop and select.
• stack-polymorphic: the entire (or most of the) instruction type 𝑡*1 → 𝑡*2 of the instruction is uncon-
strained. That is the case for all control instructions that perform an unconditional control transfer, such
as unreachable, br, or return.
In both cases, the unconstrained types or type sequences can be chosen arbitrarily, as long as they meet the con-
straints imposed for the surrounding parts of the program.
Note
For example, the select instruction is valid with type 𝑡 𝑡 i32 → 𝑡, for any possible number type 𝑡. Consequently,
both instruction sequences
and
are valid, with 𝑡 in the typing of select being instantiated to i32 or f 64, respectively.
The unreachable instruction is stack-polymorphic, and hence valid with type 𝑡*1 → 𝑡*2 for any possible sequences
of value types 𝑡*1 and 𝑡*2 . Consequently,
(unreachable) ([Link])
is invalid, because there is no possible type to pick for the unreachable instruction that would make the sequence
well-typed.
3.4. Instructions 47
WebAssembly Specification, Release 3.0 (2025-11-14)
The Appendix describes a type checking algorithm that efficiently implements validation of instruction sequences
as prescribed by the rules given here.
𝐶 ⊢ nop : 𝜖 → 𝜖
unreachable
The instruction unreachable is valid with the instruction type 𝑡*1 → 𝑡*2 if:
• The instruction type 𝑡*1 → 𝑡*2 is valid.
𝐶 ⊢ 𝑡*1 → 𝑡*2 : ok
𝐶 ⊢ unreachable : 𝑡*1 → 𝑡*2
Note
drop
The instruction drop is valid with the instruction type 𝑡 → 𝜖 if:
• The value type 𝑡 is valid.
𝐶 ⊢ 𝑡 : ok
𝐶 ⊢ drop : 𝑡 → 𝜖
Note
select (𝑡* )?
The instruction (select valtype ? ) is valid with the instruction type 𝑡 𝑡 i32 → 𝑡 if:
• The value type 𝑡 is valid.
• Either:
– The value type sequence valtype ? is of the form 𝑡.
• Or:
– The value type sequence valtype ? is absent.
– The value type 𝑡 matches the value type 𝑡′ .
– The value type 𝑡′ is of the form numtype or 𝑡′ is of the form vectype.
𝐶 ⊢ 𝑡 : ok 𝐶 ⊢ 𝑡 : ok 𝐶 ⊢ 𝑡 ≤ 𝑡′ 𝑡′ = numtype ∨ 𝑡′ = vectype
𝐶 ⊢ select 𝑡 : 𝑡 𝑡 i32 → 𝑡 𝐶 ⊢ select : 𝑡 𝑡 i32 → 𝑡
Note
In future versions of WebAssembly, select may allow more than one value per choice.
48 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
The notation {labels (𝑡* )} ⊕ 𝐶 inserts the new label type at index 0, shifting all others. The same applies to all
other block instructions.
br 𝑙
The instruction (br 𝑙) is valid with the instruction type 𝑡*1 𝑡* → 𝑡*2 if:
• The label 𝐶.labels[𝑙] exists.
• The label 𝐶.labels[𝑙] is of the form 𝑡* .
• The instruction type 𝑡*1 → 𝑡*2 is valid.
𝐶.labels[𝑙] = 𝑡* 𝐶 ⊢ 𝑡*1 → 𝑡*2 : ok
𝐶 ⊢ br 𝑙 : 𝑡*1 𝑡* → 𝑡*2
Note
The label index space in the context 𝐶 contains the most recent label first, so that 𝐶.labels[𝑙] performs a relative
lookup as expected. This applies to other branch instructions as well.
3.4. Instructions 49
WebAssembly Specification, Release 3.0 (2025-11-14)
br_if 𝑙
The instruction (br_if 𝑙) is valid with the instruction type 𝑡* i32 → 𝑡* if:
• The label 𝐶.labels[𝑙] exists.
• The label 𝐶.labels[𝑙] is of the form 𝑡* .
𝐶.labels[𝑙] = 𝑡*
𝐶 ⊢ br_if 𝑙 : 𝑡* i32 → 𝑡*
br_table 𝑙* 𝑙𝑁
The instruction (br_table 𝑙* 𝑙′ ) is valid with the instruction type 𝑡*1 𝑡* i32 → 𝑡*2 if:
• For all 𝑙 in 𝑙* :
– The label 𝐶.labels[𝑙] exists.
– The result type 𝑡* matches the label 𝐶.labels[𝑙].
• The label 𝐶.labels[𝑙′ ] exists.
• The result type 𝑡* matches the label 𝐶.labels[𝑙′ ].
• The instruction type 𝑡*1 𝑡* i32 → 𝑡*2 is valid.
(𝐶 ⊢ 𝑡* ≤ 𝐶.labels[𝑙])* 𝐶 ⊢ 𝑡* ≤ 𝐶.labels[𝑙′ ] 𝐶 ⊢ 𝑡*1 𝑡* i32 → 𝑡*2 : ok
𝐶 ⊢ br_table 𝑙* 𝑙′ : 𝑡*1 𝑡* i32 → 𝑡*2
Note
br_on_null 𝑙
The instruction (br_on_null 𝑙) is valid with the instruction type 𝑡* (ref null ht) → 𝑡* (ref ht) if:
• The label 𝐶.labels[𝑙] exists.
• The label 𝐶.labels[𝑙] is of the form 𝑡* .
• The heap type ht is valid.
𝐶.labels[𝑙] = 𝑡* 𝐶 ⊢ ht : ok
𝐶 ⊢ br_on_null 𝑙 : 𝑡* (ref null ht) → 𝑡* (ref ht)
br_on_non_null 𝑙
The instruction (br_on_non_null 𝑙) is valid with the instruction type 𝑡* (ref null ht) → 𝑡* if:
• The label 𝐶.labels[𝑙] exists.
• The label 𝐶.labels[𝑙] is of the form 𝑡* (ref null? ht).
𝐶.labels[𝑙] = 𝑡* (ref null? ht)
𝐶 ⊢ br_on_non_null 𝑙 : 𝑡* (ref null ht) → 𝑡*
50 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
br_on_cast 𝑙 rt 1 rt 2
The instruction (br_on_cast 𝑙 rt 1 rt 2 ) is valid with the instruction type 𝑡* rt 1 → 𝑡* reftype if:
• The label 𝐶.labels[𝑙] exists.
• The label 𝐶.labels[𝑙] is of the form 𝑡* rt.
• The reference type rt 1 is valid.
• The reference type rt 2 is valid.
• The reference type rt 2 matches the reference type rt 1 .
• The reference type rt 2 matches the reference type rt.
• The reference type reftype is rt 1 ∖ rt 2 .
𝐶.labels[𝑙] = 𝑡* rt 𝐶 ⊢ rt 1 : ok 𝐶 ⊢ rt 2 : ok 𝐶 ⊢ rt 2 ≤ rt 1 𝐶 ⊢ rt 2 ≤ rt
* *
𝐶 ⊢ br_on_cast 𝑙 rt 1 rt 2 : 𝑡 rt 1 → 𝑡 (rt 1 ∖ rt 2 )
br_on_cast_fail 𝑙 rt 1 rt 2
The instruction (br_on_cast_fail 𝑙 rt 1 rt 2 ) is valid with the instruction type 𝑡* rt 1 → 𝑡* rt 2 if:
• The label 𝐶.labels[𝑙] exists.
• The label 𝐶.labels[𝑙] is of the form 𝑡* rt.
• The reference type rt 1 is valid.
• The reference type rt 2 is valid.
• The reference type rt 2 matches the reference type rt 1 .
• The reference type rt 1 ∖ rt 2 matches the reference type rt.
𝐶.labels[𝑙] = 𝑡* rt 𝐶 ⊢ rt 1 : ok 𝐶 ⊢ rt 2 : ok 𝐶 ⊢ rt 2 ≤ rt 1 𝐶 ⊢ rt 1 ∖ rt 2 ≤ rt
𝐶 ⊢ br_on_cast_fail 𝑙 rt 1 rt 2 : 𝑡* rt 1 → 𝑡* rt 2
call 𝑥
The instruction (call 𝑥) is valid with the instruction type 𝑡*1 → 𝑡*2 if:
• The function 𝐶.funcs[𝑥] exists.
• The expansion of 𝐶.funcs[𝑥] is (func 𝑡*1 → 𝑡*2 ).
𝐶.funcs[𝑥] ≈ func 𝑡*1 → 𝑡*2
𝐶 ⊢ call 𝑥 : 𝑡*1 → 𝑡*2
call_ref 𝑥
The instruction (call_ref 𝑥) is valid with the instruction type 𝑡*1 (ref null 𝑥) → 𝑡*2 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (func 𝑡*1 → 𝑡*2 ).
𝐶.types[𝑥] ≈ func 𝑡*1 → 𝑡*2
𝐶 ⊢ call_ref 𝑥 : 𝑡*1 (ref null 𝑥) → 𝑡*2
call_indirect 𝑥 𝑦
The instruction (call_indirect 𝑥 𝑦) is valid with the instruction type 𝑡*1 at → 𝑡*2 if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form (at lim rt).
• The reference type rt matches the reference type (ref null func).
3.4. Instructions 51
WebAssembly Specification, Release 3.0 (2025-11-14)
return
The instruction return is valid with the instruction type 𝑡*1 𝑡* → 𝑡*2 if:
• The result type 𝐶.return is of the form 𝑡* .
• The instruction type 𝑡*1 → 𝑡*2 is valid.
𝐶.return = (𝑡* ) 𝐶 ⊢ 𝑡*1 → 𝑡*2 : ok
𝐶 ⊢ return : 𝑡*1 𝑡* → 𝑡*2
Note
return_call 𝑥
The instruction (return_call 𝑥) is valid with the instruction type 𝑡*3 𝑡*1 → 𝑡*4 if:
• The function 𝐶.funcs[𝑥] exists.
• The expansion of 𝐶.funcs[𝑥] is (func 𝑡*1 → 𝑡*2 ).
*
• The result type 𝐶.return is of the form 𝑡′ 2 .
*
• The result type 𝑡*2 matches the result type 𝑡′ 2 .
• The instruction type 𝑡*3 → 𝑡*4 is valid.
* *
𝐶.funcs[𝑥] ≈ func 𝑡*1 → 𝑡*2 𝐶.return = (𝑡′ 2 ) 𝐶 ⊢ 𝑡*2 ≤ 𝑡′ 2 𝐶 ⊢ 𝑡*3 → 𝑡*4 : ok
𝐶 ⊢ return_call 𝑥 : 𝑡*3 𝑡*1 → 𝑡*4
Note
return_call_ref 𝑥
The instruction (return_call_ref 𝑥) is valid with the instruction type 𝑡*3 𝑡*1 (ref null 𝑥) → 𝑡*4 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (func 𝑡*1 → 𝑡*2 ).
*
• The result type 𝐶.return is of the form 𝑡′ 2 .
*
• The result type 𝑡*2 matches the result type 𝑡′ 2 .
• The instruction type 𝑡*3 → 𝑡*4 is valid.
* *
𝐶.types[𝑥] ≈ func 𝑡*1 → 𝑡*2 𝐶.return = (𝑡′ 2 ) 𝐶 ⊢ 𝑡*2 ≤ 𝑡′ 2 𝐶 ⊢ 𝑡*3 → 𝑡*4 : ok
𝐶 ⊢ return_call_ref 𝑥 : 𝑡*3 𝑡*1 (ref null 𝑥) → 𝑡*4
52 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
return_call_indirect 𝑥 𝑦
The instruction (return_call_indirect 𝑥 𝑦) is valid with the instruction type 𝑡*3 𝑡*1 at → 𝑡*4 if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form (at lim rt).
• The reference type rt matches the reference type (ref null func).
• The type 𝐶.types[𝑦] exists.
• The expansion of 𝐶.types[𝑦] is (func 𝑡*1 → 𝑡*2 ).
*
• The result type 𝐶.return is of the form 𝑡′ 2 .
*
• The result type 𝑡*2 matches the result type 𝑡′ 2 .
• The instruction type 𝑡*3 → 𝑡*4 is valid.
𝐶.tables[𝑥] = at lim rt 𝐶 ⊢ rt ≤ (ref null func)
* *
𝐶.types[𝑦] ≈ func 𝑡*1 → 𝑡*2 𝐶.return = (𝑡′ 2 ) 𝐶 ⊢ 𝑡*2 ≤ 𝑡′ 2 𝐶 ⊢ 𝑡*3 → 𝑡*4 : ok
𝐶 ⊢ return_call_indirect 𝑥 𝑦 : 𝑡*3 𝑡*1 at → 𝑡*4
Note
throw 𝑥
The instruction (throw 𝑥) is valid with the instruction type 𝑡*1 𝑡* → 𝑡*2 if:
• The tag 𝐶.tags[𝑥] exists.
• The expansion of 𝐶.tags[𝑥] is (func 𝑡* →).
• The instruction type 𝑡*1 → 𝑡*2 is valid.
𝐶.tags[𝑥] ≈ func 𝑡* → 𝜖 𝐶 ⊢ 𝑡*1 → 𝑡*2 : ok
𝐶 ⊢ throw 𝑥 : 𝑡1 𝑡 → 𝑡*2
* *
Note
throw_ref
The instruction throw_ref is valid with the instruction type 𝑡*1 (ref null exn) → 𝑡*2 if:
• The instruction type 𝑡*1 → 𝑡*2 is valid.
𝐶 ⊢ 𝑡*1 → 𝑡*2 : ok
𝐶 ⊢ throw_ref : 𝑡*1 (ref null exn) → 𝑡*2
Note
3.4. Instructions 53
WebAssembly Specification, Release 3.0 (2025-11-14)
catch 𝑥 𝑙
The catch clause (catch 𝑥 𝑙) is valid if:
• The tag 𝐶.tags[𝑥] exists.
• The expansion of 𝐶.tags[𝑥] is (func 𝑡* →).
• The label 𝐶.labels[𝑙] exists.
• The result type 𝑡* matches the label 𝐶.labels[𝑙].
𝐶.tags[𝑥] ≈ func 𝑡* → 𝜖 𝐶 ⊢ 𝑡* ≤ 𝐶.labels[𝑙]
𝐶 ⊢ catch 𝑥 𝑙 : ok
catch_ref 𝑥 𝑙
The catch clause (catch_ref 𝑥 𝑙) is valid if:
• The tag 𝐶.tags[𝑥] exists.
• The expansion of 𝐶.tags[𝑥] is (func 𝑡* →).
• The label 𝐶.labels[𝑙] exists.
• The result type 𝑡* (ref exn) matches the label 𝐶.labels[𝑙].
𝐶.tags[𝑥] ≈ func 𝑡* → 𝜖 𝐶 ⊢ 𝑡* (ref exn) ≤ 𝐶.labels[𝑙]
𝐶 ⊢ catch_ref 𝑥 𝑙 : ok
catch_all 𝑙
The catch clause (catch_all 𝑙) is valid if:
• The label 𝐶.labels[𝑙] exists.
• The result type 𝜖 matches the label 𝐶.labels[𝑙].
𝐶 ⊢ 𝜖 ≤ 𝐶.labels[𝑙]
𝐶 ⊢ catch_all 𝑙 : ok
catch_all_ref 𝑙
The catch clause (catch_all_ref 𝑙) is valid if:
• The label 𝐶.labels[𝑙] exists.
• The result type (ref exn) matches the label 𝐶.labels[𝑙].
𝐶 ⊢ (ref exn) ≤ 𝐶.labels[𝑙]
𝐶 ⊢ catch_all_ref 𝑙 : ok
54 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝑡 →𝑥 𝜖 if:
• The local 𝐶.locals[𝑥] exists.
• The local 𝐶.locals[𝑥] is of the form (init 𝑡).
𝐶.locals[𝑥] = init 𝑡
𝐶 ⊢ [Link] 𝑥 : 𝑡 →𝑥 𝜖
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝑡 →𝑥 𝑡 if:
• The local 𝐶.locals[𝑥] exists.
• The local 𝐶.locals[𝑥] is of the form (init 𝑡).
𝐶.locals[𝑥] = init 𝑡
𝐶 ⊢ [Link] 𝑥 : 𝑡 →𝑥 𝑡
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝜖 → 𝑡 if:
• The global 𝐶.globals[𝑥] exists.
• The global 𝐶.globals[𝑥] is of the form (mut? 𝑡).
𝐶.globals[𝑥] = mut? 𝑡
𝐶 ⊢ [Link] 𝑥 : 𝜖 → 𝑡
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝑡 → 𝜖 if:
• The global 𝐶.globals[𝑥] exists.
• The global 𝐶.globals[𝑥] is of the form (mut 𝑡).
𝐶.globals[𝑥] = mut 𝑡
𝐶 ⊢ [Link] 𝑥 : 𝑡 → 𝜖
3.4. Instructions 55
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type at rt → 𝜖 if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form (at lim rt).
𝐶.tables[𝑥] = at lim rt
𝐶 ⊢ [Link] 𝑥 : at rt → 𝜖
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝜖 → at if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form (at lim rt).
𝐶.tables[𝑥] = at lim rt
𝐶 ⊢ [Link] 𝑥 : 𝜖 → at
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type rt at → i32 if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form (at lim rt).
𝐶.tables[𝑥] = at lim rt
𝐶 ⊢ [Link] 𝑥 : rt at → i32
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type at rt at → 𝜖 if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form (at lim rt).
𝐶.tables[𝑥] = at lim rt
𝐶 ⊢ [Link] 𝑥 : at rt at → 𝜖
[Link] 𝑥 𝑦
The instruction ([Link] 𝑥1 𝑥2 ) is valid with the instruction type at 1 at 2 addrtype → 𝜖 if:
• The table 𝐶.tables[𝑥1 ] exists.
• The table 𝐶.tables[𝑥1 ] is of the form (at 1 lim 1 rt 1 ).
• The table 𝐶.tables[𝑥2 ] exists.
• The table 𝐶.tables[𝑥2 ] is of the form (at 2 lim 2 rt 2 ).
• The reference type rt 2 matches the reference type rt 1 .
• The address type addrtype is min(at 1 , at 2 ).
𝐶.tables[𝑥1 ] = at 1 lim 1 rt 1 𝐶.tables[𝑥2 ] = at 2 lim 2 rt 2 𝐶 ⊢ rt 2 ≤ rt 1
𝐶 ⊢ [Link] 𝑥1 𝑥2 : at 1 at 2 min(at 1 , at 2 ) → 𝜖
56 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥 𝑦
The instruction ([Link] 𝑥 𝑦) is valid with the instruction type at i32 i32 → 𝜖 if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form (at lim rt 1 ).
• The element segment 𝐶.elems[𝑦] exists.
• The element segment 𝐶.elems[𝑦] is of the form rt 2 .
• The reference type rt 2 matches the reference type rt 1 .
𝐶.tables[𝑥] = at lim rt 1 𝐶.elems[𝑦] = rt 2 𝐶 ⊢ rt 2 ≤ rt 1
𝐶 ⊢ [Link] 𝑥 𝑦 : at i32 i32 → 𝜖
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝜖 → 𝜖 if:
• The element segment 𝐶.elems[𝑥] exists.
𝐶.elems[𝑥] = rt
𝐶 ⊢ [Link] 𝑥 : 𝜖 → 𝜖
memarg
{align 𝑛, offset 𝑚} is valid for at and 𝑁 if:
• 2𝑛 is less than or equal to 𝑁/8.
• 𝑚 is less than 2|at| .
2𝑛 ≤ 𝑁/8 𝑚 < 2|at|
⊢ {align 𝑛, offset 𝑚} : at → 𝑁
𝑡.load 𝑥 memarg
The instruction ([Link] 𝑥 memarg) is valid with the instruction type at → nt if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
• memarg is valid for at and |nt|.
𝐶.mems[𝑥] = at lim page ⊢ memarg : at → |nt|
𝐶 ⊢ [Link] 𝑥 memarg : at → nt
3.4. Instructions 57
WebAssembly Specification, Release 3.0 (2025-11-14)
𝑡.store 𝑥 memarg
The instruction ([Link] 𝑥 memarg) is valid with the instruction type at nt → 𝜖 if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
• memarg is valid for at and |nt|.
𝐶.mems[𝑥] = at lim page ⊢ memarg : at → |nt|
𝐶 ⊢ [Link] 𝑥 memarg : at nt → 𝜖
𝑡.store𝑁 𝑥 memarg
The instruction (i𝑁 .store𝑀 𝑥 memarg) is valid with the instruction type at i𝑁 → 𝜖 if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
• memarg is valid for at and 𝑀 .
𝐶.mems[𝑥] = at lim page ⊢ memarg : at → 𝑀
𝐶 ⊢ i𝑁 .store𝑀 𝑥 memarg : at i𝑁 → 𝜖
[Link] 𝑥 memarg
The instruction ([Link] 𝑥 memarg) is valid with the instruction type at → v128 if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
• memarg is valid for at and |v128|.
𝐶.mems[𝑥] = at lim page ⊢ memarg : at → |v128|
𝐶 ⊢ [Link] 𝑥 memarg : at → v128
58 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥 memarg
The instruction ([Link] 𝑥 memarg) is valid with the instruction type at v128 → 𝜖 if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
• memarg is valid for at and |v128|.
𝐶.mems[𝑥] = at lim page ⊢ memarg : at → |v128|
𝐶 ⊢ [Link] 𝑥 memarg : at v128 → 𝜖
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝜖 → at if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
𝐶.mems[𝑥] = at lim page
𝐶 ⊢ [Link] 𝑥 : 𝜖 → at
3.4. Instructions 59
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type at → at if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
𝐶.mems[𝑥] = at lim page
𝐶 ⊢ [Link] 𝑥 : at → at
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type at i32 at → 𝜖 if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
𝐶.mems[𝑥] = at lim page
𝐶 ⊢ [Link] 𝑥 : at i32 at → 𝜖
[Link] 𝑥 𝑦
The instruction ([Link] 𝑥1 𝑥2 ) is valid with the instruction type at 1 at 2 addrtype → 𝜖 if:
• The memory 𝐶.mems[𝑥1 ] exists.
• The memory 𝐶.mems[𝑥1 ] is of the form (at 1 lim 1 page).
• The memory 𝐶.mems[𝑥2 ] exists.
• The memory 𝐶.mems[𝑥2 ] is of the form (at 2 lim 2 page).
• The address type addrtype is min(at 1 , at 2 ).
𝐶.mems[𝑥1 ] = at 1 lim 1 page 𝐶.mems[𝑥2 ] = at 2 lim 2 page
𝐶 ⊢ [Link] 𝑥1 𝑥2 : at 1 at 2 min(at 1 , at 2 ) → 𝜖
[Link] 𝑥 𝑦
The instruction ([Link] 𝑥 𝑦) is valid with the instruction type at i32 i32 → 𝜖 if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form (at lim page).
• The data segment 𝐶.datas[𝑦] exists.
• The data segment 𝐶.datas[𝑦] is of the form ok.
𝐶.mems[𝑥] = at lim page 𝐶.datas[𝑦] = ok
𝐶 ⊢ [Link] 𝑥 𝑦 : at i32 i32 → 𝜖
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝜖 → 𝜖 if:
• The data segment 𝐶.datas[𝑥] exists.
• The data segment 𝐶.datas[𝑥] is of the form ok.
𝐶.datas[𝑥] = ok
𝐶 ⊢ [Link] 𝑥 : 𝜖 → 𝜖
60 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝜖 → (ref dt) if:
• The function 𝐶.funcs[𝑥] exists.
• The function 𝐶.funcs[𝑥] is of the form dt.
• 𝑥 is contained in 𝐶.refs.
𝐶.funcs[𝑥] = dt 𝑥 ∈ 𝐶.refs
𝐶 ⊢ [Link] 𝑥 : 𝜖 → (ref dt)
ref.is_null
The instruction ref.is_null is valid with the instruction type (ref null ht) → i32 if:
• The heap type ht is valid.
𝐶 ⊢ ht : ok
𝐶 ⊢ ref.is_null : (ref null ht) → i32
ref.as_non_null
The instruction ref.as_non_null is valid with the instruction type (ref null ht) → (ref ht) if:
• The heap type ht is valid.
𝐶 ⊢ ht : ok
𝐶 ⊢ ref.as_non_null : (ref null ht) → (ref ht)
[Link]
The instruction [Link] is valid with the instruction type (ref null eq) (ref null eq) → i32.
[Link] rt
The instruction ([Link] rt) is valid with the instruction type rt ′ → i32 if:
• The reference type rt is valid.
• The reference type rt ′ is valid.
• The reference type rt matches the reference type rt ′ .
𝐶 ⊢ rt : ok 𝐶 ⊢ rt ′ : ok 𝐶 ⊢ rt ≤ rt ′
𝐶 ⊢ [Link] rt : rt ′ → i32
Note
The liberty to pick a supertype rt ′ allows typing the instruction with the least precise super type of rt as input,
that is, the top type in the corresponding heap subtyping hierarchy.
3.4. Instructions 61
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] rt
The instruction ([Link] rt) is valid with the instruction type rt ′ → rt if:
• The reference type rt is valid.
• The reference type rt ′ is valid.
• The reference type rt matches the reference type rt ′ .
𝐶 ⊢ rt : ok 𝐶 ⊢ rt ′ : ok 𝐶 ⊢ rt ≤ rt ′
𝐶 ⊢ [Link] rt : rt ′ → rt
Note
The liberty to pick a supertype rt ′ allows typing the instruction with the least precise super type of rt as input,
that is, the top type in the corresponding heap subtyping hierarchy.
struct.new_default 𝑥
The instruction (struct.new_default 𝑥) is valid with the instruction type 𝜖 → (ref 𝑥) if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (struct (mut? zt)* ).
• For all zt in zt * :
– A default value for unpack(zt) is defined.
𝐶.types[𝑥] ≈ struct (mut? zt)* (defaultunpack(zt) ̸= 𝜖)*
𝐶 ⊢ struct.new_default 𝑥 : 𝜖 → (ref 𝑥)
struct.get_sx ? 𝑥 𝑦
The instruction (struct.get_sx ? 𝑥 𝑖) is valid with the instruction type (ref null 𝑥) → 𝑡 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (struct ft * ).
• The length of ft * is greater than 𝑖.
• The field type ft * [𝑖] is of the form (mut? zt).
• The signedness sx ? is absent if and only if zt is a packed type.
• The value type 𝑡 is unpack(zt).
𝐶.types[𝑥] ≈ struct ft * ft * [𝑖] = mut? zt sx ? = 𝜖 ⇔ zt = unpack(zt)
?
𝐶 ⊢ struct.get_sx 𝑥 𝑖 : (ref null 𝑥) → unpack(zt)
62 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥 𝑦
The instruction ([Link] 𝑥 𝑖) is valid with the instruction type (ref null 𝑥) 𝑡 → 𝜖 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (struct ft * ).
• The length of ft * is greater than 𝑖.
• The field type ft * [𝑖] is of the form (mut zt).
• The value type 𝑡 is unpack(zt).
𝐶.types[𝑥] ≈ struct ft * ft * [𝑖] = mut zt
𝐶 ⊢ [Link] 𝑥 𝑖 : (ref null 𝑥) unpack(zt) → 𝜖
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type 𝑡 i32 → (ref 𝑥) if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut? zt)).
• The value type 𝑡 is unpack(zt).
𝐶.types[𝑥] ≈ array (mut? zt)
𝐶 ⊢ [Link] 𝑥 : unpack(zt) i32 → (ref 𝑥)
array.new_default 𝑥
The instruction (array.new_default 𝑥) is valid with the instruction type i32 → (ref 𝑥) if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut? zt)).
• A default value for unpack(zt) is defined.
𝐶.types[𝑥] ≈ array (mut? zt) defaultunpack(zt) ̸= 𝜖
𝐶 ⊢ array.new_default 𝑥 : i32 → (ref 𝑥)
array.new_fixed 𝑥 𝑛
The instruction (array.new_fixed 𝑥 𝑛) is valid with the instruction type 𝑡𝑛 → (ref 𝑥) if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut? zt)).
• The value type 𝑡 is unpack(zt).
𝐶.types[𝑥] ≈ array (mut? zt)
𝐶 ⊢ array.new_fixed 𝑥 𝑛 : unpack(zt)𝑛 → (ref 𝑥)
array.new_elem 𝑥 𝑦
The instruction (array.new_elem 𝑥 𝑦) is valid with the instruction type i32 i32 → (ref 𝑥) if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut? rt)).
• The element segment 𝐶.elems[𝑦] exists.
• The element segment 𝐶.elems[𝑦] matches the reference type rt.
𝐶.types[𝑥] ≈ array (mut? rt) 𝐶 ⊢ 𝐶.elems[𝑦] ≤ rt
𝐶 ⊢ array.new_elem 𝑥 𝑦 : i32 i32 → (ref 𝑥)
3.4. Instructions 63
WebAssembly Specification, Release 3.0 (2025-11-14)
array.new_data 𝑥 𝑦
The instruction (array.new_data 𝑥 𝑦) is valid with the instruction type i32 i32 → (ref 𝑥) if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut? zt)).
• The value type unpack(zt) is of the form numtype or unpack(zt) is of the form vectype.
• The data segment 𝐶.datas[𝑦] exists.
• The data segment 𝐶.datas[𝑦] is of the form ok.
𝐶.types[𝑥] ≈ array (mut? zt) unpack(zt) = numtype ∨ unpack(zt) = vectype 𝐶.datas[𝑦] = ok
𝐶 ⊢ array.new_data 𝑥 𝑦 : i32 i32 → (ref 𝑥)
array.get_sx ? 𝑥
The instruction (array.get_sx ? 𝑥) is valid with the instruction type (ref null 𝑥) i32 → 𝑡 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut? zt)).
• The signedness sx ? is absent if and only if zt is a packed type.
• The value type 𝑡 is unpack(zt).
𝐶.types[𝑥] ≈ array (mut? zt) sx ? = 𝜖 ⇔ zt = unpack(zt)
?
𝐶 ⊢ array.get_sx 𝑥 : (ref null 𝑥) i32 → unpack(zt)
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type (ref null 𝑥) i32 𝑡 → 𝜖 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut zt)).
• The value type 𝑡 is unpack(zt).
𝐶.types[𝑥] ≈ array (mut zt)
𝐶 ⊢ [Link] 𝑥 : (ref null 𝑥) i32 unpack(zt) → 𝜖
[Link]
The instruction [Link] is valid with the instruction type (ref null array) → i32.
[Link] 𝑥
The instruction ([Link] 𝑥) is valid with the instruction type (ref null 𝑥) i32 𝑡 i32 → 𝜖 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut zt)).
• The value type 𝑡 is unpack(zt).
𝐶.types[𝑥] ≈ array (mut zt)
𝐶 ⊢ [Link] 𝑥 : (ref null 𝑥) i32 unpack(zt) i32 → 𝜖
64 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link] 𝑥 𝑦
The instruction ([Link] 𝑥1 𝑥2 ) is valid with the instruction type (ref null 𝑥1 ) i32 (ref null 𝑥2 ) i32 i32 → 𝜖 if:
• The type 𝐶.types[𝑥1 ] exists.
• The expansion of 𝐶.types[𝑥1 ] is (array (mut zt 1 )).
• The type 𝐶.types[𝑥2 ] exists.
• The expansion of 𝐶.types[𝑥2 ] is (array (mut? zt 2 )).
• The storage type zt 2 matches the storage type zt 1 .
𝐶.types[𝑥1 ] ≈ array (mut zt 1 ) 𝐶.types[𝑥2 ] ≈ array (mut? zt 2 ) 𝐶 ⊢ zt 2 ≤ zt 1
𝐶 ⊢ [Link] 𝑥1 𝑥2 : (ref null 𝑥1 ) i32 (ref null 𝑥2 ) i32 i32 → 𝜖
array.init_elem 𝑥 𝑦
The instruction (array.init_elem 𝑥 𝑦) is valid with the instruction type (ref null 𝑥) i32 i32 i32 → 𝜖 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut zt)).
• The element segment 𝐶.elems[𝑦] exists.
• The element segment 𝐶.elems[𝑦] matches the storage type zt.
𝐶.types[𝑥] ≈ array (mut zt) 𝐶 ⊢ 𝐶.elems[𝑦] ≤ zt
𝐶 ⊢ array.init_elem 𝑥 𝑦 : (ref null 𝑥) i32 i32 i32 → 𝜖
array.init_data 𝑥 𝑦
The instruction (array.init_data 𝑥 𝑦) is valid with the instruction type (ref null 𝑥) i32 i32 i32 → 𝜖 if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (array (mut zt)).
• The value type unpack(zt) is of the form numtype or unpack(zt) is of the form vectype.
• The data segment 𝐶.datas[𝑦] exists.
• The data segment 𝐶.datas[𝑦] is of the form ok.
𝐶.types[𝑥] ≈ array (mut zt) unpack(zt) = numtype ∨ unpack(zt) = vectype 𝐶.datas[𝑦] = ok
𝐶 ⊢ array.init_data 𝑥 𝑦 : (ref null 𝑥) i32 i32 i32 → 𝜖
i31.get_sx
The instruction (i31.get_sx ) is valid with the instruction type (ref null i31) → i32.
3.4. Instructions 65
WebAssembly Specification, Release 3.0 (2025-11-14)
The instruction any.convert_extern is valid with the instruction type (ref null?1 extern) → (ref null?2 any) if:
• null?1 is of the form null?2 .
null?1 = null?2
𝐶 ⊢ any.convert_extern : (ref null?1 extern) → (ref null?2 any)
extern.convert_any
The instruction extern.convert_any is valid with the instruction type (ref null?1 any) → (ref null?2 extern) if:
• null?1 is of the form null?2 .
null?1 = null?2
𝐶 ⊢ extern.convert_any : (ref null?1 any) → (ref null?2 extern)
𝐶 ⊢ [Link] 𝑐nt : 𝜖 → nt
𝑡.unop
The instruction ([Link] nt ) is valid with the instruction type nt → nt.
𝐶 ⊢ [Link] nt : nt → nt
𝑡.binop
The instruction ([Link] nt ) is valid with the instruction type nt nt → nt.
𝐶 ⊢ [Link] nt : nt nt → nt
𝑡.testop
The instruction ([Link] nt ) is valid with the instruction type nt → i32.
𝐶 ⊢ [Link] nt : nt → i32
𝑡.relop
The instruction ([Link] nt ) is valid with the instruction type nt nt → i32.
𝐶 ⊢ [Link] nt : nt nt → i32
𝑡1 .cvtop_𝑡2 _sx ?
The instruction (nt 1 .cvtop_nt 2 ) is valid with the instruction type nt 2 → nt 1 .
𝐶 ⊢ nt 1 .cvtop_nt 2 : nt 2 → nt 1
66 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
unpack(i𝑁 x𝑁 ) = unpack(i𝑁 )
[Link] 𝑐
The instruction ([Link] 𝑐) is valid with the instruction type 𝜖 → v128.
𝐶 ⊢ [Link] 𝑐 : 𝜖 → v128
[Link]
The instruction ([Link]) is valid with the instruction type v128 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 v128 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 v128 v128 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 → i32.
[Link]
The instruction ([Link]) is valid with the instruction type v128 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 v128 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 v128 v128 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 → i32.
3.4. Instructions 67
WebAssembly Specification, Release 3.0 (2025-11-14)
[Link]
The instruction ([Link]) is valid with the instruction type v128 v128 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 i32 → v128.
[Link]
The instruction ([Link]) is valid with the instruction type v128 → i32.
[Link]
The instruction ([Link]) is valid with the instruction type v128 v128 → v128.
[Link] laneidx 16
The instruction ([Link] 𝑖* ) is valid with the instruction type v128 v128 → v128 if:
• For all 𝑖 in 𝑖* :
– The lane index 𝑖 is less than 2 · dim(sh).
(𝑖 < 2 · dim(sh))*
𝐶 ⊢ [Link] 𝑖* : v128 v128 → v128
[Link]
The instruction ([Link]) is valid with the instruction type numtype → v128 if:
• The number type numtype is unpack(sh).
shape.extract_lane_sx ? laneidx
The instruction (sh.extract_lane_sx ? 𝑖) is valid with the instruction type v128 → numtype if:
• The lane index 𝑖 is less than dim(sh).
• The number type numtype is unpack(sh).
𝑖 < dim(sh)
𝐶 ⊢ sh.extract_lane_sx ? 𝑖 : v128 → unpack(sh)
shape.replace_lane laneidx
The instruction (sh.replace_lane 𝑖) is valid with the instruction type v128 numtype → v128 if:
• The lane index 𝑖 is less than dim(sh).
• The number type numtype is unpack(sh).
𝑖 < dim(sh)
𝐶 ⊢ sh.replace_lane 𝑖 : v128 unpack(sh) → v128
68 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
ishape 1 .vextunop_ishape 2
The instruction (sh 1 .vextunop_sh 2 ) is valid with the instruction type v128 → v128.
ishape 1 .vextbinop_ishape 2
The instruction (sh 1 .vextbinop_sh 2 ) is valid with the instruction type v128 v128 → v128.
ishape 1 .vextternop_ishape 2
The instruction (sh 1 .vextternop_sh 2 ) is valid with the instruction type v128 v128 v128 → v128.
3.4. Instructions 69
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶⊢𝜖:𝜖→𝜖
𝐶 ⊢ instr 1 : 𝑡*1 →𝑥*1 𝑡*2 (𝐶.locals[𝑥1 ] = init 𝑡)* 𝐶[.local[𝑥*1 ] = (set 𝑡)* ] ⊢ instr *2 : 𝑡*2 →𝑥*2 𝑡*3
𝐶 ⊢ instr 1 instr *2 : 𝑡*1 →𝑥*1 𝑥*2 𝑡*3
𝐶 ⊢ instr * : it 𝐶 ⊢ it ≤ it ′ 𝐶 ⊢ it ′ : ok 𝐶 ⊢ instr * : 𝑡*1 →𝑥* 𝑡*2 𝐶 ⊢ 𝑡* : ok
*
𝐶 ⊢ instr * : it ′ 𝐶 ⊢ instr : (𝑡 𝑡1 ) →𝑥* (𝑡* 𝑡*2 )
* *
Note
In combination with the previous rule, subsumption allows to compose instructions whose types would not
directly fit otherwise. For example, consider the instruction sequence
To type this sequence, its subsequence ([Link] 2) ([Link]) needs to be valid with an intermediate type. But
the direct type of ([Link] 2) is 𝜖 → i32, not matching the two inputs expected by [Link]. The subsumption
rule allows to weaken the type of (const i32 2) to the supertype i32 → i32 i32, such that it can be composed with
[Link] and yields the intermediate type i32 → i32 i32 for the subsequence. That can in turn be composed with
the first constant.
Furthermore, subsumption allows to drop init variables 𝑥* from the instruction type in a context where they are
not needed, for example, at the end of the body of a block.
3.4.13 Expressions
Expressions expr are classified by result types 𝑡* .
The expression instr * is valid with the result type 𝑡* if:
• The instruction sequence instr * is valid with the instruction type 𝜖 → 𝑡* .
𝐶 ⊢ instr * : 𝜖 →𝜖 𝑡*
𝐶 ⊢ instr * : 𝑡*
Constant Expressions
In a constant expression, all instructions must be constant.
instr * is constant if:
• For all instr in instr * :
– instr is constant.
val is constant if:
• Either:
– The value val is of the form ([Link] 𝑐nt ).
• Or:
70 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
3.4. Instructions 71
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
Currently, constant expressions occurring in globals are further constrained in that contained [Link] in-
structions are only allowed to refer to imported or previously defined globals. Constant expressions occurring
in tables may only have [Link] instructions that refer to imported globals. This is enforced in the validation
rule for modules by constraining the context 𝐶 accordingly.
The definition of constant expression may be extended in future versions of WebAssembly.
3.5 Modules
Modules are valid when all the components they contain are valid. To verify this, most definitions are themselves
classified with a suitable type.
3.5.1 Types
The sequence of types defined in a module is validated incrementally, yielding a sequence of defined types repre-
senting them individually.
The type definition (type rectype) is valid with the defined type sequence dt * if:
• The length of 𝐶.types is equal to 𝑥.
• The defined type sequence dt * is of the form roll*𝑥 (rectype).
• Let 𝐶 ′ be the same context as 𝐶, but with the defined type sequence dt * appended to the field types.
• Under the context 𝐶 ′ , the recursive type rectype is valid for the type index 𝑥.
𝑥 = |𝐶.types| dt * = roll*𝑥 (rectype) 𝐶 ⊕ {types dt * } ⊢ rectype : ok(𝑥)
𝐶 ⊢ type rectype : dt *
The type definition sequence type * is valid with the defined type sequence deftype * if:
• Either:
– The type definition sequence type * is empty.
– The defined type sequence deftype * is empty.
• Or:
*
– The type definition sequence type * is of the form type 1 type ′ .
– The defined type sequence deftype * is of the form dt *1 dt * .
– The type definition type 1 is valid with the defined type sequence dt *1 .
– Let 𝐶 ′ be the same context as 𝐶, but with the defined type sequence dt *1 appended to the field types.
*
– Under the context 𝐶 ′ , the type definition sequence type ′ is valid with the defined type sequence dt * .
𝐶 ⊢ type 1 : dt *1 𝐶 ⊕ {types dt *1 } ⊢ type * : dt *
𝐶⊢𝜖:𝜖 𝐶 ⊢ type 1 type * : dt *1 dt *
3.5.2 Tags
Tags tag are classified by their tag types, which are defined types expanding to function types.
The tag (tag tagtype) is valid with the tag type tagtype ′ if:
• The tag type tagtype is valid.
• The tag type tagtype ′ is clos𝐶 (tagtype).
72 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
𝐶 ⊢ tagtype : ok
𝐶 ⊢ tag tagtype : clos𝐶 (tagtype)
3.5.3 Globals
Globals global are classified by global types.
The global (global globaltype expr ) is valid with the global type globaltype if:
• The global type globaltype is valid.
• The global type globaltype is of the form (mut? 𝑡).
• The expression expr is valid with the value type 𝑡.
• expr is constant.
𝐶 ⊢ globaltype : ok globaltype = mut? 𝑡 𝐶 ⊢ expr : 𝑡 const
𝐶 ⊢ global globaltype expr : globaltype
Sequences of globals are handled incrementally, such that each definition has access to previous definitions.
The global sequence global * is valid with the global type sequence globaltype * if:
• Either:
– The global sequence global * is empty.
– The global type sequence globaltype * is empty.
• Or:
*
– The global sequence global * is of the form global 1 global ′ .
– The global type sequence globaltype * is of the form gt 1 gt * .
– The global global 1 is valid with the global type gt 1 .
– Let 𝐶 ′ be the same context as 𝐶, but with the global type sequence gt 1 appended to the field globals.
*
– Under the context 𝐶 ′ , the global sequence global ′ is valid with the global type sequence gt * .
𝐶 ⊢ global 1 : gt 1 𝐶 ⊕ {globals gt 1 } ⊢ global * : gt *
𝐶⊢𝜖:𝜖 𝐶 ⊢ global 1 global * : gt 1 gt *
3.5.4 Memories
Memories mem are classified by memory types.
The memory (memory memtype) is valid with the memory type memtype if:
• The memory type memtype is valid.
𝐶 ⊢ memtype : ok
𝐶 ⊢ memory memtype : memtype
3.5.5 Tables
Tables table are classified by table types.
The table (table tabletype expr ) is valid with the table type tabletype if:
• The table type tabletype is valid.
• The table type tabletype is of the form (at lim rt).
• The expression expr is valid with the value type rt.
• expr is constant.
3.5. Modules 73
WebAssembly Specification, Release 3.0 (2025-11-14)
3.5.6 Functions
Functions func are classified by defined types that expand to function types of the form func 𝑡*1 → 𝑡*2 .
The function (func 𝑥 local * expr ) is valid with the type 𝐶.types[𝑥] if:
• The type 𝐶.types[𝑥] exists.
• The expansion of 𝐶.types[𝑥] is (func 𝑡*1 → 𝑡*2 ).
• For all local in local * :
– The local local is valid with the local type lt.
*
• lt is the concatenation of all such lt.
• Under the context 𝐶 with the field locals appended by (set 𝑡1 )* lt * and the field labels appended by 𝑡*2 and
the field return appended by 𝑡*2 , the expression expr is valid with the result type 𝑡*2 .
𝐶.types[𝑥] ≈ func 𝑡*1 → 𝑡*2 (𝐶 ⊢ local : lt)*
𝐶 ⊕ {locals (set 𝑡1 )* lt * , labels (𝑡*2 ), return (𝑡*2 )} ⊢ expr : 𝑡*2
𝐶 ⊢ func 𝑥 local * expr : 𝐶.types[𝑥]
3.5.7 Locals
Locals local are classified with local types.
The local (local 𝑡) is valid with the local type (init 𝑡) if:
• Either:
– The initialization status init is of the form set.
– A default value for 𝑡 is defined.
• Or:
– The initialization status init is of the form unset.
– A default value for 𝑡 is not defined.
default𝑡 ̸= 𝜖 default𝑡 = 𝜖
𝐶 ⊢ local 𝑡 : set 𝑡 𝐶 ⊢ local 𝑡 : unset 𝑡
Note
For cases where both rules are applicable, the former yields the more permissable type.
74 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
• Or:
– The data mode datamode is of the form (active 𝑥 expr ).
– The memory 𝐶.mems[𝑥] exists.
– The memory 𝐶.mems[𝑥] is of the form (at lim page).
– The expression expr is valid with the value type at.
– expr is constant.
𝐶.mems[𝑥] = at lim page 𝐶 ⊢ expr : at const
𝐶 ⊢ passive : ok 𝐶 ⊢ active 𝑥 expr : ok
𝐶 ⊢ passive : rt 𝐶 ⊢ declare : rt
𝐶.tables[𝑥] = at lim rt ′ 𝐶 ⊢ rt ≤ rt ′ 𝐶 ⊢ expr : at const
𝐶 ⊢ active 𝑥 expr : rt
3.5. Modules 75
WebAssembly Specification, Release 3.0 (2025-11-14)
3.5.11 Imports
Imports import are classified by external types.
The import (import name 1 name 2 xt) is valid with the external type externtype if:
• The external type xt is valid.
• The external type externtype is clos𝐶 (xt).
𝐶 ⊢ xt : ok
𝐶 ⊢ import name 1 name 2 xt : clos𝐶 (xt)
3.5.12 Exports
Exports export are classified by their external type.
The export (export name externidx ) is valid with the name name and the external type xt if:
• The external index externidx is valid with the external type xt.
𝐶 ⊢ externidx : xt
𝐶 ⊢ export name externidx : name xt
tag 𝑥
The external index (tag 𝑥) is valid with the external type (tag jt) if:
• The tag 𝐶.tags[𝑥] exists.
• The tag 𝐶.tags[𝑥] is of the form jt.
𝐶.tags[𝑥] = jt
𝐶 ⊢ tag 𝑥 : tag jt
global 𝑥
The external index (global 𝑥) is valid with the external type (global gt) if:
• The global 𝐶.globals[𝑥] exists.
• The global 𝐶.globals[𝑥] is of the form gt.
𝐶.globals[𝑥] = gt
𝐶 ⊢ global 𝑥 : global gt
memory 𝑥
The external index (memory 𝑥) is valid with the external type (mem mt) if:
• The memory 𝐶.mems[𝑥] exists.
• The memory 𝐶.mems[𝑥] is of the form mt.
𝐶.mems[𝑥] = mt
𝐶 ⊢ memory 𝑥 : mem mt
76 Chapter 3. Validation
WebAssembly Specification, Release 3.0 (2025-11-14)
table 𝑥
The external index (table 𝑥) is valid with the external type (table tt) if:
• The table 𝐶.tables[𝑥] exists.
• The table 𝐶.tables[𝑥] is of the form tt.
𝐶.tables[𝑥] = tt
𝐶 ⊢ table 𝑥 : table tt
func 𝑥
The external index (func 𝑥) is valid with the external type (func dt) if:
• The function 𝐶.funcs[𝑥] exists.
• The function 𝐶.funcs[𝑥] is of the form dt.
𝐶.funcs[𝑥] = dt
𝐶 ⊢ func 𝑥 : func dt
3.5.13 Modules
Modules are classified by their mapping from the external types of their imports to those of their exports.
A module is entirely closed, that is, its components can only refer to definitions that appear in the module itself.
Consequently, no initial context is required. Instead, the context 𝐶 for validation of the module’s content is con-
structed from the definitions in the module.
The module (module type * import * tag * global * mem * table * func * data * elem * start ? export * ) is valid with
the module type moduletype if:
*
• Under the context {return 𝜖}, the type definition sequence type * is valid with the defined type sequence dt ′ .
• For all import in import * :
*
– Under the context {types dt ′ , return 𝜖}, the import import is valid with the external type xt i .
• xt *i is the concatenation of all such xt i .
• For all tag in tag * :
– Under the context 𝐶 ′ , the tag tag is valid with the tag type jt.
• jt * is the concatenation of all such jt.
• Under the context 𝐶 ′ , the global sequence global * is valid with the global type sequence gt * .
• For all mem in mem * :
– Under the context 𝐶 ′ , the memory mem is valid with the memory type mt.
• mt * is the concatenation of all such mt.
• For all table in table * :
– Under the context 𝐶 ′ , the table table is valid with the table type tt.
• tt * is the concatenation of all such tt.
• For all func in func * :
– The function func is valid with the defined type dt.
• dt * is the concatenation of all such dt.
• For all data in data * :
– The memory segment data is valid.
*
• ok is the concatenation of all such ok .
3.5. Modules 77
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
All functions in a module are mutually recursive. Consequently, the definition of the context 𝐶 in this rule
is recursive: it depends on the outcome of validation of the function, table, memory, and global definitions
contained in the module, which itself depends on 𝐶. However, this recursion is just a specification device. All
types needed to construct 𝐶 can easily be determined from a simple pre-pass over the module that does not
perform any actual validation.
Globals, however, are not recursive but evaluated sequentially, such that each constant expressions only has
access to imported or previously defined globals.
78 Chapter 3. Validation
CHAPTER 4
Execution
4.1 Conventions
WebAssembly code is executed when instantiating a module or invoking an exported function on the resulting
module instance.
Execution behavior is defined in terms of an abstract machine that models the program state. It includes a stack,
which records operand values and control constructs, and an abstract store containing global state.
For each instruction, there is a rule that specifies the effect of its execution on the program state. Furthermore,
there are rules describing the instantiation of a module. As with validation, all rules are given in two equivalent
forms:
1. In prose, describing the execution in intuitive form.
2. In formal notation, describing the rule in mathematical form.18
Note
As with validation, the prose and formal rules are equivalent, so that understanding of the formal notation is
not required to read this specification. The formalism offers a more concise description in notation that is used
widely in programming languages semantics and is readily amenable to mathematical proof.
Wagner, Alon Zakai, JF Bastien, Michael Holman. Bringing the Web up to Speed with WebAssembly19 . Proceedings of the 38th ACM
SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017). ACM 2017.
19 [Link]
79
WebAssembly Specification, Release 3.0 (2025-11-14)
• Both the store and the current frame are mutated by replacing some of their components. Such replacement
is assumed to apply globally.
• The execution of an instruction may trap, in which case the entire computation is aborted and no further
modifications to the store are performed by it. (Other computations can still be initiated afterwards.)
• The execution of an instruction may also end in a jump to a designated target, which defines the next instruc-
tion to execute.
• Execution can enter and exit instruction sequences that form blocks.
• Instruction sequences are implicitly executed in order, unless a trap, jump, or exception occurs.
• In various places the rules contain assertions expressing crucial invariants about the program state.
Note
This section gives a brief explanation of the notation for specifying execution formally. For the interested reader,
a more thorough introduction can be found in respective text books.20
The formal execution rules use a standard approach for specifying operational semantics, rendering them into
reduction rules. Every rule has the following general form:
configuration ˓→ configuration
A configuration is a syntactic description of a program state. Each rule specifies one step of execution. As long
as there is at most one reduction rule applicable to a given configuration, reduction – and thereby execution – is
deterministic. WebAssembly has only very few exceptions to this, which are noted explicitly in this specification.
For WebAssembly, a configuration typically is a tuple (𝑠; 𝑓 ; instr * ) consisting of the current store 𝑠, the call frame
𝑓 of the current function, and the sequence of instructions that is to be executed. (A more precise definition is
given later.)
To avoid unnecessary clutter, the store 𝑠 and the frame 𝑓 are often combined into a state 𝑧, which is a pair (𝑠; 𝑓 ).
Moreover, 𝑧 is omitted from reduction rules that do not touch them.
There is no separate representation of the stack. Instead, it is conveniently represented as part of the configuration’s
instruction sequence. In particular, values are defined to coincide with const and ref instructions, and a sequence
of such instructions can be interpreted as an operand “stack” that grows to the right.
Note
For example, the reduction rule for the [Link] instruction can be given as follows:
Per this rule, two const instructions and the add instruction itself are removed from the instruction stream and
replaced with one new const instruction. This can be interpreted as popping two values off the stack and pushing
the result.
When no result is produced, an instruction reduces to the empty sequence:
nop ˓→ 𝜖
80 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
The order of reduction is determined by the details of the reduction rules. Usually, the left-most instruction that is
not a constant will be the subject of the next reduction step.
Reduction terminates when no more reduction rules are applicable. Soundness of the WebAssembly type system
guarantees that this is only the case when the original instruction sequence has either been reduced to a sequence
of value instructions, which can be interpreted as the values of the resulting operand stack, or if an exception or
trap occurred.
Note
4.2.1 Values
WebAssembly computations manipulate values of either the four basic number types, i.e., integers and floating-
point data of 32 or 64 bit width each, or vectors of 128 bit width, or of reference type.
In most places of the semantics, values of different types can occur. In order to avoid ambiguities, values are
therefore represented with an abstract syntax that makes their type explicit. It is convenient to reuse the same
notation as for the const instructions and [Link] producing them.
References other than null are represented with additional administrative instructions. They either are scalar ref-
erences, containing a 31-bit integer, structure references, pointing to a specific structure address, array references,
pointing to a specific array address, function references, pointing to a specific function address, exception refer-
ences, pointing to a specific exception address, or host references pointing to an uninterpreted form of host address
defined by the embedder. Any of the aformentioned references can furthermore be wrapped up as an external
reference.
Note
Value types can have an associated default value; it is the respective value 0 for number types, 0 for vector types,
and null for nullable reference types. For other references, no default value is defined, default𝑡 hence is an optional
value val ? .
defaulti𝑁 = (i𝑁 .const 0)
defaultf𝑁 = (f𝑁 .const +0)
defaultv𝑁 = (v𝑁 .const 0)
defaultref null ht = ([Link] ht)
defaultref ht = 𝜖
Convention
• The meta variable 𝑟 ranges over reference values where clear from context.
4.2.2 Results
A result is the outcome of a computation. It is either a sequence of values, a thrown exception, or a trap.
4.2.3 Store
The store represents all global state that can be manipulated by WebAssembly programs. It consists of the runtime
representation of all instances of functions, tables, memories, globals, tags, element segments, data segments, and
structures, arrays or exceptions that have been allocated during the life time of the abstract machine.
It is an invariant of the semantics that no element or data instance is addressed from anywhere else but the owning
module instances.
Syntactically, the store is defined as a record listing the existing instances of each category:
Note
In practice, implementations may apply techniques like garbage collection or reference counting to remove
objects from the store that are no longer referenced. However, such techniques are not semantically observable,
and hence outside the scope of this specification.
Convention
• The meta variable 𝑠 ranges over stores where clear from context.
82 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
4.2.4 Addresses
Function instances, table instances, memory instances, global instances, tag instances, element instances, data
instances and structure, array or exception instances in the store are referenced with abstract addresses. These are
simply indices into the respective store component. In addition, an embedder may supply an uninterpreted set of
host addresses.
addr ::= 0 | 1 | 2 | ...
funcaddr ::= addr
tableaddr ::= addr
memaddr ::= addr
globaladdr ::= addr
tagaddr ::= addr
elemaddr ::= addr
dataaddr ::= addr
structaddr ::= addr
arrayaddr ::= addr
hostaddr ::= addr
An embedder may assign identity to exported store objects corresponding to their addresses, even where this iden-
tity is not observable from within WebAssembly code itself (such as for function instances or immutable globals).
Note
Addresses are dynamic, globally unique references to runtime objects, in contrast to indices, which are static,
module-local references to their original definitions. A memory address memaddr denotes the abstract address
of a memory instance in the store, not an offset inside a memory instance.
There is no specific limit on the number of allocations of store objects, hence logical addresses can be arbitrarily
large natural numbers.
Conventions
• The notation addr(𝐴) denotes the set of addresses from address space addr occurring free in 𝐴. We some-
times reinterpret this set as the list of its elements, without assuming any particular order.
externaddr ::= tag tagaddr | global globaladdr | mem memaddr | table tableaddr | func funcaddr
Each component references runtime instances corresponding to respective declarations from the original module
– whether imported or defined – in the order of their static indices. Function instances, table instances, memory
instances, global instances, and tag instances are denoted by their respective addresses in the store.
It is an invariant of the semantics that all export instances in a given module instance have different names.
Note
All record fields except exports are to be considered private components of a module instance. They are not
accessible to other modules, only to function instances originating from the same module.
A host function is a function expressed outside WebAssembly but passed to a module as an import. The definition
and behavior of host functions are outside the scope of this specification. For the purpose of this specification, it
is assumed that when invoked, a host function behaves non-deterministically, but within certain constraints that
ensure the integrity of the runtime.
Note
Function instances are immutable, and their identity is not observable by WebAssembly code. However, an
embedder might provide implicit or explicit means for distinguishing their addresses.
Table elements can be mutated through table instructions, the execution of an active element segment, or by external
means provided by the embedder.
It is an invariant of the semantics that all table elements have a type matching the element type of tabletype. It
also is an invariant that the length of the element sequence never exceeds the maximum size of tabletype.
The length of the sequence always is a multiple of the WebAssembly page size, which is defined to be the constant
65536 – abbreviated 64 Ki.
A memory’s bytes can be mutated through memory instructions, the execution of an active data segment, or by
external means provided by the embedder.
It is an invariant of the semantics that the length of the byte sequence, divided by page size, never exceeds the
maximum size of memtype.
84 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
Conventions
The following auxiliary functions are assumed on sequences of external addresses. They extract addresses of a
specific kind in an order-preserving fashion:
• funcs(xa * ) extracts all function addresses from xa * ,
• tables(xa * ) extracts all table addresses from xa * ,
• mems(xa * ) extracts all memory addresses from xa * ,
• globals(xa * ) extracts all global addresses from xa * ,
• tags(xa * ) extracts all tag addresses from xa * .
Conventions
• Conversion of a regular value to a field value is defined as follows:
4.2.17 Stack
Besides the store, most instructions interact with an implicit stack. The stack contains the two kinds of entries:
• Values: the operands of instructions.
• Control Frames: currently active control flow structures.
The latter can in turn be one of the following:
• Labels: active structured control instructions that can be targeted by branches.
• (Call) Frames: the activation records of active function calls.
• Handlers: active exception handlers.
Note
All these entries can occur on the stack in any order during the execution of a program. Stack entries are described
by abstract syntax as follows.
Note
It is possible to model the WebAssembly semantics using separate stacks for operands, control constructs, and
calls. However, because the stacks are interdependent, additional book keeping about associated stack heights
would be required. For the purpose of this specification, an interleaved representation is simpler.
Values
Values are represented by themselves.
Labels
Labels carry an argument arity 𝑛 and their associated branch target, which is expressed syntactically as an instruc-
tion sequence:
86 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
Intuitively, instr * is the continuation to execute when the branch is taken, in place of the original control construct.
Note
label𝑛 {(loop bt . . . )}
When performing a branch to this label, this executes the loop, effectively restarting it from the beginning.
Conversely, a simple block label has the form
label𝑛 {𝜖}
When branching, the empty continuation ends the targeted block, such that execution can proceed with consec-
utive instructions.
Call Frames
Call frames carry the return arity 𝑛 of the respective function, hold the values of its locals (including arguments)
in the order corresponding to their static local indices, and a reference to the function’s own module instance:
Locals may be uninitialized, in which case they are empty. Locals are mutated by respective variable instructions.
Exception Handlers
Exception handlers are installed by try_table instructions and record the corresponding list of catch clauses:
Conventions
• The meta variable 𝐿 ranges over labels where clear from context.
• The meta variable 𝑓 ranges over frame states where clear from context.
• The meta variable 𝐻 ranges over exception handlers where clear from context.
• The following auxiliary definition takes a block type and looks up the instruction type that it denotes in the
current frame:
instrtype𝑧 (𝑥) = 𝑡*1 → 𝑡*2 if 𝑧.types[𝑥] ≈ func 𝑡*1 → 𝑡*2
instrtype𝑧 (𝑡? ) = 𝜖 → 𝑡?
Note
In order to express the reduction of traps, calls, exception handling, and control instructions, the syntax of instruc-
tions is extended to include the following administrative instructions:
An address reference represents an allocated reference value of respective form “on the stack”.
The label, frame, and handler instructions model labels, frames, and active exception handlers, respectively, “on
the stack”. Moreover, the administrative syntax maintains the nesting structure of the original structured control
instruction or function body and their instruction sequences.
The trap instruction represents the occurrence of a trap. Traps are bubbled up through nested instruction sequences,
ultimately reducing the entire program to a single trap instruction, signalling abrupt termination.
Note
if the block type bt denotes a function type func 𝑡𝑚 1 → 𝑡2 , such that 𝑛 is the block’s result arity. This rule
𝑛
replaces the block with a label instruction, which can be interpreted as “pushing” the label on the stack. When
its end is reached, i.e., the inner instruction sequence has been reduced to the empty sequence – or rather,
a sequence of 𝑛 values representing the results – then the label instruction is eliminated courtesy of its own
reduction rule:
This can be interpreted as removing the label from the stack and only leaving the locally accumulated operand
values. Validation guarantees that 𝑛 matches the number |val * | of resulting values at this point.
Configurations
A configuration describes the current computation. It consists of the computations’s state and the sequence of
instructions left to execute. The state in turn consists of a global store and a current frame referring to the module
instance in which the computation runs, i.e., where the current function originates from.
Note
The current version of WebAssembly is single-threaded, but configurations with multiple threads may be sup-
ported in the future.
4.3 Numerics
Numeric primitives are defined in a generic manner, by operators indexed over a bit width 𝑁 .
Some operators are non-deterministic, because they can return one of several possible results (such as different NaN
values). Technically, each operator thus returns a set of allowed values. For convenience, deterministic results are
expressed as plain values, which are assumed to be identified with a respective singleton set.
88 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
Some operators are partial, because they are not defined on certain inputs. Technically, an empty set of results is
returned for these inputs.
In formal notation, each operator is defined by equational clauses that apply in decreasing order of precedence.
That is, the first clause that is applicable to the given arguments defines the result. In some cases, similar clauses
are combined into one by using the notation ± or ∓. When several of these placeholders occur in a single clause,
then they must be resolved consistently: either the upper sign is chosen for all of them or the lower sign.
Note
This definition is to be read as a shorthand for the following expansion of each clause into two separate ones:
Numeric operators are lifted to input sequences by applying the operator element-wise, returning a sequence of
results. When there are multiple inputs, they must be of equal length.
𝑜𝑝(𝑐𝑛1 , . . . , 𝑐𝑛𝑘 ) = 𝑜𝑝(𝑐𝑛1 [0], . . . , 𝑐𝑛𝑘 [0]) . . . 𝑜𝑝(𝑐𝑛1 [𝑛 − 1], . . . , 𝑐𝑛𝑘 [𝑛 − 1])
Note
For example, the unary operator fabs, when given a sequence of floating-point values, return a sequence of
floating-point results:
The binary operator iadd, when given two sequences of integers of the same length, 𝑛, return a sequence of
integer results:
iadd𝑁 (𝑖𝑛1 , 𝑖𝑛2 ) = iadd𝑁 (𝑖1 [0], 𝑖2 [0]) . . . iadd𝑁 (𝑖1 [𝑛], 𝑖2 [𝑛])
Conventions:
• The meta variable 𝑑 is used to range over single bits.
• The meta variable 𝑝 is used to range over (signless) magnitudes of floating-point values, including nan and
∞.
• The meta variable 𝑞 is used to range over (signless) rational magnitudes, excluding nan or ∞.
• The notation 𝑓 −1 denotes the inverse of a bijective function 𝑓 .
• Truncation of rational values is written trunc(±𝑞), with the usual mathematical definition:
• Saturation of integers is written sat_u𝑁 (𝑖) and sat_s𝑁 (𝑖). The arguments to these two functions range over
arbitrary signed integers.
4.3. Numerics 89
WebAssembly Specification, Release 3.0 (2025-11-14)
4.3.1 Representations
Numbers and numeric vectors have an underlying binary representation as a sequence of bits:
The first case of these applies to representations of both integer value types and packed types.
Each of these functions is a bijection, hence they are invertible.
Integers
Integers are represented as base two unsigned numbers:
ibits𝑁 (𝑖) = 𝑑𝑁 −1 . . . 𝑑0 (𝑖 = 2𝑁 −1 · 𝑑𝑁 −1 + · · · + 20 · 𝑑0 )
Boolean operators like ∧, ∨, or ⊻ are lifted to bit sequences of equal length by applying them pointwise.
Floating-Point
Floating-point values are represented in the respective binary format defined by IEEE 75422 (Section 3.4):
Vectors
Numeric vectors of type v𝑁 have the same underlying representation as an i𝑁 . They can also be interpreted as a
sequence of numeric values packed into a v𝑁 with a particular shape 𝑡x𝑀 , provided that 𝑁 = |𝑡| · 𝑀 .
lanes𝑡x𝑀 (𝑐) = 𝑐0 . . . 𝑐𝑀 −1
(where 𝑤 = |𝑡|/8
∧ 𝑏* = bytesi𝑁 (𝑐)
∧ 𝑐𝑖 = bytes−1 *
𝑡 (𝑏 [𝑖 · 𝑤 : 𝑤]))
90 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
Numeric values can be packed into lanes of a specific lane type and vice versa:
packnumtype (𝑐) = 𝑐
packpacktype (𝑐) = wrap|unpack(packtype)|,|packtype| (𝑐)
unpacknumtype (𝑐) = 𝑐
unpackpacktype (𝑐) = extendu|packtype|,|unpack(packtype)| (𝑐)
Storage
When a number is stored into memory, it is converted into a sequence of bytes in little endian23 byte order:
Boolean Interpretation
The integer result of predicates – i.e., tests and relational operators – is defined with the help of the following
auxiliary function producing the value 1 or 0 depending on a condition.
bool(𝐶) = 1 (if 𝐶)
bool(𝐶) = 0 (otherwise)
iadd𝑁 (𝑖1 , 𝑖2 )
• Return the result of adding 𝑖1 and 𝑖2 modulo 2𝑁 .
iadd𝑁 (𝑖1 , 𝑖2 ) = (𝑖1 + 𝑖2 ) mod 2𝑁
isub𝑁 (𝑖1 , 𝑖2 )
• Return the result of subtracting 𝑖2 from 𝑖1 modulo 2𝑁 .
isub𝑁 (𝑖1 , 𝑖2 ) = (𝑖1 − 𝑖2 + 2𝑁 ) mod 2𝑁
imul𝑁 (𝑖1 , 𝑖2 )
• Return the result of multiplying 𝑖1 and 𝑖2 modulo 2𝑁 .
imul𝑁 (𝑖1 , 𝑖2 ) = (𝑖1 · 𝑖2 ) mod 2𝑁
23 [Link]
4.3. Numerics 91
WebAssembly Specification, Release 3.0 (2025-11-14)
idiv_u𝑁 (𝑖1 , 𝑖2 )
• If 𝑖2 is 0, then the result is undefined.
• Else, return the result of dividing 𝑖1 by 𝑖2 , truncated toward zero.
idiv_u𝑁 (𝑖1 , 0) = {}
idiv_u𝑁 (𝑖1 , 𝑖2 ) = trunc(𝑖1 /𝑖2 )
Note
idiv_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1 .
• Let 𝑗2 be the signed interpretation of 𝑖2 .
• If 𝑗2 is 0, then the result is undefined.
• Else if 𝑗1 divided by 𝑗2 is 2𝑁 −1 , then the result is undefined.
• Else, return the result of dividing 𝑗1 by 𝑗2 , truncated toward zero.
idiv_s𝑁 (𝑖1 , 0) = {}
idiv_s𝑁 (𝑖1 , 𝑖2 ) = {} (if signed𝑁 (𝑖1 )/signed𝑁 (𝑖2 ) = 2𝑁 −1 )
−1
idiv_s𝑁 (𝑖1 , 𝑖2 ) = signed𝑁 (trunc(signed𝑁 (𝑖1 )/signed𝑁 (𝑖2 )))
Note
This operator is partial. Besides division by 0, the result of (−2𝑁 −1 )/(−1) = +2𝑁 −1 is not representable as
an 𝑁 -bit signed integer.
irem_u𝑁 (𝑖1 , 𝑖2 )
• If 𝑖2 is 0, then the result is undefined.
• Else, return the remainder of dividing 𝑖1 by 𝑖2 .
irem_u𝑁 (𝑖1 , 0) = {}
irem_u𝑁 (𝑖1 , 𝑖2 ) = 𝑖1 − 𝑖2 · trunc(𝑖1 /𝑖2 )
Note
irem_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1 .
• Let 𝑗2 be the signed interpretation of 𝑖2 .
• If 𝑖2 is 0, then the result is undefined.
• Else, return the remainder of dividing 𝑗1 by 𝑗2 , with the sign of the dividend 𝑗1 .
92 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
irem_s𝑁 (𝑖1 , 0) = {}
irem_s𝑁 (𝑖1 , 𝑖2 ) = signed−1
𝑁 (𝑗1 − 𝑗2 · trunc(𝑗1 /𝑗2 ))
(where 𝑗1 = signed𝑁 (𝑖1 ) ∧ 𝑗2 = signed𝑁 (𝑖2 ))
Note
inot𝑁 (𝑖)
• Return the bitwise negation of 𝑖.
inot𝑁 (𝑖) = ibits−1 𝑁
𝑁 (ibits𝑁 (𝑖) ⊻ ibits𝑁 (2 − 1))
irev𝑁 (𝑖)
• Return the bitwise reversal of 𝑖.
irev𝑁 (𝑖) = ibits−1 𝑁
𝑁 ((𝑑 [𝑁 − 𝑖])
𝑖≤𝑁
) (if 𝑑𝑁 = ibits𝑁 (𝑖))
iand𝑁 (𝑖1 , 𝑖2 )
• Return the bitwise conjunction of 𝑖1 and 𝑖2 .
iand𝑁 (𝑖1 , 𝑖2 ) = ibits−1
𝑁 (ibits𝑁 (𝑖1 ) ∧ ibits𝑁 (𝑖2 ))
iandnot𝑁 (𝑖1 , 𝑖2 )
• Return the bitwise conjunction of 𝑖1 and the bitwise negation of 𝑖2 .
iandnot𝑁 (𝑖1 , 𝑖2 ) = iand𝑁 (𝑖1 , inot𝑁 (𝑖2 ))
ior𝑁 (𝑖1 , 𝑖2 )
• Return the bitwise disjunction of 𝑖1 and 𝑖2 .
ior𝑁 (𝑖1 , 𝑖2 ) = ibits−1
𝑁 (ibits𝑁 (𝑖1 ) ∨ ibits𝑁 (𝑖2 ))
ixor𝑁 (𝑖1 , 𝑖2 )
• Return the bitwise exclusive disjunction of 𝑖1 and 𝑖2 .
ixor𝑁 (𝑖1 , 𝑖2 ) = ibits−1
𝑁 (ibits𝑁 (𝑖1 ) ⊻ ibits𝑁 (𝑖2 ))
ishl𝑁 (𝑖1 , 𝑖2 )
• Let 𝑘 be 𝑖2 modulo 𝑁 .
• Return the result of shifting 𝑖1 left by 𝑘 bits, modulo 2𝑁 .
ishl𝑁 (𝑖1 , 𝑖2 ) = ibits−1 𝑁 −𝑘 𝑘
𝑁 (𝑑2 0 ) (if ibits𝑁 (𝑖1 ) = 𝑑𝑘1 𝑑2𝑁 −𝑘 ∧ 𝑘 = 𝑖2 mod 𝑁 )
ishr_u𝑁 (𝑖1 , 𝑖2 )
• Let 𝑘 be 𝑖2 modulo 𝑁 .
• Return the result of shifting 𝑖1 right by 𝑘 bits, extended with 0 bits.
ishr_u𝑁 (𝑖1 , 𝑖2 ) = ibits−1 𝑘 𝑁 −𝑘
𝑁 (0 𝑑1 ) (if ibits𝑁 (𝑖1 ) = 𝑑𝑁
1
−𝑘 𝑘
𝑑2 ∧ 𝑘 = 𝑖2 mod 𝑁 )
4.3. Numerics 93
WebAssembly Specification, Release 3.0 (2025-11-14)
ishr_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑘 be 𝑖2 modulo 𝑁 .
• Return the result of shifting 𝑖1 right by 𝑘 bits, extended with the most significant bit of the original value.
ishr_s𝑁 (𝑖1 , 𝑖2 ) = ibits−1 𝑘+1 𝑁 −𝑘−1
𝑁 (𝑑0 𝑑1 ) (if ibits𝑁 (𝑖1 ) = 𝑑0 𝑑1𝑁 −𝑘−1 𝑑𝑘2 ∧ 𝑘 = 𝑖2 mod 𝑁 )
irotl𝑁 (𝑖1 , 𝑖2 )
• Let 𝑘 be 𝑖2 modulo 𝑁 .
• Return the result of rotating 𝑖1 left by 𝑘 bits.
irotl𝑁 (𝑖1 , 𝑖2 ) = ibits−1 𝑁 −𝑘 𝑘
𝑁 (𝑑2 𝑑1 ) (if ibits𝑁 (𝑖1 ) = 𝑑𝑘1 𝑑𝑁
2
−𝑘
∧ 𝑘 = 𝑖2 mod 𝑁 )
irotr𝑁 (𝑖1 , 𝑖2 )
• Let 𝑘 be 𝑖2 modulo 𝑁 .
• Return the result of rotating 𝑖1 right by 𝑘 bits.
irotr𝑁 (𝑖1 , 𝑖2 ) = ibits−1 𝑘 𝑁 −𝑘
𝑁 (𝑑2 𝑑1 ) (if ibits𝑁 (𝑖1 ) = 𝑑1𝑁 −𝑘 𝑑𝑘2 ∧ 𝑘 = 𝑖2 mod 𝑁 )
iclz𝑁 (𝑖)
• Return the count of leading zero bits in 𝑖; all bits are considered leading zeros if 𝑖 is 0.
iclz𝑁 (𝑖) = 𝑘 (if ibits𝑁 (𝑖) = 0𝑘 (1 𝑑* )? )
ictz𝑁 (𝑖)
• Return the count of trailing zero bits in 𝑖; all bits are considered trailing zeros if 𝑖 is 0.
ictz𝑁 (𝑖) = 𝑘 (if ibits𝑁 (𝑖) = (𝑑* 1)? 0𝑘 )
ipopcnt𝑁 (𝑖)
• Return the count of non-zero bits in 𝑖.
ipopcnt𝑁 (𝑖) = 𝑘 (if ibits𝑁 (𝑖) = (0* 1)𝑘 0* )
ieqz𝑁 (𝑖)
• Return 1 if 𝑖 is zero, 0 otherwise.
ieqz𝑁 (𝑖) = bool(𝑖 = 0)
inez𝑁 (𝑖)
• Return 0 if 𝑖 is zero, 1 otherwise.
inez𝑁 (𝑖) = bool(𝑖 ̸= 0)
ieq𝑁 (𝑖1 , 𝑖2 )
• Return 1 if 𝑖1 equals 𝑖2 , 0 otherwise.
ieq𝑁 (𝑖1 , 𝑖2 ) = bool(𝑖1 = 𝑖2 )
94 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
ine𝑁 (𝑖1 , 𝑖2 )
• Return 1 if 𝑖1 does not equal 𝑖2 , 0 otherwise.
ine𝑁 (𝑖1 , 𝑖2 ) = bool(𝑖1 ̸= 𝑖2 )
ilt_u𝑁 (𝑖1 , 𝑖2 )
• Return 1 if 𝑖1 is less than 𝑖2 , 0 otherwise.
ilt_u𝑁 (𝑖1 , 𝑖2 ) = bool(𝑖1 < 𝑖2 )
ilt_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1 .
• Let 𝑗2 be the signed interpretation of 𝑖2 .
• Return 1 if 𝑗1 is less than 𝑗2 , 0 otherwise.
ilt_s𝑁 (𝑖1 , 𝑖2 ) = bool(signed𝑁 (𝑖1 ) < signed𝑁 (𝑖2 ))
igt_u𝑁 (𝑖1 , 𝑖2 )
• Return 1 if 𝑖1 is greater than 𝑖2 , 0 otherwise.
igt_u𝑁 (𝑖1 , 𝑖2 ) = bool(𝑖1 > 𝑖2 )
igt_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1 .
• Let 𝑗2 be the signed interpretation of 𝑖2 .
• Return 1 if 𝑗1 is greater than 𝑗2 , 0 otherwise.
igt_s𝑁 (𝑖1 , 𝑖2 ) = bool(signed𝑁 (𝑖1 ) > signed𝑁 (𝑖2 ))
ile_u𝑁 (𝑖1 , 𝑖2 )
• Return 1 if 𝑖1 is less than or equal to 𝑖2 , 0 otherwise.
ile_u𝑁 (𝑖1 , 𝑖2 ) = bool(𝑖1 ≤ 𝑖2 )
ile_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1 .
• Let 𝑗2 be the signed interpretation of 𝑖2 .
• Return 1 if 𝑗1 is less than or equal to 𝑗2 , 0 otherwise.
ile_s𝑁 (𝑖1 , 𝑖2 ) = bool(signed𝑁 (𝑖1 ) ≤ signed𝑁 (𝑖2 ))
ige_u𝑁 (𝑖1 , 𝑖2 )
• Return 1 if 𝑖1 is greater than or equal to 𝑖2 , 0 otherwise.
ige_u𝑁 (𝑖1 , 𝑖2 ) = bool(𝑖1 ≥ 𝑖2 )
4.3. Numerics 95
WebAssembly Specification, Release 3.0 (2025-11-14)
ige_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1 .
• Let 𝑗2 be the signed interpretation of 𝑖2 .
• Return 1 if 𝑗1 is greater than or equal to 𝑗2 , 0 otherwise.
ige_s𝑁 (𝑖1 , 𝑖2 ) = bool(signed𝑁 (𝑖1 ) ≥ signed𝑁 (𝑖2 ))
ibitselect𝑁 (𝑖1 , 𝑖2 , 𝑖3 )
• Let 𝑗1 be the bitwise conjunction of 𝑖1 and 𝑖3 .
• Let 𝑗3′ be the bitwise negation of 𝑖3 .
• Let 𝑗2 be the bitwise conjunction of 𝑖2 and 𝑗3′ .
• Return the bitwise disjunction of 𝑗1 and 𝑗2 .
ibitselect𝑁 (𝑖1 , 𝑖2 , 𝑖3 ) = ior𝑁 (iand𝑁 (𝑖1 , 𝑖3 ), iand𝑁 (𝑖2 , inot𝑁 (𝑖3 )))
iabs𝑁 (𝑖)
• Let 𝑗 be the signed interpretation of 𝑖.
• If 𝑗 is greater than or equal to 0, then return 𝑖.
• Else return the negation of j, modulo 2𝑁 .
iabs𝑁 (𝑖) = 𝑖 (if signed𝑁 (𝑖) ≥ 0)
iabs𝑁 (𝑖) = −signed𝑁 (𝑖) mod 2𝑁 (otherwise)
ineg𝑁 (𝑖)
• Return the result of negating 𝑖, modulo 2𝑁 .
ineg𝑁 (𝑖) = (2𝑁 − 𝑖) mod 2𝑁
imin_u𝑁 (𝑖1 , 𝑖2 )
• Return 𝑖1 if ilt_u𝑁 (𝑖1 , 𝑖2 ) is 1, return 𝑖2 otherwise.
imin_u𝑁 (𝑖1 , 𝑖2 ) = 𝑖1 (if ilt_u𝑁 (𝑖1 , 𝑖2 ) = 1)
imin_u𝑁 (𝑖1 , 𝑖2 ) = 𝑖2 (otherwise)
imin_s𝑁 (𝑖1 , 𝑖2 )
• Return 𝑖1 if ilt_s𝑁 (𝑖1 , 𝑖2 ) is 1, return 𝑖2 otherwise.
imin_s𝑁 (𝑖1 , 𝑖2 ) = 𝑖1 (if ilt_s𝑁 (𝑖1 , 𝑖2 ) = 1)
imin_s𝑁 (𝑖1 , 𝑖2 ) = 𝑖2 (otherwise)
imax_u𝑁 (𝑖1 , 𝑖2 )
• Return 𝑖1 if igt_u𝑁 (𝑖1 , 𝑖2 ) is 1, return 𝑖2 otherwise.
imax_u𝑁 (𝑖1 , 𝑖2 ) = 𝑖1 (if igt_u𝑁 (𝑖1 , 𝑖2 ) = 1)
imax_u𝑁 (𝑖1 , 𝑖2 ) = 𝑖2 (otherwise)
96 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
imax_s𝑁 (𝑖1 , 𝑖2 )
• Return 𝑖1 if igt_s𝑁 (𝑖1 , 𝑖2 ) is 1, return 𝑖2 otherwise.
imax_s𝑁 (𝑖1 , 𝑖2 ) = 𝑖1 (if igt_s𝑁 (𝑖1 , 𝑖2 ) = 1)
imax_s𝑁 (𝑖1 , 𝑖2 ) = 𝑖2 (otherwise)
iadd_sat_u𝑁 (𝑖1 , 𝑖2 )
• Let 𝑖 be the result of adding 𝑖1 and 𝑖2 .
• Return sat_u𝑁 (𝑖).
iadd_sat_u𝑁 (𝑖1 , 𝑖2 ) = sat_u𝑁 (𝑖1 + 𝑖2 )
iadd_sat_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1
• Let 𝑗2 be the signed interpretation of 𝑖2
• Let 𝑗 be the result of adding 𝑗1 and 𝑗2 .
• Return the value whose signed interpretation is sat_s𝑁 (𝑗).
iadd_sat_s𝑁 (𝑖1 , 𝑖2 ) = signed−1
𝑁 (sat_s𝑁 (signed𝑁 (𝑖1 ) + signed𝑁 (𝑖2 )))
isub_sat_u𝑁 (𝑖1 , 𝑖2 )
• Let 𝑖 be the result of subtracting 𝑖2 from 𝑖1 .
• Return sat_u𝑁 (𝑖).
isub_sat_u𝑁 (𝑖1 , 𝑖2 ) = sat_u𝑁 (𝑖1 − 𝑖2 )
isub_sat_s𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗1 be the signed interpretation of 𝑖1
• Let 𝑗2 be the signed interpretation of 𝑖2
• Let 𝑗 be the result of subtracting 𝑗2 from 𝑗1 .
• Return the value whose signed interpretation is sat_s𝑁 (𝑗).
isub_sat_s𝑁 (𝑖1 , 𝑖2 ) = signed−1
𝑁 (sat_s𝑁 (signed𝑁 (𝑖1 ) − signed𝑁 (𝑖2 )))
iavgr_u𝑁 (𝑖1 , 𝑖2 )
• Let 𝑗 be the result of adding 𝑖1 , 𝑖2 , and 1.
• Return the result of dividing 𝑗 by 2, truncated toward zero.
iavgr_u𝑁 (𝑖1 , 𝑖2 ) = trunc((𝑖1 + 𝑖2 + 1)/2)
iq15mulrsat_s𝑁 (𝑖1 , 𝑖2 )
• Return the whose signed interpretation is the result of sat_s𝑁 (ishr_s𝑁 (𝑖1 · 𝑖2 + 214 , 15)).
iq15mulrsat_s𝑁 (𝑖1 , 𝑖2 ) = signed−1 14
𝑁 (sat_s𝑁 (ishr_s𝑁 (𝑖1 · 𝑖2 + 2 , 15)))
4.3. Numerics 97
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
Rounding
Rounding always is round-to-nearest ties-to-even, in correspondence with IEEE 75425 (Section 4.3.1).
An exact floating-point number is a rational number that is exactly representable as a floating-point number of
given bit width 𝑁 .
A limit number for a given floating-point bit width 𝑁 is a positive or negative number whose magnitude is the
smallest power of 2 that is not exactly representable as a floating-point number of width 𝑁 (that magnitude is 2128
for 𝑁 = 32 and 21024 for 𝑁 = 64).
A candidate number is either an exact floating-point number or a positive or negative limit number for the given
bit width 𝑁 .
A candidate pair is a pair 𝑧1 , 𝑧2 of candidate numbers, such that no candidate number exists that lies between the
two.
A real number 𝑟 is converted to a floating-point value of bit width 𝑁 as follows:
• If 𝑟 is 0, then return +0.
• Else if 𝑟 is an exact floating-point number, then return 𝑟.
• Else if 𝑟 greater than or equal to the positive limit, then return +∞.
• Else if 𝑟 is less than or equal to the negative limit, then return −∞.
• Else if 𝑧1 and 𝑧2 are a candidate pair such that 𝑧1 < 𝑟 < 𝑧2 , then:
– If |𝑟 − 𝑧1 | < |𝑟 − 𝑧2 |, then let 𝑧 be 𝑧1 .
– Else if |𝑟 − 𝑧1 | > |𝑟 − 𝑧2 |, then let 𝑧 be 𝑧2 .
– Else if |𝑟 − 𝑧1 | = |𝑟 − 𝑧2 | and the significand of 𝑧1 is even, then let 𝑧 be 𝑧1 .
– Else, let 𝑧 be 𝑧2 .
• If 𝑧 is 0, then:
– If 𝑟 < 0, then return −0.
– Else, return +0.
• Else if 𝑧 is a limit number, then:
– If 𝑟 < 0, then return −∞.
– Else, return +∞.
24 [Link]
25 [Link]
98 Chapter 4. Execution
WebAssembly Specification, Release 3.0 (2025-11-14)
• Else, return 𝑧.
float𝑁 (0) = +0
float𝑁 (𝑟) = 𝑟 (if 𝑟 ∈ exact𝑁 )
float𝑁 (𝑟) = +∞ (if 𝑟 ≥ +limit𝑁 )
float𝑁 (𝑟) = −∞ (if 𝑟 ≤ −limit𝑁 )
float𝑁 (𝑟) = closest𝑁 (𝑟, 𝑧1 , 𝑧2 ) (if 𝑧1 < 𝑟 < 𝑧2 ∧ (𝑧1 , 𝑧2 ) ∈ candidatepair𝑁 )
closest𝑁 (𝑟, 𝑧1 , 𝑧2 ) = rectify𝑁 (𝑟, 𝑧1 ) (if |𝑟 − 𝑧1 | < |𝑟 − 𝑧2 |)
closest𝑁 (𝑟, 𝑧1 , 𝑧2 ) = rectify𝑁 (𝑟, 𝑧2 ) (if |𝑟 − 𝑧1 | > |𝑟 − 𝑧2 |)
closest𝑁 (𝑟, 𝑧1 , 𝑧2 ) = rectify𝑁 (𝑟, 𝑧1 ) (if |𝑟 − 𝑧1 | = |𝑟 − 𝑧2 | ∧ even𝑁 (𝑧1 ))
closest𝑁 (𝑟, 𝑧1 , 𝑧2 ) = rectify𝑁 (𝑟, 𝑧2 ) (if |𝑟 − 𝑧1 | = |𝑟 − 𝑧2 | ∧ even𝑁 (𝑧2 ))
rectify𝑁 (𝑟, ±limit𝑁 ) = ±∞
rectify𝑁 (𝑟, 0) = +0 (𝑟 ≥ 0)
rectify𝑁 (𝑟, 0) = −0 (𝑟 < 0)
rectify𝑁 (𝑟, 𝑧) = 𝑧
where:
exact𝑁 = f 𝑁 ∩Q
expon(𝑁 )−1
limit𝑁 = 22
candidate𝑁 = exact𝑁 ∪ {+limit𝑁 , −limit𝑁 }
candidatepair𝑁 = {(𝑧1 , 𝑧2 ) ∈ candidate2𝑁 | 𝑧1 < 𝑧2 ∧ ∀𝑧 ∈ candidate𝑁 , 𝑧 ≤ 𝑧1 ∨ 𝑧 ≥ 𝑧2 }
even𝑁 ((𝑑 + 𝑚 · 2−𝑀 ) · 2𝑒 ) ⇔ 𝑚 mod 2 = 0
even𝑁 (±limit𝑁 ) ⇔ true
NaN Propagation
When the result of a floating-point operator other than fneg, fabs, or fcopysign is a NaN, then its sign is non-
deterministic and the payload is computed as follows:
• If the payload of all NaN inputs to the operator is canonical (including the case that there are no NaN inputs),
then the payload of the output is canonical as well.
• Otherwise the payload is picked non-deterministically among all arithmetic NaNs; that is, its most significant
bit is 1 and all others are unspecified.
• In the deterministic profile, however, a positive canonical NaNs is reliably produced in the latter case.
The non-deterministic result is expressed by the following auxiliary function producing a set of allowed outputs
from a set of inputs:
nans𝑁 {𝑧 * } = {+nan(canon𝑁 )}
[!DET]
nans𝑁 {𝑧 * } = {+nan(𝑛), −nan(𝑛) | 𝑛 = canon𝑁 } (if {𝑧 * } ⊆ {+nan(canon𝑁 ), −nan(canon𝑁 )}
[!DET]
nans𝑁 {𝑧 * } = {+nan(𝑛), −nan(𝑛) | 𝑛 ≥ canon𝑁 } (if {𝑧 * } ̸⊆ {+nan(canon𝑁 ), −nan(canon𝑁 )}
fadd𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return an element of nans𝑁 {𝑧1 , 𝑧2 }.
• Else if both 𝑧1 and 𝑧2 are infinities of opposite signs, then return an element of nans𝑁 {}.
• Else if both 𝑧1 and 𝑧2 are infinities of equal sign, then return that infinity.
• Else if either 𝑧1 or 𝑧2 is an infinity, then return that infinity.
• Else if both 𝑧1 and 𝑧2 are zeroes of opposite sign, then return positive zero.
• Else if both 𝑧1 and 𝑧2 are zeroes of equal sign, then return that zero.
• Else if either 𝑧1 or 𝑧2 is a zero, then return the other operand.
• Else if both 𝑧1 and 𝑧2 are values with the same magnitude but opposite signs, then return positive zero.
• Else return the result of adding 𝑧1 and 𝑧2 , rounded to the nearest representable value.
4.3. Numerics 99
WebAssembly Specification, Release 3.0 (2025-11-14)
fsub𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return an element of nans𝑁 {𝑧1 , 𝑧2 }.
• Else if both 𝑧1 and 𝑧2 are infinities of equal signs, then return an element of nans𝑁 {}.
• Else if both 𝑧1 and 𝑧2 are infinities of opposite sign, then return 𝑧1 .
• Else if 𝑧1 is an infinity, then return that infinity.
• Else if 𝑧2 is an infinity, then return that infinity negated.
• Else if both 𝑧1 and 𝑧2 are zeroes of equal sign, then return positive zero.
• Else if both 𝑧1 and 𝑧2 are zeroes of opposite sign, then return 𝑧1 .
• Else if 𝑧2 is a zero, then return 𝑧1 .
• Else if 𝑧1 is a zero, then return 𝑧2 negated.
• Else if both 𝑧1 and 𝑧2 are the same value, then return positive zero.
• Else return the result of subtracting 𝑧2 from 𝑧1 , rounded to the nearest representable value.
fsub𝑁 (±nan(𝑛), 𝑧2 ) = nans𝑁 {±nan(𝑛), 𝑧2 }
fsub𝑁 (𝑧1 , ±nan(𝑛)) = nans𝑁 {±nan(𝑛), 𝑧1 }
fsub𝑁 (±∞, ±∞) = nans𝑁 {}
fsub𝑁 (±∞, ∓∞) = ±∞
fsub𝑁 (𝑧1 , ±∞) = ∓∞
fsub𝑁 (±∞, 𝑧2 ) = ±∞
fsub𝑁 (±0, ±0) = +0
fsub𝑁 (±0, ∓0) = ±0
fsub𝑁 (𝑧1 , ±0) = 𝑧1
fsub𝑁 (±0, ±𝑞2 ) = ∓𝑞2
fsub𝑁 (±𝑞, ±𝑞) = +0
fsub𝑁 (𝑧1 , 𝑧2 ) = float𝑁 (𝑧1 − 𝑧2 )
Note
Up to the non-determinism regarding NaNs, it always holds that fsub𝑁 (𝑧1 , 𝑧2 ) = fadd𝑁 (𝑧1 , fneg𝑁 (𝑧2 )).
fmul𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return an element of nans𝑁 {𝑧1 , 𝑧2 }.
• Else if one of 𝑧1 and 𝑧2 is a zero and the other an infinity, then return an element of nans𝑁 {}.
• Else if both 𝑧1 and 𝑧2 are infinities of equal sign, then return positive infinity.
• Else if both 𝑧1 and 𝑧2 are infinities of opposite sign, then return negative infinity.
• Else if either 𝑧1 or 𝑧2 is an infinity and the other a value with equal sign, then return positive infinity.
• Else if either 𝑧1 or 𝑧2 is an infinity and the other a value with opposite sign, then return negative infinity.
• Else if both 𝑧1 and 𝑧2 are zeroes of equal sign, then return positive zero.
• Else if both 𝑧1 and 𝑧2 are zeroes of opposite sign, then return negative zero.
• Else return the result of multiplying 𝑧1 and 𝑧2 , rounded to the nearest representable value.
fmul𝑁 (±nan(𝑛), 𝑧2 ) = nans𝑁 {±nan(𝑛), 𝑧2 }
fmul𝑁 (𝑧1 , ±nan(𝑛)) = nans𝑁 {±nan(𝑛), 𝑧1 }
fmul𝑁 (±∞, ±0) = nans𝑁 {}
fmul𝑁 (±∞, ∓0) = nans𝑁 {}
fmul𝑁 (±0, ±∞) = nans𝑁 {}
fmul𝑁 (±0, ∓∞) = nans𝑁 {}
fmul𝑁 (±∞, ±∞) = +∞
fmul𝑁 (±∞, ∓∞) = −∞
fmul𝑁 (±𝑞1 , ±∞) = +∞
fmul𝑁 (±𝑞1 , ∓∞) = −∞
fmul𝑁 (±∞, ±𝑞2 ) = +∞
fmul𝑁 (±∞, ∓𝑞2 ) = −∞
fmul𝑁 (±0, ±0) = +0
fmul𝑁 (±0, ∓0) = −0
fmul𝑁 (𝑧1 , 𝑧2 ) = float𝑁 (𝑧1 · 𝑧2 )
fdiv𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return an element of nans𝑁 {𝑧1 , 𝑧2 }.
• Else if both 𝑧1 and 𝑧2 are infinities, then return an element of nans𝑁 {}.
• Else if both 𝑧1 and 𝑧2 are zeroes, then return an element of nans𝑁 {𝑧1 , 𝑧2 }.
• Else if 𝑧1 is an infinity and 𝑧2 a value with equal sign, then return positive infinity.
• Else if 𝑧1 is an infinity and 𝑧2 a value with opposite sign, then return negative infinity.
• Else if 𝑧2 is an infinity and 𝑧1 a value with equal sign, then return positive zero.
• Else if 𝑧2 is an infinity and 𝑧1 a value with opposite sign, then return negative zero.
• Else if 𝑧1 is a zero and 𝑧2 a value with equal sign, then return positive zero.
• Else if 𝑧1 is a zero and 𝑧2 a value with opposite sign, then return negative zero.
• Else if 𝑧2 is a zero and 𝑧1 a value with equal sign, then return positive infinity.
• Else if 𝑧2 is a zero and 𝑧1 a value with opposite sign, then return negative infinity.
• Else return the result of dividing 𝑧1 by 𝑧2 , rounded to the nearest representable value.
fdiv𝑁 (±nan(𝑛), 𝑧2 ) = nans𝑁 {±nan(𝑛), 𝑧2 }
fdiv𝑁 (𝑧1 , ±nan(𝑛)) = nans𝑁 {±nan(𝑛), 𝑧1 }
fdiv𝑁 (±∞, ±∞) = nans𝑁 {}
fdiv𝑁 (±∞, ∓∞) = nans𝑁 {}
fdiv𝑁 (±0, ±0) = nans𝑁 {}
fdiv𝑁 (±0, ∓0) = nans𝑁 {}
fdiv𝑁 (±∞, ±𝑞2 ) = +∞
fdiv𝑁 (±∞, ∓𝑞2 ) = −∞
fdiv𝑁 (±𝑞1 , ±∞) = +0
fdiv𝑁 (±𝑞1 , ∓∞) = −0
fdiv𝑁 (±0, ±𝑞2 ) = +0
fdiv𝑁 (±0, ∓𝑞2 ) = −0
fdiv𝑁 (±𝑞1 , ±0) = +∞
fdiv𝑁 (±𝑞1 , ∓0) = −∞
fdiv𝑁 (𝑧1 , 𝑧2 ) = float𝑁 (𝑧1 /𝑧2 )
fma𝑁 (𝑧1 , 𝑧2 , 𝑧3 )
The function fma is the same as fusedMultiplyAdd defined by IEEE 75426 (Section 5.4.1). It computes (𝑧1 ·𝑧2 )+𝑧3
as if with unbounded range and precision, rounding only once for the final result.
• If either 𝑧1 or 𝑧2 or 𝑧3 is a NaN, return an element of nans𝑁 𝑧1 , 𝑧2 , 𝑧3 .
• Else if either 𝑧1 or 𝑧2 is a zero and the other is an infinity, then return an element of nans𝑁 {}.
• Else if both 𝑧1 or 𝑧2 are infinities of equal sign, and 𝑧3 is a negative infinity, then return an element of
nans𝑁 {}.
• Else if both 𝑧1 or 𝑧2 are infinities of opposite sign, and 𝑧3 is a positive infinity, then return an element of
nans𝑁 {}.
• Else if either 𝑧1 or 𝑧2 is an infinity and the other is a value of the same sign, and 𝑧3 is a negative infinity,
then return an element of nans𝑁 {}.
• Else if either 𝑧1 or 𝑧2 is an infinity and the other is a value of the opposite sign, and 𝑧3 is a positive infinity,
then return an element of nans𝑁 {}.
• Else if both 𝑧1 and 𝑧2 are zeroes of the same sign and 𝑧3 is a zero, then return positive zero.
• Else if both 𝑧1 and 𝑧2 are zeroes of the opposite sign and 𝑧3 is a positive zero, then return positive zero.
• Else if both 𝑧1 and 𝑧2 are zeroes of the opposite sign and 𝑧3 is a negative zero, then return negative zero.
• Else return the result of multiplying 𝑧1 and 𝑧2 , adding 𝑧3 to the intermediate, and the final result ref:rounded
<aux-ieee> to the nearest representable value.
fma𝑁 (±nan(𝑛), 𝑧2 , 𝑧3 ) = nans𝑁 {±nan(𝑛), 𝑧2 , 𝑧3 }
fma𝑁 (𝑧1 , ±nan(𝑛), 𝑧3 ) = nans𝑁 {±nan(𝑛), 𝑧1 , 𝑧3 }
fma𝑁 (𝑧1 , 𝑧2 , ±nan(𝑛)) = nans𝑁 {±nan(𝑛), 𝑧1 , 𝑧2 }
fma𝑁 (±∞, ±0, 𝑧3 ) = nans𝑁 {}
fma𝑁 (±∞, ∓0, 𝑧3 ) = nans𝑁 {}
fma𝑁 (±∞, ±∞, −∞) = nans𝑁 {}
fma𝑁 (±∞, ∓∞, +∞) = nans𝑁 {}
fma𝑁 (±𝑞1 , ±∞, −∞) = nans𝑁 {}
fma𝑁 (±𝑞1 , ∓∞, +∞) = nans𝑁 {}
fma𝑁 (±∞, ±𝑞1 , −∞) = nans𝑁 {}
fma𝑁 (∓∞, ±𝑞1 , +∞) = nans𝑁 {}
fma𝑁 (±0, ±0, ∓0) = +0
fma𝑁 (±0, ±0, ±0) = +0
fma𝑁 (±0, ∓0, +0) = +0
fma𝑁 (±0, ∓0, −0) = −0
fma𝑁 (𝑧1 , 𝑧2 , 𝑧3 ) = float𝑁 (𝑧1 · 𝑧2 + 𝑧3 )
fmin𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return an element of nans𝑁 {𝑧1 , 𝑧2 }.
• Else if either 𝑧1 or 𝑧2 is a negative infinity, then return negative infinity.
• Else if either 𝑧1 or 𝑧2 is a positive infinity, then return the other value.
• Else if both 𝑧1 and 𝑧2 are zeroes of opposite signs, then return negative zero.
• Else return the smaller value of 𝑧1 and 𝑧2 .
26 [Link]
fmax𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return an element of nans𝑁 {𝑧1 , 𝑧2 }.
• Else if either 𝑧1 or 𝑧2 is a positive infinity, then return positive infinity.
• Else if either 𝑧1 or 𝑧2 is a negative infinity, then return the other value.
• Else if both 𝑧1 and 𝑧2 are zeroes of opposite signs, then return positive zero.
• Else return the larger value of 𝑧1 and 𝑧2 .
fmax𝑁 (±nan(𝑛), 𝑧2 ) = nans𝑁 {±nan(𝑛), 𝑧2 }
fmax𝑁 (𝑧1 , ±nan(𝑛)) = nans𝑁 {±nan(𝑛), 𝑧1 }
fmax𝑁 (+∞, 𝑧2 ) = +∞
fmax𝑁 (−∞, 𝑧2 ) = 𝑧2
fmax𝑁 (𝑧1 , +∞) = +∞
fmax𝑁 (𝑧1 , −∞) = 𝑧1
fmax𝑁 (±0, ∓0) = +0
fmax𝑁 (𝑧1 , 𝑧2 ) = 𝑧1 (if 𝑧1 ≥ 𝑧2 )
fmax𝑁 (𝑧1 , 𝑧2 ) = 𝑧2 (if 𝑧2 ≥ 𝑧1 )
fcopysign𝑁 (𝑧1 , 𝑧2 )
• If 𝑧1 and 𝑧2 have the same sign, then return 𝑧1 .
• Else return 𝑧1 with negated sign.
fcopysign𝑁 (±𝑝1 , ±𝑝2 ) = ±𝑝1
fcopysign𝑁 (±𝑝1 , ∓𝑝2 ) = ∓𝑝1
fabs𝑁 (𝑧)
• If 𝑧 is a NaN, then return 𝑧 with positive sign.
• Else if 𝑧 is an infinity, then return positive infinity.
• Else if 𝑧 is a zero, then return positive zero.
• Else if 𝑧 is a positive value, then 𝑧.
• Else return 𝑧 negated.
fabs𝑁 (±nan(𝑛)) = +nan(𝑛)
fabs𝑁 (±∞) = +∞
fabs𝑁 (±0) = +0
fabs𝑁 (±𝑞) = +𝑞
fneg𝑁 (𝑧)
• If 𝑧 is a NaN, then return 𝑧 with negated sign.
• Else if 𝑧 is an infinity, then return that infinity negated.
• Else if 𝑧 is a zero, then return that zero negated.
• Else return 𝑧 negated.
fsqrt𝑁 (𝑧)
• If 𝑧 is a NaN, then return an element of nans𝑁 {𝑧}.
• Else if 𝑧 is negative infinity, then return an element of nans𝑁 {}.
• Else if 𝑧 is positive infinity, then return positive infinity.
• Else if 𝑧 is a zero, then return that zero.
• Else if 𝑧 has a negative sign, then return an element of nans𝑁 {}.
• Else return the square root of 𝑧.
fsqrt𝑁 (±nan(𝑛)) = nans𝑁 {±nan(𝑛)}
fsqrt𝑁 (−∞) = nans𝑁 {}
fsqrt𝑁 (+∞) = +∞
fsqrt𝑁 (±0) = ±0
fsqrt𝑁 (−𝑞) = nans𝑁 {}
(︀√ )︀
fsqrt𝑁 (+𝑞) = float𝑁 𝑞
fceil𝑁 (𝑧)
• If 𝑧 is a NaN, then return an element of nans𝑁 {𝑧}.
• Else if 𝑧 is an infinity, then return 𝑧.
• Else if 𝑧 is a zero, then return 𝑧.
• Else if 𝑧 is smaller than 0 but greater than −1, then return negative zero.
• Else return the smallest integral value that is not smaller than 𝑧.
fceil𝑁 (±nan(𝑛)) = nans𝑁 {±nan(𝑛)}
fceil𝑁 (±∞) = ±∞
fceil𝑁 (±0) = ±0
fceil𝑁 (−𝑞) = −0 (if −1 < −𝑞 < 0)
fceil𝑁 (±𝑞) = float𝑁 (𝑖) (if ±𝑞 ≤ 𝑖 < ±𝑞 + 1)
ffloor𝑁 (𝑧)
• If 𝑧 is a NaN, then return an element of nans𝑁 {𝑧}.
• Else if 𝑧 is an infinity, then return 𝑧.
• Else if 𝑧 is a zero, then return 𝑧.
• Else if 𝑧 is greater than 0 but smaller than 1, then return positive zero.
• Else return the largest integral value that is not larger than 𝑧.
ffloor𝑁 (±nan(𝑛)) = nans𝑁 {±nan(𝑛)}
ffloor𝑁 (±∞) = ±∞
ffloor𝑁 (±0) = ±0
ffloor𝑁 (+𝑞) = +0 (if 0 < +𝑞 < 1)
ffloor𝑁 (±𝑞) = float𝑁 (𝑖) (if ±𝑞 − 1 < 𝑖 ≤ ±𝑞)
ftrunc𝑁 (𝑧)
• If 𝑧 is a NaN, then return an element of nans𝑁 {𝑧}.
• Else if 𝑧 is an infinity, then return 𝑧.
• Else if 𝑧 is a zero, then return 𝑧.
• Else if 𝑧 is greater than 0 but smaller than 1, then return positive zero.
• Else if 𝑧 is smaller than 0 but greater than −1, then return negative zero.
• Else return the integral value with the same sign as 𝑧 and the largest magnitude that is not larger than the
magnitude of 𝑧.
ftrunc𝑁 (±nan(𝑛)) = nans𝑁 {±nan(𝑛)}
ftrunc𝑁 (±∞) = ±∞
ftrunc𝑁 (±0) = ±0
ftrunc𝑁 (+𝑞) = +0 (if 0 < +𝑞 < 1)
ftrunc𝑁 (−𝑞) = −0 (if −1 < −𝑞 < 0)
ftrunc𝑁 (±𝑞) = float𝑁 (±𝑖) (if +𝑞 − 1 < 𝑖 ≤ +𝑞)
fnearest𝑁 (𝑧)
• If 𝑧 is a NaN, then return an element of nans𝑁 {𝑧}.
• Else if 𝑧 is an infinity, then return 𝑧.
• Else if 𝑧 is a zero, then return 𝑧.
• Else if 𝑧 is greater than 0 but smaller than or equal to 0.5, then return positive zero.
• Else if 𝑧 is smaller than 0 but greater than or equal to −0.5, then return negative zero.
• Else return the integral value that is nearest to 𝑧; if two values are equally near, return the even one.
fnearest𝑁 (±nan(𝑛)) = nans𝑁 {±nan(𝑛)}
fnearest𝑁 (±∞) = ±∞
fnearest𝑁 (±0) = ±0
fnearest𝑁 (+𝑞) = +0 (if 0 < +𝑞 ≤ 0.5)
fnearest𝑁 (−𝑞) = −0 (if −0.5 ≤ −𝑞 < 0)
fnearest𝑁 (±𝑞) = float𝑁 (±𝑖) (if |𝑖 − 𝑞| < 0.5)
fnearest𝑁 (±𝑞) = float𝑁 (±𝑖) (if |𝑖 − 𝑞| = 0.5 ∧ 𝑖 even)
feq𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return 0.
• Else if both 𝑧1 and 𝑧2 are zeroes, then return 1.
• Else if both 𝑧1 and 𝑧2 are the same value, then return 1.
• Else return 0.
feq𝑁 (±nan(𝑛), 𝑧2 ) = 0
feq𝑁 (𝑧1 , ±nan(𝑛)) = 0
feq𝑁 (±0, ∓0) = 1
feq𝑁 (𝑧1 , 𝑧2 ) = bool(𝑧1 = 𝑧2 )
fne𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return 1.
• Else if both 𝑧1 and 𝑧2 are zeroes, then return 0.
• Else if both 𝑧1 and 𝑧2 are the same value, then return 0.
• Else return 1.
fne𝑁 (±nan(𝑛), 𝑧2 ) = 1
fne𝑁 (𝑧1 , ±nan(𝑛)) = 1
fne𝑁 (±0, ∓0) = 0
fne𝑁 (𝑧1 , 𝑧2 ) = bool(𝑧1 ̸= 𝑧2 )
flt𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return 0.
• Else if 𝑧1 and 𝑧2 are the same value, then return 0.
• Else if 𝑧1 is positive infinity, then return 0.
• Else if 𝑧1 is negative infinity, then return 1.
• Else if 𝑧2 is positive infinity, then return 1.
• Else if 𝑧2 is negative infinity, then return 0.
• Else if both 𝑧1 and 𝑧2 are zeroes, then return 0.
• Else if 𝑧1 is smaller than 𝑧2 , then return 1.
• Else return 0.
flt𝑁 (±nan(𝑛), 𝑧2 ) = 0
flt𝑁 (𝑧1 , ±nan(𝑛)) = 0
flt𝑁 (𝑧, 𝑧) = 0
flt𝑁 (+∞, 𝑧2 ) = 0
flt𝑁 (−∞, 𝑧2 ) = 1
flt𝑁 (𝑧1 , +∞) = 1
flt𝑁 (𝑧1 , −∞) = 0
flt𝑁 (±0, ∓0) = 0
flt𝑁 (𝑧1 , 𝑧2 ) = bool(𝑧1 < 𝑧2 )
fgt𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return 0.
• Else if 𝑧1 and 𝑧2 are the same value, then return 0.
• Else if 𝑧1 is positive infinity, then return 1.
• Else if 𝑧1 is negative infinity, then return 0.
• Else if 𝑧2 is positive infinity, then return 0.
• Else if 𝑧2 is negative infinity, then return 1.
• Else if both 𝑧1 and 𝑧2 are zeroes, then return 0.
• Else if 𝑧1 is larger than 𝑧2 , then return 1.
• Else return 0.
fgt𝑁 (±nan(𝑛), 𝑧2 ) = 0
fgt𝑁 (𝑧1 , ±nan(𝑛)) = 0
fgt𝑁 (𝑧, 𝑧) = 0
fgt𝑁 (+∞, 𝑧2 ) = 1
fgt𝑁 (−∞, 𝑧2 ) = 0
fgt𝑁 (𝑧1 , +∞) = 0
fgt𝑁 (𝑧1 , −∞) = 1
fgt𝑁 (±0, ∓0) = 0
fgt𝑁 (𝑧1 , 𝑧2 ) = bool(𝑧1 > 𝑧2 )
fle𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return 0.
• Else if 𝑧1 and 𝑧2 are the same value, then return 1.
• Else if 𝑧1 is positive infinity, then return 0.
• Else if 𝑧1 is negative infinity, then return 1.
• Else if 𝑧2 is positive infinity, then return 1.
• Else if 𝑧2 is negative infinity, then return 0.
• Else if both 𝑧1 and 𝑧2 are zeroes, then return 1.
• Else if 𝑧1 is smaller than or equal to 𝑧2 , then return 1.
• Else return 0.
fle𝑁 (±nan(𝑛), 𝑧2 ) = 0
fle𝑁 (𝑧1 , ±nan(𝑛)) = 0
fle𝑁 (𝑧, 𝑧) = 1
fle𝑁 (+∞, 𝑧2 ) = 0
fle𝑁 (−∞, 𝑧2 ) = 1
fle𝑁 (𝑧1 , +∞) = 1
fle𝑁 (𝑧1 , −∞) = 0
fle𝑁 (±0, ∓0) = 1
fle𝑁 (𝑧1 , 𝑧2 ) = bool(𝑧1 ≤ 𝑧2 )
fge𝑁 (𝑧1 , 𝑧2 )
• If either 𝑧1 or 𝑧2 is a NaN, then return 0.
• Else if 𝑧1 and 𝑧2 are the same value, then return 1.
• Else if 𝑧1 is positive infinity, then return 1.
• Else if 𝑧1 is negative infinity, then return 0.
• Else if 𝑧2 is positive infinity, then return 0.
• Else if 𝑧2 is negative infinity, then return 1.
• Else if both 𝑧1 and 𝑧2 are zeroes, then return 1.
• Else if 𝑧1 is larger than or equal to 𝑧2 , then return 1.
• Else return 0.
fge𝑁 (±nan(𝑛), 𝑧2 ) = 0
fge𝑁 (𝑧1 , ±nan(𝑛)) = 0
fge𝑁 (𝑧, 𝑧) = 1
fge𝑁 (+∞, 𝑧2 ) = 1
fge𝑁 (−∞, 𝑧2 ) = 0
fge𝑁 (𝑧1 , +∞) = 0
fge𝑁 (𝑧1 , −∞) = 1
fge𝑁 (±0, ∓0) = 1
fge𝑁 (𝑧1 , 𝑧2 ) = bool(𝑧1 ≥ 𝑧2 )
fpmin𝑁 (𝑧1 , 𝑧2 )
• If 𝑧2 is less than 𝑧1 then return 𝑧2 .
• Else return 𝑧1 .
fpmin𝑁 (𝑧1 , 𝑧2 ) = 𝑧2 (if flt𝑁 (𝑧2 , 𝑧1 ) = 1)
fpmin𝑁 (𝑧1 , 𝑧2 ) = 𝑧1 (otherwise)
fpmax𝑁 (𝑧1 , 𝑧2 )
• If 𝑧1 is less than 𝑧2 then return 𝑧2 .
• Else return 𝑧1 .
fpmax𝑁 (𝑧1 , 𝑧2 ) = 𝑧2 (if flt𝑁 (𝑧1 , 𝑧2 ) = 1)
fpmax𝑁 (𝑧1 , 𝑧2 ) = 𝑧1 (otherwise)
4.3.4 Conversions
extendu 𝑀,𝑁 (𝑖)
• Return 𝑖.
extendu 𝑀,𝑁 (𝑖) = 𝑖
Note
In the abstract syntax, unsigned extension just reinterprets the same value.
wrap𝑀,𝑁 (𝑖)
• Return 𝑖 modulo 2𝑁 .
wrap𝑀,𝑁 (𝑖) = 𝑖 mod 2𝑁
Note
This operator is partial. It is not defined for NaNs, infinities, or values for which the result is out of range.
Note
This operator is partial. It is not defined for NaNs, infinities, or values for which the result is out of range.
trunc_sat_u𝑀,𝑁 (𝑧)
• If 𝑧 is a NaN, then return 0.
• Else if 𝑧 is negative infinity, then return 0.
• Else if 𝑧 is positive infinity, then return 2𝑁 − 1.
• Else, return sat_u𝑁 (trunc(𝑧)).
trunc_sat_u𝑀,𝑁 (±nan(𝑛)) = 0
trunc_sat_u𝑀,𝑁 (−∞) = 0
trunc_sat_u𝑀,𝑁 (+∞) = 2𝑁 − 1
trunc_sat_u𝑀,𝑁 (𝑧) = sat_u𝑁 (trunc(𝑧))
trunc_sat_s𝑀,𝑁 (𝑧)
• If 𝑧 is a NaN, then return 0.
• Else if 𝑧 is negative infinity, then return −2𝑁 −1 .
• Else if 𝑧 is positive infinity, then return 2𝑁 −1 − 1.
• Else, return the value whose signed interpretation is sat_s𝑁 (trunc(𝑧)).
trunc_sat_s𝑀,𝑁 (±nan(𝑛)) = 0
trunc_sat_s𝑀,𝑁 (−∞) = −2𝑁 −1
trunc_sat_s𝑀,𝑁 (+∞) = 2𝑁 −1 − 1
trunc_sat_s𝑀,𝑁 (𝑧) = signed−1
𝑁 (sat_s𝑁 (trunc(𝑧)))
promote𝑀,𝑁 (𝑧)
• If 𝑧 is a canonical NaN, then return an element of nans𝑁 {} (i.e., a canonical NaN of size 𝑁 ).
• Else if 𝑧 is a NaN, then return an element of nans𝑁 {±nan(1)} (i.e., any arithmetic NaN of size 𝑁 ).
• Else, return 𝑧.
promote𝑀,𝑁 (±nan(𝑛)) = nans𝑁 {} (if 𝑛 = canon𝑁 )
promote𝑀,𝑁 (±nan(𝑛)) = nans𝑁 {+nan(1)} (otherwise)
promote𝑀,𝑁 (𝑧) = 𝑧
demote𝑀,𝑁 (𝑧)
• If 𝑧 is a canonical NaN, then return an element of nans𝑁 {} (i.e., a canonical NaN of size 𝑁 ).
• Else if 𝑧 is a NaN, then return an element of nans𝑁 {±nan(1)} (i.e., any NaN of size 𝑁 ).
• Else if 𝑧 is an infinity, then return that infinity.
• Else if 𝑧 is a zero, then return that zero.
• Else, return float𝑁 (𝑧).
ivbitmask𝑁 (𝑖𝑚 )
1. For each 𝑖𝑘 in 𝑖𝑚 , let 𝑏𝑘 be the result of computing ilt_s𝑁 (𝑖, 0).
2. Let 𝑏𝑚 be the concatenation of all 𝑏𝑘 .
3. Return the result of computing ibits−1
32 ((0)
32−𝑚 𝑚
𝑏 ).
ivbitmask𝑁 (𝑖𝑚 ) = ibits−1
32 ((0)
32−𝑚
ilt_s𝑁 (𝑖, 0)𝑚 )
ivswizzle(𝑖𝑛 , 𝑗 𝑛 )
1. For each 𝑗𝑘 in 𝑗 𝑛 , let 𝑟𝑘 be the value ivswizzle_lane(𝑖𝑛 , 𝑗𝑘 ).
2. Let 𝑟𝑛 be the concatenation of all 𝑟𝑘 .
3. Return 𝑟𝑛 .
ivswizzle(𝑖𝑛 , 𝑗 𝑛 ) = ivswizzle_lane(𝑖𝑛 , 𝑗)𝑛
where:
ivswizzle_lane(𝑖𝑛 , 𝑗) = 𝑖𝑛 [𝑗] (if 𝑗 < 𝑛)
ivswizzle_lane(𝑖𝑛 , 𝑗) = 0 (otherwise)
ivadd_pairwise𝑁 (𝑖2𝑚 )
1. Let (𝑖1 𝑖2 )𝑚 be 𝑖2𝑚 , decomposed into pairwise elements.
2. For each 𝑖1𝑘 in 𝑖𝑚
1 and corresponding 𝑖2𝑘 in 𝑖2 , let 𝑟𝑘 be iadd𝑁 (𝑖1𝑘 , 𝑖2𝑘 ).
𝑚
ivmul𝑁 (𝑖𝑚 𝑚
1 , 𝑖2 )
ivdot𝑁 (𝑖2𝑚 2𝑚
1 , 𝑖2 )
ivdotsat𝑁 (𝑖𝑚 𝑚
1 , 𝑖2 )
vextbinop 𝑓 sx 1 sx 2 ℎ 𝑘
extmul_low_sx ivmul sx sx 0 𝑀2
extmul_high_sx ivmul sx sx 𝑀2 𝑀2
dot_s ivdot s s 0 𝑀1
relaxed_dot_s ivdotsat s relaxed(𝑅idot )[s, u] 0 𝑀1
Note
while ×{𝑥* }𝑁 transforms a sequence of 𝑁 sets of non-deterministic values into a set of non-deterministic se-
quences of 𝑁 values by computing the set product:
×(𝑆1 . . . 𝑆𝑁 ) = {𝑥1 . . . 𝑥𝑁 | 𝑥1 ∈ 𝑆1 ∧ · · · ∧ 𝑥𝑁 ∈ 𝑆𝑁 }
Note
Each parameter can be thought of as inducing a family of operations that is fixed to one particular choice by an
implementation. The fixed operation itself can still be non-deterministic or partial.
Implementations are expexted to either choose the behaviour that is the most efficient on the underlying hard-
ware, or the behaviour of the deterministic profile.
frelaxed_madd𝑁 (𝑧1 , 𝑧2 , 𝑧3 )
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅fmadd ∈ {0, 1}.
• Return relaxed(𝑅fmadd )[fadd𝑁 (fmul𝑁 (𝑧1 , 𝑧2 ), 𝑧3 ), fma𝑁 (𝑧1 , 𝑧2 , 𝑧3 )].
frelaxed_madd𝑁 (𝑧1 , 𝑧2 , 𝑧3 ) = relaxed(𝑅fmadd )[fadd𝑁 (fmul𝑁 (𝑧1 , 𝑧2 ), 𝑧3 ), fma𝑁 (𝑧1 , 𝑧2 , 𝑧3 )]
Note
Relaxed multiply-add allows for fused or unfused results, which leads to implementation-dependent rounding
behaviour. In the deterministic profile, the unfused behaviour is used.
frelaxed_nmadd𝑁 (𝑧1 , 𝑧2 , 𝑧3 )
• Return frelaxed_madd(−𝑧1 , 𝑧2 , 𝑧3 ).
frelaxed_nmadd𝑁 (𝑧1 , 𝑧2 , 𝑧3 ) = frelaxed_madd𝑁 (−𝑧1 , 𝑧2 , 𝑧3 )
Note
frelaxed_min𝑁 (𝑧1 , 𝑧2 )
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅fmin ∈
{0, 1, 2, 3}.
• If 𝑧1 is a NaN, then return relaxed(𝑅fmin )[fmin𝑁 (𝑧1 , 𝑧2 ), nan(𝑛), 𝑧2 , 𝑧2 ].
• If 𝑧2 is a NaN, then return relaxed(𝑅fmin )[fmin𝑁 (𝑧1 , 𝑧2 ), 𝑧1 , nan(𝑛), 𝑧1 ].
• If both 𝑧1 and 𝑧2 are zeroes of opposite sign, then return relaxed(𝑅fmin )[fmin𝑁 (𝑧1 , 𝑧2 ), pm 0, mp 0, -0 ]`.
• Return fmin𝑁 (𝑧1 , 𝑧2 ).
frelaxed_min𝑁 (±nan(𝑛), 𝑧2 ) = relaxed(𝑅fmin )[fmin𝑁 (±nan(𝑛), 𝑧2 ), nan(𝑛), 𝑧2 , 𝑧2 ]
frelaxed_min𝑁 (𝑧1 , ±nan(𝑛)) = relaxed(𝑅fmin )[fmin𝑁 (𝑧1 , ±nan(𝑛)), 𝑧1 , nan(𝑛), 𝑧1 ]
frelaxed_min𝑁 (±0, ∓0) = relaxed(𝑅fmin )[fmin𝑁 (±0, ∓0), ±0, ∓0, −0]
frelaxed_min𝑁 (𝑧1 , 𝑧2 ) = fmin𝑁 (𝑧1 , 𝑧2 ) (otherwise)
Note
Relaxed minimum is implementation-dependent for NaNs and for zeroes with different signs. In the determin-
istic profile, it behaves like regular fmin.
frelaxed_max𝑁 (𝑧1 , 𝑧2 )
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅fmax ∈
{0, 1, 2, 3}.
• If 𝑧1 is a NaN, then return relaxed(𝑅fmax )[fmax𝑁 (𝑧1 , 𝑧2 ), nan(𝑛), 𝑧2 , 𝑧2 ].
• If 𝑧2 is a NaN, then return relaxed(𝑅fmax )[fmax𝑁 (𝑧1 , 𝑧2 ), 𝑧1 , nan(𝑛), 𝑧1 ].
• If both 𝑧1 and 𝑧2 are zeroes of opposite sign, then return relaxed(𝑅fmax )[fmax𝑁 (𝑧1 , 𝑧2 ), pm 0, mp 0, +0 ]`.
• Return fmax𝑁 (𝑧1 , 𝑧2 ).
frelaxed_max𝑁 (±nan(𝑛), 𝑧2 ) = relaxed(𝑅fmax )[fmax𝑁 (±nan(𝑛), 𝑧2 ), nan(𝑛), 𝑧2 , 𝑧2 ]
frelaxed_max𝑁 (𝑧1 , ±nan(𝑛)) = relaxed(𝑅fmax )[fmax𝑁 (𝑧1 , ±nan(𝑛)), 𝑧1 , nan(𝑛), 𝑧1 ]
frelaxed_max𝑁 (±0, ∓0) = relaxed(𝑅fmax )[fmax𝑁 (±0, ∓0), ±0, ∓0, +0]
frelaxed_max𝑁 (𝑧1 , 𝑧2 ) = fmax𝑁 (𝑧1 , 𝑧2 ) (otherwise)
Note
Relaxed maximum is implementation-dependent for NaNs and for zeroes with different signs. In the determin-
istic profile, it behaves like regular fmax.
irelaxed_q15mulr_s𝑁 (𝑖1 , 𝑖2 )
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅iq15mulr ∈ {0, 1}.
• If both 𝑖1 and 𝑖2 equal (signed−1
𝑁 (−2
𝑁 −1
), then return relaxed(𝑅iq15mulr )[2𝑁 −1 − 1, signed−1
𝑁 (−2
𝑁 −1
)].
• Return iq15mulrsat_s(𝑖1 , 𝑖2 )
irelaxed_q15mulr_s𝑁 (signed−1 𝑁 (−2
𝑁 −1
), signed−1
𝑁 (−2
𝑁 −1
)) = relaxed(𝑅iq15mulr )[2𝑁 −1 − 1, signed−1
𝑁 (−2
𝑁 −1
)]
irelaxed_q15mulr_s𝑁 (𝑖1 , 𝑖2 ) = iq15mulrsat_s(𝑖1 , 𝑖2 )
Note
Relaxed Q15 multiplication is implementation-dependent when the result overflows. In the deterministic pro-
file, it behaves like regular iq15mulrsat_s.
relaxed_trunc𝑢𝑀,𝑁 (𝑧)
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅trunc_u ∈ {0, 1}.
• If 𝑧 is normal or subnormal and trunc(𝑧) is non-negative and less than 2𝑁 , then return truncu 𝑀,𝑁 (𝑧).
• Else, return relaxed(𝑅trunc_u )[trunc_sat_u𝑀,𝑁 (𝑧), R].
relaxed_trunc𝑢𝑀,𝑁 (±𝑞) = truncu 𝑀,𝑁 (±𝑞) (if 0 ≤ trunc(±𝑞) < 2𝑁 )
relaxed_trunc𝑢𝑀,𝑁 (𝑧) = relaxed(𝑅trunc_u )[trunc_sat_u𝑀,𝑁 (𝑧), R] (otherwise)
Note
Relaxed unsigned truncation is non-deterministic for NaNs and out-of-range values. In the deterministic profile,
it behaves like regular trunc_sat_u.
relaxed_trunc𝑠𝑀,𝑁 (𝑧)
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅trunc_s ∈ {0, 1}.
• If 𝑧 is normal or subnormal and trunc(𝑧) is greater than or equal to −2𝑁 −1 and less than 2𝑁 −1 , then return
truncs 𝑀,𝑁 (𝑧).
• Else, return relaxed(𝑅trunc_s )[trunc_sat_s𝑀,𝑁 (𝑧), R].
relaxed_trunc𝑠𝑀,𝑁 (±𝑞) = truncs 𝑀,𝑁 (±𝑞) (if −2𝑁 −1 ≤ trunc(±𝑞) < 2𝑁 −1 )
relaxed_trunc𝑠𝑀,𝑁 (𝑧) = relaxed(𝑅trunc_s )[trunc_sat_s𝑀,𝑁 (𝑧), R] (otherwise)
Note
Relaxed signed truncation is non-deterministic for NaNs and out-of-range values. In the deterministic profile,
it behaves like regular trunc_sat_s.
ivrelaxed_swizzle(𝑖𝑛 , 𝑗 𝑛 )
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅swizzle ∈ {0, 1}.
• For each 𝑗𝑘 in 𝑗 𝑛 , let 𝑟𝑘 be the value ivrelaxed_swizzle_lane(𝑖𝑛 , 𝑗𝑘 ).
• Let 𝑟𝑛 be the concatenation of all 𝑟𝑘 .
• Return 𝑟𝑛 .
Note
Relaxed swizzle is implementation-dependent if the signed interpretation of any of the 8-bit indices in 𝑗 𝑛 is
larger than or equal to 16. In the deterministic profile, it behaves like regular ivswizzle.
relaxed_dot(𝑖1 , 𝑖2 )
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅idot ∈ {0, 1}. It
also affects the behaviour of relaxed_dot_add.
Its definition is part of the definition of vextbinop specified above.
Note
Relaxed dot product is implementation-dependent when the second operand is negative in a signed intepretation.
In the deterministic profile, it behaves like signed dot product.
irelaxed_laneselect𝑁 (𝑖1 , 𝑖2 , 𝑖3 )
The implementation-specific behaviour of this operation is determined by the global parameter 𝑅laneselect ∈ {0, 1}.
• If 𝑖3 is smaller than 2𝑁 −1 , then let 𝑖′3 be the value 0, otherwise 2𝑁 − 1.
• Let 𝑖′′3 be relaxed(𝑅laneselect )[𝑖3 , 𝑖′3 ].
• Return ibitselect𝑁 (𝑖1 , 𝑖2 , 𝑖′′3 ).
irelaxed_laneselect𝑁 (𝑖1 , 𝑖2 , 𝑖3 ) = ibitselect𝑁 (𝑖1 , 𝑖2 , relaxed(𝑅laneselect )[𝑖3 , extends 1,𝑁 (ishr_u𝑁 (𝑖3 , 𝑁 − 1))])
Note
Relaxed lane selection is non-deterministic when the mask mixes set and cleared bits, since the value of the
high bit may or may not be expanded to all bits. In the deterministic profile, it behaves like ibitselect.
4.4 Types
Execution has to check and compare types in a few places, such as executing call_indirect or instantiating modules.
It is an invariant of the semantics that all types occurring during execution are closed.
Note
Runtime type checks generally involve types from multiple modules or types not defined by a module at all,
such that any module-local type indices occurring inside them would not generally be meaningful.
4.4.1 Instantiation
Any form of type can be instantiated into a closed type inside a module instance by substituting each type index 𝑥
occurring in it with the corresponding defined type [Link][𝑥].
closmoduleinst (𝑡) = 𝑡[:= dt * ] if dt * = [Link]
Note
This is the runtime equivalent to type closure, which is applied at validation time.
4.5 Values
4.5.1 Value Typing
For the purpose of checking argument values against the parameter types of exported functions, values are classified
by value types. The following auxiliary typing rules specify this typing relation relative to a store 𝑆 in which
possibly referenced addresses live.
Numeric Values
The number value ([Link] 𝑐) is valid with the number type nt.
𝑠 ⊢ [Link] 𝑐 : nt
Vector Values
The vector value ([Link] 𝑐) is valid with the vector type vt.
𝑠 ⊢ [Link] 𝑐 : vt
Null References
The reference value ([Link] ht) is valid with the reference type (ref null ht ′ ) if:
• The heap type ht ′ matches the heap type ht.
{} ⊢ ht ′ ≤ ht
𝑠 ⊢ [Link] ht : (ref null ht ′ )
Note
A null reference can be typed with any smaller type. In particular, that allows it to be typed with the least type
in its respective hierarchy. That ensures that the value is compatible with any nullable type in that hierarchy.
Scalar References
The reference value (ref.i31 𝑖) is valid with the reference type (ref i31).
Structure References
The reference value ([Link] 𝑎) is valid with the reference type (ref dt) if:
• The structure instance 𝑠.structs[𝑎] exists.
• The defined type 𝑠.structs[𝑎].type is of the form dt.
𝑠.structs[𝑎].type = dt
𝑠 ⊢ [Link] 𝑎 : (ref dt)
Array References
The reference value ([Link] 𝑎) is valid with the reference type (ref dt) if:
• The array instance 𝑠.arrays[𝑎] exists.
• The defined type 𝑠.arrays[𝑎].type is of the form dt.
𝑠.arrays[𝑎].type = dt
𝑠 ⊢ [Link] 𝑎 : (ref dt)
Exception References
The reference value ([Link] 𝑎) is valid with the reference type (ref exn) if:
• The exception instance 𝑠.exns[𝑎] exists.
𝑠.exns[𝑎] = exn
𝑠 ⊢ [Link] 𝑎 : (ref exn)
Function References
The reference value ([Link] 𝑎) is valid with the reference type (ref dt) if:
• The function instance 𝑠.funcs[𝑎] exists.
• The defined type 𝑠.funcs[𝑎].type is of the form dt.
𝑠.funcs[𝑎].type = dt
𝑠 ⊢ [Link] 𝑎 : (ref dt)
Host References
The reference value ([Link] 𝑎) is valid with the reference type (ref any).
Note
External References
The reference value ([Link] addrref ) is valid with the reference type (ref extern) if:
• The reference value addrref is valid with the reference type (ref any).
𝑠 ⊢ addrref : (ref any)
𝑠 ⊢ [Link] addrref : (ref extern)
Subsumption
The reference value ref is valid with the reference type rt if:
• The reference value ref is valid with the reference type rt ′ .
• The reference type rt ′ matches the reference type rt.
𝑠 ⊢ ref : rt ′ {} ⊢ rt ′ ≤ rt
𝑠 ⊢ ref : rt
Functions
The external address (func 𝑎) is valid with the external type (func [Link]) if:
• The function instance 𝑠.funcs[𝑎] exists.
• The function instance 𝑠.funcs[𝑎] is of the form funcinst.
𝑠.funcs[𝑎] = funcinst
𝑠 ⊢ func 𝑎 : func [Link]
Tables
The external address (table 𝑎) is valid with the external type (table [Link]) if:
• The table instance 𝑠.tables[𝑎] exists.
• The table instance 𝑠.tables[𝑎] is of the form tableinst.
𝑠.tables[𝑎] = tableinst
𝑠 ⊢ table 𝑎 : table [Link]
Memories
The external address (mem 𝑎) is valid with the external type (mem [Link]) if:
• The memory instance 𝑠.mems[𝑎] exists.
• The memory instance 𝑠.mems[𝑎] is of the form meminst.
𝑠.mems[𝑎] = meminst
𝑠 ⊢ mem 𝑎 : mem [Link]
Globals
The external address (global 𝑎) is valid with the external type (global [Link]) if:
• The global instance 𝑠.globals[𝑎] exists.
• The global instance 𝑠.globals[𝑎] is of the form globalinst.
𝑠.globals[𝑎] = globalinst
𝑠 ⊢ global 𝑎 : global [Link]
Tags
The external address (tag 𝑎) is valid with the external type (tag [Link]) if:
• The tag instance 𝑠.tags[𝑎] exists.
• The tag instance 𝑠.tags[𝑎] is of the form taginst.
𝑠.tags[𝑎] = taginst
𝑠 ⊢ tag 𝑎 : tag [Link]
Subsumption
The external address externaddr is valid with the external type xt if:
• The external address externaddr is valid with the external type xt ′ .
• The external type xt ′ matches the external type xt.
𝑠 ⊢ externaddr : xt ′ {} ⊢ xt ′ ≤ xt
𝑠 ⊢ externaddr : xt
4.6 Instructions
WebAssembly computation is performed by executing individual instructions.
unreachable
1. Trap.
unreachable ˓→ trap
drop
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value val from the stack.
val drop ˓→ 𝜖
select (𝑡* )?
1. Assert: Due to validation, a value of number type i32 is on the top of the stack.
2. Pop the value ([Link] 𝑐) from the stack.
3. Assert: Due to validation, a value is on the top of the stack.
4. Pop the value val 2 from the stack.
5. Assert: Due to validation, a value is on the top of the stack.
6. Pop the value val 1 from the stack.
7. If 𝑐 ̸= 0, then:
a. Push the value val 1 to the stack.
8. Else:
a. Push the value val 2 to the stack.
val 1 val 2 ([Link] 𝑐) (select (𝑡* )? ) ˓→ val 1 if 𝑐 ̸= 0
val 1 val 2 ([Link] 𝑐) (select (𝑡* )? ) ˓→ val 2 if 𝑐 = 0
Note
In future versions of WebAssembly, select may allow more than one value per choice.
loop bt instr *
1. Let 𝑧 be the current state.
2. Let 𝑡𝑚
1 →localidx * 𝑡𝑛 be the destructuring of instrtype𝑧 (bt).
0 2
if bt instr *1 instr *2
1. Assert: Due to validation, a value of number type i32 is on the top of the stack.
2. Pop the value ([Link] 𝑐) from the stack.
3. If 𝑐 ̸= 0, then:
a. Execute the instruction (block bt instr *1 ).
4. Else:
a. Execute the instruction (block bt instr *2 ).
([Link] 𝑐) (if bt instr *1 else instr *2 ) ˓→ (block bt instr *1 ) if 𝑐 ̸= 0
([Link] 𝑐) (if bt instr *1 else instr *2 ) ˓→ (block bt instr *2 ) if 𝑐 = 0
br 𝑙
1. If the first non-value entry of the stack is a label, then:
a. Let 𝐿 be the topmost label.
b. Let 𝑛 be the arity of 𝐿
c. If 𝑙 = 0, then:
1) Assert: Due to validation, there are at least 𝑛 values on the top of the stack.
2) Pop the values val 𝑛 from the stack.
*
3) Pop all values val ′ from the top of the stack.
4) Pop the label from the stack.
br_if 𝑙
1. Assert: Due to validation, a value of number type i32 is on the top of the stack.
2. Pop the value ([Link] 𝑐) from the stack.
3. If 𝑐 ̸= 0, then:
a. Execute the instruction (br 𝑙).
4. Else:
a. Do nothing.
([Link] 𝑐) (br_if 𝑙) ˓→ (br 𝑙) if 𝑐 ̸= 0
([Link] 𝑐) (br_if 𝑙) ˓→ 𝜖 if 𝑐 = 0
br_table 𝑙* 𝑙′
1. Assert: Due to validation, a value of number type i32 is on the top of the stack.
2. Pop the value ([Link] 𝑖) from the stack.
3. If 𝑖 < |𝑙* |, then:
a. Execute the instruction (br 𝑙* [𝑖]).
4. Else:
a. Execute the instruction (br 𝑙′ ).
([Link] 𝑖) (br_table 𝑙* 𝑙′ ) ˓→ (br 𝑙* [𝑖]) if 𝑖 < |𝑙* |
([Link] 𝑖) (br_table 𝑙* 𝑙′ ) ˓→ (br 𝑙′ ) if 𝑖 ≥ |𝑙* |
br_on_null 𝑙
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value val from the stack.
3. If val is some [Link] heaptype, then:
a. Execute the instruction (br 𝑙).
4. Else:
a. Push the value val to the stack.
val (br_on_null 𝑙) ˓→ (br 𝑙) if val = [Link] ht
val (br_on_null 𝑙) ˓→ val otherwise
br_on_non_null 𝑙
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value val from the stack.
3. If val is some [Link] heaptype, then:
a. Do nothing.
4. Else:
a. Push the value val to the stack.
b. Execute the instruction (br 𝑙).
val (br_on_non_null 𝑙) ˓→ 𝜖 if val = [Link] ht
val (br_on_non_null 𝑙) ˓→ val (br 𝑙) otherwise
br_on_cast 𝑙 rt 1 rt 2
1. Let 𝑓 be the topmost frame.
2. Assert: Due to validation, a reference value is on the top of the stack.
3. Pop the value ref from the stack.
4. Let rt be the type of ref .
5. Push the value ref to the stack.
6. If rt matches clos𝑓.module (rt 2 ), then:
a. Execute the instruction (br 𝑙).
7. Else:
a. Do nothing.
𝑠; 𝑓 ; ref (br_on_cast 𝑙 rt 1 rt 2 ) ˓→ ref (br 𝑙) if 𝑠 ⊢ ref : rt
∧ {} ⊢ rt ≤ clos𝑓.module (rt 2 )
𝑠; 𝑓 ; ref (br_on_cast 𝑙 rt 1 rt 2 ) ˓→ ref otherwise
br_on_cast_fail 𝑙 rt 1 rt 2
1. Let 𝑓 be the topmost frame.
2. Assert: Due to validation, a reference value is on the top of the stack.
3. Pop the value ref from the stack.
4. Let rt be the type of ref .
5. Push the value ref to the stack.
6. If rt matches clos𝑓.module (rt 2 ), then:
a. Do nothing.
7. Else:
a. Execute the instruction (br 𝑙).
𝑠; 𝑓 ; ref (br_on_cast_fail 𝑙 rt 1 rt 2 ) ˓→ ref if 𝑠 ⊢ ref : rt
∧ {} ⊢ rt ≤ clos𝑓.module (rt 2 )
𝑠; 𝑓 ; ref (br_on_cast_fail 𝑙 rt 1 rt 2 ) ˓→ ref (br 𝑙) otherwise
return
1. If the first non-value entry of the stack is a frame, then:
a. Let 𝑓 be the topmost frame.
b. Let 𝑛 be the arity of 𝑓
c. Assert: Due to validation, there are at least 𝑛 values on the top of the stack.
d. Pop the values val 𝑛 from the stack.
*
e. Pop all values val ′ from the top of the stack.
f. Pop the frame from the stack.
g. Push the values val 𝑛 to the stack.
2. Else if the first non-value entry of the stack is a label, then:
a. Pop all values val * from the top of the stack.
b. Pop the label from the stack.
c. Push the values val * to the stack.
d. Execute the instruction return.
3. Else:
a. Assert: Due to validation, the first non-value entry of the stack is a handler.
b. Pop all values val * from the top of the stack.
c. Pop the handler from the stack.
d. Push the values val * to the stack.
e. Execute the instruction return.
*
(frame𝑛 {𝑓 } val ′ val 𝑛 return instr * ) ˓→ val 𝑛
*
(label𝑛 {instr ′ } val * return instr * ) ˓→ val * return
(handler𝑛 {catch * } val * return instr * ) ˓→ val * return
call 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, 𝑥 < |𝑧.[Link]|.
3. Let 𝑎 be the address 𝑧.[Link][𝑥].
4. Assert: Due to validation, 𝑎 < |𝑧.funcs|.
5. Push the value ([Link] 𝑎) to the stack.
6. Execute the instruction (call_ref 𝑧.funcs[𝑎].type).
𝑧; (call 𝑥) ˓→ ([Link] 𝑎) (call_ref 𝑧.funcs[𝑎].type) if 𝑧.[Link][𝑥] = 𝑎
call_ref 𝑥
Todo
(*) Prose not spliced, for the prose merges the two cases of null and non-null references.
1. Assert: due to validation, a null or function reference is on the top of the stack.
2. Pop the reference value 𝑟 from the stack.
3. If 𝑟 is [Link] ht, then:
a. Trap.
4. Assert: due to validation, 𝑟 is a function reference.
5. Let [Link] 𝑎 be the reference 𝑟.
6. Invoke the function instance at address 𝑎.
𝑧; ([Link] ht) (call_ref 𝑦) ˓→ trap
Note
The formal rule for calling a non-null function reference is described below.
call_indirect 𝑥 𝑦
1. Execute the instruction ([Link] 𝑥).
2. Execute the instruction ([Link] (ref null 𝑦)).
3. Execute the instruction (call_ref 𝑦).
(call_indirect 𝑥 𝑦) ˓→ ([Link] 𝑥) ([Link] (ref null 𝑦)) (call_ref 𝑦)
return_call 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, 𝑥 < |𝑧.[Link]|.
3. Let 𝑎 be the address 𝑧.[Link][𝑥].
4. Assert: Due to validation, 𝑎 < |𝑧.funcs|.
5. Push the value ([Link] 𝑎) to the stack.
6. Execute the instruction (return_call_ref 𝑧.funcs[𝑎].type).
𝑧; (return_call 𝑥) ˓→ ([Link] 𝑎) (return_call_ref 𝑧.funcs[𝑎].type) if 𝑧.[Link][𝑥] = 𝑎
return_call_ref 𝑦
1. Let 𝑧 be the current state.
2. If the first non-value entry of the stack is a label, then:
a. Pop all values val * from the top of the stack.
b. Pop the label from the stack.
c. Push the values val * to the stack.
d. Execute the instruction (return_call_ref 𝑦).
3. Else if the first non-value entry of the stack is a handler, then:
a. Pop all values val * from the top of the stack.
b. Pop the handler from the stack.
c. Push the values val * to the stack.
d. Execute the instruction (return_call_ref 𝑦).
4. Else:
a. Assert: Due to validation, the first non-value entry of the stack is a frame.
b. Assert: Due to validation, a value is on the top of the stack.
j. Assert: Due to validation, there are at least 𝑛 values on the top of the stack.
k. Pop the values val 𝑛 from the stack.
*
l. Pop all values val ′ from the top of the stack.
m. Pop the frame from the stack.
n. Push the values val 𝑛 to the stack.
o. Push the value ([Link] 𝑎) to the stack.
p. Execute the instruction (call_ref 𝑦).
*
𝑧; (label𝑘 {instr ′ } val * (return_call_ref 𝑦) instr * ) ˓→ val * (return_call_ref 𝑦)
𝑧; (handler𝑘 {catch * } val * (return_call_ref 𝑦) instr * ) ˓→ val * (return_call_ref 𝑦)
𝑧; (frame𝑘 {𝑓 } val * ([Link] ht) (return_call_ref 𝑦) instr * ) ˓→ trap
*
𝑧; (frame𝑘 {𝑓 } val ′ val 𝑛 ([Link] 𝑎) (return_call_ref 𝑦) instr * ) ˓→ val 𝑛 ([Link] 𝑎) (call_ref 𝑦)
if 𝑧.funcs[𝑎].type ≈ func 𝑡𝑛1 → 𝑡𝑚
2
return_call_indirect 𝑥 𝑦
1. Execute the instruction ([Link] 𝑥).
2. Execute the instruction ([Link] (ref null 𝑦)).
3. Execute the instruction (return_call_ref 𝑦).
(return_call_indirect 𝑥 𝑦) ˓→ ([Link] 𝑥) ([Link] (ref null 𝑦)) (return_call_ref 𝑦)
throw 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, 𝑥 < |𝑧.tags|.
3. Assert: Due to validation, the expansion of 𝑧.tags[𝑥].type is some func resulttype → resulttype.
4. Let (func 𝑡𝑛 → resulttype 0 ) be the destructuring of the expansion of 𝑧.tags[𝑥].type.
5. Assert: Due to validation, resulttype 0 = 𝜖.
6. Let 𝑎 be the length of 𝑧.exns.
7. Assert: Due to validation, there are at least 𝑛 values on the top of the stack.
8. Pop the values val 𝑛 from the stack.
9. Let exn be the exception instance {tag 𝑧.tags[𝑥], fields val 𝑛 }.
10. Append exn to 𝑧.exns.
11. Push the value ([Link] 𝑎) to the stack.
12. Execute the instruction throw_ref.
throw_ref
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value is on the top of the stack.
3. Pop the value val ′ from the stack.
4. If val ′ is some [Link] heaptype, then:
a. Trap.
5. If val ′ is some [Link] exnaddr , then:
a. Let ([Link] 𝑎) be the destructuring of val ′ .
b. Pop all values val * from the top of the stack.
c. If val * ̸= 𝜖, then:
1) Push the value ([Link] 𝑎) to the stack.
2) Execute the instruction throw_ref.
d. Else if the first non-value entry of the stack is a label, then:
1) Pop the label from the stack.
2) Push the value ([Link] 𝑎) to the stack.
3) Execute the instruction throw_ref.
e. Else:
1) If the first non-value entry of the stack is a frame, then:
a) Pop the frame from the stack.
b) Push the value ([Link] 𝑎) to the stack.
c) Execute the instruction throw_ref.
2) Else if the first non-value entry of the stack is not a handler, then:
a) Throw the exception val ′ as a result.
3) Else:
a) Let 𝐻 be the topmost handler.
b) Let 𝑛 be the arity of 𝐻
*
c) Let catch ′′ be the catch handler of 𝐻
*
d) If catch ′′ = 𝜖, then:
1. Pop the handler from the stack.
2. Push the value ([Link] 𝑎) to the stack.
3. Execute the instruction throw_ref.
e) Else if 𝑎 ≥ |𝑧.exns|, then:
* *
1. Let catch 0 catch ′ be catch ′′ .
2. If catch 0 is some catch_all labelidx , then:
a. Let (catch_all 𝑙) be the destructuring of catch 0 .
b. Pop the handler from the stack.
4.6.3 Blocks
The following auxiliary rules define the semantics of executing an instruction sequence that forms a block.
Note
No formal reduction rule is needed for entering an instruction sequence, because the label 𝐿 is embedded in
the administrative instruction that structured control instructions reduce to directly.
Note
This semantics also applies to the instruction sequence contained in a loop instruction. Therefore, execution of
a loop falls off the end, unless a backwards branch is performed explicitly.
Note
No formal reduction rule is needed for entering an exception handler because it is an administrative instruction
that the try_table instruction reduces to directly.
Note
For non-defaultable types, the respective local is left uninitialized by these rules.
Host Functions
Invoking a host function has non-deterministic behavior. It may either terminate with a trap, an exception, or return
regularly. However, in the latter case, it must consume and produce the right number and types of WebAssembly
values on the stack, according to its function type.
A host function may also modify the store. However, all store modifications must result in an extension of the
original store, i.e., they must only modify mutable contents and must not have instances removed. Furthermore,
the resulting store must be valid, i.e., all data and code in it is well-typed.
Here, hf (𝑆; val 𝑛 ) denotes the implementation-defined execution of host function hf in current store 𝑆 with argu-
ments val 𝑛 . It yields a set of possible outcomes, where each element is either a pair of a modified store 𝑆 ′ and
a result or the special value ⊥ indicating divergence. A host function is non-deterministic if there is at least one
argument for which the set of outcomes is not singular.
For a WebAssembly implementation to be sound in the presence of host functions, every host function instance
must be valid, which means that it adheres to suitable pre- and post-conditions: under a valid store 𝑆, and given
arguments val 𝑛 matching the ascribed parameter types 𝑡𝑛1 , executing the host function must yield a non-empty set
of possible outcomes each of which is either divergence or consists of a valid store 𝑆 ′ that is an extension of 𝑆 and
a result matching the ascribed return types 𝑡𝑚
2 . All these notions are made precise in the Appendix.
Note
A host function can call back into WebAssembly by invoking a function exported from a module. However, the
effects of any such call are subsumed by the non-deterministic behavior allowed for the host function.
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value is on the top of the stack.
3. Pop the value val from the stack.
4. Replace 𝑧.locals[𝑥] with val .
𝑧; val ([Link] 𝑥) ˓→ 𝑧[.locals[𝑥] = val ]; 𝜖
[Link] 𝑥
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value val from the stack.
3. Push the value val to the stack.
4. Push the value val to the stack.
5. Execute the instruction ([Link] 𝑥).
val ([Link] 𝑥) ˓→ val val ([Link] 𝑥)
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Let val be the value 𝑧.globals[𝑥].value.
3. Push the value val to the stack.
𝑧; ([Link] 𝑥) ˓→ val if 𝑧.globals[𝑥].value = val
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value is on the top of the stack.
3. Pop the value val from the stack.
4. Replace 𝑧.globals[𝑥].value with val .
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a reference value is on the top of the stack.
3. Pop the value ref from the stack.
4. Assert: Due to validation, a number value is on the top of the stack.
5. Pop the value ([Link] 𝑖) from the stack.
6. If 𝑖 ≥ |𝑧.tables[𝑥].elem|, then:
a. Trap.
7. Replace 𝑧.tables[𝑥].refs[𝑖] with ref .
𝑧; ([Link] 𝑖) ref ([Link] 𝑥) ˓→ 𝑧; trap if 𝑖 ≥ |𝑧.tables[𝑥].elem|
𝑧; ([Link] 𝑖) ref ([Link] 𝑥) ˓→ 𝑧[.tables[𝑥].elem[𝑖] = ref ]; 𝜖 if 𝑖 < |𝑧.tables[𝑥].elem|
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Let (at lim rt) be the destructuring of 𝑧.tables[𝑥].type.
3. Let 𝑛 be the length of 𝑧.tables[𝑥].elem.
4. Push the value ([Link] 𝑛) to the stack.
𝑧; ([Link] 𝑥) ˓→ ([Link] 𝑛) if |𝑧.tables[𝑥].elem| = 𝑛
∧ 𝑧.tables[𝑥].type = at lim rt
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a reference value is on the top of the stack.
5. Pop the value ref from the stack.
6. Either:
Note
The [Link] instruction is non-deterministic. It may either succeed, returning the old table size sz , or fail,
returning −1. Failure must occur if the referenced table instance has a maximum size defined that would be
exceeded. However, failure can occur in other cases as well. In practice, the choice depends on the resources
available to the embedder.
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value is on the top of the stack.
5. Pop the value val from the stack.
6. Assert: Due to validation, a value of number type at is on the top of the stack.
7. Pop the value (numtype 0 .const 𝑖) from the stack.
8. If 𝑖 + 𝑛 > |𝑧.tables[𝑥].elem|, then:
a. Trap.
9. If 𝑛 = 0, then:
a. Do nothing.
10. Else:
a. Push the value ([Link] 𝑖) to the stack.
b. Push the value val to the stack.
c. Execute the instruction ([Link] 𝑥).
d. Push the value ([Link] 𝑖 + 1) to the stack.
e. Push the value val to the stack.
f. Push the value ([Link] 𝑛 − 1) to the stack.
g. Execute the instruction ([Link] 𝑥).
𝑧; ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→ trap if 𝑖 + 𝑛 > |𝑧.tables[𝑥].elem|
𝑧; ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→ 𝜖 otherwise, if 𝑛 = 0
𝑧; ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→
([Link] 𝑖) val ([Link] 𝑥) otherwise
([Link] 𝑖 + 1) val ([Link] 𝑛 − 1) ([Link] 𝑥)
[Link] 𝑥1 𝑥2
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a number value is on the top of the stack.
5. Pop the value (at 2 .const 𝑖2 ) from the stack.
6. Assert: Due to validation, a number value is on the top of the stack.
7. Pop the value (at 1 .const 𝑖1 ) from the stack.
8. If 𝑖1 + 𝑛 > |𝑧.tables[𝑥1 ].elem|, then:
a. Trap.
9. If 𝑖2 + 𝑛 > |𝑧.tables[𝑥2 ].elem|, then:
a. Trap.
10. If 𝑛 = 0, then:
a. Do nothing.
11. Else:
a. If 𝑖1 ≤ 𝑖2 , then:
1) Push the value (at 1 .const 𝑖1 ) to the stack.
2) Push the value (at 2 .const 𝑖2 ) to the stack.
3) Execute the instruction ([Link] 𝑥2 ).
4) Execute the instruction ([Link] 𝑥1 ).
5) Push the value (at 1 .const 𝑖1 + 1) to the stack.
6) Push the value (at 2 .const 𝑖2 + 1) to the stack.
b. Else:
1) Push the value (at 1 .const 𝑖1 + 𝑛 − 1) to the stack.
2) Push the value (at 2 .const 𝑖2 + 𝑛 − 1) to the stack.
3) Execute the instruction ([Link] 𝑥2 ).
4) Execute the instruction ([Link] 𝑥1 ).
5) Push the value (at 1 .const 𝑖1 ) to the stack.
6) Push the value (at 2 .const 𝑖2 ) to the stack.
c. Push the value ([Link] 𝑛 − 1) to the stack.
d. Execute the instruction ([Link] 𝑥1 𝑥2 ).
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥1 𝑥2 ) ˓→ trap
if 𝑖1 + 𝑛 > |𝑧.tables[𝑥1 ].elem| ∨ 𝑖2 + 𝑛 > |𝑧.tables[𝑥2 ].elem|
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥 𝑦) ˓→ 𝜖 otherwise, if 𝑛 = 0
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥 𝑦) ˓→
(at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) ([Link] 𝑦) ([Link] 𝑥) otherwise, if 𝑖1 ≤ 𝑖2
(at 1 .const 𝑖1 + 1) (at 2 .const 𝑖2 + 1) (at ′ .const 𝑛 − 1) ([Link] 𝑥 𝑦)
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥 𝑦) ˓→
(at 1 .const 𝑖1 + 𝑛 − 1) (at 2 .const 𝑖2 + 𝑛 − 1) ([Link] 𝑦) ([Link] 𝑥) otherwise
(at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛 − 1) ([Link] 𝑥 𝑦)
[Link] 𝑥 𝑦
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑗) from the stack.
6. Assert: Due to validation, a number value is on the top of the stack.
7. Pop the value ([Link] 𝑖) from the stack.
8. If 𝑖 + 𝑛 > |𝑧.tables[𝑥].elem|, then:
a. Trap.
9. If 𝑗 + 𝑛 > |𝑧.elems[𝑦].elem|, then:
a. Trap.
10. If 𝑛 = 0, then:
a. Do nothing.
11. Else:
a. Assert: Due to validation, 𝑗 < |𝑧.elems[𝑦].elem|.
b. Push the value ([Link] 𝑖) to the stack.
c. Push the value 𝑧.elems[𝑦].elem[𝑗] to the stack.
d. Execute the instruction ([Link] 𝑥).
e. Push the value ([Link] 𝑖 + 1) to the stack.
f. Push the value ([Link] 𝑗 + 1) to the stack.
g. Push the value ([Link] 𝑛 − 1) to the stack.
h. Execute the instruction ([Link] 𝑥 𝑦).
𝑧; ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) ([Link] 𝑥 𝑦) ˓→ trap
if 𝑖 + 𝑛 > |𝑧.tables[𝑥].elem| ∨ 𝑗 + 𝑛 > |𝑧.elems[𝑦].elem|
𝑧; ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) ([Link] 𝑥 𝑦) ˓→ 𝜖 otherwise, if 𝑛 = 0
𝑧; ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) ([Link] 𝑥 𝑦) ˓→
([Link] 𝑖) 𝑧.elems[𝑦].elem[𝑗] ([Link] 𝑥) otherwise
([Link] 𝑖 + 1) ([Link] 𝑗 + 1) ([Link] 𝑛 − 1) ([Link] 𝑥 𝑦)
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Replace 𝑧.elems[𝑥].refs with 𝜖.
𝑧; ([Link] 𝑥) ˓→ 𝑧[.elems[𝑥].elem = 𝜖]; 𝜖
Note
The alignment [Link] in load and store instructions does not affect the semantics. It is a hint that the
offset ea at which the memory is accessed is intended to satisfy the property ea mod [Link] = 0. A
WebAssembly implementation can use this hint to optimize for the intended use. Unaligned access violating
that property is still allowed and must succeed regardless of the annotation. However, it may be substantially
slower on some hardware.
[Link] ? 𝑥 ao
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑖) from the stack.
4. If loadop ? is not defined, then:
a. If 𝑖 + [Link] + |nt|/8 > |𝑧.mems[𝑥].bytes|, then:
1) Trap.
b. Let 𝑐 be the result for which bytesnt (𝑐) = 𝑧.mems[𝑥].bytes[𝑖 + [Link] : |nt|/8].
c. Push the value ([Link] 𝑐) to the stack.
5. Else:
a. Assert: Due to validation, nt is i𝑁 .
b. Let loadop 0 be loadop ? .
c. Let 𝑛_sx be the destructuring of loadop 0 .
d. If 𝑖 + [Link] + 𝑛/8 > |𝑧.mems[𝑥].bytes|, then:
1) Trap.
e. Let 𝑐 be the result for which bytesi𝑛 (𝑐) = 𝑧.mems[𝑥].bytes[𝑖 + [Link] : 𝑛/8].
f. Push the value ([Link] extendsx
𝑛,|nt| (𝑐)) to the stack.
[Link]𝑀 x𝐾_sx 𝑥 ao
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑖) from the stack.
4. If 𝑖 + [Link] + 𝑀 · 𝐾/8 > |𝑧.mems[𝑥].bytes|, then:
a. Trap.
5. Let 𝑗 𝐾 be the result for which (bytesi𝑀 (𝑗 𝐾 ) = 𝑧.mems[𝑥].bytes[𝑖 + [Link] + 𝑘 · 𝑀/8 : 𝑀/8])𝑘<𝐾 .
6. Let i𝑁 be the result for which 𝑁 = 𝑀 · 2.
𝐾
7. Let 𝑐 be lanes−1 sx
i𝑁 x𝐾 (extend𝑀,𝑁 (𝑗) ).
[Link]𝑁 _splat 𝑥 ao
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑖) from the stack.
4. If 𝑖 + [Link] + 𝑁/8 > |𝑧.mems[𝑥].bytes|, then:
a. Trap.
5. Let 𝑀 be 128/𝑁 .
6. Let i𝑁 be the result for which |i𝑁 | = 𝑁 .
7. Let 𝑗 be the result for which bytesi𝑁 (𝑗) = 𝑧.mems[𝑥].bytes[𝑖 + [Link] : 𝑁/8].
8. Let 𝑐 be lanes−1 𝑀
i𝑁 x𝑀 (𝑗 ).
[Link]𝑁 _zero 𝑥 ao
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑖) from the stack.
4. If 𝑖 + [Link] + 𝑁/8 > |𝑧.mems[𝑥].bytes|, then:
a. Trap.
5. Let 𝑗 be the result for which bytesi𝑁 (𝑗) = 𝑧.mems[𝑥].bytes[𝑖 + [Link] : 𝑁/8].
6. Let 𝑐 be extendu𝑁,128 (𝑗).
7. Push the value ([Link] 𝑐) to the stack.
𝑧; ([Link] 𝑖) ([Link]𝑁 _zero 𝑥 ao) ˓→ trap if 𝑖 + [Link] + 𝑁/8 > |𝑧.mems[𝑥].bytes|
𝑧; ([Link] 𝑖) ([Link]𝑁 _zero 𝑥 ao) ˓→ ([Link] 𝑐)
if bytesi𝑁 (𝑗) = 𝑧.mems[𝑥].bytes[𝑖 + [Link] : 𝑁/8]
∧ 𝑐 = extendu𝑁,128 (𝑗)
[Link]𝑁 _lane 𝑥 ao 𝑗
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
3. Pop the value ([Link] 𝑐1 ) from the stack.
4. Assert: Due to validation, a number value is on the top of the stack.
5. Pop the value ([Link] 𝑖) from the stack.
6. If 𝑖 + [Link] + 𝑁/8 > |𝑧.mems[𝑥].bytes|, then:
a. Trap.
7. Let 𝑀 be |v128|/𝑁 .
8. Let i𝑁 be the result for which |i𝑁 | = 𝑁 .
9. Let 𝑘 be the result for which bytesi𝑁 (𝑘) = 𝑧.mems[𝑥].bytes[𝑖 + [Link] : 𝑁/8].
10. Let 𝑐 be lanes−1
i𝑁 x𝑀 (lanesi𝑁 x𝑀 (𝑐1 )[[𝑗] = 𝑘]).
[Link] ? 𝑥 ao
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value (nt ′ .const 𝑐) from the stack.
4. Assert: Due to validation, a value is on the top of the stack.
5. Pop the value ([Link] 𝑖) from the stack.
6. Assert: Due to validation, nt = nt ′ .
7. If storeop ? is not defined, then:
a. If 𝑖 + [Link] + |nt ′ |/8 > |𝑧.mems[𝑥].bytes|, then:
1) Trap.
b. Let 𝑏* be bytesnt ′ (𝑐).
c. Replace 𝑧.mems[𝑥].bytes[𝑖 + [Link] : |nt ′ |/8] with 𝑏* .
8. Else:
a. Assert: Due to validation, nt ′ is i𝑁 .
b. Let 𝑛 be storeop ? .
c. If 𝑖 + [Link] + 𝑛/8 > |𝑧.mems[𝑥].bytes|, then:
1) Trap.
d. Let 𝑏* be bytesi𝑛 (wrap|nt ′ |,𝑛 (𝑐)).
e. Replace 𝑧.mems[𝑥].bytes[𝑖 + [Link] : 𝑛/8] with 𝑏* .
𝑧; ([Link] 𝑖) ([Link] 𝑐) ([Link] 𝑥 ao) ˓→ 𝑧; trap
if 𝑖 + [Link] + |nt|/8 > |𝑧.mems[𝑥].bytes|
𝑧; ([Link] 𝑖) ([Link] 𝑐) ([Link] 𝑥 ao) ˓→ 𝑧[.mems[𝑥].bytes[𝑖 + [Link] : |nt|/8] = 𝑏* ]; 𝜖
if 𝑏* = bytesnt (𝑐)
𝑧; ([Link] 𝑖) (i𝑁 .const 𝑐) (i𝑁 .store𝑛 𝑥 ao) ˓→ 𝑧; trap
if 𝑖 + [Link] + 𝑛/8 > |𝑧.mems[𝑥].bytes|
𝑧; ([Link] 𝑖) (i𝑁 .const 𝑐) (i𝑁 .store𝑛 𝑥 ao) ˓→ 𝑧[.mems[𝑥].bytes[𝑖 + [Link] : 𝑛/8] = 𝑏* ]; 𝜖
if 𝑏* = bytesi𝑛 (wrap|i𝑁 |,𝑛 (𝑐))
𝑧; ([Link] 𝑖) ([Link] 𝑐) ([Link] 𝑥 ao) ˓→ 𝑧; trap
if 𝑖 + [Link] + |v128|/8 > |𝑧.mems[𝑥].bytes|
𝑧; ([Link] 𝑖) ([Link] 𝑐) ([Link] 𝑥 ao) ˓→ 𝑧[.mems[𝑥].bytes[𝑖 + [Link] : |v128|/8] = 𝑏* ]; 𝜖
if 𝑏* = bytesv128 (𝑐)
[Link]𝑁 _lane 𝑥 ao 𝑗
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
3. Pop the value ([Link] 𝑐) from the stack.
4. Assert: Due to validation, a number value is on the top of the stack.
5. Pop the value ([Link] 𝑖) from the stack.
6. If 𝑖 + [Link] + 𝑁 > |𝑧.mems[𝑥].bytes|, then:
a. Trap.
7. Let 𝑀 be 128/𝑁 .
8. Let i𝑁 be the result for which |i𝑁 | = 𝑁 .
9. Assert: Due to validation, 𝑗 < |lanesi𝑁 x𝑀 (𝑐)|.
10. Let 𝑏* be bytesi𝑁 (lanesi𝑁 x𝑀 (𝑐)[𝑗]).
11. Replace 𝑧.mems[𝑥].bytes[𝑖 + [Link] : 𝑁/8] with 𝑏* .
𝑧; ([Link] 𝑖) ([Link] 𝑐) ([Link]𝑁 _lane 𝑥 ao 𝑗) ˓→ 𝑧; trap
if 𝑖 + [Link] + 𝑁 > |𝑧.mems[𝑥].bytes|
𝑧; ([Link] 𝑖) ([Link] 𝑐) ([Link]𝑁 _lane 𝑥 ao 𝑗) ˓→ 𝑧[.mems[𝑥].bytes[𝑖 + [Link] : 𝑁/8] = 𝑏* ]; 𝜖
if 𝑁 = |i𝑁 |
∧ 𝑀 = 128/𝑁
∧ 𝑏* = bytesi𝑁 (lanesi𝑁 x𝑀 (𝑐)[𝑗])
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Let (at lim page) be the destructuring of 𝑧.mems[𝑥].type.
3. Let 𝑛 · 64 Ki be the length of 𝑧.mems[𝑥].bytes.
4. Push the value ([Link] 𝑛) to the stack.
𝑧; ([Link] 𝑥) ˓→ ([Link] 𝑛) if 𝑛 · 64 Ki = |𝑧.mems[𝑥].bytes|
∧ 𝑧.mems[𝑥].type = at lim page
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Either:
a. Let mi be the memory instance growmem(𝑧.mems[𝑥], 𝑛).
b. Push the value ([Link] |𝑧.mems[𝑥].bytes|/(64 Ki)) to the stack.
c. Replace 𝑧.mems[𝑥] with mi .
5. Or:
a. Push the value ([Link] signed−1
|at| (−1)) to the stack.
Note
The [Link] instruction is non-deterministic. It may either succeed, returning the old memory size sz ,
or fail, returning −1. Failure must occur if the referenced memory instance has a maximum size defined that
would be exceeded. However, failure can occur in other cases as well. In practice, the choice depends on the
resources available to the embedder.
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value is on the top of the stack.
5. Pop the value val from the stack.
6. Assert: Due to validation, a value of number type at is on the top of the stack.
7. Pop the value (numtype 0 .const 𝑖) from the stack.
8. If 𝑖 + 𝑛 > |𝑧.mems[𝑥].bytes|, then:
a. Trap.
9. If 𝑛 = 0, then:
a. Do nothing.
10. Else:
a. Push the value ([Link] 𝑖) to the stack.
b. Push the value val to the stack.
c. Execute the instruction (i32.store8 𝑥).
d. Push the value ([Link] 𝑖 + 1) to the stack.
e. Push the value val to the stack.
f. Push the value ([Link] 𝑛 − 1) to the stack.
g. Execute the instruction ([Link] 𝑥).
𝑧; ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→ trap if 𝑖 + 𝑛 > |𝑧.mems[𝑥].bytes|
𝑧; ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→ 𝜖 otherwise, if 𝑛 = 0
𝑧; ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→
([Link] 𝑖) val (i32.store8 𝑥) otherwise
([Link] 𝑖 + 1) val ([Link] 𝑛 − 1) ([Link] 𝑥)
[Link] 𝑥1 𝑥2
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a number value is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a number value is on the top of the stack.
5. Pop the value (at 2 .const 𝑖2 ) from the stack.
6. Assert: Due to validation, a number value is on the top of the stack.
7. Pop the value (at 1 .const 𝑖1 ) from the stack.
8. If 𝑖1 + 𝑛 > |𝑧.mems[𝑥1 ].bytes|, then:
a. Trap.
9. If 𝑖2 + 𝑛 > |𝑧.mems[𝑥2 ].bytes|, then:
a. Trap.
10. If 𝑛 = 0, then:
a. Do nothing.
11. Else:
a. If 𝑖1 ≤ 𝑖2 , then:
1) Push the value (at 1 .const 𝑖1 ) to the stack.
2) Push the value (at 2 .const 𝑖2 ) to the stack.
3) Execute the instruction (i32.load8_u 𝑥2 ).
4) Execute the instruction (i32.store8 𝑥1 ).
5) Push the value (at 1 .const 𝑖1 + 1) to the stack.
6) Push the value (at 2 .const 𝑖2 + 1) to the stack.
b. Else:
1) Push the value (at 1 .const 𝑖1 + 𝑛 − 1) to the stack.
2) Push the value (at 2 .const 𝑖2 + 𝑛 − 1) to the stack.
3) Execute the instruction (i32.load8_u 𝑥2 ).
4) Execute the instruction (i32.store8 𝑥1 ).
5) Push the value (at 1 .const 𝑖1 ) to the stack.
6) Push the value (at 2 .const 𝑖2 ) to the stack.
c. Push the value ([Link] 𝑛 − 1) to the stack.
d. Execute the instruction ([Link] 𝑥1 𝑥2 ).
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥1 𝑥2 ) ˓→ trap
if 𝑖1 + 𝑛 > |𝑧.mems[𝑥1 ].bytes| ∨ 𝑖2 + 𝑛 > |𝑧.mems[𝑥2 ].bytes|
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥1 𝑥2 ) ˓→ 𝜖 otherwise, if 𝑛 = 0
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥1 𝑥2 ) ˓→
(at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (i32.load8_u 𝑥2 ) (i32.store8 𝑥1 ) otherwise, if 𝑖1 ≤ 𝑖2
(at 1 .const 𝑖1 + 1) (at 2 .const 𝑖2 + 1) (at ′ .const 𝑛 − 1) ([Link] 𝑥1 𝑥2 )
𝑧; (at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛) ([Link] 𝑥1 𝑥2 ) ˓→
(at 1 .const 𝑖1 + 𝑛 − 1) (at 2 .const 𝑖2 + 𝑛 − 1) (i32.load8_u 𝑥2 ) (i32.store8 𝑥1 ) otherwise
(at 1 .const 𝑖1 ) (at 2 .const 𝑖2 ) (at ′ .const 𝑛 − 1) ([Link] 𝑥1 𝑥2 )
[Link] 𝑥 𝑦
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑗) from the stack.
6. Assert: Due to validation, a number value is on the top of the stack.
7. Pop the value ([Link] 𝑖) from the stack.
8. If 𝑖 + 𝑛 > |𝑧.mems[𝑥].bytes|, then:
a. Trap.
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Replace 𝑧.datas[𝑥].bytes with 𝜖.
𝑧; ([Link] 𝑥) ˓→ 𝑧[.datas[𝑥].bytes = 𝜖]; 𝜖
Note
No formal reduction rule is required for the case [Link] absheaptype, since the instruction form is already a
value.
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, 𝑥 < |𝑧.[Link]|.
3. Push the value ([Link] 𝑧.[Link][𝑥]) to the stack.
𝑧; ([Link] 𝑥) ˓→ ([Link] 𝑧.[Link][𝑥])
ref.is_null
1. Assert: Due to validation, a reference value is on the top of the stack.
2. Pop the value ref from the stack.
3. If ref is some [Link] heaptype, then:
a. Push the value ([Link] 1) to the stack.
4. Else:
a. Push the value ([Link] 0) to the stack.
ref ref.is_null ˓→ ([Link] 1) if ref = ([Link] ht)
ref ref.is_null ˓→ ([Link] 0) otherwise
ref.as_non_null
1. Assert: Due to validation, a reference value is on the top of the stack.
2. Pop the value ref from the stack.
3. If ref is some [Link] heaptype, then:
a. Trap.
4. Push the value ref to the stack.
ref ref.as_non_null ˓→ trap if ref = ([Link] ht)
ref ref.as_non_null ˓→ ref otherwise
[Link]
1. Assert: Due to validation, a reference value is on the top of the stack.
2. Pop the value ref 2 from the stack.
3. Assert: Due to validation, a reference value is on the top of the stack.
4. Pop the value ref 1 from the stack.
5. If ref 1 is some [Link] heaptype and ref 2 is some [Link] heaptype, then:
a. Push the value ([Link] 1) to the stack.
6. Else if ref 1 = ref 2 , then:
a. Push the value ([Link] 1) to the stack.
7. Else:
a. Push the value ([Link] 0) to the stack.
ref 1 ref 2 [Link] ˓→ ([Link] 1) if ref 1 = ([Link] ht 1 ) ∧ ref 2 = ([Link] ht 2 )
ref 1 ref 2 [Link] ˓→ ([Link] 1) otherwise, if ref 1 = ref 2
ref 1 ref 2 [Link] ˓→ ([Link] 0) otherwise
[Link] rt
1. Let 𝑓 be the topmost frame.
2. Assert: Due to validation, a reference value is on the top of the stack.
3. Pop the value ref from the stack.
4. Let rt ′ be the type of ref .
5. If rt ′ matches clos𝑓.module (rt), then:
a. Push the value ([Link] 1) to the stack.
6. Else:
a. Push the value ([Link] 0) to the stack.
𝑠; 𝑓 ; ref ([Link] rt) ˓→ ([Link] 1) if 𝑠 ⊢ ref : rt ′
∧ {} ⊢ rt ′ ≤ clos𝑓.module (rt)
𝑠; 𝑓 ; ref ([Link] rt) ˓→ ([Link] 0) otherwise
[Link] rt
1. Let 𝑓 be the topmost frame.
2. Assert: Due to validation, a reference value is on the top of the stack.
3. Pop the value ref from the stack.
4. Let rt ′ be the type of ref .
5. If rt ′ does not match clos𝑓.module (rt), then:
a. Trap.
6. Push the value ref to the stack.
𝑠; 𝑓 ; ref ([Link] rt) ˓→ ref if 𝑠 ⊢ ref : rt ′
∧ {} ⊢ rt ′ ≤ clos𝑓.module (rt)
𝑠; 𝑓 ; ref ([Link] rt) ˓→ trap otherwise
ref.i31
1. Assert: Due to validation, a value of number type i32 is on the top of the stack.
2. Pop the value ([Link] 𝑖) from the stack.
3. Push the value (ref.i31 wrap32,31 (𝑖)) to the stack.
([Link] 𝑖) ref.i31 ˓→ (ref.i31 wrap32,31 (𝑖))
i31.get_sx
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value val from the stack.
3. If val is some [Link] heaptype, then:
a. Trap.
4. Assert: Due to validation, val is some ref.i31 u31 .
5. Let (ref.i31 𝑖) be the destructuring of val .
6. Push the value ([Link] extendsx
31,32 (𝑖)) to the stack.
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some struct list(fieldtype).
3. Let (struct list 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
4. Let (mut? zt)𝑛 be list 0 .
5. Let 𝑎 be the length of 𝑧.structs.
6. Assert: Due to validation, there are at least 𝑛 values on the top of the stack.
7. Pop the values val 𝑛 from the stack.
8. Let si be the structure instance {type 𝑧.types[𝑥], fields packzt (val )𝑛 }.
9. Push the value ([Link] 𝑎) to the stack.
10. Append si to 𝑧.structs.
𝑧; val 𝑛 ([Link] 𝑥) ˓→ 𝑧[.structs =⊕ si ]; ([Link] 𝑎) if 𝑧.types[𝑥] ≈ struct (mut? zt)𝑛
∧ 𝑎 = |𝑧.structs|
∧ si = {type 𝑧.types[𝑥], fields (packzt (val ))𝑛 }
struct.new_default 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some struct list(fieldtype).
3. Let (struct list 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
4. Let (mut? zt)* be list 0 .
5. Assert: Due to validation, for all zt in zt * , defaultunpack(zt) is defined.
6. Let val * be the value sequence 𝜖.
7. For each zt in zt * , do:
a. Let val be defaultunpack(zt) .
b. Append val to val * .
8. Assert: Due to validation, |val * | = |zt * |.
9. Push the values val * to the stack.
10. Execute the instruction ([Link] 𝑥).
𝑧; (struct.new_default 𝑥) ˓→ val * ([Link] 𝑥) if 𝑧.types[𝑥] ≈ struct (mut? zt)*
∧ (defaultunpack(zt) = val )*
struct.get_sx ? 𝑥 𝑖
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value is on the top of the stack.
3. Pop the value val from the stack.
4. If val is some [Link] heaptype, then:
a. Trap.
5. Assert: Due to validation, val is some [Link] structaddr .
6. Let ([Link] 𝑎) be the destructuring of val .
7. Assert: Due to validation, 𝑖 < |𝑧.structs[𝑎].fields|.
8. Assert: Due to validation, 𝑎 < |𝑧.structs|.
[Link] 𝑥 𝑖
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value is on the top of the stack.
3. Pop the value val from the stack.
4. Assert: Due to validation, a value is on the top of the stack.
5. Pop the value val ′ from the stack.
6. If val ′ is some [Link] heaptype, then:
a. Trap.
7. Assert: Due to validation, val ′ is some [Link] structaddr .
8. Let ([Link] 𝑎) be the destructuring of val ′ .
9. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some struct list(fieldtype).
10. Let (struct list 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
11. Let (mut? zt)* be list 0 .
12. Assert: Due to validation, 𝑖 < |zt * |.
13. Replace 𝑧.structs[𝑎].fields[𝑖] with packzt * [𝑖] (val ).
𝑧; ([Link] ht) val ([Link] 𝑥 𝑖) ˓→ 𝑧; trap
𝑧; ([Link] 𝑎) val ([Link] 𝑥 𝑖) ˓→ 𝑧[.structs[𝑎].fields[𝑖] = packzt * [𝑖] (val )]; 𝜖
if 𝑧.types[𝑥] ≈ struct (mut? zt)*
[Link] 𝑥
1. Assert: Due to validation, a value of number type i32 is on the top of the stack.
2. Pop the value ([Link] 𝑛) from the stack.
3. Assert: Due to validation, a value is on the top of the stack.
4. Pop the value val from the stack.
5. Push the values val 𝑛 to the stack.
6. Execute the instruction (array.new_fixed 𝑥 𝑛).
val ([Link] 𝑛) ([Link] 𝑥) ˓→ val 𝑛 (array.new_fixed 𝑥 𝑛)
array.new_default 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some array fieldtype.
5. Let (array fieldtype 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
6. Let (mut? zt) be the destructuring of fieldtype 0 .
7. Assert: Due to validation, defaultunpack(zt) is defined.
8. Let val be defaultunpack(zt) .
9. Push the values val 𝑛 to the stack.
10. Execute the instruction (array.new_fixed 𝑥 𝑛).
𝑧; ([Link] 𝑛) (array.new_default 𝑥) ˓→ val 𝑛 (array.new_fixed 𝑥 𝑛) if 𝑧.types[𝑥] ≈ array (mut? zt)
∧ defaultunpack(zt) = val
array.new_fixed 𝑥 𝑛
1. Let 𝑧 be the current state.
2. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some array fieldtype.
3. Let (array fieldtype 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
4. Let (mut? zt) be the destructuring of fieldtype 0 .
5. Let 𝑎 be the length of 𝑧.arrays.
6. Assert: Due to validation, there are at least 𝑛 values on the top of the stack.
7. Pop the values val 𝑛 from the stack.
8. Let ai be the array instance {type 𝑧.types[𝑥], fields packzt (val )𝑛 }.
9. Push the value ([Link] 𝑎) to the stack.
10. Append ai to 𝑧.arrays.
𝑧; val 𝑛 (array.new_fixed 𝑥 𝑛) ˓→ 𝑧[.arrays =⊕ ai ]; ([Link] 𝑎)
if 𝑧.types[𝑥] ≈ array (mut? zt)
∧ 𝑎 = |𝑧.arrays| ∧ ai = {type 𝑧.types[𝑥], fields (packzt (val ))𝑛 }
array.new_data 𝑥 𝑦
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑖) from the stack.
6. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some array fieldtype.
7. Let (array fieldtype 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
8. Let (mut? zt) be the destructuring of fieldtype 0 .
9. If 𝑖 + 𝑛 · |zt|/8 > |𝑧.datas[𝑦].bytes|, then:
a. Trap.
* *
10. Let byte * be the result for which each byte * has length |zt|/8, and the concatenation of byte * is
𝑧.datas[𝑦].bytes[𝑖 : 𝑛 · |zt|/8].
11. Let 𝑐𝑛 be the result for which (byteszt (𝑐𝑛 ) = byte * )* .
12. Push the values unpack(zt).const unpackzt (𝑐)𝑛 to the stack.
13. Execute the instruction (array.new_fixed 𝑥 𝑛).
𝑧; ([Link] 𝑖) ([Link] 𝑛) (array.new_data 𝑥 𝑦) ˓→ trap
if 𝑧.types[𝑥] ≈ array (mut? zt)
∧ 𝑖 + 𝑛 · |zt|/8 > |𝑧.datas[𝑦].bytes|
𝑧; ([Link] 𝑖) ([Link] 𝑛) (array.new_data 𝑥 𝑦) ˓→ (unpack(zt).const unpackzt (𝑐))𝑛 (array.new_fixed 𝑥 𝑛)
if 𝑧.types[𝑥] ≈ array (mut? zt)
byteszt (𝑐)𝑛 = 𝑧.datas[𝑦].bytes[𝑖 : 𝑛 · |zt|/8]
⨁︀
∧
array.new_elem 𝑥 𝑦
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑖) from the stack.
6. If 𝑖 + 𝑛 > |𝑧.elems[𝑦].elem|, then:
a. Trap.
7. Let ref 𝑛 be 𝑧.elems[𝑦].elem[𝑖 : 𝑛].
8. Push the values ref 𝑛 to the stack.
9. Execute the instruction (array.new_fixed 𝑥 𝑛).
𝑧; ([Link] 𝑖) ([Link] 𝑛) (array.new_elem 𝑥 𝑦) ˓→ trap if 𝑖 + 𝑛 > |𝑧.elems[𝑦].elem|
𝑧; ([Link] 𝑖) ([Link] 𝑛) (array.new_elem 𝑥 𝑦) ˓→ ref 𝑛 (array.new_fixed 𝑥 𝑛)
if ref 𝑛 = 𝑧.elems[𝑦].elem[𝑖 : 𝑛]
array.get_sx ? 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑖) from the stack.
4. Assert: Due to validation, a value is on the top of the stack.
5. Pop the value val from the stack.
6. If val is some [Link] heaptype, then:
a. Trap.
7. Assert: Due to validation, val is some [Link] arrayaddr .
8. Let ([Link] 𝑎) be the destructuring of val .
9. If 𝑎 < |𝑧.arrays| and 𝑖 ≥ |𝑧.arrays[𝑎].fields|, then:
a. Trap.
10. If 𝑖 < |𝑧.arrays[𝑎].fields| and 𝑎 < |𝑧.arrays|, then:
a. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some array fieldtype.
b. Let (array fieldtype 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
c. Let (mut? zt) be the destructuring of fieldtype 0 .
?
d. Push the value unpacksx
zt (𝑧.arrays[𝑎].fields[𝑖]) to the stack.
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value is on the top of the stack.
3. Pop the value val from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑖) from the stack.
6. Assert: Due to validation, a value is on the top of the stack.
7. Pop the value val ′ from the stack.
8. If val ′ is some [Link] heaptype, then:
a. Trap.
9. Assert: Due to validation, val ′ is some [Link] arrayaddr .
10. Let ([Link] 𝑎) be the destructuring of val ′ .
11. If 𝑎 < |𝑧.arrays| and 𝑖 ≥ |𝑧.arrays[𝑎].fields|, then:
a. Trap.
12. Assert: Due to validation, the expansion of 𝑧.types[𝑥] is some array fieldtype.
13. Let (array fieldtype 0 ) be the destructuring of the expansion of 𝑧.types[𝑥].
14. Let (mut? zt) be the destructuring of fieldtype 0 .
15. Replace 𝑧.arrays[𝑎].fields[𝑖] with packzt (val ).
𝑧; ([Link] ht) ([Link] 𝑖) val ([Link] 𝑥) ˓→ 𝑧; trap
𝑧; ([Link] 𝑎) ([Link] 𝑖) val ([Link] 𝑥) ˓→ 𝑧; trap if 𝑖 ≥ |𝑧.arrays[𝑎].fields|
𝑧; ([Link] 𝑎) ([Link] 𝑖) val ([Link] 𝑥) ˓→ 𝑧[.arrays[𝑎].fields[𝑖] = packzt (val )]; 𝜖
if 𝑧.types[𝑥] ≈ array (mut? zt)
[Link]
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value is on the top of the stack.
3. Pop the value val from the stack.
4. If val is some [Link] heaptype, then:
a. Trap.
5. Assert: Due to validation, val is some [Link] arrayaddr .
6. Let ([Link] 𝑎) be the destructuring of val .
7. Assert: Due to validation, 𝑎 < |𝑧.arrays|.
8. Push the value ([Link] |𝑧.arrays[𝑎].fields|) to the stack.
𝑧; ([Link] ht) [Link] ˓→ trap
𝑧; ([Link] 𝑎) [Link] ˓→ ([Link] |𝑧.arrays[𝑎].fields|)
[Link] 𝑥
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value is on the top of the stack.
5. Pop the value val from the stack.
6. Assert: Due to validation, a value of number type i32 is on the top of the stack.
7. Pop the value ([Link] 𝑖) from the stack.
8. Assert: Due to validation, a value is on the top of the stack.
9. Pop the value val ′ from the stack.
10. If val ′ is some [Link] heaptype, then:
a. Trap.
11. Assert: Due to validation, val ′ is some [Link] arrayaddr .
12. Let ([Link] 𝑎) be the destructuring of val ′ .
13. If 𝑎 ≥ |𝑧.arrays|, then:
a. Do nothing.
14. Else if 𝑖 + 𝑛 > |𝑧.arrays[𝑎].fields|, then:
a. Trap.
15. If 𝑛 = 0, then:
a. Do nothing.
16. Else:
a. Push the value ([Link] 𝑎) to the stack.
b. Push the value ([Link] 𝑖) to the stack.
c. Push the value val to the stack.
d. Execute the instruction ([Link] 𝑥).
e. Push the value ([Link] 𝑎) to the stack.
f. Push the value ([Link] 𝑖 + 1) to the stack.
g. Push the value val to the stack.
h. Push the value ([Link] 𝑛 − 1) to the stack.
i. Execute the instruction ([Link] 𝑥).
𝑧; ([Link] ht) ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→ trap
𝑧; ([Link] 𝑎) ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→ trap if 𝑖 + 𝑛 > |𝑧.arrays[𝑎].fields|
𝑧; ([Link] 𝑎) ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→ 𝜖 otherwise, if 𝑛 = 0
𝑧; ([Link] 𝑎) ([Link] 𝑖) val ([Link] 𝑛) ([Link] 𝑥) ˓→
([Link] 𝑎) ([Link] 𝑖) val ([Link] 𝑥) otherwise
([Link] 𝑎) ([Link] 𝑖 + 1) val ([Link] 𝑛 − 1) ([Link] 𝑥)
[Link] 𝑥1 𝑥2
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑖2 ) from the stack.
6. Assert: Due to validation, a value is on the top of the stack.
7. Pop the value val from the stack.
8. Assert: Due to validation, a value of number type i32 is on the top of the stack.
9. Pop the value ([Link] 𝑖1 ) from the stack.
10. Assert: Due to validation, a value is on the top of the stack.
11. Pop the value val ′ from the stack.
12. If val ′ is some [Link] heaptype and val is reference value, then:
a. Trap.
13. If val is some [Link] heaptype and val ′ is reference value, then:
a. Trap.
14. If val ′ is some [Link] arrayaddr , then:
a. Let ([Link] 𝑎1 ) be the destructuring of val ′ .
b. If val is some [Link] arrayaddr , then:
1) If 𝑎1 < |𝑧.arrays| and 𝑖1 + 𝑛 > |𝑧.arrays[𝑎1 ].fields|, then:
a) Trap.
2) Let ([Link] 𝑎2 ) be the destructuring of val .
3) If 𝑎2 ≥ |𝑧.arrays|, then:
a) Do nothing.
4) Else if 𝑖2 + 𝑛 > |𝑧.arrays[𝑎2 ].fields|, then:
a) Trap.
5) If 𝑛 = 0, then:
a) Do nothing.
6) Else:
a) Assert: Due to validation, the expansion of 𝑧.types[𝑥2 ] is some array fieldtype.
b) Let (array fieldtype 0 ) be the destructuring of the expansion of 𝑧.types[𝑥2 ].
c) Let (mut? zt 2 ) be the destructuring of fieldtype 0 .
d) Let sx ? be sx (zt 2 ).
e) Push the value ([Link] 𝑎1 ) to the stack.
f) If 𝑖1 ≤ 𝑖2 , then:
1. Push the value ([Link] 𝑖1 ) to the stack.
2. Push the value ([Link] 𝑎2 ) to the stack.
3. Push the value ([Link] 𝑖2 ) to the stack.
4. Execute the instruction (array.get_sx ? 𝑥2 ).
5. Execute the instruction ([Link] 𝑥1 ).
6. Push the value ([Link] 𝑎1 ) to the stack.
7. Push the value ([Link] 𝑖1 + 1) to the stack.
8. Push the value ([Link] 𝑎2 ) to the stack.
sx (consttype) = 𝜖
sx (packtype) = s
array.init_data 𝑥 𝑦
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑗) from the stack.
6. Assert: Due to validation, a value of number type i32 is on the top of the stack.
array.init_elem 𝑥 𝑦
1. Let 𝑧 be the current state.
2. Assert: Due to validation, a value of number type i32 is on the top of the stack.
3. Pop the value ([Link] 𝑛) from the stack.
4. Assert: Due to validation, a value of number type i32 is on the top of the stack.
5. Pop the value ([Link] 𝑗) from the stack.
6. Assert: Due to validation, a value of number type i32 is on the top of the stack.
7. Pop the value ([Link] 𝑖) from the stack.
8. Assert: Due to validation, a value is on the top of the stack.
9. Pop the value val from the stack.
10. If val is some [Link] heaptype, then:
a. Trap.
11. Assert: Due to validation, val is some [Link] arrayaddr .
12. Let ([Link] 𝑎) be the destructuring of val .
13. If 𝑎 < |𝑧.arrays| and 𝑖 + 𝑛 > |𝑧.arrays[𝑎].fields|, then:
a. Trap.
14. If 𝑗 + 𝑛 > |𝑧.elems[𝑦].elem|, then:
a. Trap.
15. If 𝑛 = 0, then:
a. Do nothing.
16. Else if 𝑗 < |𝑧.elems[𝑦].elem|, then:
a. Let ref be the reference value 𝑧.elems[𝑦].elem[𝑗].
b. Push the value ([Link] 𝑎) to the stack.
c. Push the value ([Link] 𝑖) to the stack.
d. Push the value ref to the stack.
e. Execute the instruction ([Link] 𝑥).
f. Push the value ([Link] 𝑎) to the stack.
g. Push the value ([Link] 𝑖 + 1) to the stack.
h. Push the value ([Link] 𝑗 + 1) to the stack.
i. Push the value ([Link] 𝑛 − 1) to the stack.
j. Execute the instruction (array.init_elem 𝑥 𝑦).
𝑧; ([Link] ht) ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) (array.init_elem 𝑥 𝑦) ˓→ trap
𝑧; ([Link] 𝑎) ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) (array.init_elem 𝑥 𝑦) ˓→ trap
if 𝑖 + 𝑛 > |𝑧.arrays[𝑎].fields|
𝑧; ([Link] 𝑎) ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) (array.init_elem 𝑥 𝑦) ˓→ trap
if 𝑗 + 𝑛 > |𝑧.elems[𝑦].elem|
𝑧; ([Link] 𝑎) ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) (array.init_elem 𝑥 𝑦) ˓→ 𝜖
otherwise, if 𝑛 = 0
𝑧; ([Link] 𝑎) ([Link] 𝑖) ([Link] 𝑗) ([Link] 𝑛) (array.init_elem 𝑥 𝑦) ˓→
([Link] 𝑎) ([Link] 𝑖) ref ([Link] 𝑥)
([Link] 𝑎) ([Link] 𝑖 + 1) ([Link] 𝑗 + 1) ([Link] 𝑛 − 1) (array.init_elem 𝑥 𝑦)
otherwise, if ref = 𝑧.elems[𝑦].elem[𝑗]
any.convert_extern
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value val from the stack.
3. If val is some [Link] heaptype, then:
a. Push the value ([Link] any) to the stack.
4. If val is some [Link] addrref , then:
a. Let ([Link] addrref ) be the destructuring of val .
b. Push the value addrref to the stack.
([Link] ht) any.convert_extern ˓→ ([Link] any)
([Link] addrref ) any.convert_extern ˓→ addrref
extern.convert_any
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value val from the stack.
3. If val is some [Link] heaptype, then:
a. Push the value ([Link] extern) to the stack.
4. If val is address value, then:
a. Push the value ([Link] val ) to the stack.
([Link] ht) extern.convert_any ˓→ ([Link] extern)
addrref extern.convert_any ˓→ ([Link] addrref )
Where the underlying operators are partial, the corresponding instruction will trap when the result is not defined.
Where the underlying operators are non-deterministic, because they may return one of multiple possible NaN
values, so are the corresponding instructions.
Note
For example, the result of instruction [Link] applied to operands 𝑖1 , 𝑖2 invokes addi32 (𝑖1 , 𝑖2 ), which maps to the
generic iadd32 (𝑖1 , 𝑖2 ) via the above definition. Similarly, i64.trunc_f 32_s applied to 𝑧 invokes truncsf32,i64 (𝑧),
which maps to the generic truncs 32,64 (𝑧).
[Link] 𝑐
1. Push the value ([Link] 𝑐) to the stack.
Note
No formal reduction rule is required for this instruction, since const instructions already are values.
[Link]
1. Assert: Due to validation, a value of number type nt is on the top of the stack.
2. Pop the value (numtype 0 .const 𝑐1 ) from the stack.
3. If unop nt (𝑐1 ) is empty, then:
a. Trap.
4. Let 𝑐 be an element of unop nt (𝑐1 ).
5. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 ∈ unop nt (𝑐1 )
([Link] 𝑐1 ) ([Link]) ˓→ trap if unop nt (𝑐1 ) = 𝜖
[Link]
1. Assert: Due to validation, a value of number type nt is on the top of the stack.
2. Pop the value (numtype 0 .const 𝑐2 ) from the stack.
3. Assert: Due to validation, a number value is on the top of the stack.
4. Pop the value (numtype 0 .const 𝑐1 ) from the stack.
5. If binop nt (𝑐1 , 𝑐2 ) is empty, then:
a. Trap.
6. Let 𝑐 be an element of binop nt (𝑐1 , 𝑐2 ).
7. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 ∈ binop nt (𝑐1 , 𝑐2 )
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link]) ˓→ trap if binop nt (𝑐1 , 𝑐2 ) = 𝜖
[Link]
1. Assert: Due to validation, a value of number type nt is on the top of the stack.
2. Pop the value (numtype 0 .const 𝑐1 ) from the stack.
3. Let 𝑐 be testop nt (𝑐1 ).
4. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 = testop nt (𝑐1 )
[Link]
1. Assert: Due to validation, a value of number type nt is on the top of the stack.
2. Pop the value (numtype 0 .const 𝑐2 ) from the stack.
3. Assert: Due to validation, a number value is on the top of the stack.
4. Pop the value (numtype 0 .const 𝑐1 ) from the stack.
5. Let 𝑐 be relop nt (𝑐1 , 𝑐2 ).
6. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 = relop nt (𝑐1 , 𝑐2 )
nt 2 .cvtop_nt 1
1. Assert: Due to validation, a value of number type nt 1 is on the top of the stack.
2. Pop the value (numtype 0 .const 𝑐1 ) from the stack.
3. If cvtop nt 1 ,nt 2 (𝑐1 ) is empty, then:
a. Trap.
4. Let 𝑐 be an element of cvtop nt 1 ,nt 2 (𝑐1 ).
5. Push the value (nt 2 .const 𝑐) to the stack.
(nt 1 .const 𝑐1 ) (nt 2 .cvtop_nt 1 ) ˓→ (nt 2 .const 𝑐) if 𝑐 ∈ cvtop nt 1 ,nt 2 (𝑐1 )
(nt 1 .const 𝑐1 ) (nt 2 .cvtop_nt 1 ) ˓→ trap if cvtop nt 1 ,nt 2 (𝑐1 ) = 𝜖
Most other vector instructions are defined in terms of numeric operators that are applied lane-wise according to
the given shape.
Note
For example, the result of instruction [Link] applied to operands 𝑣1 , 𝑣2 invokes addi32x4 (𝑣1 , 𝑣2 ), which
maps to lanes−1 i32x4 (addi32 (𝑖1 , 𝑖2 ) ), where 𝑖1 and 𝑖2 are sequences resulting from invoking lanesi32x4 (𝑣1 ) and
* * *
where ×{𝑥* }𝑁 transforms a sequence of 𝑁 sets of values into a set of sequences of 𝑁 values by computing the
set product:
×(𝑆1 . . . 𝑆𝑁 ) = {𝑥1 . . . 𝑥𝑁 | 𝑥1 ∈ 𝑆1 ∧ · · · ∧ 𝑥𝑁 ∈ 𝑆𝑁 }
[Link] 𝑐
1. Push the value ([Link] 𝑐) to the stack.
Note
No formal reduction rule is required for this instruction, since const instructions are already values.
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
3. Assert: Due to validation, |vvunop v128 (𝑐1 )| > 0.
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐2 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐1 ) from the stack.
5. Assert: Due to validation, |vvbinop v128 (𝑐1 , 𝑐2 )| > 0.
6. Let 𝑐 be an element of vvbinop v128 (𝑐1 , 𝑐2 ).
7. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 ∈ vvbinop v128 (𝑐1 , 𝑐2 )
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐3 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐2 ) from the stack.
5. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
6. Pop the value ([Link] 𝑐1 ) from the stack.
7. Assert: Due to validation, |vvternop v128 (𝑐1 , 𝑐2 , 𝑐3 )| > 0.
8. Let 𝑐 be an element of vvternop v128 (𝑐1 , 𝑐2 , 𝑐3 ).
9. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link] 𝑐3 ) ([Link]) ˓→ ([Link] 𝑐)
if 𝑐 ∈ vvternop v128 (𝑐1 , 𝑐2 , 𝑐3 )
v128.any_true
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
3. Let 𝑐 be inez|v128| (𝑐1 ).
4. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) (v128.any_true) ˓→ ([Link] 𝑐) if 𝑐 = inez|v128| (𝑐1 )
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
3. If vunop sh (𝑐1 ) is empty, then:
a. Trap.
4. Let 𝑐 be an element of vunop sh (𝑐1 ).
5. Push the value ([Link] 𝑐) to the stack.
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐2 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐1 ) from the stack.
5. If vbinop sh (𝑐1 , 𝑐2 ) is empty, then:
a. Trap.
6. Let 𝑐 be an element of vbinop sh (𝑐1 , 𝑐2 ).
7. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 ∈ vbinop sh (𝑐1 , 𝑐2 )
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link]) ˓→ trap if vbinop sh (𝑐1 , 𝑐2 ) = 𝜖
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐3 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐2 ) from the stack.
5. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
6. Pop the value ([Link] 𝑐1 ) from the stack.
7. If vternop sh (𝑐1 , 𝑐2 , 𝑐3 ) is empty, then:
a. Trap.
8. Let 𝑐 be an element of vternop sh (𝑐1 , 𝑐2 , 𝑐3 ).
9. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link] 𝑐3 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 ∈ vternop sh (𝑐1 , 𝑐2 , 𝑐3 )
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link] 𝑐3 ) ([Link]) ˓→ trap if vternop sh (𝑐1 , 𝑐2 , 𝑐3 ) = 𝜖
i𝑁 x𝑀.all_true
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
3. Let 𝑖* be lanesi𝑁 x𝑀 (𝑐1 ).
4. Let 𝑐 be Π inez𝑁 (𝑖)* .
5. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) (i𝑁 x𝑀.all_true) ˓→ ([Link] 𝑐) if 𝑖* = lanesi𝑁 x𝑀 (𝑐1 )
∧ 𝑐 = Π (inez𝑁 (𝑖)* )
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐2 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
[Link]
1. Assert: Due to validation, a value of number type i32 is on the top of the stack.
2. Pop the value ([Link] 𝑖) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐1 ) from the stack.
5. Let 𝑐 be vshiftop sh (𝑐1 , 𝑖).
6. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑖) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 = vshiftop sh (𝑐1 , 𝑖)
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
3. Let 𝑐 be bitmasksh (𝑐1 ).
4. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 = bitmasksh (𝑐1 )
[Link]
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐2 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐1 ) from the stack.
5. Let 𝑐 be swizzlop sh (𝑐1 , 𝑐2 ).
6. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link]) ˓→ ([Link] 𝑐) if 𝑐 = swizzlop sh (𝑐1 , 𝑐2 )
[Link] 𝑖*
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐2 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐1 ) from the stack.
5. Let 𝑐 be shufflesh (𝑖* , 𝑐1 , 𝑐2 ).
6. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link] 𝑖* ) ˓→ ([Link] 𝑐) if 𝑐 = shufflesh (𝑖* , 𝑐1 , 𝑐2 )
i𝑁 x𝑀.splat
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value (numtype 0 .const 𝑐1 ) from the stack.
3. Assert: Due to validation, numtype 0 = unpack(i𝑁 ).
4. Let 𝑐 be lanes−1 𝑀
i𝑁 x𝑀 (packi𝑁 (𝑐1 ) ).
?
lanetypex𝑀.extract_lane_sx ′ 𝑖
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
?
3. If sx ′ is not defined, then:
a. Assert: Due to validation, lanetype is number type.
b. Assert: Due to validation, 𝑖 < |laneslanetypex𝑀 (𝑐1 )|.
c. Let 𝑐2 be laneslanetypex𝑀 (𝑐1 )[𝑖].
d. Push the value ([Link] 𝑐2 ) to the stack.
4. Else:
a. Assert: Due to validation, lanetype is packed type.
?
b. Let sx be sx ′ .
c. Assert: Due to validation, 𝑖 < |laneslanetypex𝑀 (𝑐1 )|.
d. Let 𝑐2 be extendsx
|lanetype|,32 (laneslanetypex𝑀 (𝑐1 )[𝑖]).
i𝑁 x𝑀.replace_lane 𝑖
1. Assert: Due to validation, a value is on the top of the stack.
2. Pop the value (numtype 0 .const 𝑐2 ) from the stack.
3. Assert: Due to validation, numtype 0 = unpack(i𝑁 ).
4. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
5. Pop the value ([Link] 𝑐1 ) from the stack.
6. Let 𝑐 be lanes−1
i𝑁 x𝑀 (lanesi𝑁 x𝑀 (𝑐1 )[[𝑖] = packi𝑁 (𝑐2 )]).
sh 2 .vextunop_sh 1
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
3. Let 𝑐 be vextunop sh 1 ,sh 2 (𝑐1 ).
4. Push the value ([Link] 𝑐) to the stack.
sh 2 .vextbinop_sh 1
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐2 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐1 ) from the stack.
5. Let 𝑐 be vextbinop sh 1 ,sh 2 (𝑐1 , 𝑐2 ).
6. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) (sh 2 .vextbinop_sh 1 ) ˓→ ([Link] 𝑐) if vextbinop sh 1 ,sh 2 (𝑐1 , 𝑐2 ) = 𝑐
sh 2 .vextternop_sh 1
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐3 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐2 ) from the stack.
5. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
6. Pop the value ([Link] 𝑐1 ) from the stack.
7. Let 𝑐 be vextternop sh 1 ,sh 2 (𝑐1 , 𝑐2 , 𝑐3 ).
8. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) ([Link] 𝑐2 ) ([Link] 𝑐3 ) (sh 2 .vextternop_sh 1 ) ˓→ ([Link] 𝑐)
if vextternop sh 1 ,sh 2 (𝑐1 , 𝑐2 , 𝑐3 ) = 𝑐
sh 2 .narrow_sh 1 _sx
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐2 ) from the stack.
3. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
4. Pop the value ([Link] 𝑐1 ) from the stack.
5. Let 𝑐 be narrowsx
sh 1 ,sh 2 (𝑐1 , 𝑐2 ).
sh 2 .vcvtop_sh 1
1. Assert: Due to validation, a value of vector type v128 is on the top of the stack.
2. Pop the value ([Link] 𝑐1 ) from the stack.
3. Let 𝑐 be vcvtop sh 1 ,sh 2 (vcvtop, 𝑐1 ).
4. Push the value ([Link] 𝑐) to the stack.
([Link] 𝑐1 ) (sh 2 .vcvtop_sh 1 ) ˓→ ([Link] 𝑐) if 𝑐 = vcvtop sh 1 ,sh 2 (vcvtop, 𝑐1 )
4.6.12 Expressions
An expression is evaluated relative to a current frame pointing to its containing module instance.
eval_expr instr *
1. Execute the sequence instr * .
2. Pop the value val from the stack.
3. Return val .
𝑧; instr * ˓→* 𝑧 ′ ; val * if 𝑧; instr * ˓→* 𝑧 ′ ; val *
Note
Evaluation iterates this reduction rule until reaching a value. Expressions constituting function bodies are
executed during function invocation.
4.7 Modules
For modules, the execution semantics primarily defines instantiation, which allocates instances for a module and
its contained definitions, initializes memories and tables from contained data and element segments, and invokes
the start function if present. It also includes invocation of exported functions.
4.7.1 Allocation
New instances of tags, globals, memories, tables, functions, data segments, and element segments are allocated in
a store 𝑠, as defined by the following auxiliary functions.
Tags
alloctag(𝑠, tagtype)
1. Let taginst be the tag instance {type tagtype}.
2. Let 𝑎 be the length of 𝑠.tags.
3. Append taginst to 𝑠.tags.
4. Return 𝑎.
alloctag(𝑠, tagtype) = (𝑠 ⊕ {tags taginst}, |𝑠.tags|)
if taginst = {type tagtype}
Globals
allocglobal(𝑠, globaltype, val )
1. Let globalinst be the global instance {type globaltype, value val }.
2. Let 𝑎 be the length of 𝑠.globals.
3. Append globalinst to 𝑠.globals.
4. Return 𝑎.
allocglobal(𝑠, globaltype, val ) = (𝑠 ⊕ {globals globalinst}, |𝑠.globals|)
if globalinst = {type globaltype, value val }
Memories
allocmem(𝑠, at [𝑖 .. 𝑗 ? ] page)
1. Let meminst be the memory instance {type (at [𝑖 .. 𝑗 ? ] page), bytes 0x00𝑖·64 Ki }.
2. Let 𝑎 be the length of 𝑠.mems.
3. Append meminst to 𝑠.mems.
4. Return 𝑎.
allocmem(𝑠, at [𝑖 .. 𝑗 ? ] page) = (𝑠 ⊕ {mems meminst}, |𝑠.mems|)
if meminst = {type (at [𝑖 .. 𝑗 ? ] page), bytes (0x00)𝑖·64 Ki }
Tables
alloctable(𝑠, at [𝑖 .. 𝑗 ? ] rt, ref )
1. Let tableinst be the table instance {type (at [𝑖 .. 𝑗 ? ] rt), elem ref 𝑖 }.
2. Let 𝑎 be the length of 𝑠.tables.
3. Append tableinst to 𝑠.tables.
4. Return 𝑎.
alloctable(𝑠, at [𝑖 .. 𝑗 ? ] rt, ref ) = (𝑠 ⊕ {tables tableinst}, |𝑠.tables|)
if tableinst = {type (at [𝑖 .. 𝑗 ? ] rt), elem ref 𝑖 }
Functions
allocfunc(𝑠, deftype, code, moduleinst)
1. Let funcinst be the function instance {type deftype, module moduleinst, code code}.
2. Let 𝑎 be the length of 𝑠.funcs.
3. Append funcinst to 𝑠.funcs.
4. Return 𝑎.
allocfunc(𝑠, deftype, code, moduleinst) = (𝑠 ⊕ {funcs funcinst}, |𝑠.funcs|)
if funcinst = {type deftype, module moduleinst, code code}
Data segments
allocdata(𝑠, ok, byte * )
1. Let datainst be the data instance {bytes byte * }.
2. Let 𝑎 be the length of 𝑠.datas.
3. Append datainst to 𝑠.datas.
4. Return 𝑎.
allocdata(𝑠, ok, byte * ) = (𝑠 ⊕ {datas datainst}, |𝑠.datas|)
if datainst = {bytes byte * }
Element segments
allocelem(𝑠, elemtype, ref * )
1. Let eleminst be the element instance {type elemtype, elem ref * }.
2. Let 𝑎 be the length of 𝑠.elems.
3. Append eleminst to 𝑠.elems.
4. Return 𝑎.
allocelem(𝑠, elemtype, ref * ) = (𝑠 ⊕ {elems eleminst}, |𝑠.elems|)
if eleminst = {type elemtype, elem ref * }
Growing memories
growmem(meminst, 𝑛)
1. Let {type (at [𝑖 .. 𝑗 ? ] page), bytes 𝑏* } be the destructuring of meminst.
2. Let 𝑖′ be |𝑏* |/(64 Ki) + 𝑛.
3. If (𝑖′ ≤ 𝑗)? , then:
a. Let meminst ′ be the memory instance {type (at [𝑖′ .. 𝑗 ? ] page), bytes 𝑏* 0x00𝑛·64 Ki }.
b. Return meminst ′ .
4. Fail.
growmem(meminst, 𝑛) = meminst ′ if meminst = {type (at [𝑖 .. 𝑗 ? ] page), bytes 𝑏* }
∧ meminst ′ = {type (at [𝑖′ .. 𝑗 ? ] page), bytes 𝑏* (0x00)𝑛·64 Ki }
∧ 𝑖′ = |𝑏* |/(64 Ki) + 𝑛
∧ (𝑖′ ≤ 𝑗)?
Growing tables
growtable(tableinst, 𝑛, 𝑟)
*
1. Let {type (at [𝑖 .. 𝑗 ? ] rt), elem 𝑟′ } be the destructuring of tableinst.
*
2. Let 𝑖′ be |𝑟′ | + 𝑛.
3. If (𝑖′ ≤ 𝑗)? , then:
*
a. Let tableinst ′ be the table instance {type (at [𝑖′ .. 𝑗 ? ] rt), elem 𝑟′ 𝑟𝑛 }.
b. Return tableinst ′ .
4. Fail.
*
growtable(tableinst, 𝑛, 𝑟) = tableinst ′ if tableinst = {type (at [𝑖 .. 𝑗 ? ] rt), elem 𝑟′ }
*
∧ tableinst ′ = {type (at [𝑖′ .. 𝑗 ? ] rt), elem 𝑟′ 𝑟𝑛 }
′ ′*
∧ 𝑖 = |𝑟 | + 𝑛
∧ (𝑖′ ≤ 𝑗)?
Modules
*
allocmodule(𝑠, module, externaddr * , val *g , ref *t , ref *e )
1. Let (module type * import * tag * global * mem * table * func * data * elem * start ? export * ) be the destruc-
turing of module.
2. Let aa *i be tags(externaddr * ).
3. Let ga *i be globals(externaddr * ).
4. Let fa *i be funcs(externaddr * ).
5. Let ma *i be mems(externaddr * ).
6. Let ta *i be tables(externaddr * ).
7. Let fa * be |𝑠.funcs| + 𝑖f for all 𝑖f from 0 to |func * | − 1.
8. Let tagtype * be the tag type sequence 𝜖.
9. For each tag in tag * , do:
a. Let (tag tagtype) be the destructuring of tag.
b. Append tagtype to tagtype * .
*
10. Let byte * be the byte sequence sequence 𝜖.
11. For each data in data * , do:
a. Let (data byte * datamode) be the destructuring of data.
*
b. Append byte * to byte * .
12. Let globaltype * be the global type sequence 𝜖.
13. For each global in global * , do:
a. Let (global globaltype expr g ) be the destructuring of global .
b. Append globaltype to globaltype * .
14. Let tabletype * be the table type sequence 𝜖.
15. For each table in table * , do:
a. Let (table tabletype expr t ) be the destructuring of table.
b. Append tabletype to tabletype * .
16. Let memtype * be the memory type sequence 𝜖.
17. For each mem in mem * , do:
a. Let (memory memtype) be the destructuring of mem.
b. Append memtype to memtype * .
18. Let dt * be alloctype* (type * ).
19. Let elemtype * be the reference type sequence 𝜖.
20. For each elem in elem * , do:
a. Let (elem elemtype expr *e elemmode) be the destructuring of elem.
b. Append elemtype to elemtype * .
21. Let expr *f be the expression sequence 𝜖.
*
22. Let local * be the local sequence sequence 𝜖.
23. Let 𝑥* be the type index sequence 𝜖.
24. For each func in func * , do:
a. Let (func 𝑥 local * expr f ) be the destructuring of func.
b. Append expr f to expr *f .
*
c. Append local * to local * .
d. Append 𝑥 to 𝑥* .
25. Let aa * be 𝜖.
26. For each tagtype in tagtype * , do:
∧ moduleinst = {types dt * ,
tags aa *i aa * , globals ga *i ga * ,
mems ma *i ma * ,
tables ta *i ta * , funcs fa *i fa * , datas da * ,
elems ea * , exports xi * }
Here, the notation allocx* is shorthand for multiple allocations of object kind 𝑋, defined as follows:
For types, however, allocation is defined in terms of rolling and substitution of all preceding types to produce a list
of closed defined types:
*
alloctype* (type ′′ )
*
1. If type ′′ = 𝜖, then:
a. Return 𝜖.
* *
2. Let type ′ type be type ′′ .
3. Let (type rectype) be the destructuring of type.
* *
4. Let deftype ′ be alloctype* (type ′ ).
*
5. Let 𝑥 be the length of deftype ′ .
*
6. Let deftype * be roll*𝑥 (rectype)[:= deftype ′ ].
*
7. Return deftype ′ deftype * .
alloctype* (𝜖) = 𝜖
* * * *
alloctype* (type ′ type) = deftype ′ deftype * if deftype ′ = alloctype* (type ′ )
∧ type = type rectype
*
∧ deftype * = roll*𝑥 (rectype)[:= deftype ′ ]
*
∧ 𝑥 = |deftype ′ |
Finally, export instances are produced with the help of the following definition:
Note
The definition of module allocation is mutually recursive with the allocation of its associated functions, because
the resulting module instance is passed to the allocators as an argument, in order to form the necessary closures.
In an implementation, this recursion is easily unraveled by mutating one or the other in a secondary step.
4.7.2 Instantiation
Given a store 𝑠, a module is instantiated with a list of external addresses externaddr * supplying the required
imports as follows.
Instantiation checks that the module is valid and the provided imports match the declared types, and may fail with
an error otherwise. Instantiation can also result in an exception or trap when initializing a table or memory from
an active segment or when executing the start function. It is up to the embedder to define how such conditions are
reported.
*
evalglobal* (𝑧, globaltype * , expr ′′ )
*
1. If expr ′′ = 𝜖, then:
a. Assert: Due to validation, globaltype * = 𝜖.
b. Return 𝜖.
2. Else:
* *
a. Let expr expr ′ be expr ′′ .
b. Assert: Due to validation, |globaltype * | ≥ 1.
*
c. Let gt gt ′ be globaltype * .
d. Let (𝑠, 𝑓 ) be the destructuring of 𝑧.
e. Let val be the result of evaluating expr with state 𝑧.
f. Let 𝑎 be allocglobal(𝑠, gt, val ).
g. Append 𝑎 to 𝑓.[Link].
* * *
h. Let val ′ be evalglobal* ((𝑠, 𝑓 ), gt ′ , expr ′ ).
*
i. Return val val ′ .
Note
Checking import types assumes that the module instance has already been allocated to compute the respective
closed defined types. However, this forward reference merely is a way to simplify the specification. In practice,
implementations will likely allocate or canonicalize types beforehand, when compiling a module, in a stage
before instantiation and before imports are checked.
Similarly, module allocation and the evaluation of global and table initializers as well as element segments
*
are mutually recursive because the global initialization values val *g , ref t , and element segment contents ref *e
are passed to the module allocator while depending on the module instance moduleinst and store 𝑠′ returned
by allocation. Again, this recursion is just a specification device. In practice, the initialization values can be
determined beforehand by staging module allocation such that first, the module’s own function instances are
pre-allocated in the store, then the initializer expressions are evaluated in order, allocating globals on the way,
then the rest of the module instance is allocated, and finally the new function instances’ module fields are set to
that module instance. This is possible because validation ensures that initialization expressions cannot actually
call a function, only take their reference.
All failure conditions are checked before any observable mutation of the store takes place. Store mutation is
not atomic; it happens in individual steps that may be interleaved with other threads.
Evaluation of constant expressions does not affect the store.
4.7.3 Invocation
Once a module has been instantiated, any exported function can be invoked externally via its function address
funcaddr in the store 𝑠 and an appropriate list val * of argument values.
Invocation may fail with an error if the arguments do not fit the function type. Invocation can also result in an
exception or trap. It is up to the embedder to define how such conditions are reported.
Note
If the embedder API performs type checks itself, either statically or dynamically, before performing an invoca-
tion, then no failure other than traps or exceptions can occur.
Binary Format
5.1 Conventions
The binary format for WebAssembly modules is a dense linear encoding of their abstract syntax.28
The format is defined by an attribute grammar whose only terminal symbols are bytes. A byte sequence is a
well-formed encoding of a module if and only if it is generated by the grammar.
Each production of this grammar has exactly one synthesized attribute: the abstract syntax that the respective byte
sequence encodes. Thus, the attribute grammar implicitly defines a decoding function (i.e., a parsing function for
the binary format).
Except for a few exceptions, the binary grammar closely mirrors the grammar of the abstract syntax.
Note
Some phrases of abstract syntax have multiple possible encodings in the binary format. For example, numbers
may be encoded as if they had optional leading zeros. Implementations of decoders must support all possible
alternatives; implementations of encoders can pick any allowed encoding.
The recommended extension for files containing WebAssembly modules in binary format is “.wasm” and the rec-
ommended Media Type27 is “application/wasm”.
5.1.1 Grammar
The following conventions are adopted in defining grammar rules for the binary format. They mirror the conven-
tions used for abstract syntax. In order to distinguish symbols of the binary syntax from symbols of the abstract
syntax, typewriter font is adopted for the former.
• Terminal symbols are bytes expressed in hexadecimal notation: 0x0F.
• Nonterminal symbols are written in typewriter font: valtype, instr.
• 𝐵 𝑛 is a sequence of 𝑛 ≥ 0 iterations of 𝐵.
• 𝐵 * is a possibly empty sequence of iterations of 𝐵. (This is a shorthand for 𝐵 𝑛 used where 𝑛 is not relevant.)
28 Additional encoding layers – for example, introducing compression – may be defined on top of the basic representation defined here.
However, such layers are outside the scope of the current specification.
27 [Link]
177
WebAssembly Specification, Release 3.0 (2025-11-14)
Note
For example, the binary grammar for number types is given as follows:
Consequently, the byte 0x7F encodes the type i32, 0x7E encodes the type i64, and so forth. No other byte value
is allowed as the encoding of a number type.
The binary grammar for limits is defined as follows:
That is, a limits pair is encoded as either the byte 0x00 followed by the encoding of a u64 value, or the byte 0x01
followed by two such encodings. The variables 𝑛 and 𝑚 name the attributes of the respective u64 nonterminals,
which in this case are the actual unsigned integers those decode into. The attribute of the complete production
then is the abstract syntax for the limit, expressed in terms of the former values.
5.1.3 Lists
Lists are encoded with their u32 length followed by the encoding of their element sequence.
5.2 Values
5.2.1 Bytes
Bytes encode themselves.
5.2.2 Integers
All integers are encoded using the LEB12829 variable-length integer encoding, in either unsigned or signed variant.
Unsigned integers are encoded in unsigned LEB12830 format. As an additional constraint, the total number of
bytes encoding a u𝑁 value must not exceed ceil(𝑁/7) bytes.
Signed integers are encoded in signed LEB12831 format, which uses a two’s complement representation. As an
additional constraint, the total number of bytes encoding an s𝑁 value must not exceed ceil(𝑁/7) bytes.
Note
The side conditions 𝑁 > 7 in the productions for non-terminal bytes of the u𝑁 and s𝑁 encodings restrict the
encoding’s length. However, “trailing zeros” are still allowed within these bounds. For example, 0x03 and
0x83 0x00 are both well-formed encodings for the value 3 as a u8 . Similarly, either of 0x7E and 0xFE 0x7F
and 0xFE 0xFF 0x7F are well-formed encodings of the value −2 as an s16 .
The side conditions on the value 𝑛 of terminal bytes further enforce that any unused bits in these bytes must be 0
for positive values and 1 for negative ones. For example, 0x83 0x10 is malformed as a u8 encoding. Similarly,
both 0x83 0x3E and 0xFF 0x7B are malformed as s8 encodings.
5.2.3 Floating-Point
Floating-point values are encoded directly by their IEEE 75432 (Section 3.4) bit pattern in little endian33 byte order:
29 [Link]
30 [Link]
31 [Link]
32 [Link]
33 [Link]
5.2.4 Names
Names are encoded as a list of bytes containing the Unicode34 (Section 3.9) UTF-8 encoding of the name’s character
sequence.
Note
5.3 Types
Note
In some places, possible types include both type constructors or types denoted by type indices. Thus, the
binary format for type constructors corresponds to the encodings of small negative s𝑁 values, such that they
can unambiguously occur in the same place as (positive) type indices.
34 [Link]
Note
Note
35 [Link]
5.3.9 Limits
Limits are encoded with a preceding flag indicating whether a maximum is present, and a flag for the address type.
Note
In future versions of WebAssembly, the preceding zero byte may encode additional attributes.
5.4 Instructions
Instructions are encoded by opcodes. Each opcode is represented by a single byte, and is followed by the instruc-
tion’s immediate arguments, where present. The only exception are structured control instructions, which consist
of several opcodes bracketing their nested instruction sequences.
Note
The byte codes chosen to encode instructions are historical and do not follow a consistent pattern. In this section,
instructions are hence not presented in opcode order, but instead grouped consistently with other sections in
this document. An instruction index ordered by opcode can be found in the Appendix.
Gaps in the byte code ranges are reserved for future extensions.
Block types are encoded in special compressed form, by either the byte 0x40 indicating the empty type, as a single
value type, or as a type index encoded as a positive signed integer.
Note
The else opcode 0x05 in the encoding of an if instruction can be omitted if the following instruction sequence
is empty.
Unlike any other occurrence, the type index in a block type is encoded as a positive signed integer, so that its
signed LEB128 bit pattern cannot collide with the encoding of value types or the special code 0x40, which
correspond to the LEB128 encoding of negative integers. To avoid any loss in the range of allowed indices, it
is treated as a 33 bit signed integer.
All other numeric instructions are plain opcodes without any immediates.
The const instruction for vectors is followed by 16 immediate bytes, which are converted into an u128 in
littleendian byte order:
instr ::= . . .
| 0xFD 12:u32 (𝑏:byte)16 ⇒ [Link] bytes−1 16
i128 ((𝑏) )
All other vector instructions are plain opcodes without any immediates.
5.4.10 Expressions
Expressions are encoded by their instruction sequence terminated with an explicit 0x0B opcode for end.
5.5 Modules
The binary encoding of modules is organized into sections. Most sections correspond to one component of a
module record, except that function definitions are split into two sections, separating their type declarations in the
function section from their bodies in the code section.
Note
This separation enables parallel and streaming compilation of the functions in a module.
5.5.1 Indices
All basic indices are encoded with their respective value.
External indices are encoded by a distiguishing byte followed by an encoding of their respective value.
5.5.2 Sections
Each section consists of
• a one-byte section id,
• the u32 length of the contents, in bytes,
• the actual contents, whose structure is dependent on the section id.
Every section is optional; an omitted section is equivalent to the section being present with empty contents.
The following parameterized grammar rule defines the generic structure of a section with id 𝑁 and contents de-
scribed by the grammar X.
For most sections, the contents X encodes a list. In these cases, the empty result 𝜖 is interpreted as the empty list.
Note
Other than for unknown custom sections, the size is not required for decoding, but can be used to skip sections
when navigating through a binary. The module is malformed if the size does not match the length of the binary
contents X.
Id Section
0 custom section
1 type section
2 import section
3 function section
4 table section
5 memory section
6 global section
7 export section
8 start section
9 element section
10 code section
11 data section
12 data count section
13 tag section
Note
Section ids do not always correspond to the order of sections in the encoding of a module.
Note
If an implementation interprets the data of a custom section, then errors in that data, or the placement of the
section, must not invalidate the module.
Note
The encoding of a table type cannot start with byte 0x40, hence decoding is unambiguous. The zero byte
following it is reserved for future extensions.
Note
The initial integer can be interpreted as a bitfield. Bit 0 distinguishes a passive or declarative segment from
an active segment, bit 1 indicates the presence of an explicit table index for an active segment and otherwise
distinguishes passive from declarative segments, bit 2 indicates the use of element type and element expressions
instead of element kind and element indices.
Additional element kinds may be added in future versions of WebAssembly.
Here, code ranges over pairs (local * , expr ). Any code for which the length of the resulting sequence is out of
bounds of the maximum size of a list is malformed.
Note
Like with sections, the code size is not needed for decoding, but can be used to skip functions when navigating
through a binary. The module is malformed if a size does not match the length of the respective function code.
Note
The initial integer can be interpreted as a bitfield. Bit 0 indicates a passive segment, bit 1 indicates the presence
of an explicit memory index for an active segment.
Note
The data count section is used to simplify single-pass validation. Since the data section occurs after the code
section, the [Link] and [Link] instructions would not be able to check whether the data segment index
is valid until the data section is read. The data count section occurs before the code section, so a single-pass
validator can use this count instead of deferring validation.
5.5.17 Modules
The encoding of a module starts with a preamble containing a 4-byte magic number (the string ‘∖0asm’) and a
version field. The current version of the WebAssembly binary format is 1.
The preamble is followed by a sequence of sections. Custom sections may be inserted at any place in this sequence,
while other sections must occur at most once and in the prescribed order. All sections can be empty.
The lengths of lists produced by the (possibly empty) function and code section must match up.
Similarly, the optional data count must match the length of the data segment list. Furthermore, it must be present
if any data index occurs in the code section.
Note
The version of the WebAssembly binary format may increase in the future if backward-incompatible changes
have to be made to the format. However, such changes are expected to occur very infrequently, if ever. The
binary format is intended to be extensible, such that future features can be added without incrementing its
version.
Text Format
6.1 Conventions
The textual format for WebAssembly modules is a rendering of their abstract syntax into S-expressions36 .
Like the binary format, the text format is defined by an attribute grammar. A text string is a well-formed description
of a module if and only if it is generated by the grammar. Each production of this grammar has at most one
synthesized attribute: the abstract syntax that the respective character sequence expresses. Thus, the attribute
grammar implicitly defines a parsing function. Some productions also take a context as an inherited attribute that
records bound identifiers.
Except for a few exceptions, the core of the text grammar closely mirrors the grammar of the abstract syntax.
However, it also defines a number of abbreviations that are “syntactic sugar” over the core syntax.
The recommended extension for files containing WebAssembly modules in text format is “.wat”. Files with this
extension are assumed to be encoded in UTF-8, as per Unicode37 (Section 2.5).
6.1.1 Grammar
The following conventions are adopted in defining grammar rules of the text format. They mirror the conventions
used for abstract syntax and for the binary format. In order to distinguish symbols of the textual syntax from
symbols of the abstract syntax, typewriter font is adopted for the former.
• Terminal symbols are either literal strings of characters enclosed in quotes or expressed as Unicode38 scalar
values: ‘module’, U+0A. (All characters written literally are unambiguously drawn from the 7-bit ASCII39
subset of Unicode.)
• Nonterminal symbols are written in typewriter font: valtype, instr.
• 𝑇 𝑛 is a sequence of 𝑛 ≥ 0 iterations of 𝑇 .
• 𝑇 * is a possibly empty sequence of iterations of 𝑇 . (This is a shorthand for 𝑇 𝑛 used where 𝑛 is not relevant.)
• 𝑇 + is a possibly empty sequence of iterations of 𝑇 . (This is a shorthand for 𝑇 𝑛 used where 𝑛 is not relevant.)
• 𝑇 ? is an optional occurrence of 𝑇 . (This is a shorthand for 𝑇 𝑛 where 𝑛 ≤ 1.)
36 [Link]
37 [Link]
38 [Link]
39 [Link]
205
WebAssembly Specification, Release 3.0 (2025-11-14)
• 𝑥:𝑇 denotes the same language as the nonterminal 𝑇 , but also binds the variable 𝑥 to the attribute synthesized
for 𝑇 . A pattern may also be used instead of a variable, e.g., 7:𝑇 .
• Productions are written sym ::= 𝑇1 ⇒ 𝐴1 | . . . | 𝑇𝑛 ⇒ 𝐴𝑛 , where each 𝐴𝑖 is the attribute that is syn-
thesized for sym in the given case, usually from attribute variables bound in 𝑇𝑖 .
• Large productions may be split into multiple definitions, indicated by ending the first one with explicit el-
lipses, sym ::= 𝐵1 , and starting continuations with ellipses, sym ::= . . . | 𝐵2 .
• Some productions are augmented by side conditions in parentheses, which restrict the applicability of the
production. They provide a shorthand for a combinatorial expansion of the production into many separate
cases.
• If the same meta variable or non-terminal symbol appears multiple times in a production (in the syntax or in
an attribute), then all those occurrences must have the same instantiation.
• A distinction is made between lexical and syntactic productions. For the latter, arbitrary white space is
allowed in any place where the grammar contains spaces. The productions defining lexical syntax and the
syntax of values are considered lexical, all others are syntactic.
Note
For example, the textual grammar for number types is given as follows:
The variables 𝑛 and 𝑚 name the attributes of the respective u64 nonterminals, which in this case are the actual
unsigned integers those parse into. The attribute of the complete production then is the abstract syntax for the
limit, expressed in terms of the former values.
6.1.2 Abbreviations
In addition to the core grammar, which corresponds directly to the abstract syntax, the textual syntax also defines
a number of abbreviations that can be used for convenience and readability.
Abbreviations are defined by rewrite rules specifying their expansion into the core syntax:
These expansions are assumed to be applied, recursively and in order of appearance, before applying the core
grammar rules to construct the abstract syntax.
6.1.3 Contexts
The text format allows the use of symbolic identifiers in place of indices. To resolve these identifiers into concrete
indices, some grammar productions are indexed by an identifier context 𝐼 as a synthesized attribute that records
the declared identifiers in each index space. In addition, the context records the types defined in the module, so
that parameter indices can be computed for functions.
For each index space, such a context contains the list of names assigned to the defined indices, which were denoted
by the corresponding identifiers. Unnamed indices are associated with empty (𝜖) entries in these lists. Fields have
dependent name spaces, and hence a separate list of field identifiers per type.
In addition, the field typedefs records the defined type associated with each type index. They are needed to look
up the number of parameters of function types when used in a function definition, in order to produce the correct
indices for locals.
An identifier context is well-formed if no index space contains duplicate identifiers. For fields, names need only
be unique within a single type.
Conventions
To avoid unnecessary clutter, empty components are omitted when writing out identifier contexts. For example,
the record {} is shorthand for an identifier context whose components are all empty.
6.1.4 Lists
Lists are written as plain sequences, but with a restriction on the length of these sequence.
Note
While source text may contain any Unicode character in comments or string literals, the rest of the grammar is
formed exclusively from the characters supported by the 7-bit ASCII41 subset of Unicode.
40 [Link]
41 [Link]
6.2.2 Tokens
The character stream in the source text is divided, from left to right, into a sequence of tokens, as defined by the
following grammar.
Tokens are formed from the input character stream according to the longest match rule. That is, the next token
always consists of the longest possible sequence of characters that is recognized by the above lexical grammar.
Tokens can be separated by white space, but except for strings, they cannot themselves contain whitespace.
Keyword tokens always start with a lower-case letter. The set of keywords is defined implicitly: only those tokens
are defined to be keywords that occur as a terminal symbol in literal form, such as ‘keyword’, in a syntactic
production of this chapter.
Any token that does not fall into any of the other categories is considered reserved, and cannot occur in source text.
Note
The effect of defining the set of reserved tokens is that all tokens must be separated by either parentheses, white
space, or comments. For example, ‘0$x’ is a single reserved token, as is ‘’’a’’’’b’’’. Consequently, they are
not recognized as two separate tokens ‘0’ and ‘$x’, or ‘’’a’’’ and ‘’’b’’’, respectively, but instead disallowed.
This property of tokenization is not affected by the fact that the definition of reserved tokens overlaps with other
token classes.
6.2.4 Comments
A comment can either be a line comment, started with a double semicolon ‘;;’ and extending to the end of the line,
or a block comment, enclosed in delimiters ‘(;’ . . . ‘;)’. Block comments can be nested.
Here, the pseudo token eof indicates the end of the input. The look-ahead restrictions on the productions for
blockchar disambiguate the grammar such that only well-bracketed uses of block comment delimiters are allowed.
42 [Link]
Note
6.2.5 Annotations
An annotation is a bracketed token sequence headed by an annotation id of the form ‘@id’ or ‘@’’...’’’. No
space is allowed between the opening parenthesis and this id. Annotations are intended to be used for third-party
extensions; they can appear anywhere in a program but are ignored by the WebAssembly semantics itself, which
treats them as white space.
Annotations can contain other parenthesized token sequences (including nested annotations), as long as they are
well-nested. String literals and comments occurring in an annotation must also be properly nested and closed.
annot ::= ‘(@’ annotid (space | token)* ‘)’
annotid ::= idchar+ | name
Note
The annotation id is meant to be an identifier categorising the extension, and plays a role similar to the name of a
custom section. By convention, annotations corresponding to a custom section should use the custom section’s
name as an id.
Implementations are expected to ignore annotations with ids that they do not recognize. On the other hand,
they may impose restrictions on annotations that they do recognize, e.g., requiring a specific structure by super-
imposing a more concrete grammar. It is up to an implementation how it deals with errors in such annotations.
6.3 Values
The grammar productions in this section define lexical syntax, hence no white space is allowed.
6.3.1 Integers
All integers can be written in either decimal or hexadecimal notation. In both cases, digits can optionally be
separated by underscores.
sign ::= 𝜖 ⇒ +1 | ‘+’ ⇒ +1 | ‘-’ ⇒ −1
digit ::= ‘0’ ⇒ 0 | . . . | ‘9’ ⇒ 9
hexdigit ::= 𝑑:digit ⇒ 𝑑
| ‘A’ ⇒ 10 | . . . | ‘F’ ⇒ 15
| ‘a’ ⇒ 10 | . . . | ‘f’ ⇒ 15
num ::= 𝑑:digit ⇒ 𝑑
| 𝑛:num ‘_’? 𝑑:digit ⇒ 10 𝑛 + 𝑑
hexnum ::= ℎ:hexdigit ⇒ ℎ
| 𝑛:hexnum ‘_’? ℎ:hexdigit ⇒ 16 𝑛 + ℎ
The allowed syntax for integer literals depends on size and signedness. Moreover, their value must lie within the
range of the respective type.
u𝑁 ::= 𝑛:num ⇒ 𝑛 if 𝑛 < 2𝑁
| ‘0x’ 𝑛:hexnum ⇒ 𝑛 if 𝑛 < 2𝑁
s𝑁 ::= 𝑠:sign 𝑛:u𝑁 ⇒ 𝑠·𝑛 if −2𝑁 −1 ≤ 𝑠 · 𝑛 < 2𝑁 −1
Uninterpreted integers can be written as either signed or unsigned, and are normalized to unsigned in the abstract
syntax.
i𝑁 ::= 𝑛:u𝑁 ⇒ 𝑛
| 𝑖:s𝑁 ⇒ signed−1
𝑁 (𝑖)
6.3.2 Floating-Point
Floating-point values can be represented in either decimal or hexadecimal notation.
The value of a literal must not lie outside the representable range of the corresponding IEEE 75443 type (that is, a
numeric value must not overflow to ±∞), but it may be rounded to the nearest representable value.
Note
Rounding can be prevented by using hexadecimal notation with no more significant bits than supported by the
required type.
Floating-point values may also be written as constants for infinity or canonical NaN (not a number). Furthermore,
arbitrary NaN values may be expressed by providing an explicit payload value.
6.3.3 Strings
Strings denote sequences of bytes that can represent both textual and binary data. They are enclosed in quotation
marks and may contain any character other than ASCII44 control characters, quotation marks (‘’’’), or backslash
(‘∖’), except when expressed with an escape sequence.
⨁︀ * *
string ::= ‘’’’ (𝑏* :stringelem)* ‘’’’ if | 𝑏* * | < 232
⨁︀
⇒ 𝑏
stringelem ::= 𝑐:stringchar ⇒ utf 8(𝑐)
| ‘∖’ ℎ1 :hexdigit ℎ2 :hexdigit ⇒ 16 ℎ1 + ℎ2
43 [Link]
44 [Link]
Each character in a string literal represents the byte sequence corresponding to its UTF-8 Unicode45 (Section 2.5)
encoding, except for hexadecimal escape sequences ‘∖hh’, which represent raw bytes of the respective value.
6.3.4 Names
Names are strings denoting a literal character sequence. A name string must form a valid UTF-8 encoding as
defined by Unicode46 (Section 2.5) and is interpreted as a string of Unicode scalar values.
Note
Presuming the source text is itself encoded correctly, strings that do not contain any uses of hexadecimal byte
escapes are always valid names.
6.3.5 Identifiers
Indices can be given in both numeric and symbolic form. Symbolic identifiers that stand in lieu of indices start with
‘$’, followed by eiter a sequence of printable ASCII47 characters that does not contain a space, quotation mark,
comma, semicolon, or bracket, or by a quoted name.
Note
Conventions
The expansion rules of some abbreviations require insertion of a fresh identifier. That may be any syntactically
valid identifier that does not already occur in the given source text.
45 [Link]
46 [Link]
47 [Link]
6.4 Types
6.4.1 Number Types
numtype ::= ‘i32’ ⇒ i32
| ‘i64’ ⇒ i64
| ‘f32’ ⇒ f 32
| ‘f64’ ⇒ f 64
Abbreviations
There are shorthands for references to abstract heap types.
Note
The optional identifier names for parameters in a function type only have documentation purpose. They cannot
be referenced from anywhere.
Abbreviations
Multiple anonymous structure fields or parameters or multiple results may be combined into a single declaration:
field𝐼 ::= . . . | ‘(’ ‘field’ fieldtype*𝐼 ‘)’ ≡ (‘(’ ‘field’ fieldtype𝐼 ‘)’)*
param𝐼 ::= . . . | ‘(’ ‘param’ valtype*𝐼 ‘)’ ≡ (‘(’ ‘param’ valtype𝐼 ‘)’)*
result𝐼 ::= . . . | ‘(’ ‘result’ valtype*𝐼 ‘)’ ≡ (‘(’ ‘param’ valtype𝐼 ‘)’)*
Abbreviations
Final sub types with no super-types can omit the ‘sub’ keyword and its arguments:
subtype𝐼 ::= . . . | comptype𝐼 ≡ ‘(’ ‘sub’ ‘final’ comptype𝐼 ‘)’
Similarly, singular recursive types can omit the ‘rec’ keyword:
rectype𝐼 ::= . . . | typedef𝐼 ≡ ‘(’ ‘rec’ typedef𝐼 ‘)’
Abbreviations
The address type can be omitted, in which case it defaults i32:
6.4.9 Limits
limits ::= 𝑛:u64 ⇒ [𝑛 .. 𝜖]
| 𝑛:u64 𝑚:u64 ⇒ [𝑛 .. 𝑚]
Note
If inline declarations are given, their types must be syntactically equal to the types from the indexed definition;
possible type substitutions from other definitions that might make them equal are not taken into account. This
is to simplify syntactic pre-processing.
The synthesized attribute of a typeuse is a pair consisting of both the used type index and the local identifier
context containing possible parameter identifiers.
Note
Both productions overlap for the case that the function type is func 𝜖 → 𝜖. However, in that case, they also
produce the same results, so that the choice is immaterial.
The well-formedness condition on 𝐼 ′ ensures that the parameters do not contain duplicate identifiers.
Abbreviations
A type use may also be replaced entirely by inline parameter and result declarations. In that case, a type index is
automatically inserted:
typeuse𝐼 ::= . . . | (𝑡1 , id ? )* :param*𝐼 𝑡*2 :result*𝐼 ≡ ‘(’ ‘type’ 𝑥:typeidx𝐼 ‘)’ param*𝐼 result*𝐼
if 𝐼.typedefs[𝑥] = (rec (sub final (func 𝑡*1 → 𝑡*2 ))).0
∧ (𝐼.typedefs[𝑖] ̸= (rec (sub final (func 𝑡*1 → 𝑡*2 ))).0)𝑖<𝑥
where 𝑥 is the smallest existing type index whose recursive type definition parses into a singular, final function
type with the same parameters and results. If no such index exists, then a new recursive type of the same form is
inserted at the end of the module.
Abbreviations are expanded in the order they appear, such that previously inserted type definitions are reused by
consecutive expansions.
6.5 Instructions
Instructions are syntactically distinguished into plain and structured instructions.
instr𝐼 ::= in:plaininstr𝐼 ⇒ in
| in:blockinstr𝐼 ⇒ in
instrs𝐼 ::= in * :instr*𝐼 ⇒ in *
In addition, as a syntactic abbreviation, instructions can be written as S-expressions in folded form, to group them
visually.
6.5.1 Labels
Structured control instructions can be annotated with a symbolic label identifier. They are the only symbolic
identifiers that can be bound locally in an instruction sequence. The following grammar handles the corresponding
update to the identifier context by composing the context with an additional label entry.
Note
The new label entry is inserted at the beginning of the label list in the identifier context. This effectively shifts
all existing labels up by one, mirroring the fact that control instructions are indexed relatively not absolutely.
If a label with the same name already exists, then it is shadowed and the earlier label becomes inaccessible.
Note
The side condition stating that the identifier context 𝐼 ′ must only contain unnamed entries in the rule for
typeuse block types enforces that no identifier can be bound in any param declaration for a block type.
Note
The side condition stating that the identifier context 𝐼 ′ must only contain unnamed entries in the rule for
call_indirect enforces that no identifier can be bound in any param declaration appearing in the type anno-
tation.
Abbreviations
The ‘else’ keyword of an ‘if’ instruction can be omitted if the following instruction sequence is empty.
blockinstr𝐼 ::= . . .
| ‘if’ label𝐼 blocktype𝐼 instrs𝐼 ‘end’ id? ≡
‘if’ label𝐼 blocktype𝐼 instrs𝐼 ‘else’ ‘end’ id?
Also, for backwards compatibility, the table index to ‘call_indirect’ and ‘return_call_indirect’ can be
omitted, defaulting to 0.
plaininstr𝐼 ::= . . .
| ‘call_indirect’ typeuse𝐼 ≡ ‘call_indirect’ ‘0’ typeuse𝐼
| ‘return_call_indirect’ typeuse𝐼 ≡ ‘return_call_indirect’ ‘0’ typeuse𝐼
Abbreviations
For backwards compatibility, all table indices may be omitted from table instructions, defaulting to 0.
phrase is considered a single keyword token, so no white space is allowed around the ‘=’.
memarg𝑁 ::= 𝑛:offset 𝑚:align𝑁 ⇒ {align 𝑛, offset 𝑚}
offset ::= ‘offset=’ 𝑛:u64 ⇒ 𝑛
| 𝜖 ⇒ 0
align𝑁 ::= ‘align=’ 𝑚:u64 ⇒ 𝑚 if 𝑚 = 2𝑛
| 𝜖 ⇒ 𝑁
laneidx ::= 𝑖:u8 ⇒ 𝑖
plaininstr𝐼 ::= ...
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg4 ⇒ [Link] 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg8 ⇒ [Link] 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg4 ⇒ f [Link] 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg8 ⇒ f [Link] 𝑥 ao
| ‘i32.load8_s’ 𝑥:memidx𝐼 ao:memarg1 ⇒ i32.load8_s 𝑥 ao
| ‘i32.load8_u’ 𝑥:memidx𝐼 ao:memarg1 ⇒ i32.load8_u 𝑥 ao
| ‘i32.load16_s’ 𝑥:memidx𝐼 ao:memarg2 ⇒ i32.load16_s 𝑥 ao
| ‘i32.load16_u’ 𝑥:memidx𝐼 ao:memarg2 ⇒ i32.load16_u 𝑥 ao
| ‘i64.load8_s’ 𝑥:memidx𝐼 ao:memarg1 ⇒ i64.load8_s 𝑥 ao
| ‘i64.load8_u’ 𝑥:memidx𝐼 ao:memarg1 ⇒ i64.load8_u 𝑥 ao
| ‘i64.load16_s’ 𝑥:memidx𝐼 ao:memarg2 ⇒ i64.load16_s 𝑥 ao
| ‘i64.load16_u’ 𝑥:memidx𝐼 ao:memarg2 ⇒ i64.load16_u 𝑥 ao
| ‘i64.load32_s’ 𝑥:memidx𝐼 ao:memarg4 ⇒ i64.load32_s 𝑥 ao
| ‘i64.load32_u’ 𝑥:memidx𝐼 ao:memarg4 ⇒ i64.load32_u 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg16 ⇒ [Link] 𝑥 ao
| ‘v128.load8x8_s’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load8x8_s 𝑥 ao
| ‘v128.load8x8_u’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load8x8_u 𝑥 ao
| ‘v128.load16x4_s’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load16x4_s 𝑥 ao
| ‘v128.load16x4_u’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load16x4_u 𝑥 ao
| ‘v128.load32x2_s’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load32x2_s 𝑥 ao
| ‘v128.load32x2_u’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load32x2_u 𝑥 ao
| ‘v128.load8_splat’ 𝑥:memidx𝐼 ao:memarg1 ⇒ v128.load8_splat 𝑥 ao
| ‘v128.load16_splat’ 𝑥:memidx𝐼 ao:memarg2 ⇒ v128.load16_splat 𝑥 ao
| ‘v128.load32_splat’ 𝑥:memidx𝐼 ao:memarg4 ⇒ v128.load32_splat 𝑥 ao
| ‘v128.load64_splat’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load64_splat 𝑥 ao
| ‘v128.load32_zero’ 𝑥:memidx𝐼 ao:memarg4 ⇒ v128.load32_zero 𝑥 ao
| ‘v128.load64_zero’ 𝑥:memidx𝐼 ao:memarg8 ⇒ v128.load64_zero 𝑥 ao
| ‘v128.load8_lane’ 𝑥:memidx𝐼 ao:memarg1 𝑖:laneidx ⇒ v128.load8_lane 𝑥 ao 𝑖
| ‘v128.load16_lane’ 𝑥:memidx𝐼 ao:memarg2 𝑖:laneidx ⇒ v128.load16_lane 𝑥 ao 𝑖
| ‘v128.load32_lane’ 𝑥:memidx𝐼 ao:memarg4 𝑖:laneidx ⇒ v128.load32_lane 𝑥 ao 𝑖
| ‘v128.load64_lane’ 𝑥:memidx𝐼 ao:memarg8 𝑖:laneidx ⇒ v128.load64_lane 𝑥 ao 𝑖
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg4 ⇒ [Link] 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg8 ⇒ [Link] 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg4 ⇒ f [Link] 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg8 ⇒ f [Link] 𝑥 ao
| ‘i32.store8’ 𝑥:memidx𝐼 ao:memarg1 ⇒ i32.store8 𝑥 ao
| ‘i32.store16’ 𝑥:memidx𝐼 ao:memarg2 ⇒ i32.store16 𝑥 ao
| ‘i64.store8’ 𝑥:memidx𝐼 ao:memarg1 ⇒ i64.store8 𝑥 ao
| ‘i64.store16’ 𝑥:memidx𝐼 ao:memarg2 ⇒ i64.store16 𝑥 ao
| ‘i64.store32’ 𝑥:memidx𝐼 ao:memarg4 ⇒ i64.store32 𝑥 ao
| ‘[Link]’ 𝑥:memidx𝐼 ao:memarg16 ⇒ [Link] 𝑥 ao
| ‘v128.store8_lane’ 𝑥:memidx𝐼 ao:memarg1 𝑖:laneidx ⇒ v128.store8_lane 𝑥 ao 𝑖
| ‘v128.store16_lane’ 𝑥:memidx𝐼 ao:memarg2 𝑖:laneidx ⇒ v128.store16_lane 𝑥 ao 𝑖
| ‘v128.store32_lane’ 𝑥:memidx𝐼 ao:memarg4 𝑖:laneidx ⇒ v128.store32_lane 𝑥 ao 𝑖
| ‘v128.store64_lane’ 𝑥:memidx𝐼 ao:memarg8 𝑖:laneidx ⇒ v128.store64_lane 𝑥 ao 𝑖
| ‘[Link]’ 𝑥:memidx𝐼 ⇒ [Link] 𝑥
| ‘[Link]’ 𝑥:memidx𝐼 ⇒ [Link] 𝑥
| ‘[Link]’ 𝑥:memidx𝐼 ⇒ [Link] 𝑥
| ‘[Link]’ 𝑥1 :memidx𝐼 𝑥2 :memidx𝐼 ⇒ [Link] 𝑥1 𝑥2
| ‘[Link]’ 𝑥:memidx𝐼 𝑦:dataidx𝐼 ⇒ [Link] 𝑥 𝑦
| ‘[Link]’ 𝑥:dataidx𝐼 ⇒ [Link] 𝑥
6.5. Instructions 219
WebAssembly Specification, Release 3.0 (2025-11-14)
Abbreviations
As an abbreviation, the memory index can be omitted in all memory instructions, defaulting to 0.
foldedinstr. Such a folded instruction can appear anywhere a regular instruction can.
foldedinstr𝐼 ::= ‘(’ plaininstr𝐼 instrs𝐼 ‘)’ ≡
instrs𝐼 plaininstr𝐼
| ‘(’ ‘block’ label𝐼 blocktype𝐼 instrs𝐼 ′ ‘)’ ≡
‘block’ label𝐼 blocktype𝐼 instrs𝐼 ′ ‘end’
| ‘(’ ‘loop’ label𝐼 blocktype𝐼 instrs𝐼 ′ ‘)’ ≡
‘loop’ label𝐼 blocktype𝐼 instrs𝐼 ′ ‘end’
| ‘(’ ‘if’ label𝐼 blocktype𝐼 foldedinstr*𝐼 ≡
‘(’ ‘then’ in *1 :instrs𝐼 ′ ‘)’ (‘(’ ‘else’ in *2 :instrs𝐼 ′ ‘)’)? ‘)’
foldedinstr*𝐼 ‘if’ label𝐼 blocktype𝐼 in *1 :instrs𝐼 ′ (‘else’ in *2 :instrs𝐼 ′ )? ‘end’
| ‘(’ ‘try_table’ label𝐼 blocktype𝐼 catch*𝐼 instrs𝐼 ′ ‘)’ ≡
‘try_table’ label𝐼 blocktype𝐼 catch*𝐼 instrs𝐼 ′ ‘end’
Note
Folded instructions are solely syntactic sugar, no additional syntactic or type-based checking is implied.
6.5.12 Expressions
Expressions are written as instruction sequences.
6.6 Modules
Modules consist of a sequence of declarations. The grammar rules for each declaration construct produce a pair,
consisting of not just the abstract syntax representing the respective declaration, but also an identifier context
recording the new symbolic identifiers bound by the construct, for use in the remainder of the module.
6.6.1 Indices
Indices can be given either in raw numeric form or as symbolic identifiers when bound by a respective construct.
Such identifiers are looked up in the suitable space of the identifier context 𝐼.
idxids ::= 𝑥:u32 ⇒ 𝑥
| id :id ⇒ 𝑥 if ids[𝑥] = id
typeidx𝐼 ::= idx𝐼.types
tagidx𝐼 ::= idx𝐼.tags
globalidx𝐼 ::= idx𝐼.globals
memidx𝐼 ::= idx𝐼.mems
tableidx𝐼 ::= idx𝐼.tables
funcidx𝐼 ::= idx𝐼.funcs
dataidx𝐼 ::= idx𝐼.datas
elemidx𝐼 ::= idx𝐼.elems
localidx𝐼 ::= idx𝐼.locals
labelidx𝐼 ::= idx𝐼.labels
fieldidx𝐼,𝑥 ::= idx𝐼.fields[𝑥]
6.6.2 Types
A type definition consists of a recursive type. The identifier context produced for the local bindings is further
extended with the respective sequence of defined types that the recursive type generates.
6.6.3 Tags
A tag definition can bind a symbolic tag identifier.
tag𝐼 ::= ‘(’ ‘tag’ id ? :id? jt:tagtype𝐼 ‘)’ ⇒ (tag jt, {tags (id ? )})
Abbreviations
Tags can be defined as imports or exports inline:
import𝐼 ::= . . .
| ‘(’ ‘tag’ id? ‘(’ ‘import’ name2 ‘)’ tagtype𝐼 ‘)’ ≡
‘(’ ‘import’ name2 ‘(’ ‘tag’ id? tagtype𝐼 ‘)’ ‘)’
export𝐼 ::= ‘(’ ‘tag’ id ? :id? ‘(’ ‘export’ name ‘)’ . . . ‘)’ ≡
‘(’ ‘tag’ id ′ :id . . . ‘)’
‘(’ ‘export’ name ‘(’ ‘tag’ id ‘)’ ‘)’
if id ? = id ′ ∨ id ? = 𝜖 ∧ id ′ ∈
/ 𝐼.tags
Note
The latter abbreviation can be applied repeatedly, if “. . .” contains additional export clauses. Consequently, a
memory declaration can contain any number of exports, possibly followed by an import.
6.6.4 Globals
Global definitions can bind a symbolic global identifier.
global𝐼 ::= ‘(’ ‘global’ id ? :id? gt:globaltype𝐼 𝑒:expr𝐼 ‘)’ ⇒ (global gt 𝑒, {globals (id ? )})
Abbreviations
Globals can be defined as imports or exports inline:
import𝐼 ::= . . .
| ‘(’ ‘global’ id? ‘(’ ‘import’ name2 ‘)’ globaltype𝐼 ‘)’ ≡
‘(’ ‘import’ name2 ‘(’ ‘global’ id? globaltype𝐼 ‘)’ ‘)’
export𝐼 ::= ‘(’ ‘global’ id ? :id? ‘(’ ‘export’ name ‘)’ . . . ‘)’ ≡
‘(’ ‘global’ id ′ :id . . . ‘)’
‘(’ ‘export’ name ‘(’ ‘global’ id ‘)’ ‘)’
if id ? = id ′ ∨ id ? = 𝜖 ∧ id ′ ∈
/ 𝐼.globals
Note
The latter abbreviation can be applied repeatedly, if “. . .” contains additional export clauses. Consequently, a
global declaration can contain any number of exports, possibly followed by an import.
6.6.5 Memories
Memory definitions can bind a symbolic memory identifier.
mem𝐼 ::= ‘(’ ‘memory’ id ? :id? mt:memtype𝐼 ‘)’ ⇒ (memory mt, {mems (id ? )})
Abbreviations
A data segment can be given inline with a memory definition, in which case its offset is 0 and the limits of the
memory type are inferred from the length of the data, rounded up to page size:
mem𝐼 ::= ‘(’ ‘memory’ id ? :id? at ? :addrtype? ‘(’ ‘data’ 𝑏* :datastring ‘)’ ‘)’ ≡
‘(’ ‘memory’ id ′ :id at ? :addrtype? 𝑛:u64 𝑛:u64 ‘)’
‘(’ ‘data’ ‘(’ ‘memory’ id ′ :id ‘)’ ‘(’ at ′ :addrtype ‘.const’ ‘0’ ‘)’ datastring ‘)’
if id ? = id ′ ∨ id ? = 𝜖 ∧ id ′ ∈
/ 𝐼.mems
∧ at ? = at ′ ∨ at ? = 𝜖 ∧ at ′ = i32
∧ 𝑛 = ceil(|𝑏* |/64 · Ki)
Note
The latter abbreviation can be applied repeatedly, if “. . .” contains additional export clauses. Consequently, a
memory declaration can contain any number of exports, possibly followed by an import.
6.6.6 Tables
Table definitions can bind a symbolic table identifier.
table𝐼 ::= ‘(’ ‘table’ id ? :id? tt:tabletype𝐼 𝑒:expr𝐼 ‘)’ ⇒ (table tt 𝑒, {tables (id ? )})
Abbreviations
A table’s initialization expression can be omitted, in which case it defaults to [Link]:
table𝐼 ::= . . .
| ‘(’ ‘table’ id? tt:tabletype𝐼 ‘)’ ≡
‘(’ ‘table’ id? tt:tabletype𝐼 ‘(’ ‘[Link]’ ht:heaptype𝐼 ‘)’ ‘)’
if tt = at lim (ref null? ht)
An element segment can be given inline with a table definition, in which case its offset is 0 and the limits of the
table type are inferred from the length of the given segment:
table𝐼 ::= ‘(’ ‘table’ id ? :id? at ? :addrtype? reftype𝐼 ‘(’ ‘elem’ (rt, 𝑒* ):elemlist𝐼 ‘)’ ‘)’ ≡
‘(’ ‘table’ id ′ :id at ? :addrtype? 𝑛:u64 𝑛:u64 reftype𝐼 ‘)’
‘(’ ‘elem’ ‘(’ ‘table’ id ′ :id ‘)’ ‘(’ at ′ :addrtype ‘.const’ ‘0’ ‘)’ elemlist𝐼 ‘)’
if id ? = id ′ ∨ id ? = 𝜖 ∧ id ′ ∈/ 𝐼.tables
? ′ ? ′
∧ at = at ∨ at = 𝜖 ∧ at = i32
∧ 𝑛 = |𝑒* |
import𝐼 ::= . . .
| ‘(’ ‘table’ id? ‘(’ ‘import’ name2 ‘)’ tabletype𝐼 ‘)’ ≡
‘(’ ‘import’ name2 ‘(’ ‘table’ id? tabletype𝐼 ‘)’ ‘)’
export𝐼 ::= ‘(’ ‘table’ id ? :id? ‘(’ ‘export’ name ‘)’ . . . ‘)’ ≡
‘(’ ‘table’ id ′ :id . . . ‘)’
‘(’ ‘export’ name ‘(’ ‘table’ id ‘)’ ‘)’
if id ? = id ′ ∨ id ? = 𝜖 ∧ id ′ ∈
/ 𝐼.tables
Note
The latter abbreviation can be applied repeatedly, if “. . .” contains additional export clauses. Consequently, a
table declaration can contain any number of exports, possibly followed by an import.
6.6.7 Functions
Function definitions can bind a symbolic function identifier, and local identifiers for its parameters and locals.
func𝐼 ::= ‘(’ ‘func’ id ? :id? (𝑥, 𝐼 1 ):typeuse𝐼 ((loc * , 𝐼 2 ):local𝐼 )* 𝑒:expr𝐼 ′ ‘)’ ⇒
*
(func 𝑥 ( loc * ) 𝑒, {funcs ?
⨁︀
⨁︀ * (id )})
if 𝐼 = 𝐼 ⊕ 𝐼 1 ⊕
′
𝐼2
∧ ⊢ 𝐼 ′ : ok
Note
The well-formedness condition on 𝐼 ′ ensures that parameters and locals do not contain duplicate identifiers.
Abbreviations
Multiple anonymous locals may be combined into a single declaration:
local𝐼 ::= . . . | ‘(’ ‘local’ 𝑡:valtype*𝐼 ‘)’ ≡ (‘(’ ‘local’ 𝑡:valtype𝐼 ‘)’)*
import𝐼 ::= . . .
| ‘(’ ‘func’ id? ‘(’ ‘import’ name2 ‘)’ typeuse𝐼 ‘)’ ≡
‘(’ ‘import’ name2 ‘(’ ‘func’ id? typeuse𝐼 ‘)’ ‘)’
export𝐼 ::= ‘(’ ‘func’ id ? :id? ‘(’ ‘export’ name ‘)’ . . . ‘)’ ≡
‘(’ ‘func’ id ′ :id . . . ‘)’
‘(’ ‘export’ name ‘(’ ‘func’ id ‘)’ ‘)’
if id ? = id ′ ∨ id ? = 𝜖 ∧ id ′ ∈
/ 𝐼.funcs
Note
The latter abbreviation can be applied repeatedly, if “. . .” contains additional export clauses. Consequently, a
function declaration can contain any number of exports, possibly followed by an import.
Note
In the current version of WebAssembly, the only valid memory index is 0 or a symbolic memory identifier
resolving to the same value.
Abbreviations
As an abbreviation, a single folded instruction may occur in place of the offset of an active segment:
As another abbreviation, data segments may also be specified inline with memory definitions; see the respective
section.
Abbreviations
As an abbreviation, a single folded instruction may occur in place of the offset of an active element segment or as
an element expression:
Also, the element list may be written as just a sequence of function indices:
elemlist𝐼 ::= . . . | ‘func’ 𝑥* :funcidx*𝐼 ≡ ‘(’ ‘ref’ ‘func’ ‘)’ (‘(’ ‘[Link]’ funcidx𝐼 ‘)’)*
Furthermore, for backwards compatibility with earlier versions of WebAssembly, if the table use is omitted, the
‘func’ keyword can be omitted as well.
As yet another abbreviation, element segments may also be specified inline with table definitions; see the respective
section.
Note
At most one start function may occur in a module, which is ensured by a suitable side condition on the module
grammar.
6.6.11 Imports
The external type in imports can bind a symbolic tag, global, memory, or function identifier.
import𝐼 ::= ‘(’ ‘import’ nm 1 :name nm 2 :name (xt, 𝐼 ′ ):externtype𝐼 ‘)’ ⇒ (import nm 1 nm 2 xt, 𝐼 ′ )
Abbreviations
As an abbreviation, imports may also be specified inline with tag, global, memory, table, or function definitions;
see the respective sections.
6.6.12 Exports
The syntax for exports mirrors their abstract syntax directly.
Abbreviations
As an abbreviation, exports may also be specified inline with tag, global, memory, table, or function definitions;
see the respective sections.
6.6.13 Modules
A module consists of a sequence of declarations that can occur in any order.
decl ::= type | import | tag | global | mem | table | func | data | elem | start | export
All declarations and their respective bound identifiers scope over the entire module, including the text preceding
them. A module itself may optionally bind an identifier that names the module. The name serves a documentary
role only.
Note
Tools may include the module name in the name section of the binary format.
* *
ordered(decl ′ ) = true if imports(decl ′ ) = 𝜖
* *
ordered(decl 1 import decl 2 ) =
imports(decl *1 ) = 𝜖 ∧ tags(decl *1 ) = 𝜖 ∧ globals(decl *1 ) = 𝜖 ∧ mems(decl *1 ) = 𝜖 ∧ tables(decl *1 ) = 𝜖 ∧ funcs(decl *1 ) = 𝜖
Abbreviations
In a source file, the toplevel ‘(’ ‘module’ decldots ‘)’ surrounding the module body may be omitted.
Appendix
7.1 Embedding
A WebAssembly implementation will typically be embedded into a host environment. An embedder implements
the connection between such a host environment and the WebAssembly semantics as defined in the main body of
this specification. An embedder is expected to interact with the semantics in well-defined ways.
This section defines a suitable interface to the WebAssembly semantics in the form of entry points through which
an embedder can access it. The interface is intended to be complete, in the sense that an embedder does not need
to reference other functional parts of the WebAssembly specification directly.
Note
On the other hand, an embedder does not need to provide the host environment with access to all functionality
defined in this interface. For example, an implementation may not support parsing of the text format.
7.1.1 Types
In the description of the embedder interface, syntactic classes from the abstract syntax and the runtime’s abstract
machine are used as names for variables that range over the possible objects from that class. Hence, these syntactic
classes can also be interpreted as types.
For numeric parameters, notation like 𝑖 : u64 is used to specify a symbolic name in addition to the respective value
range.
7.1.2 Booleans
Interface operation that are predicates return Boolean values:
239
WebAssembly Specification, Release 3.0 (2025-11-14)
In addition to the error conditions specified explicitly in this section, such as invalid arguments or exceptions and
traps resulting from execution, implementations may also return errors when specific implementation limitations
are reached.
Note
Errors are abstract and unspecific with this definition. Implementations can refine it to carry suitable classifi-
cations and diagnostic messages.
Note
As long as an embedder treats runtime objects as abstract and only creates and manipulates them through the
interface defined here, all implicit pre-conditions are automatically met.
7.1.5 Store
store_init() : store
1. Return the empty store.
store_init() = {}
7.1.6 Modules
module_decode(byte * ) : module | error
1. If there exists a derivation for the byte sequence byte * as a module according to the binary grammar for
modules, yielding a module 𝑚, then return 𝑚.
2. Else, return error.
*
module_decode(𝑏* ) = 𝑚 (if module =⇒ 𝑚:𝑏* )
module_decode(𝑏* ) = error (otherwise)
module_validate(module) : error ?
1. If module is valid, then return nothing.
2. Else, return error.
*
module_validate(𝑚) = 𝜖 (if ⊢ 𝑚 : externtype * → externtype ′ )
module_validate(𝑚) = error (otherwise)
Note
module_exports(𝑚) = (
𝑚𝑎𝑡ℎ𝑖𝑡𝑛𝑚, externtype ′ )*
*
(if (export nm xt * )* ∈ 𝑚 ∧ ⊢ 𝑚 : externtype * → externtype ′ )
7.1.8 Functions
func_alloc(store, deftype, hostfunc) : (store, funcaddr )
1. Pre-condition: the defined type deftype is valid under the empty context and expands to a function type.
2. Let funcaddr be the result of allocating a host function in store with defined type deftype, host function
code hostfunc and an empty module instance.
3. Return the new store paired with funcaddr .
func_alloc(𝑆, dt, code) = (𝑆 ′ , a) (if allocfunc(𝑆, dt, code, {}) = 𝑆 ′ , a)
Note
This operation assumes that hostfunc satisfies the pre- and post-conditions required for a function instance with
type deftype.
Regular (non-host) function instances can only be created indirectly through module instantiation.
* *
func_invoke(𝑆, 𝑎, 𝑣 * ) = (𝑆 ′ , 𝑣 ′ ) (if invoke(𝑆, 𝑎, 𝑣 * ) ˓→ * 𝑆 ′ ; 𝐹 ; 𝑣 ′ )
func_invoke(𝑆, 𝑎, 𝑣 * ) = (𝑆 ′ , exception 𝑎′ ) (if invoke(𝑆, 𝑎, 𝑣 * ) ˓→ * 𝑆 ′ ; 𝐹 ; ([Link] 𝑎′ ) throw_ref
func_invoke(𝑆, 𝑎, 𝑣 * ) = (𝑆 ′ , error) (if invoke(𝑆, 𝑎, 𝑣 * ) ˓→ * 𝑆 ′ ; 𝐹 ; trap)
Note
7.1.9 Tables
table_alloc(store, tabletype, ref ) : (store, tableaddr )
1. Pre-condition: the tabletype is valid under the empty context.
2. Let tableaddr be the result of allocating a table in store with table type tabletype and initialization value
ref .
3. Return the new store paired with tableaddr .
table_alloc(𝑆, tt, 𝑟) = (𝑆 ′ , a) (if alloctable(𝑆, tt, 𝑟) = 𝑆 ′ , a)
7.1.10 Memories
mem_alloc(store, memtype) : (store, memaddr )
1. Pre-condition: the memtype is valid under the empty context.
2. Let memaddr be the result of allocating a memory in store with memory type memtype.
3. Return the new store paired with memaddr .
mem_alloc(𝑆, mt) = (𝑆 ′ , a) (if allocmem(𝑆, mt) = 𝑆 ′ , a)
7.1.11 Tags
tag_alloc(store, tagtype) : (store, tagaddr )
1. Pre-condition: 𝑡𝑎𝑔𝑡𝑦𝑝𝑒 is valid.
2. Let tagaddr be the result of allocating a tag in store with tag type tagtype.
3. Return the new store paired with tagaddr .
tag_alloc(𝑆, tt) = (𝑆 ′ , a) (if alloctag(𝑆, tt) = 𝑆 ′ , a)
7.1.12 Exceptions
exn_alloc(store, tagaddr , val * ) : (store, exnaddr )
1. Pre-condition: tagaddr is an allocated tag address.
2. Let exnaddr be the result of allocating an exception instance in store with tag address tagaddr and initial-
ization values val * .
3. Return the new store paired with exnaddr .
exn_alloc(𝑆, tagaddr , val * ) = (𝑆 ⊕ {exns exninst}, |𝑆.exns|) (if exninst = {tag tagaddr , fields val * }
7.1.13 Globals
global_alloc(store, globaltype, val ) : (store, globaladdr )
1. Pre-condition: the globaltype is valid under the empty context.
2. Let globaladdr be the result of allocating a global in store with global type globaltype and initialization
value val .
3. Return the new store paired with globaladdr .
global_alloc(𝑆, gt, 𝑣) = (𝑆 ′ , a) (if allocglobal(𝑆, gt, 𝑣) = 𝑆 ′ , a)
7.1.14 Values
ref_type(store, ref ) : reftype
1. Pre-condition: the reference ref is valid under store 𝑆.
2. Return the reference type 𝑡 with which ref is valid.
3. Post-condition: the returned reference type is valid under the empty context.
ref_type(𝑆, 𝑟) = 𝑡 (if 𝑆 ⊢ 𝑟 : 𝑡)
Note
In future versions of WebAssembly, not all references may carry precise type information at run time. In such
cases, this function may return a less precise supertype.
val_default(valtype) : val
1. If default𝑣𝑎𝑙𝑡𝑦𝑝𝑒 is not defined, then return error.
1. Else, return the value default𝑣𝑎𝑙𝑡𝑦𝑝𝑒 .
val_default(𝑡) = 𝑣 (if default𝑡 = 𝑣)
val_default(𝑡) = error (if default𝑡 = 𝜖)
7.1.15 Matching
match_valtype(valtype 1 , valtype 2 ) : bool
1. Pre-condition: the value types valtype 1 and valtype 2 are valid under the empty context.
2. If valtype 1 matches valtype 2 , then return true.
3. Else, return false.
match_reftype(𝑡1 , 𝑡2 ) = true (if ⊢ 𝑡1 ≤ 𝑡2 )
match_reftype(𝑡1 , 𝑡2 ) = false (otherwise)
7.2 Profiles
To enable the use of WebAssembly in as many environments as possible, profiles specify coherent language subsets
that fit constraints imposed by common classes of host environments. A host platform can thereby decide to support
the language only under a restricted profile, or even the intersection of multiple profiles.
7.2.1 Conventions
A profile modification is specified by decorating selected rules in the main body of this specification with a profile
annotation that defines them as conditional on the choice of profile.
For that purpose, every profile defines a profile marker, an alphanumeric short-hand like ABC. A profile annotation
of the form [!ABC XYZ] on a rule indicates that this rule is excluded for either of the profiles whose marker is ABC
or XYZ.
There are two ways of subsetting the language in a profile:
• Syntactic, by omitting a feature, in which case certain constructs are removed from the syntax altogether.
• Semantic, by restricting a feature, in which case certain constructs are still present but some behaviours are
ruled out.
Syntax Annotations
To omit a construct from a profile syntactically, respective productions in the grammar of the abstract syntax are
annotated with an associated profile marker. This is defined to have the following implications:
1. Any production in the binary or textual syntax that produces abstract syntax with a marked construct is
omitted by extension.
2. Any validation or execution rule that handles a marked construct is omitted by extension.
The overall effect is that the respective construct is no longer part of the language under a respective profile.
Note
For example, a “busy” profile marked BUSY could rule out the nop instruction by marking the production for
it in the abstract syntax as follows:
instr ::= . . .
[!BUSY]
| nop
| unreachable
A rule may be annotated by multiple markers, which could be the case if a construct is in the intersection of
multiple features.
Semantics Annotations
To restrict certain behaviours in a profile, individual validation or reduction rules or auxiliary definitions are an-
notated with an associated marker.
This has the consequence that the respective rule is no longer applicable under the given profile.
Note
For example, an “infinite” profile marked INF could define that growing memory never fails:
Properties
All profiles are defined such that the following properties are preserved:
• All profiles represent syntactic and semantic subsets of the full profile, i.e., they do not add syntax or alter
behaviour.
• All profiles are mutually compatible, i.e., no two profiles subset semantic behaviour in inconsistent or am-
biguous ways, and any intersection of profiles preserves the properties described here.
• Profiles do not violate soundness, i.e., all configurations valid under that profile still have well-defined exe-
cution behaviour.
Note
Tools are generally expected to handle and produce code for the full profile by default. In particular, producers
should not generate code that depends on specific profiles. Instead, all code should preserve correctness when
executed under the full profile.
Moreover, profiles should be considered static and fixed for a given platform or ecosystem. Runtime condition-
ing on the “current” profile is not intended and should be avoided.
Note
The number of defined profiles is expected to remain small in the future. Profiles are intended for broad and
permanent use cases only. In particular, profiles are not intended for language versioning.
Note
In future versions of WebAssembly, new non-deterministic behaviour may be added to the language, such that
the deterministic profile will induce additional restrictions.
Note
A conforming implementation is not allowed to leave out individual features. However, designated subsets of
WebAssembly may be specified in the future.
Note
The last item allows embedders that operate in limited environments without support for Unicode48 to limit the
names of imports and exports to common subsets like ASCII49 .
Binary Format
For a module given in binary format, additional limitations may be imposed on the following dimensions:
• the size of a module
• the size of any section
• the size of an individual function’s code
• the size of a structured control instruction
48 [Link]
49 [Link]
Text Format
For a module given in text format, additional limitations may be imposed on the following dimensions:
• the size of the source text
• the size of any syntactic element
• the size of an individual token
• the nesting depth of folded instructions
• the length of symbolic identifiers
• the range of literal characters allowed in the source text
7.3.2 Validation
An implementation may defer validation of individual functions until they are first invoked.
If a function turns out to be invalid, then the invocation, and every consecutive call to the same function, results in
a trap.
Note
This is to allow implementations to use interpretation or just-in-time compilation for functions. The function
must still be fully validated before execution of its body begins.
7.3.3 Execution
Restrictions on the following dimensions may be imposed during execution of a WebAssembly program:
• the number of allocated module instances
• the number of allocated function instances
• the number of allocated table instances
• the number of allocated memory instances
• the number of allocated global instances
• the number of allocated tag instances
• the number of allocated structure instances
• the number of allocated array instances
• the number of allocated exception instances
• the size of a table instance
• the size of a memory instance
• the size of an array instance
• the number of frames on the stack
• the number of labels on the stack
• the number of values on the stack
If the runtime limits of an implementation are exceeded during execution of a computation, then it may terminate
that computation and report an embedder-specific error to the invoking code.
Some of the above limits may already be verified during instantiation, in which case an implementation may report
exceedance in the same manner as for syntactic limits.
Note
Concrete limits are usually not fixed but may be dependent on specifics, interdependent, vary over time, or
depend on other implementation- or embedder-specific situations or events.
7.4.1 Contexts
In order to check rolled up recursive types, the context is locally extended with an additional component that records
the sub type corresponding to each recursive type index within the current recursive type:
7.4.2 Types
Well-formedness for extended type forms is defined as follows.
𝐶 ⊢ bot : ok
50 The formalization and theorems are derived from the following article: Andreas Haas, Andreas Rossberg, Derek Schuff, Ben Titzer, Dan
Gohman, Luke Wagner, Alon Zakai, JF Bastien, Michael Holman. Bringing the Web up to Speed with WebAssembly51 . Proceedings of the
38th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017). ACM 2017.
51 [Link]
𝐶 ⊢ bot : ok
Note
*
|ht * | ≤ 1 (ht ≺ 𝑥, 𝑖)* (unroll𝐶 (ht) = sub ht ′ comptype ′ )*
𝐶 ⊢ comptype : ok (𝐶 ⊢ comptype ≤ comptype ′ )*
𝐶 ⊢ sub final? ht * comptype : ok(𝑥, 𝑖)
where:
(deftype ≺ 𝑥, 𝑖) = true
(𝑦 ≺ 𝑥, 𝑖) = 𝑦<𝑥
(rec 𝑗 ≺ 𝑥, 𝑖) = 𝑗<𝑖
Note
This rule is a generalisation of the ones previously given, which only allowed type indices as supertypes.
7.4.3 Subtyping
In a rolled-up recursive type, a recursive type indices rec 𝑖 matches another heap type ht if:
*
• Let sub final? ht ′ comptype be the sub type 𝐶.recs[𝑖].
*
• The heap type ht is contained in ht ′ .
𝐶.recs[𝑖] = sub final? (ht *1 ht ht *2 ) comptype
𝐶 ⊢ rec 𝑖 ≤ ht
Note
This rule is only invoked when checking validity of rolled-up recursive types.
7.4.4 Results
Results can be classified by result types as follows.
Results val *
• For each value val 𝑖 in val * :
– The value val 𝑖 is valid with some value type 𝑡𝑖 .
• Let 𝑡* be the concatenation of all 𝑡𝑖 .
• Then the result is valid with result type [𝑡* ].
(𝑆 ⊢ val : 𝑡)*
𝑆 ⊢ val * : [𝑡* ]
Results trap
• The result is valid with result type [𝑡* ], for any valid closed result types.
⊢ [𝑡* ] : ok
𝑆 ⊢ trap : [𝑡* ]
Store 𝑆
• Each tag instance taginst 𝑖 in 𝑆.tags must be valid with some tag type tagtype 𝑖 .
• Each global instance globalinst 𝑖 in 𝑆.globals must be valid with some global type globaltype 𝑖 .
• Each memory instance meminst 𝑖 in 𝑆.mems must be valid with some memory type memtype 𝑖 .
• Each table instance tableinst 𝑖 in 𝑆.tables must be valid with some table type tabletype 𝑖 .
• Each function instance funcinst 𝑖 in 𝑆.funcs must be valid with some defined type deftype 𝑖 .
• Each data instance datainst 𝑖 in 𝑆.datas must be valid.
• Each element instance eleminst 𝑖 in 𝑆.elems must be valid with some reference type reftype 𝑖 .
• Each structure instance structinst 𝑖 in 𝑆.structs must be valid.
• Each array instance arrayinst 𝑖 in 𝑆.arrays must be valid.
• Each exception instance exninst 𝑖 in 𝑆.exns must be valid.
• No reference to a bound structure address must be reachable from itself through a path consisting only of
indirections through immutable structure, or array fields or fields of exception instances.
• No reference to a bound array address must be reachable from itself through a path consisting only of indi-
rections through immutable structure or array fields or fields of exception instances.
• No reference to a bound exception address must be reachable from itself through a path consisting only of
indirections through immutable structure or array fields or fields of exception instances.
• Then the store is valid.
datas datainst * , elems eleminst * , structs structinst * , arrays arrayinst * , exns exninst * }
(𝑆.structs[𝑎s ] = structinst)* (([Link] 𝑎s ) ̸≫+𝑆 ([Link] 𝑎s ))
*
* + *
(𝑆.arrays[𝑎a ] = arrayinst) (([Link] 𝑎a ) ̸≫𝑆 ([Link] 𝑎a ))
(𝑆.exns[𝑎e ] = exninst)* (([Link] 𝑎e ) ̸≫+ 𝑆 ([Link] 𝑎e ))
*
⊢ 𝑆 : ok
where val 1 ≫+
𝑆 val 2 denotes the transitive closure of the following immutable reachability relation on values:
Note
The constraint on reachability through immutable fields prevents the presence of cyclic data structures that can
not be constructed in the language. Cycles can only be formed using mutation.
Note
This rule states that, if appropriate pre-conditions about store and arguments are satisfied, then executing the
host function must satisfy appropriate post-conditions about store and results. The post-conditions match the
ones in the execution rule for invoking host functions.
Any store under which the function is invoked is assumed to be an extension of the current store. That way, the
function itself is able to make sufficient assumptions about future stores.
𝑆 ⊢ {bytes 𝑏* } : ok
𝑆 ⊢ [Link] 𝑖 : pt
• For each element address elemaddr 𝑖 in [Link], the element instance 𝑆.elems[elemaddr 𝑖 ] must
be valid with some reference type reftype 𝑖 .
• Each export instance exportinst 𝑖 in [Link] must be valid.
• For each export instance exportinst 𝑖 in [Link], the name exportinst 𝑖 .name must be different
from any other name occurring in [Link].
• Let deftype * be the concatenation of all deftype 𝑖 in order.
• Let tagtype * be the concatenation of all tagtype 𝑖 in order.
• Let globaltype * be the concatenation of all globaltype 𝑖 in order.
• Let memtype * be the concatenation of all memtype 𝑖 in order.
• Let tabletype * be the concatenation of all tabletype 𝑖 in order.
• Let deftype *F be the concatenation of all deftype F𝑖 in order.
• Let reftype * be the concatenation of all reftype 𝑖 in order.
• Let ok * be the concatenation of all ok 𝑖 in order.
• Let 𝑚 be the length of [Link].
• Let 𝑥* be the sequence of function indices from 0 to 𝑚 − 1.
• Then the module instance is valid with context {types deftype * , tags tagtype * , globals globaltype * ,
mems memtype * , tables tabletype * , funcs deftype *F , datas ok * , elems reftype * , refs 𝑥* }.
Finally, frames are classified with frame contexts, which extend the module contexts of a frame’s associated module
instance with the locals that the frame contains.
Configurations 𝑆; 𝑇
• The store 𝑆 must be valid.
• Under no allowed return type, the thread 𝑇 must be valid with some result type [𝑡* ].
• Then the configuration is valid with the result type [𝑡* ].
⊢ 𝑆 : ok 𝑆; 𝜖 ⊢ 𝑇 : [𝑡* ]
⊢ 𝑆; 𝑇 : [𝑡* ]
Threads 𝐹 ; instr *
• Let resulttype ? be the current allowed return type.
• The frame 𝐹 must be valid with a context 𝐶.
• Let 𝐶 ′ be the same context as 𝐶, but with return set to resulttype ? .
• Under context 𝐶 ′ , the instruction sequence instr * must be valid with some type [] → [𝑡* ].
• Then the thread is valid with the result type [𝑡* ].
𝑆⊢𝐹 :𝐶 𝑆; 𝐶, return resulttype ? ⊢ instr * : [] → [𝑡* ]
𝑆; resulttype ? ⊢ 𝐹 ; instr * : [𝑡* ]
trap
• The instruction is valid with any valid instruction type of the form [𝑡*1 ] → [𝑡*2 ].
𝐶 ⊢ [𝑡*1 ] → [𝑡*2 ] : ok
𝑆; 𝐶 ⊢ trap : [𝑡*1 ] → [𝑡*2 ]
val
• The value val must be valid with value type 𝑡.
• Then it is valid as an instruction with type [] → [𝑡].
𝑆 ⊢ val : 𝑡
𝑆; 𝐶 ⊢ val : [] → [𝑡]
frame𝑛 {𝐹 } instr *
• Under the valid return type [𝑡𝑛 ], the thread 𝐹 ; instr * must be valid with result type [𝑡𝑛 ].
• Then the compound instruction is valid with type [] → [𝑡𝑛 ].
𝐶 ⊢ [𝑡𝑛 ] : ok 𝑆; [𝑡𝑛 ] ⊢ 𝐹 ; instr * : [𝑡𝑛 ]
𝑆; 𝐶 ⊢ frame𝑛 {𝐹 } instr * : [] → [𝑡𝑛 ]
Note
Extension does not imply that the new store is valid, which is defined separately above.
Store 𝑆
• The length of 𝑆.tags must not shrink.
• The length of 𝑆.globals must not shrink.
• The length of 𝑆.mems must not shrink.
• The length of 𝑆.tables must not shrink.
• The length of 𝑆.funcs must not shrink.
• The length of 𝑆.datas must not shrink.
• The length of 𝑆.elems must not shrink.
• The length of 𝑆.structs must not shrink.
• The length of 𝑆.arrays must not shrink.
• The length of 𝑆.exns must not shrink.
• For each tag instance taginst 𝑖 in the original 𝑆.tags, the new tag instance must be an extension of the old.
• For each global instance globalinst 𝑖 in the original 𝑆.globals, the new global instance must be an extension
of the old.
• For each memory instance meminst 𝑖 in the original 𝑆.mems, the new memory instance must be an extension
of the old.
• For each table instance tableinst 𝑖 in the original 𝑆.tables, the new table instance must be an extension of the
old.
• For each function instance funcinst 𝑖 in the original 𝑆.funcs, the new function instance must be an extension
of the old.
• For each data instance datainst 𝑖 in the original 𝑆.datas, the new data instance must be an extension of the
old.
• For each element instance eleminst 𝑖 in the original 𝑆.elems, the new element instance must be an extension
of the old.
• For each structure instance structinst 𝑖 in the original 𝑆.structs, the new structure instance must be an ex-
tension of the old.
• For each array instance arrayinst 𝑖 in the original 𝑆.arrays, the new array instance must be an extension of
the old.
• For each exception instance exninst 𝑖 in the original 𝑆.exns, the new exception instance must be an extension
of the old.
*
𝑆1 .tags = taginst *1 𝑆2 .tags = taginst ′1 taginst *2 (⊢ taginst 1 ⪯ taginst ′1 )*
*
𝑆1 .globals = globalinst *1 𝑆2 .globals = globalinst ′1 globalinst *2 (⊢ globalinst 1 ⪯ globalinst ′1 )*
*
𝑆1 .mems = meminst *1 𝑆2 .mems = meminst ′1 meminst *2 (⊢ meminst 1 ⪯ meminst ′1 )*
*
𝑆1 .tables = tableinst *1 𝑆2 .tables = tableinst ′1 tableinst *2 (⊢ tableinst 1 ⪯ tableinst ′1 )*
*
𝑆1 .funcs = funcinst *1 𝑆2 .funcs = funcinst ′1 funcinst *2 (⊢ funcinst 1 ⪯ funcinst ′1 )*
*
𝑆1 .datas = datainst *1 𝑆2 .datas = datainst ′1 datainst *2 (⊢ datainst 1 ⪯ datainst ′1 )*
*
𝑆1 .elems = eleminst *1 𝑆2 .elems = eleminst ′1 eleminst *2 (⊢ eleminst 1 ⪯ eleminst ′1 )*
*
𝑆1 .structs = structinst *1 𝑆2 .structs = structinst ′1 structinst *2 (⊢ structinst 1 ⪯ structinst ′1 )*
*
𝑆1 .arrays = arrayinst *1 𝑆2 .arrays = arrayinst ′1 arrayinst *2 (⊢ arrayinst 1 ⪯ arrayinst ′1 )*
*
𝑆1 .exns = exninst *1 𝑆2 .exns = exninst ′1 exninst *2 (⊢ exninst 1 ⪯ exninst ′1 )*
⊢ 𝑆1 ⪯ 𝑆2
⊢ taginst ⪯ taginst
⊢ funcinst ⪯ funcinst
⊢ {bytes 𝑏* } ⪯ {bytes 𝑏* }
⊢ {bytes 𝑏* } ⪯ {bytes 𝜖}
⊢ exninst ⪯ exninst
7.4.9 Theorems
Given the definition of valid configurations, the standard soundness theorems hold.5254
Theorem (Preservation). If a configuration 𝑆; 𝑇 is valid with result type [𝑡* ] (i.e., ⊢ 𝑆; 𝑇 : [𝑡* ]), and steps to
𝑆 ′ ; 𝑇 ′ (i.e., 𝑆; 𝑇 ˓→ 𝑆 ′ ; 𝑇 ′ ), then 𝑆 ′ ; 𝑇 ′ is a valid configuration with the same result type (i.e., ⊢ 𝑆 ′ ; 𝑇 ′ : [𝑡* ]).
Furthermore, 𝑆 ′ is an extension of 𝑆 (i.e., ⊢ 𝑆 ⪯ 𝑆 ′ ).
A terminal thread is one whose sequence of instructions is a result. A terminal configuration is a configuration
whose thread is terminal.
Theorem (Progress). If a configuration 𝑆; 𝑇 is valid (i.e., ⊢ 𝑆; 𝑇 : [𝑡* ] for some result type [𝑡* ]), then either it is
terminal, or it can step to some configuration 𝑆 ′ ; 𝑇 ′ (i.e., 𝑆; 𝑇 ˓→ 𝑆 ′ ; 𝑇 ′ ).
From Preservation and Progress the soundness of the WebAssembly type system follows directly.
Corollary (Soundness). If a configuration 𝑆; 𝑇 is valid (i.e., ⊢ 𝑆; 𝑇 : [𝑡* ] for some result type [𝑡* ]), then it either
diverges or takes a finite number of steps to reach a terminal configuration 𝑆 ′ ; 𝑇 ′ (i.e., 𝑆; 𝑇 ˓→ * 𝑆 ′ ; 𝑇 ′ ) that is
valid with the same result type (i.e., ⊢ 𝑆 ′ ; 𝑇 ′ : [𝑡* ]) and where 𝑆 ′ is an extension of 𝑆 (i.e., ⊢ 𝑆 ⪯ 𝑆 ′ ).
In other words, every thread in a valid configuration either runs forever, traps, throws an exception, or terminates
with a result that has the expected type. Consequently, given a valid store, no computation defined by instantiation or
invocation of a valid module can “crash” or otherwise (mis)behave in ways not covered by the execution semantics
given in this specification.
Note
For example, in isolation, the instruction ref.as_non_null has the type [(ref null ht)] → [(ref ht)] for any choice
of valid heap type ht. Moreover, if the input type [(ref null ht)] is already determined, i.e., a specific ht is given,
then the output type [(ref ht)] is fully determined as well.
The implication of the latter property is that a validator for complete instruction sequences (as they occur in
valid modules) can be implemented with a simple left-to-right algorithm that does not require the introduction
of type variables.
A typing algorithm capable of handling partial instruction sequences (as might be considered for program
analysis or program manipulation) needs to introduce type variables and perform substitutions, but it does not
need to perform backtracking or record any non-syntactic constraints on these type variables.
52 A machine-verified version of the formalization and soundness proof of the PLDI 2017 paper is described in the following article: Conrad
Watt. Mechanising and Verifying the WebAssembly Specification53 . Proceedings of the 7th ACM SIGPLAN Conference on Certified Programs
and Proofs (CPP 2018). ACM 2018.
53 [Link]
54 Machine-verified formalizations and soundness proofs of the semantics from the official specification are described in the following article:
Conrad Watt, Xiaojia Rao, Jean Pichon-Pharabod, Martin Bodin, Philippa Gardner. Two Mechanisations of WebAssembly 1.055 . Proceedings
of the 24th International Symposium on Formal Methods (FM 2021). Springer 2021.
55 [Link]
Technically, the syntax of heap, value, and result types can be enriched with type variables as follows:
where each 𝛼xyz ranges over a set of type variables for syntactic class xyz , respectively. The special class
numvectype is defined as numtype | vectype | bot, and is only needed to handle unannotated select instructions.
A type is closed when it does not contain any type variables, and open otherwise. A type substitution 𝜎 is a finite
mapping from type variables to closed types of the respective syntactic class. When applied to an open type, it
replaces the type variables 𝛼 from its domain with the respective 𝜎(𝛼).
Theorem (Principal Types). If an instruction sequence instr * is valid with some closed instruction type instrtype
(i.e., 𝐶 ⊢ instr * : instrtype), then it is also valid with a possibly open instruction type instrtype min (i.e., 𝐶 ⊢
instr * : instrtype min ), such that for every closed type instrtype ′ with which instr * is valid (i.e., for all 𝐶 ⊢
instr * : instrtype ′ ), there exists a substitution 𝜎, such that 𝜎(instrtype min ) is a subtype of instrtype ′ (i.e.,
𝐶 ⊢ 𝜎(instrtype min ) ≤ instrtype ′ ). Furthermore, instrtype min is unique up to the choice of type variables.
Theorem (Closed Principal Forward Types). If closed input type [𝑡*1 ] is given and the instruction sequence instr *
is valid with instruction type [𝑡*1 ] →𝑥* [𝑡*2 ] (i.e., 𝐶 ⊢ instr * : [𝑡*1 ] →𝑥* [𝑡*2 ]), then it is also valid with instruction
type [𝑡*1 ] →𝑥* [𝛼valtype * 𝑡* ] (i.e., 𝐶 ⊢ instr * : [𝑡*1 ] →𝑥* [𝛼valtype * 𝑡* ]), where all 𝑡* are closed, such that for
* *
every closed result type [𝑡′2 ] with which instr * is valid (i.e., for all 𝐶 ⊢ instr * : [𝑡*1 ] →𝑥* [𝑡′2 ]), there exists a
′*
substitution 𝜎, such that [𝑡2 ] = [𝜎(𝛼valtype * ) 𝑡 ].*
Note
Theorem (Conditional Least Upper Bounds for Value Types). Any two value types 𝑡1 and 𝑡2 that are valid (i.e.,
𝐶 ⊢ 𝑡1 : ok and 𝐶 ⊢ 𝑡2 : ok) either have no common supertype, or there exists a valid value type 𝑡 that is a
supertype of both 𝑡1 and 𝑡2 (i.e., 𝐶 ⊢ 𝑡 : ok and 𝐶 ⊢ 𝑡1 ≤ 𝑡 and 𝐶 ⊢ 𝑡2 ≤ 𝑡), such that every valid value type 𝑡′
that also is a supertype of both 𝑡1 and 𝑡2 (i.e., for all 𝐶 ⊢ 𝑡′ : ok and 𝐶 ⊢ 𝑡1 ≤ 𝑡′ and 𝐶 ⊢ 𝑡2 ≤ 𝑡′ ), is a supertype
of 𝑡 (i.e., 𝐶 ⊢ 𝑡 ≤ 𝑡′ ).
Note
If a top type was added to the type system, a least upper bound would exist for any two types.
Corollary (Type Lattice). Assuming the addition of a provisional top type, value types form a lattice with respect
to their subtype relation.
Finally, value types can be partitioned into multiple disjoint hierarchies that are not related by subtyping, except
through bot.
Theorem (Disjoint Subtype Hierarchies). The greatest lower bound of two value types is bot or ref bot if and
only if they do not have a least upper bound.
In other words, types that do not have common supertypes, do not have common subtypes either (other than bot
or ref bot), and vice versa.
Note
Types from disjoint hierarchies can safely be represented in mutually incompatible ways in an implementation,
because their values can never flow to the same place.
7.5.3 Compositionality
Valid instruction sequences can be freely composed, as long as their types match up.
Theorem (Composition). If two instruction sequences instr *1 and instr *2 are valid with types [𝑡*1 ] →𝑥*1 [𝑡* ] and
[𝑡* ] →𝑥*2 [𝑡*2 ], respectively (i.e., 𝐶 ⊢ instr *1 : [𝑡*1 ] →𝑥*1 [𝑡* ] and 𝐶 ⊢ instr *1 : [𝑡* ] →𝑥*2 [𝑡*2 ]), then the concatenated
instruction sequence (instr *1 instr *2 ) is valid with type [𝑡*1 ] →𝑥*1 𝑥*2 [𝑡*2 ] (i.e., 𝐶 ⊢ instr *1 instr *2 : [𝑡*1 ] →𝑥*1 𝑥*2 [𝑡*2 ]).
Note
More generally, instead of a shared type [𝑡* ], it suffices if the output type of instr *1 is a subtype of the input
type of instr *1 , since the subtype can always be weakened to its supertype by subsumption.
Inversely, valid instruction sequences can also freely be decomposed, that is, splitting them anywhere produces two
instruction sequences that are both valid.
Theorem (Decomposition). If an instruction sequence instr * that is valid with type [𝑡*1 ] →𝑥* [𝑡*2 ] (i.e., 𝐶 ⊢
instr * : [𝑡*1 ] →𝑥* [𝑡*2 ]) is split into two instruction sequences instr *1 and instr *2 at any point (i.e., instr * =
instr *1 instr *2 ), then these are separately valid with some types [𝑡*1 ] →𝑥*1 [𝑡* ] and [𝑡* ] →𝑥*2 [𝑡*2 ], respectively (i.e.,
𝐶 ⊢ instr *1 : [𝑡*1 ] →𝑥*1 [𝑡* ] and 𝐶 ⊢ instr *1 : [𝑡* ] →𝑥*2 [𝑡*2 ]), where 𝑥* = 𝑥*1 𝑥*2 .
Note
This property holds because validation is required even for unreachable code. Without that, instr *2 might not
be valid in isolation.
These representations assume that all types have been closed by substituting all type indices (in concrete heap types
and in sub types) with their respective defined types. This includes recursive references to enclosing defined types,
such that type representations form graphs and may be cyclic for recursive types.
We assume that all types have been canonicalized, such that equality on two type representations holds if and only
if their closures are syntactically equivalent, making it a constant-time check.
Note
For the purpose of type canonicalization, recursive references from a heap type to an enclosing recursive type
(i.e., forward edges in the graph that form a cycle) need to be distinguished from references to previously
defined types. However, this distinction does not otherwise affect validation, so is ignored here. In the graph
representation, all recursive types are effectively infinitely unrolled.
We further assume that validation and subtyping checks are defined on value types, as well as a few auxiliary
functions on composite types:
Finally, the following function computes the least precise supertype of a given heap type (its corresponding top
type):
Context
Validation requires a context for checking uses of indices. For the purpose of presenting the algorithm, it is main-
tained in a set of global variables:
This assumes suitable representations for the various types besides val_type, which are omitted here.
For locals, there is an additional array recording the initialization status of each local.
Stacks
The algorithm uses three separate stacks: the value stack, the control stack, and the initialization stack. The value
stack tracks the types of operand values on the stack. The control stack tracks surrounding structured control
instructions and their associated blocks. The initialization stack records all locals that have been initialized since
the beginning of the function.
For each entered block, the control stack records a control frame with the originating opcode, the types on the top
of the operand stack at the start and end of the block (used to check its result as well as branches), the height of the
operand stack at the start of the block (used to check that operands do not underflow the current block), the height
of the initialization stack at the start of the block (used to reset initialization status at the end of the block), and a
flag recording whether the remainder of the block is unreachable (used to handle stack-polymorphic typing after
branches).
For the purpose of presenting the algorithm, these stacks are simply maintained as global variables:
However, these variables are not manipulated directly by the main checking function, but through a set of auxiliary
functions:
Pushing an operand value simply pushes the respective type to the value stack.
Popping an operand value checks that the value stack does not underflow the current block and then removes
one type. But first, a special case is handled where the block contains no known values, but has been marked as
unreachable. That can occur after an unconditional branch, when the stack is typed polymorphically. In that case,
the Bot type is returned, because that is a principal choice trivially satisfying all use constraints.
A second function for popping an operand value takes an expected type, which the actual operand type is checked
against. The types may differ by subtyping, including the case where the actual type is Bot, and thereby matches
unconditionally. The function returns the actual type popped from the stack.
Finally, there are accumulative functions for pushing or popping multiple operand types.
Note
The notation stack[i] is meant to index the stack from the top, so that, e.g., ctrls[0] accesses the element
pushed last.
The initialization stack and the initialization status of locals is manipulated through the following functions:
Getting a local verifies that it is known to be initialized. When a local is set that was not set already, then its
initialization status is updated and the change is recorded in the initialization stack. Thus, the initialization status
of all locals can be reset to a previous state by denoting a specific height in the initialization stack.
The size of the initialization stack is bounded by the number of (non-defaultable) locals in a function, so can be
preallocated by an algorithm.
The control stack is likewise manipulated through auxiliary functions:
func unreachable() =
[Link](ctrls[0].val_height)
ctrls[0].unreachable := true
Pushing a control frame takes the types of the label and result values. It allocates a new frame record recording
them along with the current height of the operand stack and marks the block as reachable.
Popping a frame first checks that the control stack is not empty. It then verifies that the operand stack contains the
right types of values expected at the end of the exited block and pops them off the operand stack. Afterwards, it
checks that the stack has shrunk back to its initial height. Finally, it undoes all changes to the initialization status
of locals that happened inside the block.
The type of the label associated with a control frame is either that of the stack at the start or the end of the frame,
determined by the opcode that it originates from.
Finally, the current frame can be marked as unreachable. In that case, all existing operand types are purged from the
value stack, in order to allow for the stack-polymorphism logic in pop_val to take effect. Because every function
has an implicit outermost label that corresponds to an implicit block frame, it is an invariant of the validation
algorithm that there always is at least one frame on the control stack when validating an instruction, and hence,
ctrls[0] is always defined.
Note
Even with the unreachable flag set, consecutive operands are still pushed to and popped from the operand stack.
That is necessary to detect invalid examples like (unreachable ([Link]) [Link]). However, a polymorphic
stack cannot underflow, but instead generates Bot types as needed.
func validate(opcode) =
switch (opcode)
case ([Link])
pop_val(I32)
pop_val(I32)
push_val(I32)
case (drop)
pop_val()
case (select)
pop_val(I32)
let t1 = pop_val()
let t2 = pop_val()
error_if(not (is_num(t1) && is_num(t2) || is_vec(t1) && is_vec(t2)))
error_if(t1 =/= t2 && t1 =/= Bot && t2 =/= Bot)
push_val(if (t1 = Bot) t2 else t1)
case (select t)
pop_val(I32)
pop_val(t)
pop_val(t)
push_val(t)
case (ref.is_null)
pop_ref()
push_val(I32)
case (ref.as_non_null)
let rt = pop_ref()
push_val(Ref([Link], false))
case ([Link] x)
get_local(x)
push_val(locals[x])
case ([Link] x)
pop_val(locals[x])
set_local(x)
case (unreachable)
unreachable()
case (end)
let frame = pop_ctrl()
push_vals(frame.end_types)
case (else)
let frame = pop_ctrl()
error_if([Link] =/= if)
push_ctrl(else, frame.start_types, frame.end_types)
case (br n)
error_if([Link]() < n)
pop_vals(label_types(ctrls[n]))
unreachable()
case (br_if n)
error_if([Link]() < n)
pop_val(I32)
pop_vals(label_types(ctrls[n]))
push_vals(label_types(ctrls[n]))
case (br_table n* m)
pop_val(I32)
error_if([Link]() < m)
let arity = label_types(ctrls[m]).size()
foreach (n in n*)
error_if([Link]() < n)
error_if(label_types(ctrls[n]).size() =/= arity)
push_vals(pop_vals(label_types(ctrls[n])))
pop_vals(label_types(ctrls[m]))
(continues on next page)
case (br_on_null n)
error_if([Link]() < n)
let rt = pop_ref()
pop_vals(label_types(ctrls[n]))
push_vals(label_types(ctrls[n]))
push_val(Ref([Link], false))
case (return)
pop_vals(return_types)
unreachable()
case (call_ref x)
let t = expand_def(types[x])
error_if(not is_func(t))
pop_vals([Link])
pop_val(Ref(Def(types[x])))
push_vals([Link])
case (return_call_ref x)
let t = expand_def(types[x])
error_if(not is_func(t))
pop_vals([Link])
pop_val(Ref(Def(types[x])))
error_if([Link]() =/= return_types.len())
push_vals([Link])
pop_vals(return_types)
unreachable()
case ([Link] x)
let t = expand_def(types[x])
error_if(not is_struct(t))
for (ti in reverse([Link]))
pop_val(unpack_field(ti))
push_val(Ref(Def(types[x])))
case ([Link] x n)
let t = expand_def(types[x])
error_if(not is_struct(t) || n >= [Link]())
pop_val(Ref(Def(types[x])))
pop_val(unpack_field([Link][n]))
case (throw x)
pop_vals(tags[x].[Link])
unreachable()
(continues on next page)
Note
It is an invariant under the current WebAssembly instruction set that an operand of Bot type is never duplicated
on the stack. This would change if the language were extended with stack instructions like dup. Under such an
extension, the above algorithm would need to be refined by replacing the Bot type with proper type variables
to ensure that all uses are consistent.
Note
All names are represented in Unicode56 encoded in UTF-8. Names need not be unique.
Subsections
The data of a name section consists of a sequence of subsections. Each subsection consists of a
• a one-byte subsection id,
• the u32 size of the contents, in bytes,
• the actual contents, whose structure is dependent on the subsection id.
56 [Link]
Id Subsection
0 module name
1 function names
2 local names
4 type names
10 field names
11 tag names
Each subsection may occur at most once, and in order of increasing id.
Name Maps
A name map assigns names to indices in a given index space. It consists of a list of index/name pairs in order of
increasing index value. Each index must be unique, but the assigned names need not be.
namemap ::= list(nameassoc)
nameassoc ::= idx name
An indirect name map assigns names to a two-dimensional index space, where secondary indices are grouped by
primary indices. It consists of a list of primary index/name map pairs in order of increasing index value, where
each name map in turn maps secondary indices to names. Each primary index must be unique, and likewise each
secondary index per individual name map.
indirectnamemap ::= list(indirectnameassoc)
indirectnameassoc ::= idx namemap
Module Names
The module name subsection has the id 0. It simply consists of a single name that is assigned to the module itself.
modulenamesubsec ::= namesubsection0 (name)
Function Names
The function name subsection has the id 1. It consists of a name map assigning function names to function indices.
funcnamesubsec ::= namesubsection1 (namemap)
Local Names
The local name subsection has the id 2. It consists of an indirect name map assigning local names to local indices
grouped by function indices.
localnamesubsec ::= namesubsection2 (indirectnamemap)
Type Names
The type name subsection has the id 4. It consists of a name map assigning type names to type indices.
Field Names
The field name subsection has the id 10. It consists of an indirect name map assigning field names to field indices
grouped by the type indices of their respective structure types.
Tag Names
The tag name subsection has the id 11. It consists of a name map assigning tag names to tag indices.
Note
All name annotations can be arbitrary UTF-8 strings. Names need not be unique.
Module Names
A module name annotation must be placed on a module definition, directly after the ‘module’ keyword, or if
present, after the following module identifier.
Function Names
A function name annotation must be placed on a function definition or function import, directly after the ‘func’
keyword, or if present, after the following function identifier or.
Parameter Names
A parameter name annotation must be placed on a parameter declaration, directly after the ‘param’ keyword, or
if present, after the following parameter identifier. It may only be placed on a declaration that declares exactly one
parameter.
Local Names
A local name annotation must be placed on a local declaration, directly after the ‘local’ keyword, or if present,
after the following local identifier. It may only be placed on a declaration that declares exactly one local.
Type Names
A type name annotation must be placed on a type declaration, directly after the ‘type’ keyword, or if present, after
the following type identifier.
Field Names
A field name annotation must be placed on the field of a structure type, directly after the ‘field’ keyword, or if
present, after the following field identifier. It may only be placed on a declaration that declares exactly one field.
Tag Names
A tag name annotation must be placed on a tag declaration or tag import, directly after the ‘tag’ keyword, or if
present, after the following tag identifier.
keyword, or if present, after the following module identifier. They must not be nested into other constructs.
The first string in a custom annotation denotes the name of the custom section it represents. The remaining strings
collectively represent the section’s payload data, written as a data string, which can be split up into a possibly empty
sequence of individual string literals (similar to data segments).
An arbitrary number of custom annotations (even of the same name) may occur in a module, each defining a separate
custom section when converting to binary format. Placement of the sections in the binary can be customized via
explicit placement directives, that position them either directly before or directly after a known section. That section
must exist and be non-empty in the binary encoding of the annotated module. The placements (before first)
and (after last) denote virtual sections before the first and after the last known section, respectively. When the
placement directive is omitted, it defaults to (after last).
If multiple placement directives appear for the same position, then the sections are all placed there, in order of
their appearance in the text. For this purpose, the position after a section is considered different from the position
before the consecutive section, and the former occurs before the latter.
Note
Future versions of WebAssembly may introduce additional sections between others or at the beginning or end
of a module. Using first and last guarantees that placement will still go before or after any future section,
respectively.
If a custom section with a specific section id is given as well as annotations representing the same custom section
(e.g., @name annotations as well as a @custom annotation for a name section), then two sections are assumed to
be created. Their relative placement will depend on the placement directive given for the @custom annotation as
well as the implicit placement requirements of the custom section, which are applied to the other annotation.
Note
Note
This allows previously malformed or invalid modules to become legal, e.g., by adding new features
or by relaxing typing rules.
It also allows reclassifying previously malformed modules as well-formed but invalid, or vice versa.
And it allows refining the typing of imports and exports, such that previously unlinkable modules
become linkable.
Historically, minor breaking changes to the text format have been allowed that turned previously
possible valid modules invalid, as long as they were unlikely to occur in practice.
2. All non-trapping executions of a valid program retain their behaviour with an equivalent set of possible
results (or a non-empty subset).
Note
intention is to only exercise this where the possibility of such an extension has been previously
noted.
And it allows reducing the set of observable behaviours of a program execution, e.g., by reducing
non-determinism.
In a program linking prior modules with modules using new features, a prior module may encounter
new behaviours, e.g., new forms of control flow or side effects when calling into a latter module.
In addition, future versions of WebAssembly will not allocate the opcode 0xFF to represent an instruction or
instruction prefix.
Multiple Values
Generalized the result type of blocks and functions to allow for multiple values; in addition, introduced the ability
to have block parameters.59
• Function types allow more than one result
• Block types can be arbitrary function types
Reference Types
Added funcref and externref as new value types and respective instructions.60
• New reference value types:
– funcref
– externref
• New reference instructions:
– [Link]
– [Link]
– ref.is_null
• Extended parametric instruction:
– select with optional type immediate
• New declarative form of element segment
57 [Link]
58 [Link]
59 [Link]
60 [Link]
Table Instructions
Added instructions to directly access and modify [Link] 282, 60
• Table types allow any reference type as element type
• New table instructions:
– [Link]
– [Link]
– [Link]
– [Link]
Multiple Tables
Added the ability to use multiple tables per [Link] 282, 60
• Modules may
– define multiple tables
– import multiple tables
– export multiple tables
• Table instructions take a table index immediate:
– [Link]
– [Link]
– [Link]
– [Link]
– call_indirect
• Element segments take a table index
Vector Instructions
Added vector type and instructions that manipulate multiple numeric values in parallel (also known as SIMD, single
instruction multiple data)62
• New value type:
– v128
• New memory instructions:
– [Link]
– [Link]𝑁x𝑀_sx
– [Link]𝑁 _zero
– [Link]𝑁 _splat
– [Link]𝑁 _lane
– [Link]
– [Link]𝑁 _lane
• New constant vector instruction:
– [Link]
• New unary vector instructions:
– [Link]
– i𝑁x𝑀.abs
– i𝑁x𝑀.neg
– [Link]
– f𝑁x𝑀.abs
– f𝑁x𝑀.neg
– f𝑁x𝑀.sqrt
– f𝑁x𝑀.ceil
– f𝑁x𝑀.floor
– f𝑁x𝑀.trunc
– f𝑁x𝑀.nearest
• New binary vector instructions:
– [Link]
– [Link]
– [Link]
– [Link]
– i𝑁x𝑀.add
– i𝑁x𝑀.sub
– i𝑁x𝑀.mul
– i𝑁x𝑀.add_sat_sx
– i𝑁x𝑀.sub_sat_sx
– i𝑁x𝑀.min_sx
62 [Link]
– i𝑁x𝑀.max_sx
– i𝑁x𝑀.shl
– i𝑁x𝑀.shr_sx
– f𝑁x𝑀.add
– f𝑁x𝑀.sub
– f𝑁x𝑀.mul
– f𝑁x𝑀.div
– i16x8.extadd_pairwise_i8x16_sx
– i32x4.extadd_pairwise_i16x8_sx
– i𝑁x𝑀.extmul_half _i𝑁 ′x𝑀 ′_sx
– i16x8.q15mulr_sat_s
– i32x4.dot_i16x8_s
– i8x16.avgr_u
– i16x8.avgr_u
– f𝑁x𝑀.min
– f𝑁x𝑀.max
– f𝑁x𝑀.pmin
– f𝑁x𝑀.pmax
• New ternary vector instruction:
– [Link]
• New test vector instructions:
– v128.any_true
– i𝑁x𝑀.all_true
• New relational vector instructions:
– i𝑁x𝑀.eq
– i𝑁x𝑀.ne
– i𝑁x𝑀.lt_sx
– i𝑁x𝑀.gt_sx
– i𝑁x𝑀.le_sx
– i𝑁x𝑀.ge_sx
– f𝑁x𝑀.eq
– f𝑁x𝑀.ne
– f𝑁x𝑀.lt
– f𝑁x𝑀.gt
– f𝑁x𝑀.le
– f𝑁x𝑀.ge
• New conversion vector instructions:
– i32x4.trunc_sat_f32x4_sx
– i32x4.trunc_sat_f64x2_sx _zero
– f32x4.convert_i32x4_sx
– f32x4.demote_f64x2_zero
– f64x2.convert_low_i32x4_sx
– f64x2.promote_low_f32x4
• New lane access vector instructions:
– i𝑁x𝑀.extract_lane_sx ?
– i𝑁x𝑀.replace_lane
– f𝑁x𝑀.extract_lane
– f𝑁x𝑀.replace_lane
• New lane splitting/combining vector instructions:
– i𝑁x𝑀.extend_half _i𝑁 ′x𝑀 ′_sx
– i8x16.narrow_i16x8_sx
– i16x8.narrow_i32x4_sx
• New byte reordering vector instructions:
– [Link]
– [Link]
• New injection/projection vector instructions:
– i𝑁x𝑀.splat
– f𝑁x𝑀.splat
– i𝑁x𝑀.bitmask
Note
Tail Calls
Added instructions to perform tail calls.64
• New control instructions:
– return_call
– return_call_indirect
63 [Link]
64 [Link]
Exception Handling
Added tag definitions, imports, and exports, and instructions to throw and catch exceptions.65
• Modules may
– define tags
– import tags
– export tags
• New heap types:
– exn
– noexn
• New reference type short-hands:
– exnref
– nullexnref
• New control instructions:
– throw
– throw_ref
– try_table
• New tag section in binary format.
Multiple Memories
Added the ability to use multiple memories per module.66
• Modules may
– define multiple memories
– import multiple memories
– export multiple memories
• Memory instructions take a memory index immediate:
– [Link]
– [Link]
– [Link]
– [Link]
– [Link]
– 𝑡.load
– 𝑡.store
– 𝑡.load𝑁_sx
– 𝑡.store𝑁
– [Link]𝑁x𝑀_sx
– [Link]𝑁_zero
– [Link]𝑁_splat
– [Link]𝑁_lane
65 [Link]
66 [Link]
– [Link]𝑁_lane
• Data segments take a memory index
Typeful References
Added more precise types for references.68
• New generalised form of reference types:
– (ref null? heaptype)
• New class of heap types:
– func
– extern
67 [Link]
68 [Link]
– typeidx
• Basic subtyping on reference and value types
• New reference instructions:
– ref.as_non_null
– br_on_null
– br_on_non_null
• New control instruction:
– call_ref
• Refined typing of reference instruction:
– [Link] with more precise result type
• Refined typing of local instructions and instruction sequences to track the initialization status of locals with
non-defaultable type
• Refined decoding of active element segments with implicit element type and plain function indices (opcode
0) to produce non-null reference type
• Extended table definitions with optional initializer expression
Garbage Collection
Added managed reference types.69
• New forms of heap types:
– any
– eq
– i31
– struct
– array
– none
– nofunc
– noextern
• New reference type short-hands:
– anyref
– eqref
– i31ref
– structref
– arrayref
– nullref
– nullfuncref
– nullexternref
• New forms of type definitions:
– structure
– array types
69 [Link]
– sub types
– recursive types
• Enriched subtyping based on explicitly declared sub types and the new heap types
• New generic reference instructions:
– [Link]
– [Link]
– [Link]
– br_on_cast
– br_on_cast_fail
• New reference instructions for unboxed scalars:
– ref.i31
– i31.get_sx
• New reference instructions for structure types:
– [Link]
– struct.new_default
– struct.get_sx ?
– [Link]
• New reference instructions for array types:
– [Link]
– array.new_default
– array.new_fixed
– array.new_data
– array.new_elem
– array.get_sx ?
– [Link]
– [Link]
– [Link]
– [Link]
– array.init_data
– array.init_elem
• New reference instructions for converting external types:
– any.convert_extern
– extern.convert_any
• Extended set of constant instructions with:
– ref.i31
– [Link]
– struct.new_default
– [Link]
– array.new_default
– array.new_fixed
– any.convert_extern
– extern.convert_any
Profiles
Introduced the concept of profile for specifying language subsets.
• A new profile defining a deterministic mode of execution.
Custom Annotations
Added generic syntax for custom annotations in the text format, mirroring the role of custom sections in the binary
format.71
• Annotations of the form ‘(@id . . . )’ are allowed anywhere in the text format
• Identifiers can be escaped as ‘@” . . . ”’ with arbitrary names
• Defined name annotations ‘(@name ” . . . ”)’ for:
– module names
– type names
– function names
– local names
– field names
• Defined custom annotation ‘(@custom ” . . . ”)’ to represent arbitrary custom sections in the text format
70 [Link]
71 [Link]
Note
Multi-byte opcodes are given with the shortest possible encoding in the table. However, what is following the
first byte is actually a u32 with variable-length encoding and consequently has multiple possible representations.
Construct Judgement
Numeric type 𝐶 ⊢ numtype : ok
Vector type 𝐶 ⊢ vectype : ok
Heap type 𝐶 ⊢ heaptype : ok
Reference type 𝐶 ⊢ reftype : ok
Value type 𝐶 ⊢ valtype : ok
Packed type 𝐶 ⊢ packtype : ok
Storage type 𝐶 ⊢ storagetype : ok
Field type 𝐶 ⊢ fieldtype : ok
Result type 𝐶 ⊢ resulttype : ok
Instruction type 𝐶 ⊢ instrtype : ok
Composite type 𝐶 ⊢ comptype : ok
Sub type 𝐶 ⊢ subtype : ok
Recursive type 𝐶 ⊢ rectype : ok
Defined type 𝐶 ⊢ deftype : ok
Block type 𝐶 ⊢ blocktype : instrtype
Tag type 𝐶 ⊢ tagtype : ok
Global type 𝐶 ⊢ globaltype : ok
Memory type 𝐶 ⊢ memtype : ok
Table type 𝐶 ⊢ tabletype : ok
External type 𝐶 ⊢ externtype : ok
Type definitions 𝐶 ⊢ type * : ok
Construct Judgement
Instruction 𝑆; 𝐶 ⊢ instr : instrtype
Instruction sequence 𝑆; 𝐶 ⊢ instr * : instrtype
Catch clause 𝐶 ⊢ catch : ok
Expression 𝐶 ⊢ expr : resulttype
Limits 𝐶 ⊢ limits : 𝑘
Tag 𝐶 ⊢ tag : tagtype
Global 𝐶 ⊢ global : globaltype
Memory 𝐶 ⊢ mem : memtype
Table 𝐶 ⊢ table : tabletype
Function 𝐶 ⊢ func : deftype
Local 𝐶 ⊢ local : localtype
Element segment 𝐶 ⊢ elem : reftype
Element mode 𝐶 ⊢ elemmode : reftype
Data segment 𝐶 ⊢ data : ok
Data mode 𝐶 ⊢ datamode : ok
Start function 𝐶 ⊢ start : ok
Import 𝐶 ⊢ import : externtype
Export 𝐶 ⊢ export : externtype
Module ⊢ module : externtype * → externtype *
Construct Judgement
Value 𝑆 ⊢ val : valtype
Result 𝑆 ⊢ result : resulttype
Packed value 𝑆 ⊢ packval : packtype
Field value 𝑆 ⊢ fieldval : storagetype
External address 𝑆 ⊢ externaddr : externtype
Tag instance 𝑆 ⊢ taginst : tagtype
Global instance 𝑆 ⊢ globalinst : globaltype
Memory instance 𝑆 ⊢ meminst : memtype
Table instance 𝑆 ⊢ tableinst : tabletype
Function instance 𝑆 ⊢ funcinst : deftype
Data instance 𝑆 ⊢ datainst : ok
Element instance 𝑆 ⊢ eleminst : 𝑡
Structure instance 𝑆 ⊢ structinst : ok
Array instance 𝑆 ⊢ arrayinst : ok
Export instance 𝑆 ⊢ exportinst : ok
Module instance 𝑆 ⊢ moduleinst : 𝐶
Store ⊢ store : ok
Configuration ⊢ config : [𝑡* ]
Thread 𝑆; resulttype ? ⊢ thread : resulttype
Frame 𝑆 ⊢ frame : 𝐶
7.11.4 Constantness
Construct Judgement
Constant expression 𝐶 ⊢ expr const
Constant instruction 𝐶 ⊢ instr const
7.11.5 Matching
Construct Judgement
Number type 𝐶 ⊢ numtype 1 ≤ numtype 2
Vector type 𝐶 ⊢ vectype 1 ≤ vectype 2
Heap type 𝐶 ⊢ heaptype 1 ≤ heaptype 2
Reference type 𝐶 ⊢ reftype 1 ≤ reftype 2
Value type 𝐶 ⊢ valtype 1 ≤ valtype 2
Packed type 𝐶 ⊢ packtype 1 ≤ packtype 2
Storage type 𝐶 ⊢ storagetype 1 ≤ storagetype 2
Field type 𝐶 ⊢ fieldtype 1 ≤ fieldtype 2
Result type 𝐶 ⊢ resulttype 1 ≤ resulttype 2
Instruction type 𝐶 ⊢ instrtype 1 ≤ instrtype 2
Composite type 𝐶 ⊢ comptype 1 ≤ comptype 2
Defined type 𝐶 ⊢ deftype 1 ≤ deftype 2
Limits 𝐶 ⊢ limits 1 ≤ limits 2
Tag type 𝐶 ⊢ tagtype 1 ≤ tagtype 2
Global type 𝐶 ⊢ globaltype 1 ≤ globaltype 2
Memory type 𝐶 ⊢ memtype 1 ≤ memtype 2
Table type 𝐶 ⊢ tabletype 1 ≤ tabletype 2
External type 𝐶 ⊢ externtype 1 ≤ externtype 2
Construct Judgement
Tag instance ⊢ taginst 1 ⪯ taginst 2
Global instance ⊢ globalinst 1 ⪯ globalinst 2
Memory instance ⊢ meminst 1 ⪯ meminst 2
Table instance ⊢ tableinst 1 ⪯ tableinst 2
Function instance ⊢ funcinst 1 ⪯ funcinst 2
Data instance ⊢ datainst 1 ⪯ datainst 2
Element instance ⊢ eleminst 1 ⪯ eleminst 2
Structure instance ⊢ structinst 1 ⪯ structinst 2
Array instance ⊢ arrayinst 1 ⪯ arrayinst 2
Store ⊢ store 1 ⪯ store 2
7.11.7 Execution
Construct Judgement
*
Instruction 𝑆; 𝐹 ; instr * ˓→ 𝑆 ′ ; 𝐹 ′ ; instr ′
Expression 𝑆; 𝐹 ; expr ˓→ 𝑆 ′ ; 𝐹 ′ ; expr ′
307
WebAssembly Specification, Release 3.0 (2025-11-14)
308 Index
WebAssembly Specification, Release 3.0 (2025-11-14)
Index 309
WebAssembly Specification, Release 3.0 (2025-11-14)
E validation, 76
element, 13, 23, 25, 25, 75, 87, 166, 201, 203, 233, export instance, 83, 85, 167, 242, 259
235, 243, 249 abstract syntax, 85
abstract syntax, 25 export section, 200
address, 82 expression, 23, 24, 25, 70, 73–75, 164, 198, 200–202,
binary format, 201 231–233, 235, 286
index, 23 abstract syntax, 23
instance, 85 binary format, 198
mode, 25 constant, 23, 70, 198, 231
section, 201 execution, 164
segment, 25, 75, 201, 233, 235 text format, 231
text format, 233, 235 validation, 70
type, 13 external
validation, 75 address, 83
element address, 83, 134, 166, 167 type, 13
abstract syntax, 82 external address, 13, 83, 85, 118, 167, 259
element expression, 85 abstract syntax, 83
element index, 23, 25, 198, 231 external index, 26, 236
abstract syntax, 23 abstract syntax, 26
binary format, 198 external reference, 65, 81
text format, 231 external type, 13, 39, 46, 118, 167, 183, 214, 247,
element instance, 82, 83, 85, 134, 166, 167, 255, 259
258, 264 abstract syntax, 13
abstract syntax, 85 binary format, 183
element mode, 25 text format, 214
abstract syntax, 25 validation, 39
element section, 201
element segment, 77, 84, 85, 167, 236, 282, 283 F
element type, 13, 46 field, 23, 278, 279
abstract syntax, 13 index, 23
embedder, 2, 3, 82, 84, 85, 239 field index, 23, 198, 278
embedding, 239 abstract syntax, 23
evaluation context, 80 binary format, 198
exception, 15, 82, 86, 87, 130, 171, 174, 245, 254, field type, 12, 36, 43, 44, 181, 213, 258, 265, 268,
286 289
address, 82 abstract syntax, 12
instance, 86 binary format, 181
exception address, 82, 245 text format, 213
abstract syntax, 82 validation, 36
exception handling, 183 field value, 85, 258, 265
exception instance, 82, 86, 245, 259, 265 abstract syntax, 85
abstract syntax, 86 file extension, 177, 205
exception tag, 13, 38, 72, 85, 119, 130, 182, 202, final, 12, 37
232 floating point, 2
exception type, 245 floating-point, 3, 7, 8, 9, 19, 81, 88, 90, 97, 282
execution, 4, 9, 11, 79, 247, 251 floating-point number, 179, 210
expression, 164 abstract syntax, 7
instruction, 120, 133, 134, 137, 144, 157, 159 binary format, 179
expand, 254 text format, 210
expansion, 31 folded instruction, 230
exponent, 7, 90 frame, 86, 87, 88, 120, 131, 133, 134, 137, 251, 260–
export, 23, 26, 76, 77, 85, 167, 174, 200, 203, 232– 262, 268
234, 236, 241, 242, 249, 283, 287 abstract syntax, 86
abstract syntax, 26 full profile, 249
binary format, 200 funciton type, 43
instance, 85 function, 2, 3, 10–12, 15, 23, 25, 26, 32, 74, 77, 83,
section, 200 84, 86, 87, 117, 131, 166, 167, 174, 200, 201,
text format, 232–234, 236
310 Index
WebAssembly Specification, Release 3.0 (2025-11-14)
Index 311
WebAssembly Specification, Release 3.0 (2025-11-14)
312 Index
WebAssembly Specification, Release 3.0 (2025-11-14)
Index 313
WebAssembly Specification, Release 3.0 (2025-11-14)
result type, 11, 12, 29, 31, 32, 36, 43, 48, 70, 120, validation, 36
181, 183, 213, 216, 254, 261, 262, 282 store, 9, 79, 82, 82, 83, 86, 88, 117, 118, 120, 132–
abstract syntax, 11 134, 137, 165, 171, 174, 240, 242–245, 255,
binary format, 181 258, 260–262
validation, 36 abstract syntax, 82
rewrite rule, 206 store extension, 262
roll, 12 string, 210
rolling, 29, 31 text format, 210
rounding, 98 structure, 12, 81, 117
runtime, 81, 304 address, 82
instance, 85
S type, 12
S-expression, 205, 230 structure address
scalar reference, 65, 117 abstract syntax, 82
section, 198, 203, 250, 276 structure field, 291
binary format, 198 structure instance, 82, 85, 117, 255, 258, 265
code, 201 abstract syntax, 85
custom, 199 structure type, 12, 36, 40, 43, 85, 117, 181, 213,
data, 202 255, 268, 279, 289
data count, 202 abstract syntax, 12
element, 201 binary format, 181
export, 200 text format, 213
function, 200 validation, 36
global, 200 structured control, 15, 48, 120, 183, 216
import, 200 structured control instruction, 249
memory, 200 sub type, 12, 29, 31, 37, 182, 213, 252, 268, 289
name, 236 abstract syntax, 12, 29, 37
start, 201 binary format, 182
table, 200 text format, 213
tag, 202 substitution, 30
type, 200 subtyping, 12, 29, 37, 39, 247, 266–268, 288
security, 2 syntax, 266
segment, 87
shape, 90 T
sign, 91 table, 3, 10, 13, 15, 16, 23, 25, 25, 26, 32, 73, 75, 77,
signed integer, 7, 91, 179, 209 83, 84, 87, 166, 167, 200, 203, 233, 236, 243,
abstract syntax, 7 249, 282, 283, 288
binary format, 179 abstract syntax, 25
text format, 209 address, 82
significand, 7, 90 binary format, 200
SIMD, 8, 9, 20, 283, 291 element, 25, 75, 201, 233, 235
soundness, 252, 265 export, 26
source text, 207, 207, 251 import, 26
stack, 79, 86, 174, 268 index, 23
stack machine, 14 instance, 84
stack type, 15 limits, 12, 13
start function, 23, 26, 75, 77, 201, 203, 236 section, 200
abstract syntax, 26 text format, 233
binary format, 201 type, 13
section, 201 validation, 73
text format, 236 table address, 83, 119, 120, 134, 166, 167, 243
validation, 75 abstract syntax, 82
start section, 201 table index, 16, 23, 25, 26, 55, 75, 76, 134, 167, 185,
state, 88 198, 200, 201, 218, 231, 233, 235, 236, 283
storage type, 12, 36, 44, 181, 213, 258, 289 abstract syntax, 23
abstract syntax, 12 binary format, 198
binary format, 181 text format, 231
text format, 213
314 Index
WebAssembly Specification, Release 3.0 (2025-11-14)
table instance, 82, 83, 84, 87, 120, 134, 166, 167, export, 232–234, 236
243, 251, 255, 256, 262, 264 expression, 231
abstract syntax, 84 external type, 214
table instruction, 16, 55, 134, 185, 218 field type, 213
table section, 200 floating-point number, 210
table type, 12, 13, 13, 25, 26, 29, 32, 38, 39, 46, function, 234
73, 76, 84, 119, 166, 183, 200, 214, 233, 236, function index, 231
243, 255, 256, 282 function type, 213
abstract syntax, 13 global, 232
binary format, 183 global index, 231
text format, 214 global type, 214
validation, 38 grammar, 205
tag, 13, 15, 23, 24, 26, 32, 72, 77, 83, 85–87, 130, 165, heap type, 212
167, 202, 203, 232, 236, 245, 249, 259, 278, identifiers, 211
279, 286 import, 232–234, 236
abstract syntax, 24 instruction, 216–218, 220, 221, 224
address, 82 integer, 209
binary format, 202 label index, 215, 231
export, 26 limits, 214
import, 26 list, 207
index, 23 local, 234
instance, 85 local index, 231
section, 202 memory, 233
text format, 232 memory index, 231
type, 13 memory type, 214
validation, 72 module, 236
tag address, 83, 86, 87, 119, 165, 167, 245, 259 mutability, 214
abstract syntax, 82 name, 211
tag index, 23, 26, 48, 76, 167, 183, 198, 200, 216, notation, 205
231, 232, 236, 278 number type, 212
abstract syntax, 23 packed type, 213
binary format, 198 recursive type, 213
text format, 231 reference type, 212
tag instance, 82, 83, 85, 87, 165, 167, 245, 255, 256, signed integer, 209
263 start function, 236
abstract syntax, 85 storage type, 213
tag section, 202 string, 210
tag type, 13, 13, 15, 24, 26, 29, 32, 38, 45, 46, 72, 76, structure type, 213
85, 119, 165, 182, 200, 202, 214, 232, 236, sub type, 213
245, 255, 256, 286 table, 233
abstract syntax, 13 table index, 231
binary format, 182 table type, 214
text format, 214 tag, 232
validation, 38 tag index, 231
terminal configuration, 265 tag type, 214
text format, 2, 205, 240, 247, 251, 276, 291 token, 207
address type, 213 type, 211, 231
aggregate type, 213 type index, 231
annotation, 209 type use, 214
array type, 213 uninterpreted integer, 209
byte, 210 unsigned integer, 209
character, 207 value, 209
comment, 208 value type, 212
composite type, 213 vector type, 212
data, 233, 235 white space, 208
data index, 231 thread, 88, 261, 265
element, 233, 235 throw, 254
element index, 231 throw context, 130, 262
Index 315
WebAssembly Specification, Release 3.0 (2025-11-14)
316 Index
WebAssembly Specification, Release 3.0 (2025-11-14)
type, 11
value type, 11, 11, 13–15, 19, 20, 25, 29, 31, 32, 35,
36, 38, 42–45, 48, 74, 81, 90, 117, 119, 137,
157, 165, 181–183, 212–214, 216, 246, 247,
252, 254, 261, 268, 282, 283, 288
abstract syntax, 11, 29
binary format, 181
text format, 212
validation, 35
variable instruction, 16
variable instructions, 54, 133, 184, 217
vector
abstract syntax, 8
vector instruction, 20, 66, 159, 191, 224, 291
vector number, 81
vector type, 9, 11, 35, 39, 81, 180, 212, 268, 283
binary format, 180
text format, 212
validation, 35
version, 203
W
white space, 207, 208
Index 317