WebAssembly Core Specification W3C Candidate Recommendation Draft , 26 August 2026 More details about this document This version: https://www.w3.org/TR/2026/CRD-wasm-core-2-20260826/ Latest published version: https://www.w3.org/TR/wasm-core-2/ Editor's Draft: https://webassembly.github.io/spec/core/bikeshed/ History: https://www.w3.org/standards/history/wasm-core-2/ Implementation Report: https://webassembly.org/features/ Feedback: GitHub Editor: Andreas Rossberg Issue Tracking: GitHub Issues Copyright © 2026 World Wide Web Consortium . W3C ® liability , trademark and permissive document license rules apply. Abstract This document describes release 3.0 of the core WebAssembly standard, a safe, portable, low-level code format designed for efficient execution and compact representation. This is part of a collection of related documents: the Core WebAssembly Specification , the WebAssembly JS Interface , and the WebAssembly Web API . Status of this document This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index. This document was published by the WebAssembly Working Group as a Candidate Recommendation Draft using the Recommendation track . Publication as a Candidate Recommendation does not imply endorsement by W3C and its Members. A Candidate Recommendation Draft integrates changes from the previous Candidate Recommendation that the Working Group intends to include in a subsequent Candidate Recommendation Snapshot. The WebAssembly Working group intends to maintain this publication in the Candidate Recommendation state and continually update it as a "living standard" rather than transition to Recommendation state. This document is maintained and updated at any time. Some parts of this document are work in progress. GitHub Issues are preferred for discussion of this specification. All issues and comments are archived . This document was produced by the WebAssembly Working Group . This document was produced by a group operating under the W3C Patent Policy . W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent that the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy . This document is governed by the 18 August 2025 W3C Process Document . Table of Contents 1 Introduction 1.1 Introduction 1.1.1 Design Goals 1.1.2 Scope 1.1.3 Security Considerations 1.1.4 Dependencies 1.2 Overview 1.2.1 Concepts 1.2.2 Semantic Phases 2 Structure 2.1 Conventions 2.1.1 Grammar Notation 2.1.2 Auxiliary Notation 2.1.3 Lists 2.2 Values 2.2.1 Bytes 2.2.2 Integers 2.2.3 Floating-Point 2.2.4 Vectors 2.2.5 Names 2.3 Types 2.3.1 Number Types 2.3.2 Vector Types 2.3.3 Type Uses 2.3.4 Heap Types 2.3.5 Reference Types 2.3.6 Value Types 2.3.7 Result Types 2.3.8 Block Types 2.3.9 Composite Types 2.3.10 Recursive Types 2.3.11 Address Types 2.3.12 Limits 2.3.13 Tag Types 2.3.14 Global Types 2.3.15 Memory Types 2.3.16 Table Types 2.3.17 Data Types 2.3.18 Element Types 2.3.19 External Types 2.4 Instructions 2.4.1 Parametric Instructions 2.4.2 Control Instructions 2.4.3 Variable Instructions 2.4.4 Table Instructions 2.4.5 Memory Instructions 2.4.6 Reference Instructions 2.4.7 Aggregate Instructions 2.4.8 Numeric Instructions 2.4.9 Vector Instructions 2.4.10 Expressions 2.5 Modules 2.5.1 Indices 2.5.2 Types 2.5.3 Tags 2.5.4 Globals 2.5.5 Memories 2.5.6 Tables 2.5.7 Functions 2.5.8 Data Segments 2.5.9 Element Segments 2.5.10 Start Function 2.5.11 Imports 2.5.12 Exports 3 Validation 3.1 Conventions 3.1.1 Types 3.1.2 Defined Types 3.1.3 Rolling and Unrolling 3.1.4 Instruction Types 3.1.5 Local Types 3.1.6 Contexts 3.1.7 Prose Notation 3.1.8 Formal Notation 3.2 Types 3.2.1 Number Types 3.2.2 Vector Types 3.2.3 Type Uses 3.2.4 Heap Types 3.2.5 Reference Types 3.2.6 Value Types 3.2.7 Result Types 3.2.8 Block Types 3.2.9 Instruction Types 3.2.10 Composite Types 3.2.11 Recursive Types 3.2.12 Limits 3.2.13 Tag Types 3.2.14 Global Types 3.2.15 Memory Types 3.2.16 Table Types 3.2.17 External Types 3.3 Matching 3.3.1 Number Types 3.3.2 Vector Types 3.3.3 Heap Types 3.3.4 Reference Types 3.3.5 Value Types 3.3.6 Result Types 3.3.7 Instruction Types 3.3.8 Composite Types 3.3.9 Field Types 3.3.10 Defined Types 3.3.11 Limits 3.3.12 Tag Types 3.3.13 Global Types 3.3.14 Memory Types 3.3.15 Table Types 3.3.16 External Types 3.4 Instructions 3.4.1 Parametric Instructions 3.4.2 Control Instructions 3.4.3 Variable Instructions 3.4.4 Table Instructions 3.4.5 Memory Instructions 3.4.6 Reference Instructions 3.4.7 Aggregate Reference Instructions 3.4.8 Scalar Reference Instructions 3.4.9 External Reference Instructions 3.4.10 Numeric Instructions 3.4.11 Vector Instructions 3.4.12 Instruction Sequences 3.4.13 Expressions 3.5 Modules 3.5.1 Types 3.5.2 Tags 3.5.3 Globals 3.5.4 Memories 3.5.5 Tables 3.5.6 Functions 3.5.7 Locals 3.5.8 Data Segments 3.5.9 Element Segments 3.5.10 Start Function 3.5.11 Imports 3.5.12 Exports 3.5.13 Modules 4 Execution 4.1 Conventions 4.1.1 Prose Notation 4.1.2 Formal Notation 4.2 Runtime Structure 4.2.1 Values 4.2.2 Results 4.2.3 Store 4.2.4 Addresses 4.2.5 External Addresses 4.2.6 Module Instances 4.2.7 Function Instances 4.2.8 Table Instances 4.2.9 Memory Instances 4.2.10 Global Instances 4.2.11 Tag Instances 4.2.12 Element Instances 4.2.13 Data Instances 4.2.14 Export Instances 4.2.15 Aggregate Instances 4.2.16 Exception Instances 4.2.17 Stack 4.2.18 Administrative Instructions 4.2.19 Configurations 4.3 Numerics 4.3.1 Representations 4.3.2 Integer Operations 4.3.3 Floating-Point Operations 4.3.4 Conversions 4.3.5 Vector Operations 4.3.6 Relaxed Operations 4.4 Types 4.4.1 Instantiation 4.5 Values 4.5.1 Value Typing 4.5.2 External Typing 4.6 Instructions 4.6.1 Parametric Instructions 4.6.2 Control Instructions 4.6.3 Blocks 4.6.4 Exception Handling 4.6.5 Function Calls 4.6.6 Variable Instructions 4.6.7 Table Instructions 4.6.8 Memory Instructions 4.6.9 Reference Instructions 4.6.10 Numeric Instructions 4.6.11 Vector Instructions 4.6.12 Expressions 4.7 Modules 4.7.1 Allocation 4.7.2 Instantiation 4.7.3 Invocation 5 Binary Format 5.1 Conventions 5.1.1 Grammar 5.1.2 Auxiliary Notation 5.1.3 Lists 5.2 Values 5.2.1 Bytes 5.2.2 Integers 5.2.3 Floating-Point 5.2.4 Names 5.3 Types 5.3.1 Number Types 5.3.2 Vector Types 5.3.3 Heap Types 5.3.4 Reference Types 5.3.5 Value Types 5.3.6 Result Types 5.3.7 Composite Types 5.3.8 Recursive Types 5.3.9 Limits 5.3.10 Tag Types 5.3.11 Global Types 5.3.12 Memory Types 5.3.13 Table Types 5.3.14 External Types 5.4 Instructions 5.4.1 Parametric Instructions 5.4.2 Control Instructions 5.4.3 Variable Instructions 5.4.4 Table Instructions 5.4.5 Memory Instructions 5.4.6 Reference Instructions 5.4.7 Aggregate Instructions 5.4.8 Numeric Instructions 5.4.9 Vector Instructions 5.4.10 Expressions 5.5 Modules 5.5.1 Indices 5.5.2 Sections 5.5.3 Custom Section 5.5.4 Type Section 5.5.5 Import Section 5.5.6 Function Section 5.5.7 Table Section 5.5.8 Memory Section 5.5.9 Global Section 5.5.10 Export Section 5.5.11 Start Section 5.5.12 Element Section 5.5.13 Code Section 5.5.14 Data Section 5.5.15 Data Count Section 5.5.16 Tag Section 5.5.17 Modules 6 Text Format 6.1 Conventions 6.1.1 Grammar 6.1.2 Abbreviations 6.1.3 Contexts 6.1.4 Lists 6.2 Lexical Format 6.2.1 Characters 6.2.2 Tokens 6.2.3 White Space 6.2.4 Comments 6.2.5 Annotations 6.3 Values 6.3.1 Integers 6.3.2 Floating-Point 6.3.3 Strings 6.3.4 Names 6.3.5 Identifiers 6.4 Types 6.4.1 Number Types 6.4.2 Vector Types 6.4.3 Heap Types 6.4.4 Reference Types 6.4.5 Value Types 6.4.6 Composite Types 6.4.7 Recursive Types 6.4.8 Address Types 6.4.9 Limits 6.4.10 Tag Types 6.4.11 Global Types 6.4.12 Memory Types 6.4.13 Table Types 6.4.14 External Types 6.4.15 Type Uses 6.5 Instructions 6.5.1 Labels 6.5.2 Parametric Instructions 6.5.3 Control Instructions 6.5.4 Variable Instructions 6.5.5 Table Instructions 6.5.6 Memory Instructions 6.5.7 Reference Instructions 6.5.8 Aggregate Instructions 6.5.9 Numeric Instructions 6.5.10 Vector Instructions 6.5.11 Folded Instructions 6.5.12 Expressions 6.6 Modules 6.6.1 Indices 6.6.2 Types 6.6.3 Tags 6.6.4 Globals 6.6.5 Memories 6.6.6 Tables 6.6.7 Functions 6.6.8 Data Segments 6.6.9 Element Segments 6.6.10 Start Function 6.6.11 Imports 6.6.12 Exports 6.6.13 Modules A Appendix A.1 Embedding Types Booleans Exceptions and Errors Pre- and Post-Conditions Store Modules Module Instances Functions Tables Memories Tags Exceptions Globals Values Matching A.2 Profiles Conventions Defined Profiles A.3 Implementation Limitations Syntactic Limits Validation Execution Type Soundness Contexts Types Subtyping Results Store Validity Configuration Validity Administrative Instructions Store Extension Theorems A.7 Type System Properties Principal Types Type Lattice Compositionality A.4 Validation Algorithm Data Structures Validation of Opcode Sequences Custom Sections and Annotations Name Section Name Annotations Custom Annotations A.8 Change History Release 2.0 Release 3.0 A.9 Index of Types A.10 Index of Instructions A.11 Index of Semantic Rules Well-formedness of Types Typing of Static Constructs Typing of Runtime Constructs Constantness Matching Store Extension Execution Conformance Document conventions Conformant Algorithms References Normative References 1. Introduction 1.1. Introduction WebAssembly (abbreviated Wasm [ 1 ] ) 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 Group . This document describes version 3.0 (2026-08-26) of the core WebAssembly standard. It is intended that it will be superseded by new incremental releases with additional features in the future. 1.1.1. Design Goals The design goals of WebAssembly are the following: Fast, safe, and portable semantics : Fast : executes with near native code performance, taking advantage of capabilities common to all contemporary hardware. Safe : code is validated and executes in a memory-safe [ 2 ] , sandboxed environment preventing data corruption or security breaches. Well-defined : fully and precisely defines valid programs and their behavior in a way that is easy to reason about informally and formally. Hardware-independent : can be compiled on all modern architectures, desktop or mobile devices and embedded systems alike. Language-independent : does not privilege any particular language, programming model, or object model. Platform-independent : can be embedded in browsers, run as a stand-alone VM, or integrated in other environments. Open : programs can interoperate with their environment in a simple and universal manner. Efficient and portable representation : Compact : has a binary format that is fast to transmit by being smaller than typical text or native code formats. Modular : programs can be split up in smaller parts that can be transmitted, cached, and consumed separately. Efficient : can be decoded, validated, and compiled in a fast single pass, equally with either just-in-time (JIT) or ahead-of-time (AOT) compilation. Streamable : allows decoding, validation, and compilation to begin as soon as possible, before all data has been seen. Parallelizable : allows decoding, validation, and compilation to be split into many independent parallel tasks. Portable : makes no architectural assumptions that are not broadly supported across modern hardware. WebAssembly code is also intended to be easy to inspect and debug, especially in environments like web browsers, but such features are beyond the scope of this specification. [ 1 ] A contraction of “WebAssembly”, not an acronym, hence not using all-caps. [ 2 ] No program can break WebAssembly’s memory model. Of course, it cannot guarantee that an unsafe language compiling to WebAssembly does not corrupt its own memory layout, e.g. inside WebAssembly’s linear memory. 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 encoding, 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.3. Security Considerations WebAssembly provides no ambient access to the computing environment in which code is executed. Any interaction with the environment, such as I/O, access to resources, or operating system calls, can only be performed by invoking functions provided by the embedder and imported into a WebAssembly module . An embedder can establish security policies suitable for a respective environment by controlling or limiting which functional capabilities it makes available for import. Such considerations are an embedder’s responsibility and the subject of API definitions for a specific environment. Because WebAssembly is designed to be translated into machine code running directly on the host’s hardware, it is potentially vulnerable to side channel attacks on the hardware level. In environments where this is a concern, an embedder may have to put suitable mitigations into place to isolate WebAssembly computations. 1.1.4. Dependencies WebAssembly depends on two existing standards: [IEEE-754-2019] , for the representation of floating-point data and the semantics of respective numeric operations . [UNICODE] , 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. 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-754-2019] 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-754-2019] 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 [ 1 ] 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 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. [ 1 ] 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.2. Semantic Phases Conceptually, the semantics of WebAssembly is divided into three phases. For each part of the language, the specification specifies each of them. Decoding WebAssembly modules are distributed in a binary format . Decoding processes that format and converts it into an internal representation of a module. In this specification, this representation is modelled by abstract syntax , but a real implementation could compile directly to machine code instead. Validation A decoded module has to be valid . Validation checks a number of well-formedness conditions to guarantee that the module is meaningful and safe. In particular, it performs type checking of functions and the instruction sequences in their bodies, ensuring for example that the operand stack is used consistently. Execution Finally, a valid module can be executed . Execution can be further divided into two phases: Instantiation . A module instance is the dynamic representation of a module, complete with its own state and execution stack. Instantiation executes the module body itself, given definitions for all its imports. It initializes globals, memories and tables and invokes the module’s start function if defined. It returns the instances of the module’s exports. Invocation . Once instantiated, further WebAssembly computations can be initiated by invoking an exported function on a module instance. Given the required arguments, that executes the respective function and returns its results. Instantiation and invocation are operations within the embedding environment. 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. 2.1.1. Grammar Notation The following conventions are adopted in defining grammar rules for abstract syntax. Terminal symbols (atoms) are written in sans-serif font or in symbolic form: i 32 , nop , → , [ , ] . Nonterminal symbols are written in italic font: valtype , instr . A n is a sequence of n ≥ 0 iterations of A . A ∗ is a possibly empty sequence of iterations of A . (This is a shorthand for A n used where n is not relevant.) A + is a non-empty sequence of iterations of A . (This is a shorthand for A n where n ≥ 1 .) A ? is an optional occurrence of A . (This is a shorthand for A n where n ≤ 1 .) Productions are written sym ::= A 1 ∣ … ∣ A n . Large productions may be split into multiple definitions, indicated by ending the first one with explicit ellipses, sym ::= A 1 ∣ … , and starting continuations with ellipses, sym ::= … ∣ A 2 . Some productions are augmented with side conditions, “ if condition ”, that 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, then all those occurrences must have the same instantiation. (This is a shorthand for a side condition requiring multiple different variables to be equal.) 2.1.2. Auxiliary Notation When dealing with syntactic constructs the following notation is also used: ϵ denotes the empty sequence. ∣s∣ denotes the length of a sequence s . s [ i ] denotes the i -th element of a sequence s , starting from 0 . s [ i : n ] denotes the sub-sequence s [ i ] … s [ i + n − 1 ] of a sequence s . s [ [ i ] = A ] denotes the same sequence as s , except that the i -th element is replaced with A . s [ [ i : n ] = A n ] denotes the same sequence as s , except that the sub-sequence s [ i : n ] is replaced with A n . s 1 ⊕ s 2 denotes the sequence s 1 concatenated with s 2 ; this is equivalent to s 1 s 2 , but used for clarity. ⨁ s ∗ denotes the flattened sequence, formed by concatenating all sequences s i in s ∗ . A ∈ s denotes that A is a member of the sequence s , that is, s is of the form s 1 A s 2 for some sequences s 1 , s 2 . Moreover, the following conventions are employed: The notation x n , where x is a non-terminal symbol, is treated as a meta variable ranging over respective sequences of x (similarly for x ∗ , x + , x ? ). When given a sequence x n , then the occurrences of x in an iterated sequence ( … x … ) n are assumed to denote the individual elements of x n , respectively (similarly for x ∗ , x + , x ? ). This implicitly expresses a form of mapping syntactic constructions over a sequence. e i < n denotes the same sequence as e n , but implicitly also defines i n to be the sequence of values 0 to ( n − 1 ) . Note For example, if x n is the sequence a b c , then ( f ( x ) + 1 ) n denotes the sequence ( f ( a ) + 1 ) ( f ( b ) + 1 ) ( f ( c ) + 1 ) . The form e i < n additionally gives access to an index variable inside the iteration. For example, ( f ( x ) + i ) i < n denotes the sequence ( f ( a ) + 0 ) ( f ( b ) + 1 ) ( f ( c ) + 2 ) . Productions of the following form are interpreted as records that map a fixed set of fields field i to “values” A i , respectively: r ::= { field 1 A 1 , field 2 A 2 , … } The following notation is adopted for manipulating such records: Where the type of a record is clear from context, empty fields with value ϵ are often omitted. r . field denotes the contents of the field component of r . r [ . field = A ] denotes the same record as r , except that the value of the field component is replaced with A . r [ . field = ⊕ A ∗ ] denotes the same record as r , except that A ∗ is appended to the sequence value of the field component, that is, it is short for r [ . field = r . field ⊕ A ∗ ] . r 1 ⊕ r 2 denotes the composition of two identically shaped records by concatenating each field of sequences point-wise: { field 1 A 1 ∗ , field 2 A 2 ∗ , … } ⊕ { field 1 B 1 ∗ , field 2 B 2 ∗ , … } = { field 1 ( A 1 ∗ ⊕ B 1 ∗ ) , field 2 ( A 2 ∗ ⊕ B 2 ∗ ) , … } ⨁ r ∗ 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 ::= ( [ i ] ∣ . field ) + : s [ [ i ] pth = A ] is short for s [ [ i ] = s [ i ] [ pth = A ]] , r [ . field pth = A ] is short for r [ . field = r . field [ pth = A ]] . 2.1.3. Lists Lists are bounded sequences of the form A n (or A ∗ ), where the A can either be values or complex constructions. A list can have at most 2 32 − 1 elements. list ( X ) ::= X ∗ if ∣ X ∗ ∣ < 2 32 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. byte ::= 0x00 ∣ … ∣ 0xFF 2.2.1.1. Conventions The meta variable b ranges over bytes. Bytes are sometimes interpreted as natural numbers n < 256 . 2.2.2. Integers Different classes of integers with different value ranges are distinguished by their bit width N and by whether they are unsigned or signed . u N s N i N ::= ::= ::= 0 ∣ … ∣ 2 N − 1 − 2 N − 1 ∣ … ∣ − 1 ∣ 0 ∣ + 1 ∣ … ∣ + 2 N − 1 − 1 u N 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 u 8 , u 32 , u 64 , and u 128 . However, other sizes occur as auxiliary constructions, e.g., in the definition of floating-point numbers. 2.2.2.1. Conventions The meta variables m , n , i , j range over integers. Numbers may be denoted by simple arithmetics, as in the grammar above. In order to distinguish arithmetics like 2 N from sequences like ( 1 ) N , 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-754-2019] standard (Section 3.3). Every value has a sign and a magnitude . Magnitudes can either be expressed as normal numbers of the form m 0 . m 1 m 2 … m m ⋅ 2 e , where e is the exponent and m is the significand whose most significant bit m 0 is 1 , or as a subnormal number where the exponent is fixed to the smallest possible value and m 0 is 0 ; among the subnormals are positive and negative zero values. Since the significands are binary values, normals are represented in the form ( 1 + m ⋅ 2 − M ) ⋅ 2 e in the abstract syntax, where M is the bit width of m ; 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. f N fmag N ::= ::= ∣ ∣ ∣ + fmag N ∣ − fmag N ( 1 + m ⋅ 2 − M ) ⋅ 2 e ( 0 + m ⋅ 2 − M ) ⋅ 2 e ∞ nan ( m ) if m < 2 M ∧ 2 − 2 E − 1 ≤ e ≤ 2 E − 1 − 1 if m < 2 M ∧ 2 − 2 E − 1 = e if 1 ≤ m < 2 M where M = signif ( N ) and E = expon ( N ) with signif ( 32 ) signif ( 64 ) expon ( 32 ) expon ( 64 ) = = = = 23 52 8 11 A canonical NaN is a floating-point value ± nan ( canon N ) where canon N is a payload whose most significant bit is 1 while all others are 0 : canon N = 2 signif ( N ) − 1 An arithmetic NaN is a floating-point value ± nan ( m ) with m ≥ canon N , 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-754-2019] standard (Section 3.5.2) defines for decimal interchange formats. 2.2.3.1. Conventions The meta variable z ranges over floating-point values where clear from context. Where clear from context, shorthands like + 1 denote floating point values like + ( 1 + 0 ⋅ 2 − M ) ⋅ 2 0 . 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 u 128 . 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 [UNICODE] (Section 2.4). name char ::= ::= char ∗ U + 00 ∣ … ∣ U + D7FF ∣ U + E000 ∣ … ∣ U + 10FFFF if ∣ utf 8 ( char ∗ ) ∣ < 2 32 Due to the limitations of the binary format , the length of a name is bounded by the length of its UTF-8 encoding. 2.2.5.1. Convention Characters (Unicode scalar values) are sometimes used interchangeably with natural numbers n < 1114112 . 2.3. Types Various entities in WebAssembly are classified by types. Types are checked during validation , instantiation , and possibly execution . 2.3.1. Number Types Number types classify numeric values. numtype ::= i 32 ∣ i 64 ∣ f 32 ∣ f 64 The types i 32 and i 64 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-754-2019] 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 . 2.3.1.1. Conventions The notation ∣t∣ denotes the bit width of a number type t . That is, ∣ i 32 ∣ = ∣ f 32 ∣ = 32 and ∣ i 64 ∣ = ∣ f 64 ∣ = 64 . 2.3.2. Vector Types Vector types classify vectors of numeric values processed by vector instructions (also known as SIMD instructions, single instruction multiple data). vectype ::= v 128 The type v 128 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 . 2.3.2.1. Conventions The notation ∣t∣ for bit width extends to vector types as well, that is, ∣ v 128 ∣ = 128 . 2.3.3. Type Uses A type use is the use site of a type index referencing a composite type defined in a module . It classifies objects of the respective type. typeuse ::= typeidx ∣ … The syntax of type uses is extended with additional forms for the purpose of specifying validation and execution . 2.3.4. Heap Types Heap types classify objects in the runtime store . There are three disjoint hierarchies of heap types: function types classify functions , aggregate types classify dynamically allocated managed data, such as structures , arrays , or unboxed scalars , external types classify external references possibly owned by the embedder . The values from the latter two hierarchies are interconvertible by ways of the extern . convert_any and any . convert_extern instructions. That is, both type hierarchies are inhabited by an isomorphic set of values, but may have different, incompatible representations in practice. absheaptype heaptype ::= ∣ ∣ ∣ ∣ ::= any ∣ eq ∣ i 31 ∣ struct ∣ array ∣ none func ∣ nofunc exn ∣ noexn extern ∣ noextern … absheaptype ∣ typeuse 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 i 31 . The types struct and array denote the common supertypes of all structure and array aggregates, respectively. The type i 31 denotes unboxed scalars , that is, integers injected into references. Their observable value range is limited to 31 bits. Note Values of type i 31 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 . 2.3.5. Reference Types Reference types classify values that are first-class references to objects in the runtime store . reftype ::= ref null ? heaptype A reference type is characterised by the heap type it points to. In addition, a reference type of the form ref null ht is nullable , meaning that it can either be a proper reference to ht or null . Other references are non-null . Reference types are opaque , meaning that neither their size nor their bit pattern can be observed. Values of reference type can be stored in tables but not in memories . 2.3.5.1. 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 i 31 ref is an abbreviation for ( ref null i 31 ) . 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 ) . 2.3.6. Value Types Value types classify the individual values that WebAssembly code can compute with and the values that a variable accepts. They are either number types , vector types , or reference types . consttype valtype ::= ::= numtype ∣ vectype numtype ∣ vectype ∣ reftype ∣ … The syntax of value types is extended with additional forms for the purpose of specifying validation . 2.3.6.1. Conventions The meta variable t ranges over value types or subclasses thereof where clear from context. 2.3.7. Result Types Result types classify the result of executing instructions or functions , which is a sequence of values, written with brackets. resulttype ::= list ( valtype ) 2.3.8. Block Types Block types classify the input and output of structured control instructions delimiting blocks of instructions. blocktype ::= ∣ valtype ? typeidx They are given either as a type index that refers to a suitable function type reinterpreted as an instruction type , or as an optional value type inline, which is a shorthand for the instruction type ϵ → valtype ? . 2.3.9. Composite Types Composite types are all types composed from simpler types, including function types , structure types and array types . comptype fieldtype storagetype packtype ::= ∣ ∣ ::= ::= ::= struct list ( fieldtype ) array fieldtype func resulttype → resulttype mut ? storagetype valtype ∣ packtype i 8 ∣ i 16 Function types classify the signature of functions , mapping a list of parameters to a list of results. They are also used to classify the inputs and outputs of instructions . Aggregate types like structure or array types consist of a list of possibly mutable, possibly packed field types describing their components. Structures are heterogeneous, but require static indexing, while arrays need to be homogeneous, but allow dynamic indexing. 2.3.9.1. Conventions The notation ∣t∣ for the bit width of a value type t extends to packed types as well, that is, ∣ i 8 ∣ = 8 and ∣ i 16 ∣ = 16 . The auxiliary function unpack maps a storage type to the value type obtained when accessing a field: unpack ( valtype ) unpack ( packtype ) = = valtype i 32 2.3.10. Recursive Types Recursive types denote a group of mutually recursive composite types , each of which can optionally declare a list of type uses of supertypes that it matches . Each type can also be declared final , preventing further subtyping. rectype subtype ::= ::= rec list ( subtype ) sub final ? typeuse ∗ comptype In a module , each member of a recursive type is assigned a separate type index . 2.3.11. Address Types Address types are a subset of number types that classify the values that can be used as offsets into memories and tables . addrtype ::= i 32 ∣ i 64 2.3.11.1. 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 ) min ( at 1 , at 2 ) = = at 1 at 2 if ∣ at 1 ∣ ≤ ∣ at 2 ∣ otherwise 2.3.12. Limits Limits classify the size range of resizeable storage associated with memory types and table types . limits ::= [ u 64 .. u 64 ? ] If no maximum is present, then the respective storage can grow to any valid size. 2.3.13. Tag Types Tag types classify the signature tags with a type use referring to the definition of a function type that declares the types of parameter and result values associated with the tag. The result type is empty for exception tags. tagtype ::= typeuse 2.3.14. Global Types Global types classify global variables, which hold a value and can either be mutable or immutable. globaltype ::= mut ? valtype 2.3.15. Memory Types Memory types classify linear memories and their size range. memtype ::= addrtype limits page The limits constrain the minimum and optionally the maximum size of a memory. The limits are given in units of page size . 2.3.16. Table Types Table types classify tables over elements of reference type within a size range. tabletype ::= addrtype limits reftype Like memories, tables are constrained by limits for their minimum and optionally maximum size. The limits are given in numbers of entries. 2.3.17. Data Types Data types classify data segments . Since the contents of a data segment requires no further classification, they merely consist of a universal marker ok indicating well-formedness. datatype ::= ok 2.3.18. Element Types Element types classify element segments by the reference type of its elements. elemtype ::= reftype 2.3.19. External Types External types classify imports and external addresses with their respective types. 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. 2.3.19.1. 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 ∗ ) funcs ( externtype xt ∗ ) tables ( ϵ ) tables (( table tt ) xt ∗ ) tables ( externtype xt ∗ ) mems ( ϵ ) mems (( mem mt ) xt ∗ ) mems ( externtype xt ∗ ) globals ( ϵ ) globals (( global gt ) xt ∗ ) globals ( externtype xt ∗ ) tags ( ϵ ) tags (( tag jt ) xt ∗ ) tags ( externtype xt ∗ ) = = = = = = = = = = = = = = = ϵ dt funcs ( xt ∗ ) funcs ( xt ∗ ) ϵ tt tables ( xt ∗ ) tables ( xt ∗ ) ϵ mt mems ( xt ∗ ) mems ( xt ∗ ) ϵ gt globals ( xt ∗ ) globals ( xt ∗ ) ϵ jt tags ( xt ∗ ) tags ( xt ∗ ) otherwise otherwise otherwise otherwise 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 . 2.4.1. Parametric Instructions Instructions in this group can operate on operands of any value type . instr ::= ∣ ∣ ∣ ∣ nop unreachable drop select ( valtype ∗ ) ? … The nop instruction does nothing. The unreachable instruction causes an unconditional trap . The drop instruction simply throws away a single operand. The select instruction selects one of its first two operands based on whether its third operand is zero or not. It may include a value type determining the type of these operands. If missing, the operands must be of numeric or vector type. 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. 2.4.2. Control Instructions Instructions in this group affect the flow of control. instr catch ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ::= ∣ ∣ ∣ … block blocktype instr ∗ loop blocktype instr ∗ if blocktype instr ∗ else instr ∗ br labelidx br_if labelidx br_table labelidx ∗ labelidx br_on_null labelidx br_on_non_null labelidx br_on_cast labelidx reftype reftype br_on_cast_fail labelidx reftype reftype call funcidx call_ref typeuse call_indirect tableidx typeuse return return_call funcidx return_call_ref typeuse return_call_indirect tableidx typeuse throw tagidx throw_ref try_table blocktype list ( catch ) instr ∗ … catch tagidx labelidx catch_ref tagidx labelidx catch_all labelidx catch_all_ref labelidx The block , loop , if and try_table instructions are structured instructions. They bracket nested sequences of instructions, 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. 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 instructions 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. 2.4.3. Variable Instructions Variable instructions are concerned with access to local or global variables. instr ::= ∣ ∣ ∣ ∣ ∣ ∣ … local . get localidx local . set localidx local . tee localidx global . get globalidx global . set globalidx … These instructions get or set the values of respective variables. The local . tee instruction is like local . set but also returns its argument. 2.4.4. Table Instructions Instructions in this group are concerned with tables table . instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … table . get tableidx table . set tableidx table . size tableidx table . grow tableidx table . fill tableidx table . copy tableidx tableidx table . init tableidx elemidx elem . drop elemidx … The table . get and table . set instructions load or store an element in a table, respectively. The table . size instruction returns the current size of a table. The table . grow 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 table . fill instruction sets all entries in a range to a given value. The table . copy instruction copies elements from a source table region to a possibly overlapping destination region; the first index denotes the destination. The table . init instruction copies elements from a passive element segment into a table. The elem . drop 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 An additional instruction that accesses a table is the control instruction call_indirect . 2.4.5. Memory Instructions Instructions in this group are concerned with linear memory . memarg loadop i N storeop i N vloadop vectype instr ::= ::= ::= ::= ∣ ∣ ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ { align u 32 , offset u 64 } sz _ sx sz sz x M _ sx sz _ splat sz _ zero … numtype . load loadop numtype ? memidx memarg numtype . store storeop numtype ? memidx memarg vectype . load vloadop vectype ? memidx memarg vectype . load sz _ lane memidx memarg laneidx vectype . store memidx memarg vectype . store sz _ lane memidx memarg laneidx memory . size memidx memory . grow memidx memory . fill memidx memory . copy memidx memidx memory . init memidx dataidx data . drop dataidx … if sz < N if sz < N if sz ⋅ M = ∣ vectype ∣ /2 if sz ≥ 32 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). 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 v 128 . 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 endian 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 memory . size instruction returns the current size of a memory. The memory . grow 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 memory . fill instruction sets all values in a region of memory to a given byte. The memory . copy 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 memory . init instruction copies data from a passive data segment into a memory. The data . drop 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. 2.4.6. Reference Instructions Instructions in this group are concerned with accessing references . instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ref . func funcidx ref . null heaptype ref . is_null ref . as_non_null ref . eq ref . test reftype ref . cast reftype … The ref . null and ref . func 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 ref . eq compares two references. The instructions ref . test and ref . cast 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_non_null that branch depending on the success or failure of a null test instead of trapping. Similarly, the br_on_cast and br_on_cast_fail instructions provides versions of ref . cast 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 . 2.4.7. Aggregate Instructions Instructions in this group are concerned with creating and accessing references to aggregate types. instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … struct . new typeidx struct . new_default typeidx struct . get _ sx ? typeidx fieldidx struct . set typeidx fieldidx array . new typeidx array . new_default typeidx array . new_fixed typeidx u 32 array . new_data typeidx dataidx array . new_elem typeidx elemidx array . get _ sx ? typeidx array . set typeidx array . len array . fill typeidx array . copy typeidx typeidx array . init_data typeidx dataidx array . init_elem typeidx elemidx ref . i 31 i 31 . get _ sx extern . convert_any any . convert_extern … The instructions struct . new 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 array . get , array . get sx , and array . set access individual slots, again allowing for different sign extension modes in the case of a packed storage type; array . len produces the length of an array; array . fill fills a specified slice of an array with a given value and array . copy , 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 . i 31 and i 31 . get sx convert between type i 32 and an unboxed scalar . The instructions any . convert_extern and extern . convert_any allow lossless conversion between references represented as type ( ref null extern ) and as ( ref null any ) . 2.4.8. Numeric Instructions Numeric instructions provide basic operations over numeric values of specific type . These operations closely match respective operations available in hardware. sz sx num i N num f N instr unop i N unop f N binop i N binop f N testop i N relop i N relop f N cvtop i N 1 , i N 2 cvtop i N 1 , f N 2 cvtop f N 1 , i N 2 cvtop f N 1 , f N 2 ::= ::= ::= ::= ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ::= ::= ::= ∣ ::= ::= ::= ::= ::= ∣ ::= ∣ ::= ∣ ∣ ::= ∣ 8 ∣ 16 ∣ 32 ∣ 64 u ∣ s i N f N … numtype . const num numtype numtype . unop numtype numtype . binop numtype numtype . testop numtype numtype . relop numtype numtype 1 . cvtop numtype 2 , numtype 1 _ numtype 2 … clz ∣ ctz ∣ popcnt ∣ extend sz _ s abs ∣ neg ∣ sqrt ∣ ceil ∣ floor ∣ trunc ∣ nearest add ∣ sub ∣ mul ∣ div _ sx ∣ rem _ sx and ∣ or ∣ xor ∣ shl ∣ shr _ sx ∣ rotl ∣ rotr add ∣ sub ∣ mul ∣ div ∣ min ∣ max ∣ copysign eqz eq ∣ ne ∣ lt _ sx ∣ gt _ sx ∣ le _ sx ∣ ge _ sx eq ∣ ne ∣ lt ∣ gt ∣ le ∣ ge extend _ sx wrap convert _ sx reinterpret trunc _ sx trunc_sat _ sx reinterpret promote demote if sz < N if N 1 < N 2 if N 1 > N 2 if N 1 = N 2 if N 1 = N 2 if N 1 < N 2 if N 1 > N 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. 2.4.9. Vector Instructions Vector instructions (also known as SIMD instructions, single instruction multiple data ) provide basic operations over values of vector type . lanetype dim shape ishape bshape half zero laneidx instr ::= ::= ::= ::= ::= ::= ::= ::= ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ numtype ∣ packtype 1 ∣ 2 ∣ 4 ∣ 8 ∣ 16 lanetype x dim shape shape low ∣ high zero u 8 … vectype . const vec vectype vectype . vvunop vectype . vvbinop vectype . vvternop vectype . vvtestop shape . vunop shape shape . vbinop shape shape . vternop shape shape . vtestop shape shape . vrelop shape ishape . vshiftop ishape ishape . bitmask bshape . vswizzlop bshape bshape . shuffle laneidx ∗ ishape 1 . vextunop ishape 2 , ishape 1 _ ishape 2 ishape 1 . vextbinop ishape 2 , ishape 1 _ ishape 2 ishape 1 . vextternop ishape 2 , ishape 1 _ ishape 2 ishape 1 . narrow _ ishape 2 _ sx shape 1 . vcvtop shape 2 , shape 1 _ shape 2 shape . splat shape . extract_lane _ sx ? laneidx shape . replace_lane laneidx … if ∣ lanetype ∣ ⋅ dim = 128 if lanetype ( shape ) = i N if lanetype ( shape ) = i 8 if ∣ laneidx ∗ ∣ = dim ( bshape ) if ∣ lanetype ( ishape 2 ) ∣ = 2 ⋅ ∣ lanetype ( ishape 1 ) ∣ ≤ 32 if sx ? = ϵ ⇔ lanetype ( shape ) ∈ i 32 i 64 f 32 f 64 Vector instructions have a naming convention involving a shape prefix that determines how their operands will be interpreted, written t x N , and consisting of a lane type t , a possibly packed numeric type , and its dimension N , which denotes the number of lanes of that type. Operations are performed point-wise on the values of each lane. Instructions prefixed with v 128 do not involve a specific interpretation, and treat the v 128 as either an i 128 value or a vector of 128 individual bits. Note For example, the shape i 32 x 4 interprets the operand as four i 32 values, packed into an i 128 . The bit width of the lane type t times N always is 128 . vvunop vvbinop vvternop vvtestop vunop i N x M vunop f N x M vbinop i N x M vbinop f N x M vternop i N x M vternop f N x M vtestop i N x M vrelop i N x M vrelop f N x M vswizzlop i 8 x M vshiftop i N x M vextunop i N 1 x M 1 , i N 2 x M 2 vextbinop i N 1 x M 1 , i N 2 x M 2 vextternop i N 1 x M 1 , i N 2 x M 2 vcvtop i N 1 x M 1 , i N 2 x M 2 vcvtop i N 1 x M 1 , f N 2 x M 2 vcvtop f N 1 x M 1 , i N 2 x M 2 vcvtop f N 1 x M 1 , f N 2 x M 2 ::= ::= ::= ::= ::= ∣ ::= ∣ ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ::= ∣ ∣ ::= ::= ::= ::= ∣ ∣ ∣ ∣ ::= ::= ::= ::= ::= ∣ ∣ ::= ::= ::= ::= ∣ ::= ∣ not and ∣ andnot ∣ or ∣ xor bitselect any_true abs ∣ neg popcnt abs ∣ neg ∣ sqrt ceil ∣ floor ∣ trunc ∣ nearest add sub add_sat _ sx sub_sat _ sx mul avgr _ u q 15 mulr_sat _ s relaxed_q15mulr _ s min _ sx max _ sx add ∣ sub ∣ mul ∣ div min ∣ max ∣ pmin ∣ pmax relaxed_min ∣ relaxed_max relaxed_laneselect relaxed_madd ∣ relaxed_nmadd all_true eq ∣ ne lt _ sx gt _ sx le _ sx ge _ sx eq ∣ ne ∣ lt ∣ gt ∣ le ∣ ge swizzle ∣ relaxed_swizzle shl ∣ shr _ sx extadd_pairwise _ sx extmul _ half _ sx dot _ s relaxed_dot _ s relaxed_dot_add _ s extend _ half _ sx convert _ half ? _ sx trunc_sat _ sx _ zero ? relaxed_trunc _ sx _ zero ? demote _ zero promote _ low if N = 8 if N ≤ 16 if N ≤ 16 if N ≥ 16 if N ≤ 16 if N = 16 if N = 16 if N ≤ 32 if N ≤ 32 if N = 64 ∨ sx = s if N = 64 ∨ sx = s if N = 64 ∨ sx = s if N = 64 ∨ sx = s if 16 ≤ 2 ⋅ N 1 = N 2 ≤ 32 if 2 ⋅ N 1 = N 2 ≥ 16 if 2 ⋅ N 1 = N 2 = 32 if 2 ⋅ N 1 = N 2 = 16 if 4 ⋅ N 1 = N 2 = 32 if N 2 = 2 ⋅ N 1 if N 2 = N 1 = 32 ∧ half ? = ϵ ∨ N 2 = 2 ⋅ N 1 ∧ half ? = low if N 1 = N 2 = 32 ∧ zero ? = ϵ ∨ N 1 = 2 ⋅ N 2 ∧ zero ? = zero if N 1 = N 2 = 32 ∧ zero ? = ϵ ∨ N 1 = 2 ⋅ N 2 ∧ zero ? = zero if N 1 = 2 ⋅ N 2 if 2 ⋅ N 1 = N 2 Vector instructions can be grouped into several subcategories: Constants : return a static constant. Unary Operations : consume one v 128 operand and produce one v 128 result. Binary Operations : consume two v 128 operands and produce one v 128 result. Ternary Operations : consume three v 128 operands and produce one v 128 result. Tests : consume one v 128 operand and produce a Boolean integer result. Shifts : consume a v 128 operand and an i 32 operand, producing one v 128 result. Splats : consume a value of numeric type and produce a v 128 result of a specified shape. Extract lanes : consume a v 128 operand and return the numeric value in a given lane. Replace lanes : consume a v 128 operand and a numeric value for a given lane, and produce a v 128 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. 2.4.9.1. 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 . expr ::= instr ∗ 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 list ( type ) list ( import ) list ( tag ) list ( global ) list ( mem ) list ( table ) list ( func ) list ( data ) list ( elem ) start ? list ( 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. idx typeidx funcidx globalidx tableidx memidx tagidx elemidx dataidx labelidx localidx fieldidx ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= u 32 idx idx idx idx idx idx idx idx idx idx idx 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 . 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 . 2.5.1.1. Conventions The meta variable l ranges over label indices. The meta variables x , y range over indices in any of the other index spaces. For every index space abcidx , the notation abcidx ( A ) denotes the set of indices from that index space occurring free in A . Sometimes this set is reinterpreted as the list of its elements. Note For example, if instr ∗ is ( data . drop 1 ) ( memory . init 2 3 ) , then dataidx instrs ( 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. type ::= type rectype 2.5.3. Tags The tag section of a module defines a list of tags : tag ::= tag tagtype 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): global ::= global globaltype expr 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 : mem ::= memory memtype 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 . Memories can be initialized through data segments . Memories are referenced through memory indices , starting with the smallest index not referencing a memory import . Most constructs implicitly reference memory index 0 . 2.5.6. Tables The table section of a module defines a list of tables described by their table type : table ::= table tabletype expr 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: func local ::= ::= func typeidx local ∗ expr local valtype 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 import . 2.5.8. Data Segments The data section of a module defines a list of data segments , which can be used to initialize a range of memory from a static list of bytes . data datamode ::= ::= data byte ∗ datamode active memidx expr ∣ passive 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 memory . init 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 . 2.5.9. Element Segments The elem section of a module defines a list of element segments , which can be used to initialize a subrange of a table from a static list of elements. elem elemmode ::= ::= elem reftype expr ∗ elemmode active tableidx expr ∣ passive ∣ declare Each element segment defines a reference type and a corresponding list of constant element expressions . 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 table . init 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 ref . func . The offset is given by another constant expression . Element segments are referenced through element indices . 2.5.10. Start Function The start section of a module declares the function index of a start function that is automatically invoked when the module is instantiated , after tables and memories have been initialized. start ::= start funcidx 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 . import ::= import name name externtype 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 externidx ::= ::= export name 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. 2.5.12.1. 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 x ) xx ∗ ) funcs ( externidx xx ∗ ) tables ( ϵ ) tables (( table x ) xx ∗ ) tables ( externidx xx ∗ ) mems ( ϵ ) mems (( memory x ) xx ∗ ) mems ( externidx xx ∗ ) globals ( ϵ ) globals (( global x ) xx ∗ ) globals ( externidx xx ∗ ) tags ( ϵ ) tags (( tag x ) xx ∗ ) tags ( externidx xx ∗ ) = = = = = = = = = = = = = = = ϵ x funcs ( xx ∗ ) funcs ( xx ∗ ) ϵ x tables ( xx ∗ ) tables ( xx ∗ ) ϵ x mems ( xx ∗ ) mems ( xx ∗ ) ϵ x globals ( xx ∗ ) globals ( xx ∗ ) ϵ x tags ( xx ∗ ) tags ( xx ∗ ) otherwise otherwise otherwise otherwise otherwise 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: In prose , describing the meaning in intuitive form. In formal notation , describing the rule in mathematical form. [ 1 ] 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 absheaptype typeuse ::= ::= ::= … ∣ bot … ∣ bot … ∣ deftype ∣ rec . n 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 . 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 i -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 represented such that two closed types are syntactically equal if and only if they have the same recursive structure. 3.1.1.1. Convention The difference rt 1 ∖ rt 2 between two reference types is defined as follows: ( ref null 1 ? ht 1 ) ∖ ( ref null ht 2 ) ( ref null 1 ? ht 1 ) ∖ ( ref ht 2 ) = = ( ref ht 1 ) ( ref null 1 ? ht 1 ) 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. 3.1.2. Defined Types Defined types denote the individual types defined in a module . Each such type is represented as a projection from the recursive type group it originates from, indexed by its position in that group. deftype ::= rectype . n 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 . 3.1.2.1. Conventions t [ x ∗ := dt ∗ ] denotes the parallel substitution of type indices x ∗ with corresponding defined types dt ∗ in type t , provided ∣ x ∗ ∣ = ∣ dt ∗ ∣ . t [ ( rec i ) ∗ := dt ∗ ] denotes the parallel substitution of recursive type indices ( rec i ) ∗ with defined types dt ∗ in type t , provided ∣ ( rec i ) ∗ ∣ = ∣ dt ∗ ∣ . This substitution does not proceed under recursive types , since they are considered local binders for all recursive type indices. t [ := dt ∗ ] is shorthand for the substitution t [ x ∗ := dt ∗ ] , where x ∗ = 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. 3.1.3. Rolling and Unrolling In order to allow comparing recursive types for equivalence , their representation is changed such that all type indices internal to the same recursive type are replaced by recursive type indices . 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 x ( rectype ) unroll ( rectype ) roll x ∗ ( rectype ) unroll ( rectype . i ) = = = = rec ( subtype [ ( x + i ) i < n := ( rec . i ) i < n ] ) n rec ( subtype [ ( rec . i ) i < n := ( rectype . i ) i < n ] ) n (( rec subtype n ) . i ) i < n subtype ∗ [ i ] if rectype = rec subtype n if rectype = rec subtype n if roll x ( rectype ) = rec subtype n if unroll ( rectype ) = rec subtype ∗ In addition, the following auxiliary relation denotes the expansion of a defined type or type use : deftype deftype typeidx ≈ ≈ C ≈ C comptype comptype comptype if unroll ( deftype ) = sub final ? typeuse ∗ comptype if deftype ≈ comptype if C . types [ typeidx ] ≈ comptype 3.1.4. Instruction Types Instruction types classify the behaviour of instructions or instruction sequences, by describing how they manipulate the operand stack and the initialization status of locals : instrtype ::= resulttype → localidx ∗ resulttype An instruction type t 1 ∗ → x ∗ t 2 ∗ describes the required input stack with argument values of types t 1 ∗ that an instruction pops off and the provided output stack with result values of types t 2 ∗ that it pushes back. Moreover, it enumerates the indices x ∗ of locals that have been set by the instruction or sequence. Note Instruction types are only used for validation , they do not occur in programs. 3.1.5. Local Types Local types classify locals , by describing their value type as well as their initialization status : localtype init ::= ::= init valtype set ∣ unset 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. 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. More concretely, contexts are defined as records C with abstract syntax: context ::= { types deftype ∗ tags tagtype ∗ globals globaltype ∗ mems memtype ∗ tables tabletype ∗ funcs deftype ∗ datas datatype ∗ elems elemtype ∗ locals localtype ∗ labels resulttype ∗ return resulttype ? refs funcidx ∗ … } Note The definition of contexts needs to be extended with additional fields for the purpose of proving type soundness . 3.1.6.1. 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 x occurring in it with its own corresponding defined type C . types [ x ] , after first closing the types in C . types themselves. clos C ( t ) clos ∗ ( ϵ ) clos ∗ ( dt ∗ dt n ) = = = t [ := dt ∗ ] ϵ dt ′ ∗ dt n [ := dt ′ ∗ ] if dt ∗ = clos ∗ ( C . types ) if dt ′ ∗ = clos ∗ ( dt ∗ ) Note Free type indices referring to types within the same recursive type are handled separately by rolling up recursive types before closing them. 3.1.7. Prose Notation Validation is specified by stylised rules for each relevant part of the abstract syntax . The rules not only state constraints defining when a phrase is valid, they also classify it with a type. The following conventions are adopted in stating these rules. A phrase A is said to be “valid with type T ” if and only if all constraints expressed by the respective rules are met. The form of T depends on the syntactic class of A . Note For example, if A is a function , then T is a defined function type ; for an A that is a global , T is a global type ; and so on. The rules implicitly assume a given context C . In some places, this context is locally extended to a context C ′ with additional entries. The formulation “Under context C ′ , … statement …” is adopted to express that the following statement must apply under the assumptions embodied in the extended context. 3.1.8. Formal Notation 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. [ 2 ] The proposition that a phrase A has a respective type T is written A : T . In general, however, typing is dependent on a context C . To express this explicitly, the complete form is a judgement C ⊢ A : T , which says that A : T holds under the assumptions encoded in C . The formal typing rules use a standard approach for specifying type systems, rendering them into deduction rules . Every rule has the following general form: conclusion premise 1 premise 2 … premise n 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 C ⊢ A : T , and there usually is one respective rule for each relevant construct A of the abstract syntax. Note For example, the typing rule for the i 32 . add instruction can be given as an axiom: C ⊢ i 32 . add : i 32 i 32 → i 32 The instruction is always valid with type i 32 i 32 → i 32 (saying that it consumes two i 32 values and produces one), independent of any side conditions. An instruction like global . get can be typed as follows: C ⊢ global . get x : ϵ → t C . globals [ x ] = mut t Here, the premise enforces that the immediate global index x exists in the context. The instruction produces a value of its respective type t (and does not consume any values). If C . globals [ x ] 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: C ⊢ block blocktype instr ∗ : t 1 ∗ → t 2 ∗ C ⊢ blocktype : t 1 ∗ → t 2 ∗ { labels ( t 2 ∗ )} ⊕ C ⊢ instr ∗ : t 1 ∗ → t 2 ∗ 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 C with the additional label information for the premise. [ 1 ] 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 WebAssembly . Proceedings of the 38th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017). ACM 2017. [ 2 ] For example: Benjamin Pierce. Types and Programming Languages . The MIT Press 2002 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. 3.2.1. Number Types The number type numtype is always valid . C ⊢ numtype : ok 3.2.2. Vector Types The vector type vectype is always valid . C ⊢ vectype : ok 3.2.3. Type Uses The type use typeidx is valid if: The type C . types [ typeidx ] exists. C ⊢ typeidx : ok C . types [ typeidx ] = dt 3.2.4. Heap Types The heap type absheaptype is always valid . C ⊢ absheaptype : ok 3.2.5. Reference Types The reference type ( ref null ? heaptype ) is valid if: The heap type heaptype is valid . C ⊢ ref null ? heaptype : ok C ⊢ heaptype : ok 3.2.6. Value Types The value type valtype is valid if: Either: The value type valtype is of the form numtype . The number type numtype is valid . Or: The value type valtype is of the form vectype . The vector type vectype is valid . Or: The value type valtype is of the form reftype . The reference type reftype is valid . Or: The value type valtype is of the form bot . C ⊢ bot : ok 3.2.7. Result Types The result type t ∗ is valid if: For all t in t ∗ : The value type t is valid . C ⊢ t ∗ : ok ( C ⊢ t : ok ) ∗ 3.2.8. Block Types Block types may be expressed in one of two forms, both of which are converted to instruction types by the following rules. The block type typeidx is valid as the instruction type t 1 ∗ → t 2 ∗ if: The type C . types [ typeidx ] exists. The expansion of C . types [ typeidx ] is ( func t 1 ∗ → t 2 ∗ ) . C ⊢ typeidx : t 1 ∗ → t 2 ∗ C . types [ typeidx ] ≈ func t 1 ∗ → t 2 ∗ The block type valtype ? is valid as the instruction type ϵ → valtype ? if: If valtype is defined, then: The value type valtype is valid . C ⊢ valtype ? : ϵ → valtype ? ( C ⊢ valtype : ok ) ? 3.2.9. Instruction Types The instruction type t 1 ∗ → x ∗ t 2 ∗ is valid if: The result type t 1 ∗ is valid . The result type t 2 ∗ is valid . For all x in x ∗ : The local C . locals [ x ] exists. C ⊢ t 1 ∗ → x ∗ t 2 ∗ : ok C ⊢ t 1 ∗ : ok C ⊢ t 2 ∗ : ok ( C . locals [ x ] = lt ) ∗ 3.2.10. Composite Types The composite type ( struct fieldtype ∗ ) is valid if: For all fieldtype in fieldtype ∗ : The field type fieldtype is valid . C ⊢ struct fieldtype ∗ : ok ( C ⊢ fieldtype : ok ) ∗ The composite type ( array fieldtype ) is valid if: The field type fieldtype is valid . C ⊢ array fieldtype : ok C ⊢ fieldtype : ok The composite type ( func t 1 ∗ → t 2 ∗ ) is valid if: The result type t 1 ∗ is valid . The result type t 2 ∗ is valid . C ⊢ func t 1 ∗ → t 2 ∗ : ok C ⊢ t 1 ∗ : ok C ⊢ t 2 ∗ : ok The field type ( mut ? storagetype ) is valid if: The storage type storagetype is valid . C ⊢ mut ? storagetype : ok C ⊢ storagetype : ok The packed type packtype is always valid . C ⊢ packtype : ok 3.2.11. Recursive Types Recursive types are validated with respect to the first type index defined by the recursive group. The recursive type ( rec subtype ∗ ) is valid for the type index x 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 x . The recursive type ( rec subtype ′ ∗ ) is valid for the type index x + 1 . C ⊢ rec ϵ : ok ( x ) C ⊢ rec ( subtype 1 subtype ∗ ) : ok ( x ) C ⊢ subtype 1 : ok ( x ) C ⊢ rec subtype ∗ : ok ( x + 1 ) The sub type ( sub final ? x ∗ comptype ) is valid for the type index x 0 if: The length of x ∗ is less than or equal to 1 . For all x in x ∗ : The index x is less than x 0 . The type C . types [ x ] exists. The sub type unroll ( C . types [ x ]) is of the form ( sub y ∗ comptype ′ ) . comptype ′ ∗ is the concatenation of all such comptype ′ . The composite type comptype is valid . For all comptype ′ in comptype ′ ∗ : The composite type comptype matches the composite type comptype ′ . C ⊢ sub final ? x ∗ comptype : ok ( x 0 ) ∣ x ∗ ∣ ≤ 1 ( x < x 0 ) ∗ ( unroll ( C . types [ x ]) = sub y ∗ comptype ′ ) ∗ C ⊢ comptype : ok ( C ⊢ comptype ≤ comptype ′ ) ∗ 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 [ n .. m ? ] is valid within k if: n is less than or equal to k . If m is defined, then: n is less than or equal to m . m is less than or equal to k . C ⊢ [ n .. m ? ] : k n ≤ k ( n ≤ m ≤ k ) ? 3.2.13. Tag Types The tag type typeuse is valid if: The type use typeuse is valid . The expansion of C is ( func t 1 ∗ → ) . C ⊢ typeuse : ok C ⊢ typeuse : ok typeuse ≈ C func t 1 ∗ → [ ] 3.2.14. Global Types The global type ( mut ? t ) is valid if: The value type t is valid . C ⊢ mut ? t : ok C ⊢ t : ok 3.2.15. Memory Types The memory type ( addrtype limits page ) is valid if: The limits range limits is valid within 2 ∣ addrtype ∣ − 16 . C ⊢ addrtype limits page : ok C ⊢ limits : 2 ∣ addrtype ∣ − 16 3.2.16. Table Types The table type ( addrtype limits reftype ) is valid if: The limits range limits is valid within 2 ∣ addrtype ∣ − 1 . The reference type reftype is valid . C ⊢ addrtype limits reftype : ok C ⊢ limits : 2 ∣ addrtype ∣ − 1 C ⊢ reftype : ok 3.2.17. External Types The external type ( tag tagtype ) is valid if: The tag type tagtype is valid . C ⊢ tag tagtype : ok C ⊢ tagtype : ok The external type ( global globaltype ) is valid if: The global type globaltype is valid . C ⊢ global globaltype : ok C ⊢ globaltype : ok The external type ( mem memtype ) is valid if: The memory type memtype is valid . C ⊢ mem memtype : ok C ⊢ memtype : ok The external type ( table tabletype ) is valid if: The table type tabletype is valid . C ⊢ table tabletype : ok C ⊢ tabletype : ok The external type ( func typeuse ) is valid if: The type use typeuse is valid . The expansion of C is ( func t 1 ∗ → t 2 ∗ ) . C ⊢ func typeuse : ok C ⊢ typeuse : ok typeuse ≈ C func t 1 ∗ → t 2 ∗ 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. 3.3.1. Number Types The number type numtype matches only itself. C ⊢ numtype ≤ numtype 3.3.2. Vector Types The vector type vectype matches only itself. C ⊢ vectype ≤ vectype 3.3.3. Heap Types The heap type heaptype 1 matches the heap type heaptype 2 if: Either: The heap type heaptype 2 is of the form heaptype 1 . Or: The heap type heaptype ′ is valid . The heap type heaptype 1 matches the heap type heaptype ′ . The heap type heaptype ′ matches the heap type heaptype 2 . Or: The heap type heaptype 1 is of the form eq . The heap type heaptype 2 is of the form any . Or: The heap type heaptype 1 is of the form i 31 . The heap type heaptype 2 is of the form eq . Or: The heap type heaptype 1 is of the form struct . The heap type heaptype 2 is of the form eq . Or: The heap type heaptype 1 is of the form array . The heap type heaptype 2 is of the form eq . Or: The heap type heaptype 1 is of the form deftype . The heap type heaptype 2 is of the form struct . The expansion of deftype is ( struct fieldtype ∗ ) . Or: The heap type heaptype 1 is of the form deftype . The heap type heaptype 2 is of the form array . The expansion of deftype is ( array fieldtype ) . Or: The heap type heaptype 1 is of the form deftype . The heap type heaptype 2 is of the form func . The expansion of deftype is ( func t 1 ∗ → t 2 ∗ ) . Or: The heap type heaptype 1 is of the form deftype 1 . The heap type heaptype 2 is of the form deftype 2 . The defined type deftype 1 matches the defined type deftype 2 . Or: The heap type heaptype 1 is of the form typeidx . The type C . types [ typeidx ] exists. The type C . types [ typeidx ] matches the heap type heaptype 2 . Or: The heap type heaptype 2 is of the form typeidx . The type C . types [ typeidx ] exists. The heap type heaptype 1 matches the type C . types [ typeidx ] . Or: The heap type heaptype 1 is of the form ( rec . i ) . The heap type heaptype 2 is of the form struct . The recursive type C . recs [ i ] exists. The recursive type C . recs [ i ] is of the form ( sub final ? ( struct fieldtype ∗ )) . Or: The heap type heaptype 1 is of the form ( rec . i ) . The heap type heaptype 2 is of the form array . The recursive type C . recs [ i ] exists. The recursive type C . recs [ i ] is of the form ( sub final ? ( array fieldtype )) . Or: The heap type heaptype 1 is of the form ( rec . i ) . The heap type heaptype 2 is of the form func . The recursive type C . recs [ i ] exists. The recursive type C . recs [ i ] is of the form ( sub final ? ( func t 1 ∗ → t 2 ∗ )) . Or: The heap type heaptype 1 is of the form ( rec . i ) . The length of typeuse ∗ is greater than j . The heap type heaptype 2 is of the form typeuse ∗ [ j ] . The recursive type C . recs [ i ] exists. The recursive type C . recs [ i ] is of the form ( sub final ? typeuse ∗ ct ) . Or: The heap type heaptype 1 is of the form none . The heap type heaptype 2 matches the heap type any . The heap type heaptype 2 is not of the form bot . Or: The heap type heaptype 1 is of the form nofunc . The heap type heaptype 2 matches the heap type func . The heap type heaptype 2 is not of the form bot . Or: The heap type heaptype 1 is of the form noexn . The heap type heaptype 2 matches the heap type exn . The heap type heaptype 2 is not of the form bot . Or: The heap type heaptype 1 is of the form noextern . The heap type heaptype 2 matches the heap type extern . The heap type heaptype 2 is not of the form bot . Or: The heap type heaptype 1 is of the form bot . C ⊢ heaptype ≤ heaptype C ⊢ heaptype 1 ≤ heaptype 2 C ⊢ heaptype ′ : ok C ⊢ heaptype 1 ≤ heaptype ′ C ⊢ heaptype ′ ≤ heaptype 2 C ⊢ eq ≤ any C ⊢ i 31 ≤ eq C ⊢ struct ≤ eq C ⊢ array ≤ eq C ⊢ deftype ≤ struct deftype ≈ struct fieldtype ∗ C ⊢ deftype ≤ array deftype ≈ array fieldtype C ⊢ deftype ≤ func deftype ≈ func t 1 ∗ → t 2 ∗ C ⊢ typeidx ≤ heaptype C ⊢ C . types [ typeidx ] ≤ heaptype C ⊢ heaptype ≤ typeidx C ⊢ heaptype ≤ C . types [ typeidx ] C ⊢ none ≤ heaptype C ⊢ heaptype ≤ any heaptype = bot C ⊢ nofunc ≤ heaptype C ⊢ heaptype ≤ func heaptype = bot C ⊢ noexn ≤ heaptype C ⊢ heaptype ≤ exn heaptype = bot C ⊢ noextern ≤ heaptype C ⊢ heaptype ≤ extern heaptype = bot C ⊢ bot ≤ heaptype 3.3.4. Reference Types The reference type ( ref null 1 ? ht 1 ) matches the reference type ( ref null 2 ? ht 2 ) if: The heap type ht 1 matches the heap type ht 2 . Either: null 1 ? is absent. null 2 ? is absent. Or: null 1 ? is of the form null ? . null 2 ? is of the form null . C ⊢ ref ht 1 ≤ ref ht 2 C ⊢ ht 1 ≤ ht 2 C ⊢ ref null ? ht 1 ≤ ref null ht 2 C ⊢ ht 1 ≤ ht 2 3.3.5. Value Types The value type valtype 1 matches the value type valtype 2 if: Either: The value type valtype 1 is of the form numtype 1 . The value type valtype 2 is of the form numtype 2 . The number type numtype 1 matches the number type numtype 2 . Or: The value type valtype 1 is of the form vectype 1 . The value type valtype 2 is of the form vectype 2 . The vector type vectype 1 matches the vector type vectype 2 . Or: The value type valtype 1 is of the form reftype 1 . The value type valtype 2 is of the form reftype 2 . The reference type reftype 1 matches the reference type reftype 2 . Or: The value type valtype 1 is of the form bot . C ⊢ bot ≤ valtype 3.3.6. Result Types Subtyping is lifted to result types in a pointwise manner. The result type t 1 ∗ matches the result type t 2 ∗ if: For all t 1 in t 1 ∗ , and corresponding t 2 in t 2 ∗ : The value type t 1 matches the value type t 2 . C ⊢ t 1 ∗ ≤ t 2 ∗ ( C ⊢ t 1 ≤ t 2 ) ∗ 3.3.7. Instruction Types Subtyping is further lifted to instruction types . The instruction type t 11 ∗ → x 1 ∗ t 12 ∗ matches the instruction type t 21 ∗ → x 2 ∗ t 22 ∗ if: The result type t 21 ∗ matches the result type t 11 ∗ . The result type t 12 ∗ matches the result type t 22 ∗ . The local index sequence x ∗ is of the form x 2 ∗ ∖ x 1 ∗ . For all x in x ∗ : The local C . locals [ x ] exists. The local C . locals [ x ] is of the form ( set t ) . C ⊢ t 11 ∗ → x 1 ∗ t 12 ∗ ≤ t 21 ∗ → x 2 ∗ t 22 ∗ C ⊢ t 21 ∗ ≤ t 11 ∗ C ⊢ t 12 ∗ ≤ t 22 ∗ x ∗ = x 2 ∗ ∖ x 1 ∗ ( C . locals [ x ] = set t ) ∗ Note Instruction types are contravariant in their input and covariant in their output. Moreover, the supertype may ignore variables from the init set x 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.8. Composite Types The composite type comptype 1 matches the composite type comptype 2 if: Either: The composite type comptype 1 is of the form ( struct ft 1 ∗ ft ′ 1 ∗ ) . The composite type comptype 2 is of the form ( struct ft 2 ∗ ) . For all ft 1 in ft 1 ∗ , and corresponding ft 2 in ft 2 ∗ : The field type ft 1 matches the field type ft 2 . Or: The composite type comptype 1 is of the form ( array ft 1 ) . The composite type comptype 2 is of the form ( array ft 2 ) . The field type ft 1 matches the field type ft 2 . Or: The composite type comptype 1 is of the form ( func t 11 ∗ → t 12 ∗ ) . The composite type comptype 2 is of the form ( func t 21 ∗ → t 22 ∗ ) . The result type t 21 ∗ matches the result type t 11 ∗ . The result type t 12 ∗ matches the result type t 22 ∗ . C ⊢ struct ( ft 1 ∗ ft ′ 1 ∗ ) ≤ struct ft 2 ∗ ( C ⊢ ft 1 ≤ ft 2 ) ∗ C ⊢ array ft 1 ≤ array ft 2 C ⊢ ft 1 ≤ ft 2 C ⊢ func t 11 ∗ → t 12 ∗ ≤ func t 21 ∗ → t 22 ∗ C ⊢ t 21 ∗ ≤ t 11 ∗ C ⊢ t 12 ∗ ≤ t 22 ∗ 3.3.9. Field Types The field type ( mut 1 ? zt 1 ) matches the field type ( mut 2 ? zt 2 ) if: The storage type zt 1 matches the storage type zt 2 . Either: mut 1 ? is absent. mut 2 ? is absent. Or: mut 1 ? is of the form mut . mut 2 ? is of the form mut . The storage type zt 2 matches the storage type zt 1 . C ⊢ zt 1 ≤ zt 2 C ⊢ zt 1 ≤ zt 2 C ⊢ mut zt 1 ≤ mut zt 2 C ⊢ zt 1 ≤ zt 2 C ⊢ zt 2 ≤ zt 1 The storage type storagetype 1 matches the storage type storagetype 2 if: Either: The storage type storagetype 1 is of the form valtype 1 . The storage type storagetype 2 is of the form valtype 2 . The value type valtype 1 matches the value type valtype 2 . Or: The storage type storagetype 1 is of the form packtype 1 . The storage type storagetype 2 is of the form packtype 2 . The packed type packtype 1 matches the packed type packtype 2 . The packed type packtype matches only itself. C ⊢ packtype ≤ packtype 3.3.10. Defined Types The defined type deftype 1 matches the defined type deftype 2 if: Either: The defined type clos C ( deftype 1 ) is of the form clos C ( deftype 2 ) . Or: The sub type unroll ( deftype 1 ) is of the form ( sub final ? typeuse ∗ ct ) . The length of typeuse ∗ is greater than i . The type use typeuse ∗ [ i ] matches the heap type deftype 2 . C ⊢ deftype 1 ≤ deftype 2 clos C ( deftype 1 ) = clos C ( deftype 2 ) C ⊢ deftype 1 ≤ deftype 2 unroll ( deftype 1 ) = sub final ? typeuse ∗ ct C ⊢ typeuse ∗ [ i ] ≤ deftype 2 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 [ n 1 .. u 64 1 ? ] matches the limits range [ n 2 .. u 64 2 ? ] if: n 1 is greater than or equal to n 2 . Either: u 64 1 ? is of the form m 1 . If u 64 2 is defined, then: m 1 is less than or equal to u 64 2 . Or: u 64 1 ? is absent. u 64 2 ? is absent. C ⊢ [ n 1 .. m 1 ] ≤ [ n 2 .. m 2 ? ] n 1 ≥ n 2 ( m 1 ≤ m 2 ) ? C ⊢ [ n 1 .. ϵ ] ≤ [ n 2 .. ϵ ] n 1 ≥ n 2 3.3.12. Tag Types The tag type deftype 1 matches the tag type deftype 2 if: The defined type deftype 1 matches the defined type deftype 2 . The defined type deftype 2 matches the defined type deftype 1 . C ⊢ deftype 1 ≤ deftype 2 C ⊢ deftype 1 ≤ deftype 2 C ⊢ deftype 2 ≤ deftype 1 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.13. Global Types The global type ( mut 1 ? valtype 1 ) matches the global type ( mut 2 ? valtype 2 ) if: The value type valtype 1 matches the value type valtype 2 . Either: 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 . C ⊢ valtype 1 ≤ valtype 2 C ⊢ valtype 1 ≤ valtype 2 C ⊢ mut valtype 1 ≤ mut valtype 2 C ⊢ valtype 1 ≤ valtype 2 C ⊢ valtype 2 ≤ valtype 1 3.3.14. Memory Types The memory type ( addrtype limits 1 page ) matches the memory type ( addrtype limits 2 page ) if: The limits range limits 1 matches the limits range limits 2 . C ⊢ addrtype limits 1 page ≤ addrtype limits 2 page C ⊢ limits 1 ≤ limits 2 3.3.15. Table Types The table type ( addrtype limits 1 reftype 1 ) matches the table type ( addrtype limits 2 reftype 2 ) if: The limits range limits 1 matches the limits range limits 2 . The reference type reftype 1 matches the reference type reftype 2 . The reference type reftype 2 matches the reference type reftype 1 . C ⊢ addrtype limits 1 reftype 1 ≤ addrtype limits 2 reftype 2 C ⊢ limits 1 ≤ limits 2 C ⊢ reftype 1 ≤ reftype 2 C ⊢ reftype 2 ≤ reftype 1 3.3.16. External Types The external type ( tag tagtype 1 ) matches the external type ( tag tagtype 2 ) if: The tag type tagtype 1 matches the tag type tagtype 2 . C ⊢ tag tagtype 1 ≤ tag tagtype 2 C ⊢ tagtype 1 ≤ tagtype 2 The external type ( global globaltype 1 ) matches the external type ( global globaltype 2 ) if: The global type globaltype 1 matches the global type globaltype 2 . C ⊢ global globaltype 1 ≤ global globaltype 2 C ⊢ globaltype 1 ≤ globaltype 2 The external type ( mem memtype 1 ) matches the external type ( mem memtype 2 ) if: The memory type memtype 1 matches the memory type memtype 2 . C ⊢ mem memtype 1 ≤ mem memtype 2 C ⊢ memtype 1 ≤ memtype 2 The external type ( table tabletype 1 ) matches the external type ( table tabletype 2 ) if: The table type tabletype 1 matches the table type tabletype 2 . C ⊢ table tabletype 1 ≤ table tabletype 2 C ⊢ tabletype 1 ≤ tabletype 2 The external type ( func deftype 1 ) matches the external type ( func deftype 2 ) if: The defined type deftype 1 matches the defined type deftype 2 . C ⊢ func deftype 1 ≤ func deftype 2 C ⊢ deftype 1 ≤ deftype 2 3.4. Instructions Instructions are classified by instruction types that describe how they manipulate the operand stack and initialize locals : A type t 1 ∗ → x ∗ t 2 ∗ describes the required input stack with argument values of types t 1 ∗ that an instruction pops off and the provided output stack with result values of types t 2 ∗ that it pushes back. Moreover, it enumerates the indices x ∗ of locals that have been set by the instruction. In most cases, this is empty. Note For example, the instruction i 32 . add has type i 32 i 32 → i 32 , consuming two i 32 values and producing one. The instruction ( local . set x ) has type t → x ϵ , provided t is the type declared for the local x . Typing extends to instruction sequences instr ∗ . Such a sequence has an instruction type t 1 ∗ → x ∗ t 2 ∗ if the accumulative effect of executing the instructions is consuming values of types t 1 ∗ off the operand stack, pushing new values of types t 2 ∗ , and setting all locals x ∗ . 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 t 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 t 1 ∗ → t 2 ∗ of the instruction is unconstrained. 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 are valid in the current context and meet the constraints imposed for the surrounding parts of the program. Note For example, the select instruction is valid with type t t i 32 → t , for any possible number type t . Consequently, both instruction sequences ( i 32 . const 1 ) ( i 32 . const 2 ) ( i 32 . const 3 ) ( select ) and ( f 64 . const + 1 ) ( f 64 . const + 2 ) ( i 32 . const 3 ) ( select ) are valid, with t in the typing of select being instantiated to i 32 or f 64 , respectively. The unreachable instruction is stack-polymorphic, and hence valid with type t 1 ∗ → t 2 ∗ for any possible sequences of value types t 1 ∗ and t 2 ∗ . Consequently, ( unreachable ) ( i 32 . add ) is valid by assuming type ϵ → i 32 for the unreachable instruction. In contrast, ( unreachable ) ( i 64 . const 0 ) ( i 32 . add ) is invalid, because there is no possible type to pick for the unreachable instruction that would make the sequence well-typed. The Appendix describes a type checking algorithm that efficiently implements validation of instruction sequences as prescribed by the rules given here. 3.4.1. Parametric Instructions 3.4.1.1. nop The instruction nop is valid with the instruction type ϵ → ϵ . C ⊢ nop : ϵ → ϵ 3.4.1.2. unreachable The instruction unreachable is valid with the instruction type t 1 ∗ → t 2 ∗ if: The instruction type t 1 ∗ → t 2 ∗ is valid . C ⊢ unreachable : t 1 ∗ → t 2 ∗ C ⊢ t 1 ∗ → t 2 ∗ : ok Note The unreachable instruction is stack-polymorphic . 3.4.1.3. drop The instruction drop is valid with the instruction type t → ϵ if: The value type t is valid . C ⊢ drop : t → ϵ C ⊢ t : ok Note Both drop and select without annotation are value-polymorphic instructions. 3.4.1.4. select ( t ∗ ) ? The instruction ( select valtype ? ) is valid with the instruction type t t i 32 → t if: The value type t is valid . Either: The value type sequence valtype ? is of the form t . Or: The value type sequence valtype ? is absent. The value type t matches the value type t ′ . The value type t ′ is of the form numtype or t ′ is of the form vectype . C ⊢ select t : t t i 32 → t C ⊢ t : ok C ⊢ select : t t i 32 → t C ⊢ t : ok C ⊢ t ≤ t ′ t ′ = numtype ∨ t ′ = vectype Note In future versions of WebAssembly, select may allow more than one value per choice. 3.4.2. Control Instructions 3.4.2.1. block blocktype instr ∗ The instruction ( block bt instr ∗ ) is valid with the instruction type t 1 ∗ → t 2 ∗ if: The block type bt is valid as the instruction type t 1 ∗ → t 2 ∗ . Let C ′ be the same context as C , but with the result type sequence t 2 ∗ prepended to the field labels . Under the context C ′ , the instruction sequence instr ∗ is valid with the instruction type t 1 ∗ → x ∗ t 2 ∗ . C ⊢ block bt instr ∗ : t 1 ∗ → t 2 ∗ C ⊢ bt : t 1 ∗ → t 2 ∗ { labels ( t 2 ∗ )} ⊕ C ⊢ instr ∗ : t 1 ∗ → x ∗ t 2 ∗ Note The notation { labels ( t ∗ )} ⊕ C inserts the new label type at index 0 , shifting all others. The same applies to all other block instructions. 3.4.2.2. loop blocktype instr ∗ The instruction ( loop bt instr ∗ ) is valid with the instruction type t 1 ∗ → t 2 ∗ if: The block type bt is valid as the instruction type t 1 ∗ → t 2 ∗ . Let C ′ be the same context as C , but with the result type sequence t 1 ∗ prepended to the field labels . Under the context C ′ , the instruction sequence instr ∗ is valid with the instruction type t 1 ∗ → x ∗ t 2 ∗ . C ⊢ loop bt instr ∗ : t 1 ∗ → t 2 ∗ C ⊢ bt : t 1 ∗ → t 2 ∗ { labels ( t 1 ∗ )} ⊕ C ⊢ instr ∗ : t 1 ∗ → x ∗ t 2 ∗ 3.4.2.3. if blocktype instr 1 ∗ else instr 2 ∗ The instruction ( if bt instr 1 ∗ else instr 2 ∗ ) is valid with the instruction type t 1 ∗ i 32 → t 2 ∗ if: The block type bt is valid as the instruction type t 1 ∗ → t 2 ∗ . Let C ′ be the same context as C , but with the result type sequence t 2 ∗ prepended to the field labels . Under the context C ′ , the instruction sequence instr 1 ∗ is valid with the instruction type t 1 ∗ → x 1 ∗ t 2 ∗ . Under the context C ′ , the instruction sequence instr 2 ∗ is valid with the instruction type t 1 ∗ → x 2 ∗ t 2 ∗ . C ⊢ if bt instr 1 ∗ else instr 2 ∗ : t 1 ∗ i 32 → t 2 ∗ C ⊢ bt : t 1 ∗ → t 2 ∗ { labels ( t 2 ∗ )} ⊕ C ⊢ instr 1 ∗ : t 1 ∗ → x 1 ∗ t 2 ∗ { labels ( t 2 ∗ )} ⊕ C ⊢ instr 2 ∗ : t 1 ∗ → x 2 ∗ t 2 ∗ 3.4.2.4. br l The instruction ( br l ) is valid with the instruction type t 1 ∗ t ∗ → t 2 ∗ if: The label C . labels [ l ] exists. The label C . labels [ l ] is of the form t ∗ . The instruction type t 1 ∗ → t 2 ∗ is valid . C ⊢ br l : t 1 ∗ t ∗ → t 2 ∗ C . labels [ l ] = t ∗ C ⊢ t 1 ∗ → t 2 ∗ : ok Note The label index space in the context C contains the most recent label first, so that C . labels [ l ] performs a relative lookup as expected. This applies to other branch instructions as well. The br instruction is stack-polymorphic . 3.4.2.5. br_if l The instruction ( br_if l ) is valid with the instruction type t ∗ i 32 → t ∗ if: The label C . labels [ l ] exists. The label C . labels [ l ] is of the form t ∗ . C ⊢ br_if l : t ∗ i 32 → t ∗ C . labels [ l ] = t ∗ 3.4.2.6. br_table l ∗ l N The instruction ( br_table l ∗ l ′ ) is valid with the instruction type t 1 ∗ t ∗ i 32 → t 2 ∗ if: For all l in l ∗ : The label C . labels [ l ] exists. The result type t ∗ matches the label C . labels [ l ] . The label C . labels [ l ′ ] exists. The result type t ∗ matches the label C . labels [ l ′ ] . The instruction type t 1 ∗ t ∗ i 32 → t 2 ∗ is valid . C ⊢ br_table l ∗ l ′ : t 1 ∗ t ∗ i 32 → t 2 ∗ ( C ⊢ t ∗ ≤ C . labels [ l ] ) ∗ C ⊢ t ∗ ≤ C . labels [ l ′ ] C ⊢ t 1 ∗ t ∗ i 32 → t 2 ∗ : ok Note The br_table instruction is stack-polymorphic . Furthermore, the result type t ∗ is also chosen non-deterministically in this rule. Although it may seem necessary to compute t ∗ as the greatest lower bound of all label types in practice, a simple sequential algorithm does not require this. 3.4.2.7. br_on_null l The instruction ( br_on_null l ) is valid with the instruction type t ∗ ( ref null ht ) → t ∗ ( ref ht ) if: The label C . labels [ l ] exists. The label C . labels [ l ] is of the form t ∗ . The heap type ht is valid . C ⊢ br_on_null l : t ∗ ( ref null ht ) → t ∗ ( ref ht ) C . labels [ l ] = t ∗ C ⊢ ht : ok 3.4.2.8. br_on_non_null l The instruction ( br_on_non_null l ) is valid with the instruction type t ∗ ( ref null ht ) → t ∗ if: The label C . labels [ l ] exists. The label C . labels [ l ] is of the form t ∗ ( ref null ? ht ) . C ⊢ br_on_non_null l : t ∗ ( ref null ht ) → t ∗ C . labels [ l ] = t ∗ ( ref null ? ht ) 3.4.2.9. br_on_cast l rt 1 rt 2 The instruction ( br_on_cast l rt 1 rt 2 ) is valid with the instruction type t ∗ rt 1 → t ∗ reftype if: The label C . labels [ l ] exists. The label C . labels [ l ] is of the form t ∗ 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 . C ⊢ br_on_cast l rt 1 rt 2 : t ∗ rt 1 → t ∗ ( rt 1 ∖ rt 2 ) C . labels [ l ] = t ∗ rt C ⊢ rt 1 : ok C ⊢ rt 2 : ok C ⊢ rt 2 ≤ rt 1 C ⊢ rt 2 ≤ rt 3.4.2.10. br_on_cast_fail l rt 1 rt 2 The instruction ( br_on_cast_fail l rt 1 rt 2 ) is valid with the instruction type t ∗ rt 1 → t ∗ rt 2 if: The label C . labels [ l ] exists. The label C . labels [ l ] is of the form t ∗ 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 . C ⊢ br_on_cast_fail l rt 1 rt 2 : t ∗ rt 1 → t ∗ rt 2 C . labels [ l ] = t ∗ rt C ⊢ rt 1 : ok C ⊢ rt 2 : ok C ⊢ rt 2 ≤ rt 1 C ⊢ rt 1 ∖ rt 2 ≤ rt 3.4.2.11. call x The instruction ( call x ) is valid with the instruction type t 1 ∗ → t 2 ∗ if: The function C . funcs [ x ] exists. The expansion of C . funcs [ x ] is ( func t 1 ∗ → t 2 ∗ ) . C ⊢ call x : t 1 ∗ → t 2 ∗ C . funcs [ x ] ≈ func t 1 ∗ → t 2 ∗ 3.4.2.12. call_ref x The instruction ( call_ref x ) is valid with the instruction type t 1 ∗ ( ref null x ) → t 2 ∗ if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( func t 1 ∗ → t 2 ∗ ) . C ⊢ call_ref x : t 1 ∗ ( ref null x ) → t 2 ∗ C . types [ x ] ≈ func t 1 ∗ → t 2 ∗ 3.4.2.13. call_indirect x y The instruction ( call_indirect x y ) is valid with the instruction type t 1 ∗ at → t 2 ∗ if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ) . The reference type rt matches the reference type ( ref null func ) . The type C . types [ y ] exists. The expansion of C . types [ y ] is ( func t 1 ∗ → t 2 ∗ ) . C ⊢ call_indirect x y : t 1 ∗ at → t 2 ∗ C . tables [ x ] = at lim rt C ⊢ rt ≤ ( ref null func ) C . types [ y ] ≈ func t 1 ∗ → t 2 ∗ 3.4.2.14. return The instruction return is valid with the instruction type t 1 ∗ t ∗ → t 2 ∗ if: The result type C . return is of the form t ∗ . The instruction type t 1 ∗ → t 2 ∗ is valid . C ⊢ return : t 1 ∗ t ∗ → t 2 ∗ C . return = ( t ∗ ) C ⊢ t 1 ∗ → t 2 ∗ : ok Note The return instruction is stack-polymorphic . C . return is absent (set to ϵ ) when validating an expression that is not a function body. This differs from it being set to the empty result type [ ϵ ] , which is the case for functions not returning anything. 3.4.2.15. return_call x The instruction ( return_call x ) is valid with the instruction type t 3 ∗ t 1 ∗ → t 4 ∗ if: The function C . funcs [ x ] exists. The expansion of C . funcs [ x ] is ( func t 1 ∗ → t 2 ∗ ) . The result type C . return is of the form t ′ 2 ∗ . The result type t 2 ∗ matches the result type t ′ 2 ∗ . The instruction type t 3 ∗ → t 4 ∗ is valid . C ⊢ return_call x : t 3 ∗ t 1 ∗ → t 4 ∗ C . funcs [ x ] ≈ func t 1 ∗ → t 2 ∗ C . return = ( t ′ 2 ∗ ) C ⊢ t 2 ∗ ≤ t ′ 2 ∗ C ⊢ t 3 ∗ → t 4 ∗ : ok Note The return_call instruction is stack-polymorphic . 3.4.2.16. return_call_ref x The instruction ( return_call_ref x ) is valid with the instruction type t 3 ∗ t 1 ∗ ( ref null x ) → t 4 ∗ if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( func t 1 ∗ → t 2 ∗ ) . The result type C . return is of the form t ′ 2 ∗ . The result type t 2 ∗ matches the result type t ′ 2 ∗ . The instruction type t 3 ∗ → t 4 ∗ is valid . C ⊢ return_call_ref x : t 3 ∗ t 1 ∗ ( ref null x ) → t 4 ∗ C . types [ x ] ≈ func t 1 ∗ → t 2 ∗ C . return = ( t ′ 2 ∗ ) C ⊢ t 2 ∗ ≤ t ′ 2 ∗ C ⊢ t 3 ∗ → t 4 ∗ : ok Note The return_call_ref instruction is stack-polymorphic . 3.4.2.17. return_call_indirect x y The instruction ( return_call_indirect x y ) is valid with the instruction type t 3 ∗ t 1 ∗ at → t 4 ∗ if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ) . The reference type rt matches the reference type ( ref null func ) . The type C . types [ y ] exists. The expansion of C . types [ y ] is ( func t 1 ∗ → t 2 ∗ ) . The result type C . return is of the form t ′ 2 ∗ . The result type t 2 ∗ matches the result type t ′ 2 ∗ . The instruction type t 3 ∗ → t 4 ∗ is valid . C ⊢ return_call_indirect x y : t 3 ∗ t 1 ∗ at → t 4 ∗ C . tables [ x ] = at lim rt C ⊢ rt ≤ ( ref null func ) C . types [ y ] ≈ func t 1 ∗ → t 2 ∗ C . return = ( t ′ 2 ∗ ) C ⊢ t 2 ∗ ≤ t ′ 2 ∗ C ⊢ t 3 ∗ → t 4 ∗ : ok Note The return_call_indirect instruction is stack-polymorphic . 3.4.2.18. throw x The instruction ( throw x ) is valid with the instruction type t 1 ∗ t ∗ → t 2 ∗ if: The tag C . tags [ x ] exists. The expansion of C . tags [ x ] is ( func t ∗ → ) . The instruction type t 1 ∗ → t 2 ∗ is valid . C ⊢ throw x : t 1 ∗ t ∗ → t 2 ∗ C . tags [ x ] ≈ func t ∗ → ϵ C ⊢ t 1 ∗ → t 2 ∗ : ok Note The throw instruction is stack-polymorphic . 3.4.2.19. throw_ref The instruction throw_ref is valid with the instruction type t 1 ∗ ( ref null exn ) → t 2 ∗ if: The instruction type t 1 ∗ → t 2 ∗ is valid . C ⊢ throw_ref : t 1 ∗ ( ref null exn ) → t 2 ∗ C ⊢ t 1 ∗ → t 2 ∗ : ok Note The throw_ref instruction is stack-polymorphic . 3.4.2.20. try_table blocktype catch ∗ instr ∗ The instruction ( try_table bt catch ∗ instr ∗ ) is valid with the instruction type t 1 ∗ → t 2 ∗ if: The block type bt is valid as the instruction type t 1 ∗ → t 2 ∗ . Let C ′ be the same context as C , but with the result type sequence t 2 ∗ prepended to the field labels . Under the context C ′ , the instruction sequence instr ∗ is valid with the instruction type t 1 ∗ → x ∗ t 2 ∗ . For all catch in catch ∗ : The catch clause catch is valid . C ⊢ try_table bt catch ∗ instr ∗ : t 1 ∗ → t 2 ∗ C ⊢ bt : t 1 ∗ → t 2 ∗ { labels ( t 2 ∗ )} ⊕ C ⊢ instr ∗ : t 1 ∗ → x ∗ t 2 ∗ ( C ⊢ catch : ok ) ∗ 3.4.2.21. catch x l The catch clause ( catch x l ) is valid if: The tag C . tags [ x ] exists. The expansion of C . tags [ x ] is ( func t ∗ → ) . The label C . labels [ l ] exists. The result type t ∗ matches the label C . labels [ l ] . C ⊢ catch x l : ok C . tags [ x ] ≈ func t ∗ → ϵ C ⊢ t ∗ ≤ C . labels [ l ] 3.4.2.22. catch_ref x l The catch clause ( catch_ref x l ) is valid if: The tag C . tags [ x ] exists. The expansion of C . tags [ x ] is ( func t ∗ → ) . The label C . labels [ l ] exists. The result type t ∗ ( ref exn ) matches the label C . labels [ l ] . C ⊢ catch_ref x l : ok C . tags [ x ] ≈ func t ∗ → ϵ C ⊢ t ∗ ( ref exn ) ≤ C . labels [ l ] 3.4.2.23. catch_all l The catch clause ( catch_all l ) is valid if: The label C . labels [ l ] exists. The result type ϵ matches the label C . labels [ l ] . C ⊢ catch_all l : ok C ⊢ ϵ ≤ C . labels [ l ] 3.4.2.24. catch_all_ref l The catch clause ( catch_all_ref l ) is valid if: The label C . labels [ l ] exists. The result type ( ref exn ) matches the label C . labels [ l ] . C ⊢ catch_all_ref l : ok C ⊢ ( ref exn ) ≤ C . labels [ l ] 3.4.3. Variable Instructions 3.4.3.1. local . get x The instruction ( local . get x ) is valid with the instruction type ϵ → t if: The local C . locals [ x ] exists. The local C . locals [ x ] is of the form ( set t ) . C ⊢ local . get x : ϵ → t C . locals [ x ] = set t 3.4.3.2. local . set x The instruction ( local . set x ) is valid with the instruction type t → x ϵ if: The local C . locals [ x ] exists. The local C . locals [ x ] is of the form ( init t ) . C ⊢ local . set x : t → x ϵ C . locals [ x ] = init t 3.4.3.3. local . tee x The instruction ( local . tee x ) is valid with the instruction type t → x t if: The local C . locals [ x ] exists. The local C . locals [ x ] is of the form ( init t ) . C ⊢ local . tee x : t → x t C . locals [ x ] = init t 3.4.3.4. global . get x The instruction ( global . get x ) is valid with the instruction type ϵ → t if: The global C . globals [ x ] exists. The global C . globals [ x ] is of the form ( mut ? t ) . C ⊢ global . get x : ϵ → t C . globals [ x ] = mut ? t 3.4.3.5. global . set x The instruction ( global . set x ) is valid with the instruction type t → ϵ if: The global C . globals [ x ] exists. The global C . globals [ x ] is of the form ( mut t ) . C ⊢ global . set x : t → ϵ C . globals [ x ] = mut t 3.4.4. Table Instructions 3.4.4.1. table . get x The instruction ( table . get x ) is valid with the instruction type at → rt if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ) . C ⊢ table . get x : at → rt C . tables [ x ] = at lim rt 3.4.4.2. table . set x The instruction ( table . set x ) is valid with the instruction type at rt → ϵ if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ) . C ⊢ table . set x : at rt → ϵ C . tables [ x ] = at lim rt 3.4.4.3. table . size x The instruction ( table . size x ) is valid with the instruction type ϵ → at if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ) . C ⊢ table . size x : ϵ → at C . tables [ x ] = at lim rt 3.4.4.4. table . grow x The instruction ( table . grow x ) is valid with the instruction type rt at → at if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ) . C ⊢ table . grow x : rt at → at C . tables [ x ] = at lim rt 3.4.4.5. table . fill x The instruction ( table . fill x ) is valid with the instruction type at rt at → ϵ if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ) . C ⊢ table . fill x : at rt at → ϵ C . tables [ x ] = at lim rt 3.4.4.6. table . copy x y The instruction ( table . copy x 1 x 2 ) is valid with the instruction type at 1 at 2 addrtype → ϵ if: The table C . tables [ x 1 ] exists. The table C . tables [ x 1 ] is of the form ( at 1 lim 1 rt 1 ) . The table C . tables [ x 2 ] exists. The table C . tables [ x 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 ) . C ⊢ table . copy x 1 x 2 : at 1 at 2 min ( at 1 , at 2 ) → ϵ C . tables [ x 1 ] = at 1 lim 1 rt 1 C . tables [ x 2 ] = at 2 lim 2 rt 2 C ⊢ rt 2 ≤ rt 1 3.4.4.7. table . init x y The instruction ( table . init x y ) is valid with the instruction type at i 32 i 32 → ϵ if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt 1 ) . The element segment C . elems [ y ] exists. The element segment C . elems [ y ] is of the form rt 2 . The reference type rt 2 matches the reference type rt 1 . C ⊢ table . init x y : at i 32 i 32 → ϵ C . tables [ x ] = at lim rt 1 C . elems [ y ] = rt 2 C ⊢ rt 2 ≤ rt 1 3.4.4.8. elem . drop x The instruction ( elem . drop x ) is valid with the instruction type ϵ → ϵ if: The element segment C . elems [ x ] exists. C ⊢ elem . drop x : ϵ → ϵ C . elems [ x ] = rt 3.4.5. Memory Instructions Memory instructions use memory arguments , which are classified by the address type and the and bit width of the access they are suitable for. 3.4.5.1. memarg { align n , offset m } is valid for at and N if: 2 n is less than or equal to N/8 . m is less than 2 ∣ at ∣ . ⊢ { align n , offset m } : at → N 2 n ≤ N /8 m < 2 ∣ at ∣ 3.4.5.2. t . load x memarg The instruction ( nt . load x memarg ) is valid with the instruction type at → nt if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and ∣ nt ∣ . C ⊢ nt . load x memarg : at → nt C . mems [ x ] = at lim page ⊢ memarg : at → ∣ nt ∣ 3.4.5.3. t . load N _ sx x memarg The instruction ( i N . load K _ sx x memarg ) is valid with the instruction type at → i N if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and K . C ⊢ i N . load K _ sx x memarg : at → i N C . mems [ x ] = at lim page ⊢ memarg : at → K 3.4.5.4. t . store x memarg The instruction ( nt . store x memarg ) is valid with the instruction type at nt → ϵ if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and ∣ nt ∣ . C ⊢ nt . store x memarg : at nt → ϵ C . mems [ x ] = at lim page ⊢ memarg : at → ∣ nt ∣ 3.4.5.5. t . store N x memarg The instruction ( i N . store K x memarg ) is valid with the instruction type at i N → ϵ if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and K . C ⊢ i N . store K x memarg : at i N → ϵ C . mems [ x ] = at lim page ⊢ memarg : at → K 3.4.5.6. v128. load x memarg The instruction ( v 128 . load x memarg ) is valid with the instruction type at → v 128 if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and ∣ v 128 ∣ . C ⊢ v 128 . load x memarg : at → v 128 C . mems [ x ] = at lim page ⊢ memarg : at → ∣ v 128 ∣ 3.4.5.7. v128. load N x M_ sx x memarg The instruction ( v 128 . load N x M _ sx x memarg ) is valid with the instruction type at → v 128 if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and N ⋅ M . C ⊢ v 128 . load N x M _ sx x memarg : at → v 128 C . mems [ x ] = at lim page ⊢ memarg : at → N ⋅ M 3.4.5.8. v128. load N _splat x memarg The instruction ( v 128 . load N _ splat x memarg ) is valid with the instruction type at → v 128 if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and N . C ⊢ v 128 . load N _ splat x memarg : at → v 128 C . mems [ x ] = at lim page ⊢ memarg : at → N 3.4.5.9. v128. load N _zero x memarg The instruction ( v 128 . load N _ zero x memarg ) is valid with the instruction type at → v 128 if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and N . C ⊢ v 128 . load N _ zero x memarg : at → v 128 C . mems [ x ] = at lim page ⊢ memarg : at → N 3.4.5.10. v128. load N _lane x memarg laneidx The instruction ( v 128 . load N _ lane x memarg i ) is valid with the instruction type at v 128 → v 128 if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and N . i is less than 128/N . C ⊢ v 128 . load N _ lane x memarg i : at v 128 → v 128 C . mems [ x ] = at lim page ⊢ memarg : at → N i < 128/ N 3.4.5.11. v128. store x memarg The instruction ( v 128 . store x memarg ) is valid with the instruction type at v 128 → ϵ if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and ∣ v 128 ∣ . C ⊢ v 128 . store x memarg : at v 128 → ϵ C . mems [ x ] = at lim page ⊢ memarg : at → ∣ v 128 ∣ 3.4.5.12. v128. store N _lane x memarg laneidx The instruction ( v 128 . store N _ lane x memarg i ) is valid with the instruction type at v 128 → ϵ if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . memarg is valid for at and N . i is less than 128/N . C ⊢ v 128 . store N _ lane x memarg i : at v 128 → ϵ C . mems [ x ] = at lim page ⊢ memarg : at → N i < 128/ N 3.4.5.13. memory . size x The instruction ( memory . size x ) is valid with the instruction type ϵ → at if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . C ⊢ memory . size x : ϵ → at C . mems [ x ] = at lim page 3.4.5.14. memory . grow x The instruction ( memory . grow x ) is valid with the instruction type at → at if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . C ⊢ memory . grow x : at → at C . mems [ x ] = at lim page 3.4.5.15. memory . fill x The instruction ( memory . fill x ) is valid with the instruction type at i 32 at → ϵ if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . C ⊢ memory . fill x : at i 32 at → ϵ C . mems [ x ] = at lim page 3.4.5.16. memory . copy x y The instruction ( memory . copy x 1 x 2 ) is valid with the instruction type at 1 at 2 addrtype → ϵ if: The memory C . mems [ x 1 ] exists. The memory C . mems [ x 1 ] is of the form ( at 1 lim 1 page ) . The memory C . mems [ x 2 ] exists. The memory C . mems [ x 2 ] is of the form ( at 2 lim 2 page ) . The address type addrtype is min ( at 1 , at 2 ) . C ⊢ memory . copy x 1 x 2 : at 1 at 2 min ( at 1 , at 2 ) → ϵ C . mems [ x 1 ] = at 1 lim 1 page C . mems [ x 2 ] = at 2 lim 2 page 3.4.5.17. memory . init x y The instruction ( memory . init x y ) is valid with the instruction type at i 32 i 32 → ϵ if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . The data segment C . datas [ y ] exists. The data segment C . datas [ y ] is of the form ok . C ⊢ memory . init x y : at i 32 i 32 → ϵ C . mems [ x ] = at lim page C . datas [ y ] = ok 3.4.5.18. data . drop x The instruction ( data . drop x ) is valid with the instruction type ϵ → ϵ if: The data segment C . datas [ x ] exists. The data segment C . datas [ x ] is of the form ok . C ⊢ data . drop x : ϵ → ϵ C . datas [ x ] = ok 3.4.6. Reference Instructions 3.4.6.1. ref . null ht The instruction ( ref . null ht ) is valid with the instruction type ϵ → ( ref null ht ) if: The heap type ht is valid . C ⊢ ref . null ht : ϵ → ( ref null ht ) C ⊢ ht : ok 3.4.6.2. ref . func x The instruction ( ref . func x ) is valid with the instruction type ϵ → ( ref dt ) if: The function C . funcs [ x ] exists. The function C . funcs [ x ] is of the form dt . x is contained in C . refs . C ⊢ ref . func x : ϵ → ( ref dt ) C . funcs [ x ] = dt x ∈ C . refs 3.4.6.3. ref . is_null The instruction ref . is_null is valid with the instruction type ( ref null ht ) → i 32 if: The heap type ht is valid . C ⊢ ref . is_null : ( ref null ht ) → i 32 C ⊢ ht : ok 3.4.6.4. 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 . C ⊢ ref . as_non_null : ( ref null ht ) → ( ref ht ) C ⊢ ht : ok 3.4.6.5. ref . eq The instruction ref . eq is valid with the instruction type ( ref null eq ) ( ref null eq ) → i 32 . C ⊢ ref . eq : ( ref null eq ) ( ref null eq ) → i 32 3.4.6.6. ref . test rt The instruction ( ref . test rt ) is valid with the instruction type rt ′ → i 32 if: The reference type rt is valid . The reference type rt ′ is valid . The reference type rt matches the reference type rt ′ . C ⊢ ref . test rt : rt ′ → i 32 C ⊢ rt : ok C ⊢ rt ′ : ok C ⊢ 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. 3.4.6.7. ref . cast rt The instruction ( ref . cast 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 ′ . C ⊢ ref . cast rt : rt ′ → rt C ⊢ rt : ok C ⊢ rt ′ : ok C ⊢ 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. 3.4.7. Aggregate Reference Instructions 3.4.7.1. struct . new x The instruction ( struct . new x ) is valid with the instruction type t ∗ → ( ref x ) if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( struct ( mut ? zt ) ∗ ) . The value type sequence t ∗ is unpack ( zt ) ∗ . C ⊢ struct . new x : unpack ( zt ) ∗ → ( ref x ) C . types [ x ] ≈ struct ( mut ? zt ) ∗ 3.4.7.2. struct . new_default x The instruction ( struct . new_default x ) is valid with the instruction type ϵ → ( ref x ) if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( struct ( mut ? zt ) ∗ ) . For all zt in zt ∗ : A default value for unpack ( zt ) is defined. C ⊢ struct . new_default x : ϵ → ( ref x ) C . types [ x ] ≈ struct ( mut ? zt ) ∗ ( default unpack ( zt ) = ϵ ) ∗ 3.4.7.3. struct . get _ sx ? x y The instruction ( struct . get _ sx ? x i ) is valid with the instruction type ( ref null x ) → t if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( struct ft ∗ ) . The length of ft ∗ is greater than i . The field type ft ∗ [ i ] is of the form ( mut ? zt ) . The signedness sx ? is present if and only if zt is a packed type. The value type t is unpack ( zt ) . C ⊢ struct . get _ sx ? x i : ( ref null x ) → unpack ( zt ) C . types [ x ] ≈ struct ft ∗ ft ∗ [ i ] = mut ? zt sx ? = ϵ ⇔ zt = unpack ( zt ) 3.4.7.4. struct . set x y The instruction ( struct . set x i ) is valid with the instruction type ( ref null x ) t → ϵ if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( struct ft ∗ ) . The length of ft ∗ is greater than i . The field type ft ∗ [ i ] is of the form ( mut zt ) . The value type t is unpack ( zt ) . C ⊢ struct . set x i : ( ref null x ) unpack ( zt ) → ϵ C . types [ x ] ≈ struct ft ∗ ft ∗ [ i ] = mut zt 3.4.7.5. array . new x The instruction ( array . new x ) is valid with the instruction type t i 32 → ( ref x ) if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut ? zt )) . The value type t is unpack ( zt ) . C ⊢ array . new x : unpack ( zt ) i 32 → ( ref x ) C . types [ x ] ≈ array ( mut ? zt ) 3.4.7.6. array . new_default x The instruction ( array . new_default x ) is valid with the instruction type i 32 → ( ref x ) if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut ? zt )) . A default value for unpack ( zt ) is defined. C ⊢ array . new_default x : i 32 → ( ref x ) C . types [ x ] ≈ array ( mut ? zt ) default unpack ( zt ) = ϵ 3.4.7.7. array . new_fixed x n The instruction ( array . new_fixed x n ) is valid with the instruction type t n → ( ref x ) if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut ? zt )) . The value type t is unpack ( zt ) . C ⊢ array . new_fixed x n : unpack ( zt ) n → ( ref x ) C . types [ x ] ≈ array ( mut ? zt ) 3.4.7.8. array . new_elem x y The instruction ( array . new_elem x y ) is valid with the instruction type i 32 i 32 → ( ref x ) if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut ? rt )) . The element segment C . elems [ y ] exists. The element segment C . elems [ y ] matches the reference type rt . C ⊢ array . new_elem x y : i 32 i 32 → ( ref x ) C . types [ x ] ≈ array ( mut ? rt ) C ⊢ C . elems [ y ] ≤ rt 3.4.7.9. array . new_data x y The instruction ( array . new_data x y ) is valid with the instruction type i 32 i 32 → ( ref x ) if: The type C . types [ x ] exists. The expansion of C . types [ x ] 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 C . datas [ y ] exists. The data segment C . datas [ y ] is of the form ok . C ⊢ array . new_data x y : i 32 i 32 → ( ref x ) C . types [ x ] ≈ array ( mut ? zt ) unpack ( zt ) = numtype ∨ unpack ( zt ) = vectype C . datas [ y ] = ok 3.4.7.10. array . get _ sx ? x The instruction ( array . get _ sx ? x ) is valid with the instruction type ( ref null x ) i 32 → t if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut ? zt )) . The signedness sx ? is present if and only if zt is a packed type. The value type t is unpack ( zt ) . C ⊢ array . get _ sx ? x : ( ref null x ) i 32 → unpack ( zt ) C . types [ x ] ≈ array ( mut ? zt ) sx ? = ϵ ⇔ zt = unpack ( zt ) 3.4.7.11. array . set x The instruction ( array . set x ) is valid with the instruction type ( ref null x ) i 32 t → ϵ if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut zt )) . The value type t is unpack ( zt ) . C ⊢ array . set x : ( ref null x ) i 32 unpack ( zt ) → ϵ C . types [ x ] ≈ array ( mut zt ) 3.4.7.12. array . len The instruction array . len is valid with the instruction type ( ref null array ) → i 32 . C ⊢ array . len : ( ref null array ) → i 32 3.4.7.13. array . fill x The instruction ( array . fill x ) is valid with the instruction type ( ref null x ) i 32 t i 32 → ϵ if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut zt )) . The value type t is unpack ( zt ) . C ⊢ array . fill x : ( ref null x ) i 32 unpack ( zt ) i 32 → ϵ C . types [ x ] ≈ array ( mut zt ) 3.4.7.14. array . copy x y The instruction ( array . copy x 1 x 2 ) is valid with the instruction type ( ref null x 1 ) i 32 ( ref null x 2 ) i 32 i 32 → ϵ if: The type C . types [ x 1 ] exists. The expansion of C . types [ x 1 ] is ( array ( mut zt 1 )) . The type C . types [ x 2 ] exists. The expansion of C . types [ x 2 ] is ( array ( mut ? zt 2 )) . The storage type zt 2 matches the storage type zt 1 . C ⊢ array . copy x 1 x 2 : ( ref null x 1 ) i 32 ( ref null x 2 ) i 32 i 32 → ϵ C . types [ x 1 ] ≈ array ( mut zt 1 ) C . types [ x 2 ] ≈ array ( mut ? zt 2 ) C ⊢ zt 2 ≤ zt 1 3.4.7.15. array . init_elem x y The instruction ( array . init_elem x y ) is valid with the instruction type ( ref null x ) i 32 i 32 i 32 → ϵ if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( array ( mut zt )) . The element segment C . elems [ y ] exists. The element segment C . elems [ y ] matches the storage type zt . C ⊢ array . init_elem x y : ( ref null x ) i 32 i 32 i 32 → ϵ C . types [ x ] ≈ array ( mut zt ) C ⊢ C . elems [ y ] ≤ zt 3.4.7.16. array . init_data x y The instruction ( array . init_data x y ) is valid with the instruction type ( ref null x ) i 32 i 32 i 32 → ϵ if: The type C . types [ x ] exists. The expansion of C . types [ x ] 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 C . datas [ y ] exists. The data segment C . datas [ y ] is of the form ok . C ⊢ array . init_data x y : ( ref null x ) i 32 i 32 i 32 → ϵ C . types [ x ] ≈ array ( mut zt ) unpack ( zt ) = numtype ∨ unpack ( zt ) = vectype C . datas [ y ] = ok 3.4.8. Scalar Reference Instructions 3.4.8.1. ref . i 31 The instruction ref . i 31 is valid with the instruction type i 32 → ( ref i 31 ) . C ⊢ ref . i 31 : i 32 → ( ref i 31 ) 3.4.8.2. i 31 . get _ sx The instruction ( i 31 . get _ sx ) is valid with the instruction type ( ref null i 31 ) → i 32 . C ⊢ i 31 . get _ sx : ( ref null i 31 ) → i 32 3.4.9. External Reference Instructions 3.4.9.1. any . convert_extern 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 ? . C ⊢ any . convert_extern : ( ref null 1 ? extern ) → ( ref null 2 ? any ) null 1 ? = null 2 ? 3.4.9.2. 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 ? . C ⊢ extern . convert_any : ( ref null 1 ? any ) → ( ref null 2 ? extern ) null 1 ? = null 2 ? 3.4.10. Numeric Instructions 3.4.10.1. t . const c The instruction ( nt . const c nt ) is valid with the instruction type ϵ → nt . C ⊢ nt . const c nt : ϵ → nt 3.4.10.2. t . unop The instruction ( nt . unop nt ) is valid with the instruction type nt → nt . C ⊢ nt . unop nt : nt → nt 3.4.10.3. t . binop The instruction ( nt . binop nt ) is valid with the instruction type nt nt → nt . C ⊢ nt . binop nt : nt nt → nt 3.4.10.4. t . testop The instruction ( nt . testop nt ) is valid with the instruction type nt → i 32 . C ⊢ nt . testop nt : nt → i 32 3.4.10.5. t . relop The instruction ( nt . relop nt ) is valid with the instruction type nt nt → i 32 . C ⊢ nt . relop nt : nt nt → i 32 3.4.10.6. t 1 . cvtop _ t 2 _ sx ? The instruction ( nt 1 . cvtop _ nt 2 ) is valid with the instruction type nt 2 → nt 1 . C ⊢ nt 1 . cvtop _ nt 2 : nt 2 → nt 1 3.4.11. Vector Instructions Vector instructions can have a prefix to describe the shape of the operand. Packed numeric types, i 8 and i 16 , are not value types . An auxiliary function maps such packed type shapes to value types: unpack ( i N x M ) = unpack ( i N ) 3.4.11.1. v 128 . const c The instruction ( v 128 . const c ) is valid with the instruction type ϵ → v 128 . C ⊢ v 128 . const c : ϵ → v 128 3.4.11.2. v 128 . vvunop The instruction ( v 128 . vvunop ) is valid with the instruction type v 128 → v 128 . C ⊢ v 128 . vvunop : v 128 → v 128 3.4.11.3. v 128 . vvbinop The instruction ( v 128 . vvbinop ) is valid with the instruction type v 128 v 128 → v 128 . C ⊢ v 128 . vvbinop : v 128 v 128 → v 128 3.4.11.4. v 128 . vvternop The instruction ( v 128 . vvternop ) is valid with the instruction type v 128 v 128 v 128 → v 128 . C ⊢ v 128 . vvternop : v 128 v 128 v 128 → v 128 3.4.11.5. v 128 . vvtestop The instruction ( v 128 . vvtestop ) is valid with the instruction type v 128 → i 32 . C ⊢ v 128 . vvtestop : v 128 → i 32 3.4.11.6. shape . vunop The instruction ( sh . vunop ) is valid with the instruction type v 128 → v 128 . C ⊢ sh . vunop : v 128 → v 128 3.4.11.7. shape . vbinop The instruction ( sh . vbinop ) is valid with the instruction type v 128 v 128 → v 128 . C ⊢ sh . vbinop : v 128 v 128 → v 128 3.4.11.8. shape . vternop The instruction ( sh . vternop ) is valid with the instruction type v 128 v 128 v 128 → v 128 . C ⊢ sh . vternop : v 128 v 128 v 128 → v 128 3.4.11.9. shape . vtestop The instruction ( sh . vtestop ) is valid with the instruction type v 128 → i 32 . C ⊢ sh . vtestop : v 128 → i 32 3.4.11.10. shape . vrelop The instruction ( sh . vrelop ) is valid with the instruction type v 128 v 128 → v 128 . C ⊢ sh . vrelop : v 128 v 128 → v 128 3.4.11.11. ishape . vishiftop The instruction ( sh . vshiftop ) is valid with the instruction type v 128 i 32 → v 128 . C ⊢ sh . vshiftop : v 128 i 32 → v 128 3.4.11.12. ishape . bitmask The instruction ( sh . bitmask ) is valid with the instruction type v 128 → i 32 . C ⊢ sh . bitmask : v 128 → i 32 3.4.11.13. i8x16. vswizzlop The instruction ( sh . vswizzlop ) is valid with the instruction type v 128 v 128 → v 128 . C ⊢ sh . vswizzlop : v 128 v 128 → v 128 3.4.11.14. i8x16. shuffle laneidx 16 The instruction ( sh . shuffle i ∗ ) is valid with the instruction type v 128 v 128 → v 128 if: For all i in i ∗ : The lane index i is less than 2 ⋅ dim ( sh ) . C ⊢ sh . shuffle i ∗ : v 128 v 128 → v 128 ( i < 2 ⋅ dim ( sh ) ) ∗ 3.4.11.15. shape . splat The instruction ( sh . splat ) is valid with the instruction type numtype → v 128 if: The number type numtype is unpack ( sh ) . C ⊢ sh . splat : unpack ( sh ) → v 128 3.4.11.16. shape . extract_lane _ sx ? laneidx The instruction ( sh . extract_lane _ sx ? i ) is valid with the instruction type v 128 → numtype if: The lane index i is less than dim ( sh ) . The number type numtype is unpack ( sh ) . C ⊢ sh . extract_lane _ sx ? i : v 128 → unpack ( sh ) i < dim ( sh ) 3.4.11.17. shape . replace_lane laneidx The instruction ( sh . replace_lane i ) is valid with the instruction type v 128 numtype → v 128 if: The lane index i is less than dim ( sh ) . The number type numtype is unpack ( sh ) . C ⊢ sh . replace_lane i : v 128 unpack ( sh ) → v 128 i < dim ( sh ) 3.4.11.18. ishape 1 . vextunop _ ishape 2 The instruction ( sh 1 . vextunop _ sh 2 ) is valid with the instruction type v 128 → v 128 . C ⊢ sh 1 . vextunop _ sh 2 : v 128 → v 128 3.4.11.19. ishape 1 . vextbinop _ ishape 2 The instruction ( sh 1 . vextbinop _ sh 2 ) is valid with the instruction type v 128 v 128 → v 128 . C ⊢ sh 1 . vextbinop _ sh 2 : v 128 v 128 → v 128 3.4.11.20. ishape 1 . vextternop _ ishape 2 The instruction ( sh 1 . vextternop _ sh 2 ) is valid with the instruction type v 128 v 128 v 128 → v 128 . C ⊢ sh 1 . vextternop _ sh 2 : v 128 v 128 v 128 → v 128 3.4.11.21. ishape 1 . narrow _ ishape 2 _ sx The instruction ( sh 1 . narrow _ sh 2 _ sx ) is valid with the instruction type v 128 v 128 → v 128 . C ⊢ sh 1 . narrow _ sh 2 _ sx : v 128 v 128 → v 128 3.4.11.22. shape . vcvtop _ half ? _ shape _ sx ? _zero ? The instruction ( sh 1 . vcvtop _ sh 2 ) is valid with the instruction type v 128 → v 128 . C ⊢ sh 1 . vcvtop _ sh 2 : v 128 → v 128 3.4.12. Instruction Sequences Typing of instruction sequences is defined recursively. The instruction sequence instr ∗ is valid with the instruction type it if: Either: The instruction sequence instr ∗ is empty. The instruction type it is of the form ϵ → ϵ . Or: The instruction sequence instr ∗ is of the form instr ′ . The instruction type it is of the form t 1 ∗ → x ∗ t 2 ∗ . The instruction instr ′ is valid with the instruction type t 1 ∗ → x ∗ t 2 ∗ . Or: The instruction sequence instr ∗ is of the form instr 1 ∗ instr 2 ∗ . The instruction type it is of the form t 1 ∗ → x 1 ∗ x 2 ∗ t 3 ∗ . The instruction sequence instr 1 ∗ is valid with the instruction type t 1 ∗ → x 1 ∗ t 2 ∗ . For all x 1 in x 1 ∗ : The local C . locals [ x 1 ] exists. The local C . locals [ x 1 ] is of the form ( init t ) . Under the context C with the local types of x 1 ∗ updated to ( set t ) ∗ , the instruction sequence instr 2 ∗ is valid with the instruction type t 2 ∗ → x 2 ∗ t 3 ∗ . Or: The instruction sequence instr ∗ is valid with the instruction type it ′′ . The instruction type it ′′ matches the instruction type it . The instruction type it is valid . Or: The instruction type it is of the form t ∗ t 1 ∗ → x ∗ t ∗ t 2 ∗ . The instruction sequence instr ∗ is valid with the instruction type t 1 ∗ → x ∗ t 2 ∗ . The result type t ∗ is valid . C ⊢ ϵ : ϵ → ϵ C ⊢ instr : t 1 ∗ → x ∗ t 2 ∗ C ⊢ instr : t 1 ∗ → x ∗ t 2 ∗ C ⊢ instr 1 ∗ instr 2 ∗ : t 1 ∗ → x 1 ∗ x 2 ∗ t 3 ∗ C ⊢ instr 1 ∗ : t 1 ∗ → x 1 ∗ t 2 ∗ ( C . locals [ x 1 ] = init t ) ∗ C [ . local [ x 1 ∗ ] = ( set t ) ∗ ] ⊢ instr 2 ∗ : t 2 ∗ → x 2 ∗ t 3 ∗ C ⊢ instr ∗ : it ′ C ⊢ instr ∗ : it C ⊢ it ≤ it ′ C ⊢ it ′ : ok Note This subsumption rule allows to weaken the type of an instruction sequence to a supertype, which includes the ability to drop init variables x ∗ from the instruction type in a context where they are not needed, for example, at the end of the body of a block . C ⊢ instr ∗ : ( t ∗ t 1 ∗ ) → x ∗ ( t ∗ t 2 ∗ ) C ⊢ instr ∗ : t 1 ∗ → x ∗ t 2 ∗ C ⊢ t ∗ : ok Note In combination with the previous two rules, this frame rule allows to compose instructions whose types would not directly fit otherwise. For example, consider the instruction sequence ( i 32 . const 1 ) ( i 32 . const 2 ) ( i 32 . add ) To type this sequence, its subsequence ( i 32 . const 2 ) ( i 32 . add ) needs to be valid with an intermediate type. But the direct type of ( i 32 . const 2 ) is ϵ → i 32 , not matching the two inputs expected by i 32 . add . The rule allows to weaken the type of ( i 32 . const 2 ) to the supertype i 32 → i 32 i 32 , such that it can be composed with i 32 . add and yields the intermediate type i 32 → i 32 i 32 for the subsequence. That can in turn be composed with the first constant. 3.4.13. Expressions Expressions expr are classified by result types t ∗ . The expression instr ∗ is valid with the result type t ∗ if: The instruction sequence instr ∗ is valid with the instruction type ϵ → t ∗ . C ⊢ instr ∗ : t ∗ C ⊢ instr ∗ : ϵ → t ∗ 3.4.13.1. Constant Expressions In a constant expression, all instructions must be constant. instr ∗ is constant if: For all instr in instr ∗ : instr is constant. instr is constant if: Either: The instruction instr is of the form ( nt . const c nt ) . Or: The instruction instr is of the form ( vt . const c vt ) . Or: The instruction instr is of the form ( ref . null ht ) . Or: The instruction instr is of the form ref . i 31 . Or: The instruction instr is of the form ( ref . func x ) . Or: The instruction instr is of the form ( struct . new x ) . Or: The instruction instr is of the form ( struct . new_default x ) . Or: The instruction instr is of the form ( array . new x ) . Or: The instruction instr is of the form ( array . new_default x ) . Or: The instruction instr is of the form ( array . new_fixed x n ) . Or: The instruction instr is of the form any . convert_extern . Or: The instruction instr is of the form extern . convert_any . Or: The instruction instr is of the form ( global . get x ) . The global C . globals [ x ] exists. The global C . globals [ x ] is of the form ( ϵ t ) . Or: The instruction instr is of the form ( i N . binop ) . i N is contained in [ i 32 ; i 64 ]. binop is contained in [ add ; sub ; mul ]. C ⊢ instr ∗ const ( C ⊢ instr const ) ∗ C ⊢ ( nt . const c nt ) const C ⊢ ( vt . const c vt ) const C ⊢ ( i N . binop ) const i N ∈ i 32 i 64 binop ∈ add sub mul C ⊢ ( ref . null ht ) const C ⊢ ( ref . i 31 ) const C ⊢ ( ref . func x ) const C ⊢ ( struct . new x ) const C ⊢ ( struct . new_default x ) const C ⊢ ( array . new x ) const C ⊢ ( array . new_default x ) const C ⊢ ( array . new_fixed x n ) const C ⊢ ( any . convert_extern ) const C ⊢ ( extern . convert_any ) const C ⊢ ( global . get x ) const C . globals [ x ] = t Note Currently, constant expressions occurring in globals are further constrained in that contained global . get instructions are only allowed to refer to imported or previously defined globals. Constant expressions occurring in tables may only have global . get instructions that refer to imported globals. This is enforced in the validation rule for modules by constraining the context C 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 representing them individually. The type definition ( type rectype ) is valid with the defined type sequence dt ∗ if: The length of C . types is equal to x . The defined type sequence dt ∗ is of the form roll x ∗ ( rectype ) . Let C ′ be the same context as C , but with the defined type sequence dt ∗ appended to the field types . Under the context C ′ , the recursive type rectype is valid for the type index x . C ⊢ type rectype : dt ∗ x = ∣ C . types ∣ dt ∗ = roll x ∗ ( rectype ) C ⊕ { types dt ∗ } ⊢ rectype : ok ( x ) 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 C ′ be the same context as C , but with the defined type sequence dt 1 ∗ appended to the field types . Under the context C ′ , the type definition sequence type ′ ∗ is valid with the defined type sequence dt ∗ . C ⊢ ϵ : ϵ C ⊢ type 1 type ∗ : dt 1 ∗ dt ∗ C ⊢ type 1 : dt 1 ∗ C ⊕ { types dt 1 ∗ } ⊢ type ∗ : 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 C ( tagtype ) . C ⊢ tag tagtype : clos C ( tagtype ) C ⊢ tagtype : ok 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 ? t ) . The expression expr is valid with the value type t . expr is constant. C ⊢ global globaltype expr : globaltype C ⊢ globaltype : ok globaltype = mut ? t C ⊢ expr : t const 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 C ′ be the same context as C , but with the global type sequence gt 1 appended to the field globals . Under the context C ′ , the global sequence global ′ ∗ is valid with the global type sequence gt ∗ . C ⊢ ϵ : ϵ C ⊢ global 1 global ∗ : gt 1 gt ∗ C ⊢ global 1 : gt 1 C ⊕ { globals gt 1 } ⊢ global ∗ : 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 . C ⊢ memory memtype : memtype C ⊢ memtype : ok 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. C ⊢ table tabletype expr : tabletype C ⊢ tabletype : ok tabletype = at lim rt C ⊢ expr : rt const 3.5.6. Functions Functions func are classified by defined types that expand to function types of the form func t 1 ∗ → t 2 ∗ . The function ( func x local ∗ expr ) is valid with the type C . types [ x ] if: The type C . types [ x ] exists. The expansion of C . types [ x ] is ( func t 1 ∗ → t 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 C with the field locals appended by ( set t 1 ) ∗ lt ∗ and the field labels appended by t 2 ∗ and the field return appended by t 2 ∗ , the expression expr is valid with the result type t 2 ∗ . C ⊢ func x local ∗ expr : C . types [ x ] C . types [ x ] ≈ func t 1 ∗ → t 2 ∗ ( C ⊢ local : lt ) ∗ C ⊕ { locals ( set t 1 ) ∗ lt ∗ , labels ( t 2 ∗ ) , return ( t 2 ∗ )} ⊢ expr : t 2 ∗ 3.5.7. Locals Locals local are classified with local types . The local ( local t ) is valid with the local type ( init t ) if: The value type t is valid . Either: The initialization status init is of the form set . A default value for t is defined. Or: The initialization status init is of the form unset . A default value for t is not defined. C ⊢ local t : set t C ⊢ t : ok default t = ϵ C ⊢ local t : unset t C ⊢ t : ok default t = ϵ Note For cases where both rules are applicable, the former yields the more permissable type. 3.5.8. Data Segments Data segments data are classified by the singleton data type , which merely expresses well-formedness. The memory segment ( data b ∗ datamode ) is valid if: The data mode datamode is valid . C ⊢ data b ∗ datamode : ok C ⊢ datamode : ok The data mode datamode is valid if: Either: The data mode datamode is of the form passive . Or: The data mode datamode is of the form ( active x expr ) . The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form ( at lim page ) . The expression expr is valid with the value type at . expr is constant. C ⊢ passive : ok C ⊢ active x expr : ok C . mems [ x ] = at lim page C ⊢ expr : at const 3.5.9. Element Segments Element segments elem are classified by their element type . The table segment ( elem elemtype expr ∗ elemmode ) is valid with the element type elemtype if: The reference type elemtype is valid . For all expr in expr ∗ : The expression expr is valid with the value type elemtype . expr is constant. The element mode elemmode is valid with the element type elemtype . C ⊢ elem elemtype expr ∗ elemmode : elemtype C ⊢ elemtype : ok ( C ⊢ expr : elemtype const ) ∗ C ⊢ elemmode : elemtype The element mode elemmode is valid with the element type rt if: Either: The element mode elemmode is of the form passive . Or: The element mode elemmode is of the form declare . Or: The element mode elemmode is of the form ( active x expr ) . The table C . tables [ x ] exists. The table C . tables [ x ] is of the form ( at lim rt ′ ) . The reference type rt matches the reference type rt ′ . The expression expr is valid with the value type at . expr is constant. C ⊢ passive : rt C ⊢ declare : rt C ⊢ active x expr : rt C . tables [ x ] = at lim rt ′ C ⊢ rt ≤ rt ′ C ⊢ expr : at const 3.5.10. Start Function The start function ( start x ) is valid if: The function C . funcs [ x ] exists. The expansion of C . funcs [ x ] is ( func → ) . C ⊢ start x : ok C . funcs [ x ] ≈ func ϵ → ϵ 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 C ( xt ) . C ⊢ import name 1 name 2 xt : clos C ( xt ) C ⊢ xt : ok 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 . C ⊢ export name externidx : name xt C ⊢ externidx : xt 3.5.12.1. tag x The external index ( tag x ) is valid with the external type ( tag jt ) if: The tag C . tags [ x ] exists. The tag C . tags [ x ] is of the form jt . C ⊢ tag x : tag jt C . tags [ x ] = jt 3.5.12.2. global x The external index ( global x ) is valid with the external type ( global gt ) if: The global C . globals [ x ] exists. The global C . globals [ x ] is of the form gt . C ⊢ global x : global gt C . globals [ x ] = gt 3.5.12.3. memory x The external index ( memory x ) is valid with the external type ( mem mt ) if: The memory C . mems [ x ] exists. The memory C . mems [ x ] is of the form mt . C ⊢ memory x : mem mt C . mems [ x ] = mt 3.5.12.4. table x The external index ( table x ) is valid with the external type ( table tt ) if: The table C . tables [ x ] exists. The table C . tables [ x ] is of the form tt . C ⊢ table x : table tt C . tables [ x ] = tt 3.5.12.5. func x The external index ( func x ) is valid with the external type ( func dt ) if: The function C . funcs [ x ] exists. The function C . funcs [ x ] is of the form dt . C ⊢ func x : func dt C . funcs [ x ] = 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 C for validation of the module’s content is constructed 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 C ′ , the tag tag is valid with the tag type jt . jt ∗ is the concatenation of all such jt . Under the context C ′ , the global sequence global ∗ is valid with the global type sequence gt ∗ . For all mem in mem ∗ : Under the context C ′ , 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 C ′ , 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 . For all elem in elem ∗ : The table segment elem is valid with the element type rt . rt ∗ is the concatenation of all such rt . If start is defined, then: The start function start is valid . For all export in export ∗ : The export export is valid with the name nm and the external type xt e . nm ∗ is the concatenation of all such nm . xt e ∗ is the concatenation of all such xt e . nm ∗ disjoint is true. The context C is of the form C ′ with the field tags appended by jt i ∗ jt ∗ and the field globals appended by gt ∗ and the field mems appended by mt i ∗ mt ∗ and the field tables appended by tt i ∗ tt ∗ and the field datas appended by ok ∗ and the field elems appended by rt ∗ . The context C ′ is of the form { types dt ′ ∗ , globals gt i ∗ , funcs dt i ∗ dt ∗ , return ϵ , refs x ∗ } . The function index sequence x ∗ is of the form funcidx ( global ∗ mem ∗ table ∗ elem ∗ export ∗ ) . The tag type sequence jt i ∗ is of the form tags ( xt i ∗ ) . The global type sequence gt i ∗ is of the form globals ( xt i ∗ ) . The memory type sequence mt i ∗ is of the form mems ( xt i ∗ ) . The table type sequence tt i ∗ is of the form tables ( xt i ∗ ) . The defined type sequence dt i ∗ is of the form funcs ( xt i ∗ ) . The module type moduletype is clos C ( xt i ∗ → xt e ∗ ) . ⊢ module type ∗ import ∗ tag ∗ global ∗ mem ∗ table ∗ func ∗ data ∗ elem ∗ start ? export ∗ : clos C ( xt i ∗ → xt e ∗ ) { } ⊢ type ∗ : dt ′ ∗ ({ types dt ′ ∗ } ⊢ import : xt i ) ∗ ( C ′ ⊢ tag : jt ) ∗ C ′ ⊢ global ∗ : gt ∗ ( C ′ ⊢ mem : mt ) ∗ ( C ′ ⊢ table : tt ) ∗ ( C ⊢ func : dt ) ∗ ( C ⊢ data : ok ) ∗ ( C ⊢ elem : rt ) ∗ ( C ⊢ start : ok ) ? ( C ⊢ export : nm xt e ) ∗ nm ∗ disjoint C = C ′ ⊕ { tags jt i ∗ jt ∗ , globals gt ∗ , mems mt i ∗ mt ∗ , tables tt i ∗ tt ∗ , datas ok ∗ , elems rt ∗ } C ′ = { types dt ′ ∗ , globals gt i ∗ , funcs dt i ∗ dt ∗ , refs x ∗ } x ∗ = funcidx ( global ∗ mem ∗ table ∗ elem ∗ export ∗ ) jt i ∗ = tags ( xt i ∗ ) gt i ∗ = globals ( xt i ∗ ) mt i ∗ = mems ( xt i ∗ ) tt i ∗ = tables ( xt i ∗ ) dt i ∗ = funcs ( xt i ∗ ) Note All functions in a module are mutually recursive. Consequently, the definition of the context C 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 C . However, this recursion is just a specification device. All types needed to construct C 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. 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: In prose , describing the execution in intuitive form. In formal notation , describing the rule in mathematical form. [ 1 ] 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. 4.1.1. Prose Notation Execution is specified by stylised, step-wise rules for each instruction of the abstract syntax . The following conventions are adopted in stating these rules. The execution rules implicitly assume a given store s . The execution rules also assume the presence of an implicit stack that is modified by pushing or popping values , labels , and frames . Certain rules require the stack to contain at least one frame. The most recent frame is referred to as the current frame. 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 instruction 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. 4.1.2. Formal Notation 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. [ 2 ] 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 ( s ; f ; instr ∗ ) consisting of the current store s , the call frame f 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 s and the frame f are often combined into a state z , which is a pair ( s ; f ) . Moreover, z 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 i 32 . add instruction can be given as follows: ( i 32 . const n 1 ) ( i 32 . const n 2 ) ( i 32 . add ) ↪ ( i 32 . const ( n 1 + n 2 ) mod 2 32 ) 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 ↪ ϵ Labels and frames are similarly defined to be part of an instruction sequence. 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 For example, the following instruction sequence, ( f 64 . const q 1 ) ( f 64 . const q 2 ) ( f 64 . neg ) ( f 64 . const q 3 ) ( f 64 . add ) ( f 64 . mul ) terminates after three steps: ↪ ↪ ↪ ( f 64 . const q 1 ) ( f 64 . const q 4 ) ( f 64 . const q 3 ) ( f 64 . add ) ( f 64 . mul ) ( f 64 . const q 1 ) ( f 64 . const q 5 ) ( f 64 . mul ) ( f 64 . const q 6 ) where q 4 = − q 2 and q 5 = − q 2 + q 3 and q 6 = q 1 ⋅ ( − q 2 + q 3 ) . [ 1 ] 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 WebAssembly . Proceedings of the 38th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017). ACM 2017. [ 2 ] For example: Benjamin Pierce. Types and Programming Languages . The MIT Press 2002 4.2. Runtime Structure Store , stack , and other runtime structure forming the WebAssembly abstract machine, such as values or module instances , are made precise in terms of additional auxiliary syntax. 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 ref . null producing them. References other than null are represented with additional administrative instructions . They either are scalar references , containing a 31-bit integer , null references , 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 references , 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 . val num vec ref ::= ::= ::= ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ num ∣ vec ∣ ref numtype . const num numtype vectype . const vec vectype ref . i 31 u 31 ref . null ref . struct structaddr ref . array arrayaddr ref . func funcaddr ref . exn exnaddr ref . host hostaddr ref . extern ref Note Future versions of WebAssembly may add additional forms of values. 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 t hence is an optional value val ? . default i N default f N default v N default ref null ht default ref ht = = = = = ( i N . const 0 ) ( f N . const + 0 ) ( v N . const 0 ) ref . null ϵ 4.2.1.1. Convention The meta variable r 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 . result ::= val ∗ ∣ ( ref . exn exnaddr ) throw_ref ∣ 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: store ::= { tags taginst ∗ globals globalinst ∗ mems meminst ∗ tables tableinst ∗ funcs funcinst ∗ datas datainst ∗ elems eleminst ∗ structs structinst ∗ arrays arrayinst ∗ exns exninst ∗ } 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. 4.2.3.1. Convention The meta variable s ranges over stores where clear from context. 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 funcaddr tableaddr memaddr globaladdr tagaddr elemaddr dataaddr structaddr arrayaddr exnaddr hostaddr ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= 0 ∣ 1 ∣ 2 ∣ … addr addr addr addr addr addr addr addr addr addr addr An embedder may assign identity to exported store objects corresponding to their addresses, even where this identity 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. 4.2.4.1. Conventions The notation addr ( A ) denotes the set of addresses from address space addr occurring free in A . We sometimes reinterpret this set as the list of its elements, without assuming any particular order. 4.2.5. External Addresses An external address is the runtime address of an entity that can be imported or exported. It is an address denoting either a function instance , global instance , table instance , memory instance , or tag instance in the shared store . externaddr ::= tag tagaddr ∣ global globaladdr ∣ mem memaddr ∣ table tableaddr ∣ func funcaddr 4.2.6. Module Instances A module instance is the runtime representation of a module . It is created by instantiating a module, and collects runtime representations of all entities that are imported, defined, or exported by the module. moduleinst ::= { types deftype ∗ tags tagaddr ∗ globals globaladdr ∗ mems memaddr ∗ tables tableaddr ∗ funcs funcaddr ∗ datas dataaddr ∗ elems elemaddr ∗ exports exportinst ∗ } 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. 4.2.7. Function Instances A function instance is the runtime representation of a function . It effectively is a closure of the original function over the runtime module instance of its originating module . The module instance is used to resolve references to other definitions during execution of the function. funcinst code ::= ::= { type deftype , module moduleinst , code code } func ∣ hostfunc 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 . 4.2.8. Table Instances A table instance is the runtime representation of a table . It records its type and holds a sequence of reference values . tableinst ::= { type tabletype , refs ref ∗ } 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 . 4.2.9. Memory Instances A memory instance is the runtime representation of a linear memory . It records its type and holds a sequence of bytes . meminst ::= { type memtype , bytes byte ∗ } 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 . 4.2.10. Global Instances A global instance is the runtime representation of a global variable. It records its type and holds an individual value . globalinst ::= { type globaltype , value val } The value of mutable globals can be mutated through variable instructions or by external means provided by the embedder . It is an invariant of the semantics that the value has a type matching the value type of globaltype . 4.2.11. Tag Instances A tag instance is the runtime representation of a tag definition. It records the defined type of the tag. taginst ::= { type tagtype } 4.2.12. Element Instances An element instance is the runtime representation of an element segment . It holds a list of references and its type . eleminst ::= { type elemtype , refs ref ∗ } It is an invariant of the semantics that all elements of a segment have a type matching elemtype . 4.2.13. Data Instances A data instance is the runtime representation of a data segment . It holds a list of bytes . datainst ::= { bytes byte ∗ } 4.2.14. Export Instances An export instance is the runtime representation of an export . It defines the export’s name and the associated external address . exportinst ::= { name name , addr externaddr } 4.2.14.1. 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 ∗ . 4.2.15. Aggregate Instances A structure instance is the runtime representation of a heap object allocated from a structure type . Likewise, an array instance is the runtime representation of a heap object allocated from an array type . Both record their respective defined type and hold a list of the values of their fields . structinst arrayinst fieldval packval ::= ::= ::= ::= { type deftype , fields fieldval ∗ } { type deftype , fields fieldval ∗ } val ∣ packval packtype . pack i N 4.2.15.1. Conventions Conversion of a regular value to a field value is defined as follows: pack valtype ( val ) pack packtype ( i 32 . const i ) = = val packtype . pack wrap 32 , ∣ packtype ∣ ( i ) The inverse conversion of a field value to a regular value is defined as follows: unpack valtype ϵ ( val ) unpack packtype sx ( packtype . pack i ) = = val i 32 . const extend ∣ packtype ∣ , 32 sx ( i ) 4.2.16. Exception Instances An exception instance is the runtime representation of an exception produced by a throw instruction. It holds the address of the respective tag and the argument values . exninst ::= { tag tagaddr , fields val ∗ } 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 Where clear from context, call frame is abbreviated to just frame . 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. 4.2.17.1. Values Values are represented by themselves . 4.2.17.2. Labels Labels carry an argument arity n and their associated branch target , which is expressed syntactically as an instruction sequence: label ::= label n { instr ∗ } Intuitively, instr ∗ is the continuation to execute when the branch is taken, in place of the original control construct. Note For example, a loop label has the form label n {( 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 n { ϵ } When branching, the empty continuation ends the targeted block, such that execution can proceed with consecutive instructions. 4.2.17.3. Call Frames Call frames carry the return arity n 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 : callframe frame ::= ::= frame n { frame } { locals ( val ? ) ∗ , module moduleinst } Locals may be uninitialized, in which case they are empty. Locals are mutated by respective variable instructions . 4.2.17.4. Exception Handlers Exception handlers are installed by try_table instructions and record the corresponding list of catch clauses : handler ::= handler n { catch ∗ } The handlers on the stack are searched when an exception is thrown . 4.2.17.5. Conventions The meta variable L ranges over labels where clear from context. The meta variable f ranges over frame states where clear from context. The meta variable H 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 z ( x ) instrtype z ( t ? ) = = t 1 ∗ → t 2 ∗ ϵ → t ? if z . types [ x ] ≈ func t 1 ∗ → t 2 ∗ 4.2.18. Administrative Instructions Note This section is only relevant for the formal notation . In order to express the reduction of traps , calls , exception handling , and control instructions , the syntax of instructions is extended to include the following administrative instructions : instr ::= ∣ ∣ ∣ ∣ ∣ … ref label n { instr ∗ } instr ∗ frame n { frame } instr ∗ handler n { catch ∗ } instr ∗ trap A reference represents a 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 For example, the reduction rule for block is: ( block bt instr ∗ ) ↪ ( label n { ϵ } instr ∗ ) if the block type bt denotes a function type func t 1 m → t 2 n , such that n 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 n values representing the results – then the label instruction is eliminated courtesy of its own reduction rule : ( label n { instr ∗ } val ∗ ) ↪ val ∗ This can be interpreted as removing the label from the stack and only leaving the locally accumulated operand values. Validation guarantees that n matches the number ∣ val ∗ ∣ of resulting values at this point. 4.2.19. 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. config state ::= ::= state ; instr ∗ store ; frame Note The current version of WebAssembly is single-threaded, but configurations with multiple threads may be supported in the future. 4.2.19.1. Conventions The meta variable z ranges over frame states where clear from context. The following shorthands are defined for accessing a state z = ( s ; f ) : z . types [ x ] = f . module . types [ x ] z . tags [ x ] = s . tags [ f . module . tags [ x ]] z . globals [ x ] = s . globals [ f . module . globals [ x ]] z . mems [ x ] = s . mems [ f . module . mems [ x ]] z . tables [ x ] = s . tables [ f . module . tables [ x ]] z . funcs [ x ] = s . funcs [ f . module . funcs [ x ]] z . datas [ x ] = s . datas [ f . module . datas [ x ]] z . elems [ x ] = s . elems [ f . module . elems [ x ]] z . locals [ x ] = f . locals [ x ] These shorthands also extend to notation for updating state: z [ . globals [ x ] . value = v ] = s [ . globals [ f . module . globals [ x ]] . value = v ] ; f z [ . mems [ x ] . bytes [ i : j ] = b ∗ ] = s [ . mems [ f . module . mems [ x ]] . bytes [ i : j ] = b ∗ ] ; f z [ . tables [ x ] . refs [ i ] = r ] = s [ . tables [ f . module . tables [ x ]] . refs [ i ] = r ] ; f z [ . locals [ x ] = v ] = s ; f [ . locals [ x ] = v ] 4.3. Numerics Numeric primitives are defined in a generic manner, by operators indexed over a bit width N . 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. 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 For example, the fcopysign operator is defined as follows: fcopysign N ( ± p 1 , ± p 2 ) fcopysign N ( ± p 1 , ∓ p 2 ) = = ± p 1 ∓ p 1 This definition is to be read as a shorthand for the following expansion of each clause into two separate ones: fcopysign N ( + p 1 , + p 2 ) fcopysign N ( − p 1 , − p 2 ) fcopysign N ( + p 1 , − p 2 ) fcopysign N ( − p 1 , + p 2 ) = = = = + p 1 − p 1 − p 1 + p 1 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. o p ( c 1 n , … , c k n ) = o p ( c 1 n [ 0 ] , … , c k n [ 0 ]) … o p ( c 1 n [ n − 1 ] , … , c k n [ n − 1 ]) Note For example, the unary operator fabs , when given a sequence of floating-point values, return a sequence of floating-point results: fabs N ( z n ) = fabs N ( z [ 0 ]) … fabs N ( z [ n ]) The binary operator iadd , when given two sequences of integers of the same length, n , return a sequence of integer results: iadd N ( i 1 n , i 2 n ) = iadd N ( i 1 [ 0 ] , i 2 [ 0 ]) … iadd N ( i 1 [ n ] , i 2 [ n ]) Conventions: The meta variable d is used to range over single bits. The meta variable p is used to range over (signless) magnitudes of floating-point values, including nan and ∞ . The meta variable q is used to range over (signless) rational magnitudes , excluding nan or ∞ . The notation f − 1 denotes the inverse of a bijective function f . Truncation of rational values is written trunc ( ± q ) , with the usual mathematical definition: trunc ( ± q ) = ± i ( if i ∈ N ∧ + q − 1 < i ≤ + q ) Saturation of integers is written sat_u N ( i ) and sat_s N ( i ) . The arguments to these two functions range over arbitrary signed integers. Unsigned saturation, sat_u N ( i ) clamps i to between 0 and 2 N − 1 : sat_u N ( i ) sat_u N ( i ) sat_u N ( i ) = = = 0 2 N − 1 i ( if i < 0 ) ( if i > 2 N − 1 ) ( otherwise ) Signed saturation, sat_s N ( i ) clamps i to between − 2 N − 1 and 2 N − 1 − 1 : sat_s N ( i ) sat_s N ( i ) sat_s N ( i ) = = = − 2 N − 1 2 N − 1 − 1 i ( if i < − 2 N − 1 ) ( if i > 2 N − 1 − 1 ) ( otherwise ) 4.3.1. Representations Numbers and numeric vectors have an underlying binary representation as a sequence of bits: bits i N ( i ) bits f N ( z ) bits v N ( i ) = = = ibits N ( i ) fbits N ( z ) ibits N ( i ) 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. 4.3.1.1. Integers Integers are represented as base two unsigned numbers: ibits N ( i ) = d N − 1 … d 0 ( i = 2 N − 1 ⋅ d N − 1 + ⋯ + 2 0 ⋅ d 0 ) Boolean operators like ∧ , ∨ , or ⊻ are lifted to bit sequences of equal length by applying them pointwise. 4.3.1.2. Floating-Point Floating-point values are represented in the respective binary format defined by [IEEE-754-2019] (Section 3.4): fbits N ( ± ( 1 + m ⋅ 2 − M ) ⋅ 2 e ) fbits N ( ± ( 0 + m ⋅ 2 − M ) ⋅ 2 e ) fbits N ( ± ∞ ) fbits N ( ± nan ( n )) D O U B L ES L A S H [ 1 e x ] fbias N fsign ( + ) fsign ( − ) = = = = = = = fsign ( ± ) ibits E ( e + fbias N ) ibits M ( m ) fsign ( ± ) ( 0 ) E ibits M ( m ) fsign ( ± ) ( 1 ) E ( 0 ) M fsign ( ± ) ( 1 ) E ibits M ( n ) 2 E − 1 − 1 0 1 where M = signif ( N ) and E = expon ( N ) . 4.3.1.3. Vectors Numeric vectors of type v N have the same underlying representation as an i N . They can also be interpreted as a sequence of numeric values packed into a v N with a particular shape t x M , provided that N = ∣t∣ ⋅ M . lanes t x M ( c ) = c 0 … c M − 1 ( where ∧ ∧ w b ∗ c i = = = ∣ t ∣/8 bytes i N ( c ) bytes t − 1 ( b ∗ [ i ⋅ w : w ])) This function is a bijection on i N , hence it is invertible. Numeric values can be packed into lanes of a specific lane type and vice versa: pack numtype ( c ) pack packtype ( c ) unpack numtype ( c ) unpack packtype ( c ) = = = = c wrap ∣ unpack ( packtype ) ∣ , ∣ packtype ∣ ( c ) c extend ∣ packtype ∣ , ∣ unpack ( packtype ) ∣ u ( c ) 4.3.1.4. Storage When a number is stored into memory , it is converted into a sequence of bytes in little endian byte order: bytes t ( i ) D O U B L ES L A S H [ 1 e x ] littleendian ( ϵ ) littleendian ( d 8 d ′ ∗ ) = = = littleendian ( bits t ( i )) ϵ littleendian ( d ′ ∗ ) ibits 8 − 1 ( d 8 ) Again these functions are invertible bijections. 4.3.2. Integer Operations 4.3.2.1. Sign Interpretation Integer operators are defined on i N values. Operators that use a signed interpretation convert the value using the following definition, which takes the two’s complement when the value lies in the upper half of the value range (i.e., its most significant bit is 1 ): signed N ( i ) signed N ( i ) = = i i − 2 N ( 0 ≤ i < 2 N − 1 ) ( 2 N − 1 ≤ i < 2 N ) This function is bijective, and hence invertible. 4.3.2.2. 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 ( C ) bool ( C ) = = 1 0 ( if C ) ( otherwise ) 4.3.2.3. iadd N ( i 1 , i 2 ) Return the result of adding i 1 and i 2 modulo 2 N . iadd N ( i 1 , i 2 ) = ( i 1 + i 2 ) mod 2 N 4.3.2.4. isub N ( i 1 , i 2 ) Return the result of subtracting i 2 from i 1 modulo 2 N . isub N ( i 1 , i 2 ) = ( i 1 − i 2 + 2 N ) mod 2 N 4.3.2.5. imul N ( i 1 , i 2 ) Return the result of multiplying i 1 and i 2 modulo 2 N . imul N ( i 1 , i 2 ) = ( i 1 ⋅ i 2 ) mod 2 N 4.3.2.6. idiv_u N ( i 1 , i 2 ) If i 2 is 0 , then the result is undefined. Else, return the result of dividing i 1 by i 2 , truncated toward zero. idiv_u N ( i 1 , 0 ) idiv_u N ( i 1 , i 2 ) = = { } trunc ( i 1 / i 2 ) Note This operator is partial . 4.3.2.7. idiv_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 . Let j 2 be the signed interpretation of i 2 . If j 2 is 0 , then the result is undefined. Else if j 1 divided by j 2 is 2 N − 1 , then the result is undefined. Else, return the result of dividing j 1 by j 2 , truncated toward zero. idiv_s N ( i 1 , 0 ) idiv_s N ( i 1 , i 2 ) idiv_s N ( i 1 , i 2 ) = = = { } { } ( if signed N ( i 1 ) / signed N ( i 2 ) = 2 N − 1 ) signed N − 1 ( trunc ( signed N ( i 1 ) / signed N ( i 2 ))) Note This operator is partial . Besides division by 0 , the result of ( − 2 N − 1 ) / ( − 1 ) = + 2 N − 1 is not representable as an N -bit signed integer. 4.3.2.8. irem_u N ( i 1 , i 2 ) If i 2 is 0 , then the result is undefined. Else, return the remainder of dividing i 1 by i 2 . irem_u N ( i 1 , 0 ) irem_u N ( i 1 , i 2 ) = = { } i 1 − i 2 ⋅ trunc ( i 1 / i 2 ) Note This operator is partial . As long as both operators are defined, it holds that i 1 = i 2 ⋅ idiv_u ( i 1 , i 2 ) + irem_u ( i 1 , i 2 ) . 4.3.2.9. irem_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 . Let j 2 be the signed interpretation of i 2 . If i 2 is 0 , then the result is undefined. Else, return the remainder of dividing j 1 by j 2 , with the sign of the dividend j 1 . irem_s N ( i 1 , 0 ) irem_s N ( i 1 , i 2 ) = = { } signed N − 1 ( j 1 − j 2 ⋅ trunc ( j 1 / j 2 )) ( where j 1 = signed N ( i 1 ) ∧ j 2 = signed N ( i 2 )) Note This operator is partial . As long as both operators are defined, it holds that i 1 = i 2 ⋅ idiv_s ( i 1 , i 2 ) + irem_s ( i 1 , i 2 ) . 4.3.2.10. inot N ( i ) Return the bitwise negation of i . inot N ( i ) = ibits N − 1 ( ibits N ( i ) ⊻ ibits N ( 2 N − 1 )) 4.3.2.11. irev N ( i ) Return the bitwise reversal of i . irev N ( i ) = ibits N − 1 (( d N [ N − i ] ) i ≤ N ) ( if d N = ibits N ( i )) 4.3.2.12. iand N ( i 1 , i 2 ) Return the bitwise conjunction of i 1 and i 2 . iand N ( i 1 , i 2 ) = ibits N − 1 ( ibits N ( i 1 ) ∧ ibits N ( i 2 )) 4.3.2.13. iandnot N ( i 1 , i 2 ) Return the bitwise conjunction of i 1 and the bitwise negation of i 2 . iandnot N ( i 1 , i 2 ) = iand N ( i 1 , inot N ( i 2 )) 4.3.2.14. ior N ( i 1 , i 2 ) Return the bitwise disjunction of i 1 and i 2 . ior N ( i 1 , i 2 ) = ibits N − 1 ( ibits N ( i 1 ) ∨ ibits N ( i 2 )) 4.3.2.15. ixor N ( i 1 , i 2 ) Return the bitwise exclusive disjunction of i 1 and i 2 . ixor N ( i 1 , i 2 ) = ibits N − 1 ( ibits N ( i 1 ) ⊻ ibits N ( i 2 )) 4.3.2.16. ishl N ( i 1 , i 2 ) Let k be i 2 modulo N . Return the result of shifting i 1 left by k bits, modulo 2 N . ishl N ( i 1 , i 2 ) = ibits N − 1 ( d 2 N − k 0 k ) ( if ibits N ( i 1 ) = d 1 k d 2 N − k ∧ k = i 2 mod N ) 4.3.2.17. ishr_u N ( i 1 , i 2 ) Let k be i 2 modulo N . Return the result of shifting i 1 right by k bits, extended with 0 bits. ishr_u N ( i 1 , i 2 ) = ibits N − 1 ( 0 k d 1 N − k ) ( if ibits N ( i 1 ) = d 1 N − k d 2 k ∧ k = i 2 mod N ) 4.3.2.18. ishr_s N ( i 1 , i 2 ) Let k be i 2 modulo N . Return the result of shifting i 1 right by k bits, extended with the most significant bit of the original value. ishr_s N ( i 1 , i 2 ) = ibits N − 1 ( d 0 k + 1 d 1 N − k − 1 ) ( if ibits N ( i 1 ) = d 0 d 1 N − k − 1 d 2 k ∧ k = i 2 mod N ) 4.3.2.19. irotl N ( i 1 , i 2 ) Let k be i 2 modulo N . Return the result of rotating i 1 left by k bits. irotl N ( i 1 , i 2 ) = ibits N − 1 ( d 2 N − k d 1 k ) ( if ibits N ( i 1 ) = d 1 k d 2 N − k ∧ k = i 2 mod N ) 4.3.2.20. irotr N ( i 1 , i 2 ) Let k be i 2 modulo N . Return the result of rotating i 1 right by k bits. irotr N ( i 1 , i 2 ) = ibits N − 1 ( d 2 k d 1 N − k ) ( if ibits N ( i 1 ) = d 1 N − k d 2 k ∧ k = i 2 mod N ) 4.3.2.21. iclz N ( i ) Return the count of leading zero bits in i ; all bits are considered leading zeros if i is 0 . iclz N ( i ) = k ( if ibits N ( i ) = 0 k ( 1 d ∗ ) ? ) 4.3.2.22. ictz N ( i ) Return the count of trailing zero bits in i ; all bits are considered trailing zeros if i is 0 . ictz N ( i ) = k ( if ibits N ( i ) = ( d ∗ 1 ) ? 0 k ) 4.3.2.23. ipopcnt N ( i ) Return the count of non-zero bits in i . ipopcnt N ( i ) = k ( if ibits N ( i ) = ( 0 ∗ 1 ) k 0 ∗ ) 4.3.2.24. ieqz N ( i ) Return 1 if i is zero, 0 otherwise. ieqz N ( i ) = bool ( i = 0 ) 4.3.2.25. inez N ( i ) Return 0 if i is zero, 1 otherwise. inez N ( i ) = bool ( i = 0 ) 4.3.2.26. ieq N ( i 1 , i 2 ) Return 1 if i 1 equals i 2 , 0 otherwise. ieq N ( i 1 , i 2 ) = bool ( i 1 = i 2 ) 4.3.2.27. ine N ( i 1 , i 2 ) Return 1 if i 1 does not equal i 2 , 0 otherwise. ine N ( i 1 , i 2 ) = bool ( i 1 = i 2 ) 4.3.2.28. ilt_u N ( i 1 , i 2 ) Return 1 if i 1 is less than i 2 , 0 otherwise. ilt_u N ( i 1 , i 2 ) = bool ( i 1 < i 2 ) 4.3.2.29. ilt_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 . Let j 2 be the signed interpretation of i 2 . Return 1 if j 1 is less than j 2 , 0 otherwise. ilt_s N ( i 1 , i 2 ) = bool ( signed N ( i 1 ) < signed N ( i 2 )) 4.3.2.30. igt_u N ( i 1 , i 2 ) Return 1 if i 1 is greater than i 2 , 0 otherwise. igt_u N ( i 1 , i 2 ) = bool ( i 1 > i 2 ) 4.3.2.31. igt_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 . Let j 2 be the signed interpretation of i 2 . Return 1 if j 1 is greater than j 2 , 0 otherwise. igt_s N ( i 1 , i 2 ) = bool ( signed N ( i 1 ) > signed N ( i 2 )) 4.3.2.32. ile_u N ( i 1 , i 2 ) Return 1 if i 1 is less than or equal to i 2 , 0 otherwise. ile_u N ( i 1 , i 2 ) = bool ( i 1 ≤ i 2 ) 4.3.2.33. ile_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 . Let j 2 be the signed interpretation of i 2 . Return 1 if j 1 is less than or equal to j 2 , 0 otherwise. ile_s N ( i 1 , i 2 ) = bool ( signed N ( i 1 ) ≤ signed N ( i 2 )) 4.3.2.34. ige_u N ( i 1 , i 2 ) Return 1 if i 1 is greater than or equal to i 2 , 0 otherwise. ige_u N ( i 1 , i 2 ) = bool ( i 1 ≥ i 2 ) 4.3.2.35. ige_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 . Let j 2 be the signed interpretation of i 2 . Return 1 if j 1 is greater than or equal to j 2 , 0 otherwise. ige_s N ( i 1 , i 2 ) = bool ( signed N ( i 1 ) ≥ signed N ( i 2 )) 4.3.2.36. iextend M _s N ( i ) Let j be the result of computing wrap N , M ( i ) . Return extend s M , N ( j ) . iextend M _s N ( i ) = extend s M , N ( wrap N , M ( i )) 4.3.2.37. ibitselect N ( i 1 , i 2 , i 3 ) Let j 1 be the bitwise conjunction of i 1 and i 3 . Let j 3 ′ be the bitwise negation of i 3 . Let j 2 be the bitwise conjunction of i 2 and j 3 ′ . Return the bitwise disjunction of j 1 and j 2 . ibitselect N ( i 1 , i 2 , i 3 ) = ior N ( iand N ( i 1 , i 3 ) , iand N ( i 2 , inot N ( i 3 ))) 4.3.2.38. iabs N ( i ) Let j be the signed interpretation of i . If j is greater than or equal to 0 , then return i . Else return the negation of j , modulo 2 N . iabs N ( i ) iabs N ( i ) = = i − signed N ( i ) mod 2 N ( if signed N ( i ) ≥ 0 ) ( otherwise ) 4.3.2.39. ineg N ( i ) Return the result of negating i , modulo 2 N . ineg N ( i ) = ( 2 N − i ) mod 2 N 4.3.2.40. imin_u N ( i 1 , i 2 ) Return i 1 if ilt_u N ( i 1 , i 2 ) is 1 , return i 2 otherwise. imin_u N ( i 1 , i 2 ) imin_u N ( i 1 , i 2 ) = = i 1 i 2 ( if ilt_u N ( i 1 , i 2 ) = 1 ) ( otherwise ) 4.3.2.41. imin_s N ( i 1 , i 2 ) Return i 1 if ilt_s N ( i 1 , i 2 ) is 1 , return i 2 otherwise. imin_s N ( i 1 , i 2 ) imin_s N ( i 1 , i 2 ) = = i 1 i 2 ( if ilt_s N ( i 1 , i 2 ) = 1 ) ( otherwise ) 4.3.2.42. imax_u N ( i 1 , i 2 ) Return i 1 if igt_u N ( i 1 , i 2 ) is 1 , return i 2 otherwise. imax_u N ( i 1 , i 2 ) imax_u N ( i 1 , i 2 ) = = i 1 i 2 ( if igt_u N ( i 1 , i 2 ) = 1 ) ( otherwise ) 4.3.2.43. imax_s N ( i 1 , i 2 ) Return i 1 if igt_s N ( i 1 , i 2 ) is 1 , return i 2 otherwise. imax_s N ( i 1 , i 2 ) imax_s N ( i 1 , i 2 ) = = i 1 i 2 ( if igt_s N ( i 1 , i 2 ) = 1 ) ( otherwise ) 4.3.2.44. iadd_sat_u N ( i 1 , i 2 ) Let i be the result of adding i 1 and i 2 . Return sat_u N ( i ) . iadd_sat_u N ( i 1 , i 2 ) = sat_u N ( i 1 + i 2 ) 4.3.2.45. iadd_sat_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 Let j 2 be the signed interpretation of i 2 Let j be the result of adding j 1 and j 2 . Return the value whose signed interpretation is sat_s N ( j ) . iadd_sat_s N ( i 1 , i 2 ) = signed N − 1 ( sat_s N ( signed N ( i 1 ) + signed N ( i 2 ))) 4.3.2.46. isub_sat_u N ( i 1 , i 2 ) Let i be the result of subtracting i 2 from i 1 . Return sat_u N ( i ) . isub_sat_u N ( i 1 , i 2 ) = sat_u N ( i 1 − i 2 ) 4.3.2.47. isub_sat_s N ( i 1 , i 2 ) Let j 1 be the signed interpretation of i 1 Let j 2 be the signed interpretation of i 2 Let j be the result of subtracting j 2 from j 1 . Return the value whose signed interpretation is sat_s N ( j ) . isub_sat_s N ( i 1 , i 2 ) = signed N − 1 ( sat_s N ( signed N ( i 1 ) − signed N ( i 2 ))) 4.3.2.48. iavgr_u N ( i 1 , i 2 ) Let j be the result of adding i 1 , i 2 , and 1 . Return the result of dividing j by 2 , truncated toward zero. iavgr_u N ( i 1 , i 2 ) = trunc (( i 1 + i 2 + 1 ) /2 ) 4.3.2.49. iq15mulrsat_s N ( i 1 , i 2 ) Return the whose signed interpretation is the result of sat_s N ( ishr_s N ( i 1 ⋅ i 2 + 2 14 , 15 )) . iq15mulrsat_s N ( i 1 , i 2 ) = signed N − 1 ( sat_s N ( ishr_s N ( i 1 ⋅ i 2 + 2 14 , 15 ))) 4.3.3. Floating-Point Operations Floating-point arithmetic follows the [IEEE-754-2019] standard, with the following qualifications: All operators use round-to-nearest ties-to-even, except where otherwise specified. Non-default directed rounding attributes are not supported. Following the recommendation that operators propagate NaN payloads from their operands is permitted but not required. All operators use “non-stop” mode, and floating-point exceptions are not otherwise observable. In particular, neither alternate floating-point exception handling attributes nor operators on status flags are supported. There is no observable difference between quiet and signalling NaNs. Note Some of these limitations may be lifted in future versions of WebAssembly. 4.3.3.1. Rounding Rounding always is round-to-nearest ties-to-even, in correspondence with [IEEE-754-2019] (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 N . A limit number for a given floating-point bit width N 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 N (that magnitude is 2 128 for N = 32 and 2 1024 for N = 64 ). A candidate number is either an exact floating-point number or a positive or negative limit number for the given bit width N . A candidate pair is a pair z 1 , z 2 of candidate numbers, such that no candidate number exists that lies between the two. A real number r is converted to a floating-point value of bit width N as follows: If r is 0 , then return + 0 . Else if r is an exact floating-point number, then return r . Else if r greater than or equal to the positive limit, then return + ∞ . Else if r is less than or equal to the negative limit, then return − ∞ . Else if z 1 and z 2 are a candidate pair such that z 1 < r < z 2 , then: If ∣r − z 1 ∣ < ∣r − z 2 ∣ , then let z be z 1 . Else if ∣r − z 1 ∣ > ∣r − z 2 ∣ , then let z be z 2 . Else if ∣r − z 1 ∣ = ∣r − z 2 ∣ and the significand of z 1 is even, then let z be z 1 . Else, let z be z 2 . If z is 0 , then: If r < 0 , then return − 0 . Else, return + 0 . Else if z is a limit number, then: If r < 0 , then return − ∞ . Else, return + ∞ . Else, return z . float N ( 0 ) float N ( r ) float N ( r ) float N ( r ) float N ( r ) D O U B L ES L A S H [ 1 e x ] closest N ( r , z 1 , z 2 ) closest N ( r , z 1 , z 2 ) closest N ( r , z 1 , z 2 ) closest N ( r , z 1 , z 2 ) D O U B L ES L A S H [ 1 e x ] rectify N ( r , ± limit N ) rectify N ( r , 0 ) rectify N ( r , 0 ) rectify N ( r , z ) = = = = = = = = = = = = = + 0 r + ∞ − ∞ closest N ( r , z 1 , z 2 ) rectify N ( r , z 1 ) rectify N ( r , z 2 ) rectify N ( r , z 1 ) rectify N ( r , z 2 ) ± ∞ + 0 ( r ≥ 0 ) − 0 ( r < 0 ) z ( if r ∈ exact N ) ( if r ≥ + limit N ) ( if r ≤ − limit N ) ( if z 1 < r < z 2 ∧ ( z 1 , z 2 ) ∈ candidatepair N ) ( if ∣ r − z 1 ∣ < ∣ r − z 2 ∣ ) ( if ∣ r − z 1 ∣ > ∣ r − z 2 ∣ ) ( if ∣ r − z 1 ∣ = ∣ r − z 2 ∣ ∧ even N ( z 1 )) ( if ∣ r − z 1 ∣ = ∣ r − z 2 ∣ ∧ even N ( z 2 )) where: exact N limit N candidate N candidatepair N D O U B L ES L A S H [ 1 e x ] even N (( d + m ⋅ 2 − M ) ⋅ 2 e ) even N ( ± limit N ) = = = = ⇔ ⇔ f N ∩ Q 2 2 expon ( N ) − 1 exact N ∪ { + limit N , − limit N } {( z 1 , z 2 ) ∈ candidate N 2 ∣ z 1 < z 2 ∧ ∀ z ∈ candidate N , z ≤ z 1 ∨ z ≥ z 2 } m mod 2 = 0 true 4.3.3.2. 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: [ ! DET ] [ ! DET ] nans N { z ∗ } nans N { z ∗ } nans N { z ∗ } = = = { + nan ( canon N )} { + nan ( n ) , − nan ( n ) ∣ n = canon N } { + nan ( n ) , − nan ( n ) ∣ n ≥ canon N } ( if { z ∗ } ⊆ { + nan ( canon N ) , − nan ( canon N )} ( if { z ∗ } ⊆ { + nan ( canon N ) , − nan ( canon N )} 4.3.3.3. fadd N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return an element of nans N { z 1 , z 2 } . Else if both z 1 and z 2 are infinities of opposite signs, then return an element of nans N { } . Else if both z 1 and z 2 are infinities of equal sign, then return that infinity. Else if either z 1 or z 2 is an infinity, then return that infinity. Else if both z 1 and z 2 are zeroes of opposite sign, then return positive zero. Else if both z 1 and z 2 are zeroes of equal sign, then return that zero. Else if either z 1 or z 2 is a zero, then return the other operand. Else if both z 1 and z 2 are values with the same magnitude but opposite signs, then return positive zero. Else return the result of adding z 1 and z 2 , rounded to the nearest representable value. fadd N ( ± nan ( n ) , z 2 ) fadd N ( z 1 , ± nan ( n )) fadd N ( ± ∞ , ∓ ∞ ) fadd N ( ± ∞ , ± ∞ ) fadd N ( z 1 , ± ∞ ) fadd N ( ± ∞ , z 2 ) fadd N ( ± 0 , ∓ 0 ) fadd N ( ± 0 , ± 0 ) fadd N ( z 1 , ± 0 ) fadd N ( ± 0 , z 2 ) fadd N ( ± q , ∓ q ) fadd N ( z 1 , z 2 ) = = = = = = = = = = = = nans N { ± nan ( n ) , z 2 } nans N { ± nan ( n ) , z 1 } nans N { } ± ∞ ± ∞ ± ∞ + 0 ± 0 z 1 z 2 + 0 float N ( z 1 + z 2 ) 4.3.3.4. fsub N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return an element of nans N { z 1 , z 2 } . Else if both z 1 and z 2 are infinities of equal signs, then return an element of nans N { } . Else if both z 1 and z 2 are infinities of opposite sign, then return z 1 . Else if z 1 is an infinity, then return that infinity. Else if z 2 is an infinity, then return that infinity negated. Else if both z 1 and z 2 are zeroes of equal sign, then return positive zero. Else if both z 1 and z 2 are zeroes of opposite sign, then return z 1 . Else if z 2 is a zero, then return z 1 . Else if z 1 is a zero, then return z 2 negated. Else if both z 1 and z 2 are the same value, then return positive zero. Else return the result of subtracting z 2 from z 1 , rounded to the nearest representable value. fsub N ( ± nan ( n ) , z 2 ) fsub N ( z 1 , ± nan ( n )) fsub N ( ± ∞ , ± ∞ ) fsub N ( ± ∞ , ∓ ∞ ) fsub N ( z 1 , ± ∞ ) fsub N ( ± ∞ , z 2 ) fsub N ( ± 0 , ± 0 ) fsub N ( ± 0 , ∓ 0 ) fsub N ( z 1 , ± 0 ) fsub N ( ± 0 , ± q 2 ) fsub N ( ± q , ± q ) fsub N ( z 1 , z 2 ) = = = = = = = = = = = = nans N { ± nan ( n ) , z 2 } nans N { ± nan ( n ) , z 1 } nans N { } ± ∞ ∓ ∞ ± ∞ + 0 ± 0 z 1 ∓ q 2 + 0 float N ( z 1 − z 2 ) Note Up to the non-determinism regarding NaNs, it always holds that fsub N ( z 1 , z 2 ) = fadd N ( z 1 , fneg N ( z 2 )) . 4.3.3.5. fmul N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return an element of nans N { z 1 , z 2 } . Else if one of z 1 and z 2 is a zero and the other an infinity, then return an element of nans N { } . Else if both z 1 and z 2 are infinities of equal sign, then return positive infinity. Else if both z 1 and z 2 are infinities of opposite sign, then return negative infinity. Else if either z 1 or z 2 is an infinity and the other a value with equal sign, then return positive infinity. Else if either z 1 or z 2 is an infinity and the other a value with opposite sign, then return negative infinity. Else if both z 1 and z 2 are zeroes of equal sign, then return positive zero. Else if both z 1 and z 2 are zeroes of opposite sign, then return negative zero. Else return the result of multiplying z 1 and z 2 , rounded to the nearest representable value. fmul N ( ± nan ( n ) , z 2 ) fmul N ( z 1 , ± nan ( n )) fmul N ( ± ∞ , ± 0 ) fmul N ( ± ∞ , ∓ 0 ) fmul N ( ± 0 , ± ∞ ) fmul N ( ± 0 , ∓ ∞ ) fmul N ( ± ∞ , ± ∞ ) fmul N ( ± ∞ , ∓ ∞ ) fmul N ( ± q 1 , ± ∞ ) fmul N ( ± q 1 , ∓ ∞ ) fmul N ( ± ∞ , ± q 2 ) fmul N ( ± ∞ , ∓ q 2 ) fmul N ( ± 0 , ± 0 ) fmul N ( ± 0 , ∓ 0 ) fmul N ( z 1 , z 2 ) = = = = = = = = = = = = = = = nans N { ± nan ( n ) , z 2 } nans N { ± nan ( n ) , z 1 } nans N { } nans N { } nans N { } nans N { } + ∞ − ∞ + ∞ − ∞ + ∞ − ∞ + 0 − 0 float N ( z 1 ⋅ z 2 ) 4.3.3.6. fdiv N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return an element of nans N { z 1 , z 2 } . Else if both z 1 and z 2 are infinities, then return an element of nans N { } . Else if both z 1 and z 2 are zeroes, then return an element of nans N { z 1 , z 2 } . Else if z 1 is an infinity and z 2 a value with equal sign, then return positive infinity. Else if z 1 is an infinity and z 2 a value with opposite sign, then return negative infinity. Else if z 2 is an infinity and z 1 a value with equal sign, then return positive zero. Else if z 2 is an infinity and z 1 a value with opposite sign, then return negative zero. Else if z 1 is a zero and z 2 a value with equal sign, then return positive zero. Else if z 1 is a zero and z 2 a value with opposite sign, then return negative zero. Else if z 2 is a zero and z 1 a value with equal sign, then return positive infinity. Else if z 2 is a zero and z 1 a value with opposite sign, then return negative infinity. Else return the result of dividing z 1 by z 2 , rounded to the nearest representable value. fdiv N ( ± nan ( n ) , z 2 ) fdiv N ( z 1 , ± nan ( n )) fdiv N ( ± ∞ , ± ∞ ) fdiv N ( ± ∞ , ∓ ∞ ) fdiv N ( ± 0 , ± 0 ) fdiv N ( ± 0 , ∓ 0 ) fdiv N ( ± ∞ , ± q 2 ) fdiv N ( ± ∞ , ∓ q 2 ) fdiv N ( ± q 1 , ± ∞ ) fdiv N ( ± q 1 , ∓ ∞ ) fdiv N ( ± 0 , ± q 2 ) fdiv N ( ± 0 , ∓ q 2 ) fdiv N ( ± q 1 , ± 0 ) fdiv N ( ± q 1 , ∓ 0 ) fdiv N ( z 1 , z 2 ) = = = = = = = = = = = = = = = nans N { ± nan ( n ) , z 2 } nans N { ± nan ( n ) , z 1 } nans N { } nans N { } nans N { } nans N { } + ∞ − ∞ + 0 − 0 + 0 − 0 + ∞ − ∞ float N ( z 1 / z 2 ) 4.3.3.7. fma N ( z 1 , z 2 , z 3 ) The function fma is the same as fusedMultiplyAdd defined by [IEEE-754-2019] (Section 5.4.1). It computes ( z 1 ⋅ z 2 ) + z 3 as if with unbounded range and precision, rounding only once for the final result. If either z 1 or z 2 or z 3 is a NaN, return an element of nans N z 1 , z 2 , z 3 . Else if either z 1 or z 2 is a zero and the other is an infinity, then return an element of nans N { } . Else if both z 1 or z 2 are infinities of equal sign, and z 3 is a negative infinity, then return an element of nans N { } . Else if both z 1 or z 2 are infinities of opposite sign, and z 3 is a positive infinity, then return an element of nans N { } . Else if either z 1 or z 2 is an infinity and the other is a value of the same sign, and z 3 is a negative infinity, then return an element of nans N { } . Else if either z 1 or z 2 is an infinity and the other is a value of the opposite sign, and z 3 is a positive infinity, then return an element of nans N { } . Else if both z 1 and z 2 are zeroes of the same sign and z 3 is a zero, then return positive zero. Else if both z 1 and z 2 are zeroes of the opposite sign and z 3 is a positive zero, then return positive zero. Else if both z 1 and z 2 are zeroes of the opposite sign and z 3 is a negative zero, then return negative zero. Else return the result of multiplying z 1 and z 2 , adding z 3 to the intermediate, and the final result ref: rounded <aux-ieee> to the nearest representable value. fma N ( ± nan ( n ) , z 2 , z 3 ) fma N ( z 1 , ± nan ( n ) , z 3 ) fma N ( z 1 , z 2 , ± nan ( n )) fma N ( ± ∞ , ± 0 , z 3 ) fma N ( ± ∞ , ∓ 0 , z 3 ) fma N ( ± ∞ , ± ∞ , − ∞ ) fma N ( ± ∞ , ∓ ∞ , + ∞ ) fma N ( ± q 1 , ± ∞ , − ∞ ) fma N ( ± q 1 , ∓ ∞ , + ∞ ) fma N ( ± ∞ , ± q 1 , − ∞ ) fma N ( ∓ ∞ , ± q 1 , + ∞ ) fma N ( ± 0 , ± 0 , ∓ 0 ) fma N ( ± 0 , ± 0 , ± 0 ) fma N ( ± 0 , ∓ 0 , + 0 ) fma N ( ± 0 , ∓ 0 , − 0 ) fma N ( z 1 , z 2 , z 3 ) = = = = = = = = = = = = = = = = nans N { ± nan ( n ) , z 2 , z 3 } nans N { ± nan ( n ) , z 1 , z 3 } nans N { ± nan ( n ) , z 1 , z 2 } nans N { } nans N { } nans N { } nans N { } nans N { } nans N { } nans N { } nans N { } + 0 + 0 + 0 − 0 float N ( z 1 ⋅ z 2 + z 3 ) 4.3.3.8. fmin N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return an element of nans N { z 1 , z 2 } . Else if either z 1 or z 2 is a negative infinity, then return negative infinity. Else if either z 1 or z 2 is a positive infinity, then return the other value. Else if both z 1 and z 2 are zeroes of opposite signs, then return negative zero. Else return the smaller value of z 1 and z 2 . fmin N ( ± nan ( n ) , z 2 ) fmin N ( z 1 , ± nan ( n )) fmin N ( + ∞ , z 2 ) fmin N ( − ∞ , z 2 ) fmin N ( z 1 , + ∞ ) fmin N ( z 1 , − ∞ ) fmin N ( ± 0 , ∓ 0 ) fmin N ( z 1 , z 2 ) fmin N ( z 1 , z 2 ) = = = = = = = = = nans N { ± nan ( n ) , z 2 } nans N { ± nan ( n ) , z 1 } z 2 − ∞ z 1 − ∞ − 0 z 1 z 2 ( if z 1 ≤ z 2 ) ( if z 2 ≤ z 1 ) 4.3.3.9. fmax N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return an element of nans N { z 1 , z 2 } . Else if either z 1 or z 2 is a positive infinity, then return positive infinity. Else if either z 1 or z 2 is a negative infinity, then return the other value. Else if both z 1 and z 2 are zeroes of opposite signs, then return positive zero. Else return the larger value of z 1 and z 2 . fmax N ( ± nan ( n ) , z 2 ) fmax N ( z 1 , ± nan ( n )) fmax N ( + ∞ , z 2 ) fmax N ( − ∞ , z 2 ) fmax N ( z 1 , + ∞ ) fmax N ( z 1 , − ∞ ) fmax N ( ± 0 , ∓ 0 ) fmax N ( z 1 , z 2 ) fmax N ( z 1 , z 2 ) = = = = = = = = = nans N { ± nan ( n ) , z 2 } nans N { ± nan ( n ) , z 1 } + ∞ z 2 + ∞ z 1 + 0 z 1 z 2 ( if z 1 ≥ z 2 ) ( if z 2 ≥ z 1 ) 4.3.3.10. fcopysign N ( z 1 , z 2 ) If z 1 and z 2 have the same sign, then return z 1 . Else return z 1 with negated sign. fcopysign N ( ± p 1 , ± p 2 ) fcopysign N ( ± p 1 , ∓ p 2 ) = = ± p 1 ∓ p 1 4.3.3.11. fabs N ( z ) If z is a NaN, then return z with positive sign. Else if z is an infinity, then return positive infinity. Else if z is a zero, then return positive zero. Else if z is a positive value, then z . Else return z negated. fabs N ( ± nan ( n )) fabs N ( ± ∞ ) fabs N ( ± 0 ) fabs N ( ± q ) = = = = + nan ( n ) + ∞ + 0 + q 4.3.3.12. fneg N ( z ) If z is a NaN, then return z with negated sign. Else if z is an infinity, then return that infinity negated. Else if z is a zero, then return that zero negated. Else return z negated. fneg N ( ± nan ( n )) fneg N ( ± ∞ ) fneg N ( ± 0 ) fneg N ( ± q ) = = = = ∓ nan ( n ) ∓ ∞ ∓ 0 ∓ q 4.3.3.13. fsqrt N ( z ) If z is a NaN, then return an element of nans N { z } . Else if z is negative infinity, then return an element of nans N { } . Else if z is positive infinity, then return positive infinity. Else if z is a zero, then return that zero. Else if z has a negative sign, then return an element of nans N { } . Else return the square root of z . fsqrt N ( ± nan ( n )) fsqrt N ( − ∞ ) fsqrt N ( + ∞ ) fsqrt N ( ± 0 ) fsqrt N ( − q ) fsqrt N ( + q ) = = = = = = nans N { ± nan ( n )} nans N { } + ∞ ± 0 nans N { } float N ( q ) 4.3.3.14. fceil N ( z ) If z is a NaN, then return an element of nans N { z } . Else if z is an infinity, then return z . Else if z is a zero, then return z . Else if z is smaller than 0 but greater than − 1 , then return negative zero. Else return the smallest integral value that is not smaller than z . fceil N ( ± nan ( n )) fceil N ( ± ∞ ) fceil N ( ± 0 ) fceil N ( − q ) fceil N ( ± q ) = = = = = nans N { ± nan ( n )} ± ∞ ± 0 − 0 float N ( i ) ( if − 1 < − q < 0 ) ( if ± q ≤ i < ± q + 1 ) 4.3.3.15. ffloor N ( z ) If z is a NaN, then return an element of nans N { z } . Else if z is an infinity, then return z . Else if z is a zero, then return z . Else if z is greater than 0 but smaller than 1 , then return positive zero. Else return the largest integral value that is not larger than z . ffloor N ( ± nan ( n )) ffloor N ( ± ∞ ) ffloor N ( ± 0 ) ffloor N ( + q ) ffloor N ( ± q ) = = = = = nans N { ± nan ( n )} ± ∞ ± 0 + 0 float N ( i ) ( if 0 < + q < 1 ) ( if ± q − 1 < i ≤ ± q ) 4.3.3.16. ftrunc N ( z ) If z is a NaN, then return an element of nans N { z } . Else if z is an infinity, then return z . Else if z is a zero, then return z . Else if z is greater than 0 but smaller than 1 , then return positive zero. Else if z is smaller than 0 but greater than − 1 , then return negative zero. Else return the integral value with the same sign as z and the largest magnitude that is not larger than the magnitude of z . ftrunc N ( ± nan ( n )) ftrunc N ( ± ∞ ) ftrunc N ( ± 0 ) ftrunc N ( + q ) ftrunc N ( − q ) ftrunc N ( ± q ) = = = = = = nans N { ± nan ( n )} ± ∞ ± 0 + 0 − 0 float N ( ± i ) ( if 0 < + q < 1 ) ( if − 1 < − q < 0 ) ( if + q − 1 < i ≤ + q ) 4.3.3.17. fnearest N ( z ) If z is a NaN, then return an element of nans N { z } . Else if z is an infinity, then return z . Else if z is a zero, then return z . Else if z is greater than 0 but smaller than or equal to 0.5 , then return positive zero. Else if z 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 z ; if two values are equally near, return the even one. fnearest N ( ± nan ( n )) fnearest N ( ± ∞ ) fnearest N ( ± 0 ) fnearest N ( + q ) fnearest N ( − q ) fnearest N ( ± q ) fnearest N ( ± q ) = = = = = = = nans N { ± nan ( n )} ± ∞ ± 0 + 0 − 0 float N ( ± i ) float N ( ± i ) ( if 0 < + q ≤ 0.5 ) ( if − 0.5 ≤ − q < 0 ) ( if ∣ i − q ∣ < 0.5 ) ( if ∣ i − q ∣ = 0.5 ∧ i even ) 4.3.3.18. feq N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return 0 . Else if both z 1 and z 2 are zeroes, then return 1 . Else if both z 1 and z 2 are the same value, then return 1 . Else return 0 . feq N ( ± nan ( n ) , z 2 ) feq N ( z 1 , ± nan ( n )) feq N ( ± 0 , ∓ 0 ) feq N ( z 1 , z 2 ) = = = = 0 0 1 bool ( z 1 = z 2 ) 4.3.3.19. fne N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return 1 . Else if both z 1 and z 2 are zeroes, then return 0 . Else if both z 1 and z 2 are the same value, then return 0 . Else return 1 . fne N ( ± nan ( n ) , z 2 ) fne N ( z 1 , ± nan ( n )) fne N ( ± 0 , ∓ 0 ) fne N ( z 1 , z 2 ) = = = = 1 1 0 bool ( z 1 = z 2 ) 4.3.3.20. flt N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return 0 . Else if z 1 and z 2 are the same value, then return 0 . Else if z 1 is positive infinity, then return 0 . Else if z 1 is negative infinity, then return 1 . Else if z 2 is positive infinity, then return 1 . Else if z 2 is negative infinity, then return 0 . Else if both z 1 and z 2 are zeroes, then return 0 . Else if z 1 is smaller than z 2 , then return 1 . Else return 0 . flt N ( ± nan ( n ) , z 2 ) flt N ( z 1 , ± nan ( n )) flt N ( z , z ) flt N ( + ∞ , z 2 ) flt N ( − ∞ , z 2 ) flt N ( z 1 , + ∞ ) flt N ( z 1 , − ∞ ) flt N ( ± 0 , ∓ 0 ) flt N ( z 1 , z 2 ) = = = = = = = = = 0 0 0 0 1 1 0 0 bool ( z 1 < z 2 ) 4.3.3.21. fgt N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return 0 . Else if z 1 and z 2 are the same value, then return 0 . Else if z 1 is positive infinity, then return 1 . Else if z 1 is negative infinity, then return 0 . Else if z 2 is positive infinity, then return 0 . Else if z 2 is negative infinity, then return 1 . Else if both z 1 and z 2 are zeroes, then return 0 . Else if z 1 is larger than z 2 , then return 1 . Else return 0 . fgt N ( ± nan ( n ) , z 2 ) fgt N ( z 1 , ± nan ( n )) fgt N ( z , z ) fgt N ( + ∞ , z 2 ) fgt N ( − ∞ , z 2 ) fgt N ( z 1 , + ∞ ) fgt N ( z 1 , − ∞ ) fgt N ( ± 0 , ∓ 0 ) fgt N ( z 1 , z 2 ) = = = = = = = = = 0 0 0 1 0 0 1 0 bool ( z 1 > z 2 ) 4.3.3.22. fle N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return 0 . Else if z 1 and z 2 are the same value, then return 1 . Else if z 1 is positive infinity, then return 0 . Else if z 1 is negative infinity, then return 1 . Else if z 2 is positive infinity, then return 1 . Else if z 2 is negative infinity, then return 0 . Else if both z 1 and z 2 are zeroes, then return 1 . Else if z 1 is smaller than or equal to z 2 , then return 1 . Else return 0 . fle N ( ± nan ( n ) , z 2 ) fle N ( z 1 , ± nan ( n )) fle N ( z , z ) fle N ( + ∞ , z 2 ) fle N ( − ∞ , z 2 ) fle N ( z 1 , + ∞ ) fle N ( z 1 , − ∞ ) fle N ( ± 0 , ∓ 0 ) fle N ( z 1 , z 2 ) = = = = = = = = = 0 0 1 0 1 1 0 1 bool ( z 1 ≤ z 2 ) 4.3.3.23. fge N ( z 1 , z 2 ) If either z 1 or z 2 is a NaN, then return 0 . Else if z 1 and z 2 are the same value, then return 1 . Else if z 1 is positive infinity, then return 1 . Else if z 1 is negative infinity, then return 0 . Else if z 2 is positive infinity, then return 0 . Else if z 2 is negative infinity, then return 1 . Else if both z 1 and z 2 are zeroes, then return 1 . Else if z 1 is larger than or equal to z 2 , then return 1 . Else return 0 . fge N ( ± nan ( n ) , z 2 ) fge N ( z 1 , ± nan ( n )) fge N ( z , z ) fge N ( + ∞ , z 2 ) fge N ( − ∞ , z 2 ) fge N ( z 1 , + ∞ ) fge N ( z 1 , − ∞ ) fge N ( ± 0 , ∓ 0 ) fge N ( z 1 , z 2 ) = = = = = = = = = 0 0 1 1 0 0 1 1 bool ( z 1 ≥ z 2 ) 4.3.3.24. fpmin N ( z 1 , z 2 ) If z 2 is less than z 1 then return z 2 . Else return z 1 . fpmin N ( z 1 , z 2 ) fpmin N ( z 1 , z 2 ) = = z 2 z 1 ( if flt N ( z 2 , z 1 ) = 1 ) ( otherwise ) 4.3.3.25. fpmax N ( z 1 , z 2 ) If z 1 is less than z 2 then return z 2 . Else return z 1 . fpmax N ( z 1 , z 2 ) fpmax N ( z 1 , z 2 ) = = z 2 z 1 ( if flt N ( z 1 , z 2 ) = 1 ) ( otherwise ) 4.3.4. Conversions 4.3.4.1. extend u M , N ( i ) Return i . extend u M , N ( i ) = i Note In the abstract syntax, unsigned extension just reinterprets the same value. 4.3.4.2. extend s M , N ( i ) Let j be the signed interpretation of i of size M . Return the two’s complement of j relative to size N . extend s M , N ( i ) = signed N − 1 ( signed M ( i )) 4.3.4.3. wrap M , N ( i ) Return i modulo 2 N . wrap M , N ( i ) = i mod 2 N 4.3.4.4. trunc u M , N ( z ) If z is a NaN, then the result is undefined. Else if z is an infinity, then the result is undefined. Else if z is a number and trunc ( z ) is a value within range of the target type, then return that value. Else the result is undefined. trunc u M , N ( ± nan ( n )) trunc u M , N ( ± ∞ ) trunc u M , N ( ± q ) trunc u M , N ( ± q ) = = = = { } { } trunc ( ± q ) { } ( if − 1 < trunc ( ± q ) < 2 N ) ( otherwise ) Note This operator is partial . It is not defined for NaNs, infinities, or values for which the result is out of range. 4.3.4.5. trunc s M , N ( z ) If z is a NaN, then the result is undefined. Else if z is an infinity, then the result is undefined. If z is a number and trunc ( z ) is a value within range of the target type, then return that value. Else the result is undefined. trunc s M , N ( ± nan ( n )) trunc s M , N ( ± ∞ ) trunc s M , N ( ± q ) trunc s M , N ( ± q ) = = = = { } { } trunc ( ± q ) { } ( if − 2 N − 1 − 1 < trunc ( ± q ) < 2 N − 1 ) ( otherwise ) Note This operator is partial . It is not defined for NaNs, infinities, or values for which the result is out of range. 4.3.4.6. trunc_sat_u M , N ( z ) If z is a NaN, then return 0 . Else if z is negative infinity, then return 0 . Else if z is positive infinity, then return 2 N − 1 . Else, return sat_u N ( trunc ( z )) . trunc_sat_u M , N ( ± nan ( n )) trunc_sat_u M , N ( − ∞ ) trunc_sat_u M , N ( + ∞ ) trunc_sat_u M , N ( z ) = = = = 0 0 2 N − 1 sat_u N ( trunc ( z )) 4.3.4.7. trunc_sat_s M , N ( z ) If z is a NaN, then return 0 . Else if z is negative infinity, then return − 2 N − 1 . Else if z is positive infinity, then return 2 N − 1 − 1 . Else, return the value whose signed interpretation is sat_s N ( trunc ( z )) . trunc_sat_s M , N ( ± nan ( n )) trunc_sat_s M , N ( − ∞ ) trunc_sat_s M , N ( + ∞ ) trunc_sat_s M , N ( z ) = = = = 0 − 2 N − 1 2 N − 1 − 1 signed N − 1 ( sat_s N ( trunc ( z ))) 4.3.4.8. promote M , N ( z ) If z is a canonical NaN , then return an element of nans N { } (i.e., a canonical NaN of size N ). Else if z is a NaN, then return an element of nans N { ± nan ( 1 )} (i.e., any arithmetic NaN of size N ). Else, return z . promote M , N ( ± nan ( n )) promote M , N ( ± nan ( n )) promote M , N ( z ) = = = nans N { } nans N { + nan ( 1 )} z ( if n = canon N ) ( otherwise ) 4.3.4.9. demote M , N ( z ) If z is a canonical NaN , then return an element of nans N { } (i.e., a canonical NaN of size N ). Else if z is a NaN, then return an element of nans N { ± nan ( 1 )} (i.e., any NaN of size N ). Else if z is an infinity, then return that infinity. Else if z is a zero, then return that zero. Else, return float N ( z ) . demote M , N ( ± nan ( n )) demote M , N ( ± nan ( n )) demote M , N ( ± ∞ ) demote M , N ( ± 0 ) demote M , N ( ± q ) = = = = = nans N { } nans N { + nan ( 1 )} ± ∞ ± 0 float N ( ± q ) ( if n = canon N ) ( otherwise ) 4.3.4.10. convert u M , N ( i ) Return float N ( i ) . convert u M , N ( i ) = float N ( i ) 4.3.4.11. convert s M , N ( i ) Let j be the signed interpretation of i . Return float N ( j ) . convert s M , N ( i ) = float N ( signed M ( i )) 4.3.4.12. reinterpret t 1 , t 2 ( c ) Let d ∗ be the bit sequence bits t 1 ( c ) . Return the constant c ′ for which bits t 2 ( c ′ ) = d ∗ . reinterpret t 1 , t 2 ( c ) = bits t 2 − 1 ( bits t 1 ( c )) 4.3.4.13. narrow s M , N ( i ) Let j be the signed interpretation of i of size M . Return the value whose signed interpretation is sat_s N ( j ) . narrow s M , N ( i ) = signed N − 1 ( sat_s N ( signed M ( i ))) 4.3.4.14. narrow u M , N ( i ) Let j be the signed interpretation of i of size M . Return sat_u N ( j ) . narrow u M , N ( i ) = sat_u N ( signed M ( i )) 4.3.5. Vector Operations Most vector operations are performed by applying numeric operations lanewise. However, some operators consider multiple lanes at once. 4.3.5.1. ivbitmask N ( i m ) For each i k in i m , let b k be the result of computing ilt_s N ( i , 0 ) . Let b m be the concatenation of all b k . Return the result of computing ibits 32 − 1 (( 0 ) 32 − m b m ) . ivbitmask N ( i m ) = ibits 32 − 1 (( 0 ) 32 − m ilt_s N ( i , 0 ) m ) 4.3.5.2. ivswizzle ( i n , j n ) For each j k in j n , let r k be the value ivswizzle_lane ( i n , j k ) . Let r n be the concatenation of all r k . Return r n . ivswizzle ( i n , j n ) = ivswizzle_lane ( i n , j ) n where: ivswizzle_lane ( i n , j ) ivswizzle_lane ( i n , j ) = = i n [ j ] 0 ( if j < n ) ( otherwise ) 4.3.5.3. ivshuffle ( j n , i 1 n , i 2 n ) Let i ∗ ne the concatenation of i 1 n and i 2 n . For each j k in j n , let r k be i ∗ [ j k ] . Let r n be the concatenation of all r k . Return r n . ivshuffle ( j n , i 1 n , i 2 n ) = (( i 1 n i 2 n ) [ j ] ) n ( if ( j < 2 ⋅ n ) n ) 4.3.5.4. ivadd_pairwise N ( i 2m ) Let ( i 1 i 2 ) m be i 2m , decomposed into pairwise elements. For each i 1k in i 1 m and corresponding i 2k in i 2 m , let r k be iadd N ( i 1k , i 2k ) . Let r m be the concatenation of all r k . Return r m . ivadd_pairwise N ( i 2 m ) = ( iadd N ( i 1 , i 2 ) ) m ( if i 2 m = ( i 1 i 2 ) m ) 4.3.5.5. ivmul N ( i 1 m , i 2 m ) For each i 1k in i 1 m and corresponding i 2k in i 2 m , let r k be imul N ( i 1k , i 2k ) . Let r m be the concatenation of all r k . Return r m . ivmul N ( i 1 m , i 2 m ) = ( imul N ( i 1 , i 2 ) ) m 4.3.5.6. ivdot N ( i 1 2m , i 2 2m ) For each i 1k in i 1 2m and corresponding i 2k in i 2 2m , let j k be imul N ( i 1k , i 2k ) . Let j 2m be the concatenation of all j k . Let ( j 1 j 2 ) m be j 2m , decomposed into pairwise elements. For each i 1k in i 1 m and corresponding i 2k in i 2 m , let r k be iadd N ( i 1k , i 2k ) . Let r m be the concatenation of all r k . Return r m . ivdot N ( i 1 2 m , i 2 2 m ) = ( iadd N ( j 1 , j 2 ) ) m ( if ( imul N ( i 1 , i 2 ) ) 2 m = ( j 1 j 2 ) m ) 4.3.5.7. ivdotsat N ( i 1 m , i 2 m ) For each i 1k in i 1 2m and corresponding i 2k in i 2 2m , let j k be imul N ( i 1k , i 2k ) . Let j 2m be the concatenation of all j k . Let ( j 1 j 2 ) m be j 2m , decomposed into pairwise elements. For each i 1k in i 1 m and corresponding i 2k in i 2 m , let r k be iadd_sat N ( i 1k , i 2k ) . Let r m be the concatenation of all r k . Return r m . ivdotsat N ( i 1 2 m , i 2 2 m ) = ( iadd_sat N ( j 1 , j 2 ) ) m ( if ( imul N ( i 1 , i 2 ) ) 2 m = ( j 1 j 2 ) m ) The previous operators are lifted to operators on arguments of vector type by wrapping them in corresponding lane projections and injections and intermediate extension operations: 4.3.5.8. vextunop sh 1 , sh 2 ( c ) extadd_pairwise _ sx i N 1 x M 1 , i N 2 x M 2 ( c ) = lanes i N 2 x M 2 − 1 ( j ∗ ) ( if i ∗ = lanes i N 1 x M 1 ( c ) ∧ i ′ ∗ = extend N 1 , N 2 sx ( i ) ∗ ∧ j ∗ = ivadd_pairwise N 2 ( i ′ ∗ ) 4.3.5.9. vextbinop sh 1 , sh 2 ( c 1 , c 2 ) vextbinop i N 1 x M 1 , i N 2 x M 2 ( c 1 , c 2 ) = lanes i N 2 x M 2 − 1 ( j ∗ ) ( if i 1 ∗ = lanes i N 1 x M 1 ( c 1 ) [ h : k ] ∧ i 2 ∗ = lanes i N 1 x M 1 ( c 2 ) [ h : k ] ∧ i 1 ′ ∗ = extend N 1 , N 2 sx ( i 1 ) ∗ ∧ i 2 ′ ∗ = extend N 1 , N 2 sx ( i 2 ) ∗ ∧ j ∗ = f N 2 ( i 1 ′ ∗ , i 2 ′ ∗ ) where f , sx 1 , sx 2 , h , and k are instantiated as follows, depending on the operator: vextbinop extmul _ low _ sx extmul _ high _ sx dot _ s relaxed_dot _ s f ivmul ivmul ivdot ivdotsat sx 1 sx sx s s sx 2 sx sx s relaxed ( R idot ) [ s , u ] h 0 M 2 0 0 k M 2 M 2 M 1 M 1 Note Relaxed operations and the paramater R idot are introduced below . 4.3.5.10. vextternop sh 1 , sh 2 ( c 1 , c 2 , c 3 ) relaxed_dot_add _ s i N 1 x M 1 , i N 2 x M 2 ( c 1 , c 2 , c 3 ) = c ( if N = 2 ⋅ N 1 ∧ M = 2 ⋅ M 2 ∧ c ′ = relaxed_dot _ s i N 1 x M 1 , i N x M ( c 1 , c 2 ) ∧ c ′′ = extadd_pairwise _ s i N x M , i N 2 x M 2 ( c ′ ) ∧ c ∈ add i N 2 x M 2 ( c ′′ , c 3 ) 4.3.5.11. narrow _ sx sh 1 , sh 2 ( c 1 , c 2 ) narrow _ sx i N 1 x M 1 , i N 2 x M 2 ( c 1 , c 2 ) = lanes i N 2 x M 2 − 1 ( j ∗ ) ( if i 1 ∗ = lanes i N 1 x M 1 ( c 1 ) ∧ i 2 ∗ = lanes i N 1 x M 1 ( c 2 ) ∧ i 1 ′ ∗ = narrow N 1 , N 2 sx ( i 1 ) ∗ ∧ i 2 ′ ∗ = narrow N 1 , N 2 sx ( i 2 ) ∗ ∧ j ∗ = i 1 ′ ∗ ⊕ i 1 ′ ∗ 4.3.5.12. vcvtop _ half ? _ zero sh 1 , sh 2 ? ( i ) vcvtop _ half ? _ zero t 1 x M 1 , t 2 x M 2 ? ( i ) = j ( if condition ∧ c ∗ = lanes t 1 x M 1 ( i ) [ h : k ] ∧ c ′ ∗ ∗ = × ( vcvtop ∣ t 1 ∣ , ∣ t 2 ∣ ( c ) ∗ ⊕ ( 0 ) n ) ∧ j ∈ lanes t 2 x M 2 − 1 ( c ′ ∗ ) ∗ where h , k , n , and condition are instantiated as follows, depending on the operator: half ? ϵ low high ϵ zero ? ϵ ϵ ϵ zero h 0 0 M 2 0 k M 1 M 2 M 2 M 1 n 0 0 0 M 1 condition ( M 1 = M 2 ) ( M 1 = 2 ⋅ M 2 ) ( M 1 = 2 ⋅ M 2 ) ( 2 ⋅ M 1 = M 2 ) while × { x ∗ } N transforms a sequence of N sets of non-deterministic values into a set of non-deterministic sequences of N values by computing the set product: × ( S 1 … S N ) = { x 1 … x N ∣ x 1 ∈ S 1 ∧ ⋯ ∧ x N ∈ S N } 4.3.6. Relaxed Operations The result of relaxed operators are implementation-dependent , because the set of possible results may depend on properties of the host environment, such as its hardware. Technically, their behaviour is controlled by a set of global parameters to the semantics that an implementation can instantiate in different ways. These choices are fixed, that is, parameters are constant during the execution of any given program. Every such parameter is an index into a sequence of possible sets of results and must be instantiated to a defined index. In the deterministic profile , every parameter is prescribed to be 0. This behaviour is expressed by the following auxiliary function, where R is a global parameter selecting one of the allowed outcomes: [ ! DET ] relaxed ( R ) [ A 0 , … , A n ] = A R relaxed ( R ) [ A 0 , … , A n ] = A 0 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 hardware, or the behaviour of the deterministic profile. 4.3.6.1. frelaxed_madd N ( z 1 , z 2 , z 3 ) The implementation-specific behaviour of this operation is determined by the global parameter R fmadd ∈ { 0 , 1 } . Return relaxed ( R fmadd ) [ fadd N ( fmul N ( z 1 , z 2 ) , z 3 ) , fma N ( z 1 , z 2 , z 3 )] . frelaxed_madd N ( z 1 , z 2 , z 3 ) = relaxed ( R fmadd ) [ fadd N ( fmul N ( z 1 , z 2 ) , z 3 ) , fma N ( z 1 , z 2 , z 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. 4.3.6.2. frelaxed_nmadd N ( z 1 , z 2 , z 3 ) Return frelaxed_madd ( − z 1 , z 2 , z 3 ) . frelaxed_nmadd N ( z 1 , z 2 , z 3 ) = frelaxed_madd N ( − z 1 , z 2 , z 3 ) Note This operation is implementation-dependent because frelaxed_madd is implementation-dependent. 4.3.6.3. frelaxed_min N ( z 1 , z 2 ) The implementation-specific behaviour of this operation is determined by the global parameter R fmin ∈ { 0 , 1 , 2 , 3 } . If z 1 is a NaN, then return relaxed ( R fmin ) [ fmin N ( z 1 , z 2 ) , nan ( n ) , z 2 , z 2 ] . If z 2 is a NaN, then return relaxed ( R fmin ) [ fmin N ( z 1 , z 2 ) , z 1 , nan ( n ) , z 1 ] . If both z 1 and z 2 are zeroes of opposite sign, then return relaxed ( R fmin ) [ fmin N ( z 1 , z 2 ) , pm 0, mp 0, -0 ]`. Return fmin N ( z 1 , z 2 ) . frelaxed_min N ( ± nan ( n ) , z 2 ) frelaxed_min N ( z 1 , ± nan ( n )) frelaxed_min N ( ± 0 , ∓ 0 ) frelaxed_min N ( z 1 , z 2 ) = = = = relaxed ( R fmin ) [ fmin N ( ± nan ( n ) , z 2 ) , nan ( n ) , z 2 , z 2 ] relaxed ( R fmin ) [ fmin N ( z 1 , ± nan ( n )) , z 1 , nan ( n ) , z 1 ] relaxed ( R fmin ) [ fmin N ( ± 0 , ∓ 0 ) , ± 0 , ∓ 0 , − 0 ] fmin N ( z 1 , z 2 ) ( otherwise ) Note Relaxed minimum is implementation-dependent for NaNs and for zeroes with different signs. In the deterministic profile , it behaves like regular fmin . 4.3.6.4. frelaxed_max N ( z 1 , z 2 ) The implementation-specific behaviour of this operation is determined by the global parameter R fmax ∈ { 0 , 1 , 2 , 3 } . If z 1 is a NaN, then return relaxed ( R fmax ) [ fmax N ( z 1 , z 2 ) , nan ( n ) , z 2 , z 2 ] . If z 2 is a NaN, then return relaxed ( R fmax ) [ fmax N ( z 1 , z 2 ) , z 1 , nan ( n ) , z 1 ] . If both z 1 and z 2 are zeroes of opposite sign, then return relaxed ( R fmax ) [ fmax N ( z 1 , z 2 ) , pm 0, mp 0, +0 ]`. Return fmax N ( z 1 , z 2 ) . frelaxed_max N ( ± nan ( n ) , z 2 ) frelaxed_max N ( z 1 , ± nan ( n )) frelaxed_max N ( ± 0 , ∓ 0 ) frelaxed_max N ( z 1 , z 2 ) = = = = relaxed ( R fmax ) [ fmax N ( ± nan ( n ) , z 2 ) , nan ( n ) , z 2 , z 2 ] relaxed ( R fmax ) [ fmax N ( z 1 , ± nan ( n )) , z 1 , nan ( n ) , z 1 ] relaxed ( R fmax ) [ fmax N ( ± 0 , ∓ 0 ) , ± 0 , ∓ 0 , + 0 ] fmax N ( z 1 , z 2 ) ( otherwise ) Note Relaxed maximum is implementation-dependent for NaNs and for zeroes with different signs. In the deterministic profile , it behaves like regular fmax . 4.3.6.5. irelaxed_q15mulr_s N ( i 1 , i 2 ) The implementation-specific behaviour of this operation is determined by the global parameter R iq15mulr ∈ { 0 , 1 } . If both i 1 and i 2 equal ( signed N − 1 ( − 2 N − 1 ) , then return relaxed ( R iq15mulr ) [ 2 N − 1 − 1 , signed N − 1 ( − 2 N − 1 )] . Return iq15mulrsat_s ( i 1 , i 2 ) irelaxed_q15mulr_s N ( signed N − 1 ( − 2 N − 1 ) , signed N − 1 ( − 2 N − 1 )) irelaxed_q15mulr_s N ( i 1 , i 2 ) = = relaxed ( R iq15mulr ) [ 2 N − 1 − 1 , signed N − 1 ( − 2 N − 1 )] iq15mulrsat_s ( i 1 , i 2 ) Note Relaxed Q15 multiplication is implementation-dependent when the result overflows. In the deterministic profile , it behaves like regular iq15mulrsat_s . 4.3.6.6. relaxed_trunc M , N u ( z ) The implementation-specific behaviour of this operation is determined by the global parameter R trunc_u ∈ { 0 , 1 } . If z is normal or subnormal and trunc ( z ) is non-negative and less than 2 N , then return trunc u M , N ( z ) . Else, return relaxed ( R trunc_u ) [ trunc_sat_u M , N ( z ) , R ] . relaxed_trunc M , N u ( ± q ) relaxed_trunc M , N u ( z ) = = trunc u M , N ( ± q ) relaxed ( R trunc_u ) [ trunc_sat_u M , N ( z ) , R ] ( if 0 ≤ trunc ( ± q ) < 2 N ) ( 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 . 4.3.6.7. relaxed_trunc M , N s ( z ) The implementation-specific behaviour of this operation is determined by the global parameter R trunc_s ∈ { 0 , 1 } . If z is normal or subnormal and trunc ( z ) is greater than or equal to − 2 N − 1 and less than 2 N − 1 , then return trunc s M , N ( z ) . Else, return relaxed ( R trunc_s ) [ trunc_sat_s M , N ( z ) , R ] . relaxed_trunc M , N s ( ± q ) relaxed_trunc M , N s ( z ) = = trunc s M , N ( ± q ) relaxed ( R trunc_s ) [ trunc_sat_s M , N ( z ) , R ] ( if − 2 N − 1 ≤ trunc ( ± q ) < 2 N − 1 ) ( 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 . 4.3.6.8. ivrelaxed_swizzle ( i n , j n ) The implementation-specific behaviour of this operation is determined by the global parameter R swizzle ∈ { 0 , 1 } . For each j k in j n , let r k be the value ivrelaxed_swizzle_lane ( i n , j k ) . Let r n be the concatenation of all r k . Return r n . ivrelaxed_swizzle ( i n , j n ) = ivrelaxed_swizzle_lane ( i n , j ) n where: ivrelaxed_swizzle_lane ( i n , j ) ivrelaxed_swizzle_lane ( i n , j ) ivrelaxed_swizzle_lane ( i n , j ) = = = i [ j ] 0 relaxed ( R swizzle ) [ 0 , i n [ j mod n ]] ( if j < 16 ) ( if signed 8 ( j ) < 0 ) ( otherwise ) Note Relaxed swizzle is implementation-dependent if the signed interpretation of any of the 8-bit indices in j n is larger than or equal to 16. In the deterministic profile , it behaves like regular ivswizzle . 4.3.6.9. relaxed_dot ( i 1 , i 2 ) The implementation-specific behaviour of this operation is determined by the global parameter R 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. 4.3.6.10. irelaxed_laneselect N ( i 1 , i 2 , i 3 ) The implementation-specific behaviour of this operation is determined by the global parameter R laneselect ∈ { 0 , 1 } . If i 3 is smaller than 2 N − 1 , then let i 3 ′ be the value 0 , otherwise 2 N − 1 . Let i 3 ′′ be relaxed ( R laneselect ) [ i 3 , i 3 ′ ] . Return ibitselect N ( i 1 , i 2 , i 3 ′′ ) . irelaxed_laneselect N ( i 1 , i 2 , i 3 ) = ibitselect N ( i 1 , i 2 , relaxed ( R laneselect ) [ i 3 , extend s 1 , N ( ishr_u N ( i 3 , N − 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 x occurring in it with the corresponding defined type moduleinst . types [ x ] . clos moduleinst ( t ) = t [ := moduleinst . types ] 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 S in which possibly referenced addresses live. 4.5.1.1. Numeric Values The number value ( nt . const c ) is valid with the number type nt . s ⊢ nt . const c : nt 4.5.1.2. Vector Values The vector value ( vt . const c ) is valid with the vector type vt . s ⊢ vt . const c : vt 4.5.1.3. Null References The reference value ref . null is valid with the reference type ( ref null bot ) . s ⊢ ref . null : ref null bot 4.5.1.4. Scalar References The reference value ( ref . i 31 i ) is valid with the reference type ( ref i 31 ) . s ⊢ ref . i 31 i : ref i 31 4.5.1.5. Structure References The reference value ( ref . struct a ) is valid with the reference type ( ref dt ) if: The structure instance s . structs [ a ] exists. The defined type s . structs [ a ] . type is of the form dt . s ⊢ ref . struct a : ref dt s . structs [ a ] . type = dt 4.5.1.6. Array References The reference value ( ref . array a ) is valid with the reference type ( ref dt ) if: The array instance s . arrays [ a ] exists. The defined type s . arrays [ a ] . type is of the form dt . s ⊢ ref . array a : ref dt s . arrays [ a ] . type = dt 4.5.1.7. Exception References The reference value ( ref . exn a ) is valid with the reference type ( ref exn ) if: The exception instance s . exns [ a ] exists. s ⊢ ref . exn a : ref exn s . exns [ a ] = exn 4.5.1.8. Function References The reference value ( ref . func a ) is valid with the reference type ( ref dt ) if: The function instance s . funcs [ a ] exists. The defined type s . funcs [ a ] . type is of the form dt . s ⊢ ref . func a : ref dt s . funcs [ a ] . type = dt 4.5.1.9. Host References The reference value ( ref . host a ) is valid with the reference type ( ref any ) . s ⊢ ref . host a : ref any Note A bare host reference is considered internalized. 4.5.1.10. External References The reference value ( ref . extern ref ) is valid with the reference type ( ref extern ) if: The reference value ref is valid with the reference type ( ref any ) . The reference value ref is not of the form ref . null . s ⊢ ref . extern ref : ref extern s ⊢ ref : ref any ref = ref . null 4.5.1.11. Subsumption The reference value ref is valid with the reference type rt if: The reference value ref is valid with the reference type rt ′ . Under the context { return ϵ } , the reference type rt is valid . The reference type rt ′ matches the reference type rt . s ⊢ ref : rt s ⊢ ref : rt ′ { } ⊢ rt : ok { } ⊢ rt ′ ≤ rt 4.5.2. External Typing For the purpose of checking external address against imports , such values are classified by external types . The following auxiliary typing rules specify this typing relation relative to a store S in which the referenced instances live. 4.5.2.1. Functions The external address ( func a ) is valid with the external type ( func funcinst . type ) if: The function instance s . funcs [ a ] exists. The function instance s . funcs [ a ] is of the form funcinst . s ⊢ func a : func funcinst . type s . funcs [ a ] = funcinst 4.5.2.2. Tables The external address ( table a ) is valid with the external type ( table tableinst . type ) if: The table instance s . tables [ a ] exists. The table instance s . tables [ a ] is of the form tableinst . s ⊢ table a : table tableinst . type s . tables [ a ] = tableinst 4.5.2.3. Memories The external address ( mem a ) is valid with the external type ( mem meminst . type ) if: The memory instance s . mems [ a ] exists. The memory instance s . mems [ a ] is of the form meminst . s ⊢ mem a : mem meminst . type s . mems [ a ] = meminst 4.5.2.4. Globals The external address ( global a ) is valid with the external type ( global globalinst . type ) if: The global instance s . globals [ a ] exists. The global instance s . globals [ a ] is of the form globalinst . s ⊢ global a : global globalinst . type s . globals [ a ] = globalinst 4.5.2.5. Tags The external address ( tag a ) is valid with the external type ( tag taginst . type ) if: The tag instance s . tags [ a ] exists. The tag instance s . tags [ a ] is of the form taginst . s ⊢ tag a : tag taginst . type s . tags [ a ] = taginst 4.5.2.6. Subsumption The external address externaddr is valid with the external type xt if: The external address externaddr is valid with the external type xt ′ . Under the context { return ϵ } , the external type xt is valid . The external type xt ′ matches the external type xt . s ⊢ externaddr : xt s ⊢ externaddr : xt ′ { } ⊢ xt : ok { } ⊢ xt ′ ≤ xt 4.6. Instructions WebAssembly computation is performed by executing individual instructions . 4.6.1. Parametric Instructions 4.6.1.1. nop Do nothing. nop ↪ ϵ 4.6.1.2. unreachable Trap. unreachable ↪ trap 4.6.1.3. drop Assert: Due to validation , a value is on the top of the stack. Pop the value val from the stack. val drop ↪ ϵ 4.6.1.4. select ( t ∗ ) ? Assert: Due to validation , a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const c ) from the stack. Assert: Due to validation , a value is on the top of the stack. Pop the value val 2 from the stack. Assert: Due to validation , a value is on the top of the stack. Pop the value val 1 from the stack. If c = 0 , then: Push the value val 1 to the stack. Else: Push the value val 2 to the stack. val 1 val 2 ( i 32 . const c ) ( select ( t ∗ ) ? ) val 1 val 2 ( i 32 . const c ) ( select ( t ∗ ) ? ) ↪ ↪ val 1 val 2 if c = 0 if c = 0 Note In future versions of WebAssembly, select may allow more than one value per choice. 4.6.2. Control Instructions 4.6.2.1. block bt instr ∗ Let z be the current state. Let t 1 m → localidx 0 ∗ t 2 n be the destructuring of instrtype z ( bt ) . Assert: Due to validation , localidx 0 ∗ = ϵ . Assert: Due to validation , there are at least m values on the top of the stack. Pop the values val m from the stack. Let L be the label whose arity is n and whose continuation is the end of the block. Enter the block val m instr ∗ with the label L . z ; val m ( block bt instr ∗ ) ↪ ( label n { ϵ } val m instr ∗ ) if instrtype z ( bt ) = t 1 m → t 2 n 4.6.2.2. loop bt instr ∗ Let z be the current state. Let t 1 m → localidx 0 ∗ t 2 n be the destructuring of instrtype z ( bt ) . Assert: Due to validation , localidx 0 ∗ = ϵ . Assert: Due to validation , there are at least m values on the top of the stack. Pop the values val m from the stack. Let L be the label whose arity is m and whose continuation is the start of the block. Enter the block val m instr ∗ with the label L . z ; val m ( loop bt instr ∗ ) ↪ ( label m { loop bt instr ∗ } val m instr ∗ ) if instrtype z ( bt ) = t 1 m → t 2 n 4.6.2.3. if bt instr 1 ∗ else instr 2 ∗ Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const c ) from the stack. If c = 0 , then: Execute the instruction ( block bt instr 1 ∗ ) . Else: Execute the instruction ( block bt instr 2 ∗ ) . ( i 32 . const c ) ( if bt instr 1 ∗ else instr 2 ∗ ) ( i 32 . const c ) ( if bt instr 1 ∗ else instr 2 ∗ ) ↪ ↪ ( block bt instr 1 ∗ ) ( block bt instr 2 ∗ ) if c = 0 if c = 0 4.6.2.4. br l If the first non-value entry of the stack is a label , then: Let L be the topmost label . Let n be the arity of L If l = 0 , then: Assert: Due to validation , there are at least n values on the top of the stack. Pop the values val n from the stack. Pop all values val ′ ∗ from the top of the stack. Pop the label from the stack. Push the values val n to the stack. Jump to the continuation of L . Else: Pop all values val ∗ from the top of the stack. Pop the label from the stack. Push the values val ∗ to the stack. Execute the instruction ( br l − 1 ) . Else: Assert: Due to validation , the first non-value entry of the stack is a handler . Pop all values val ∗ from the top of the stack. Pop the handler from the stack. Push the values val ∗ to the stack. Execute the instruction ( br l ) . ( label n { instr ′ ∗ } val ′ ∗ val n ( br l ) instr ∗ ) ( label n { instr ′ ∗ } val ∗ ( br l ) instr ∗ ) ( handler n { catch ∗ } val ∗ ( br l ) instr ∗ ) ↪ ↪ ↪ val n instr ′ ∗ val ∗ ( br l − 1 ) val ∗ ( br l ) if l = 0 if l > 0 4.6.2.5. br_if l Assert: Due to validation , a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const c ) from the stack. If c = 0 , then: Execute the instruction ( br l ) . Else: Do nothing. ( i 32 . const c ) ( br_if l ) ( i 32 . const c ) ( br_if l ) ↪ ↪ ( br l ) ϵ if c = 0 if c = 0 4.6.2.6. br_table l ∗ l ′ Assert: Due to validation , a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. If i < ∣ l ∗ ∣ , then: Execute the instruction ( br l ∗ [ i ]) . Else: Execute the instruction ( br l ′ ) . ( i 32 . const i ) ( br_table l ∗ l ′ ) ( i 32 . const i ) ( br_table l ∗ l ′ ) ↪ ↪ ( br l ∗ [ i ]) ( br l ′ ) if i < ∣ l ∗ ∣ if i ≥ ∣ l ∗ ∣ 4.6.2.7. br_on_null l Assert: Due to validation , a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Execute the instruction ( br l ) . Else: Push the value val to the stack. val ( br_on_null l ) val ( br_on_null l ) ↪ ↪ ( br l ) val if val = ref . null otherwise 4.6.2.8. br_on_non_null l Assert: Due to validation , a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Do nothing. Else: Push the value val to the stack. Execute the instruction ( br l ) . val ( br_on_non_null l ) val ( br_on_non_null l ) ↪ ↪ ϵ val ( br l ) if val = ref . null otherwise 4.6.2.9. br_on_cast l rt 1 rt 2 Let f be the topmost frame . Assert: Due to validation , a reference value is on the top of the stack. Pop the value ref from the stack. Push the value ref to the stack. If ref is valid with type clos f . module ( rt 2 ) , then: Execute the instruction ( br l ) . Else: Do nothing. s ; f ; ref ( br_on_cast l rt 1 rt 2 ) s ; f ; ref ( br_on_cast l rt 1 rt 2 ) ↪ ↪ ref ( br l ) ref if s ⊢ ref : clos f . module ( rt 2 ) otherwise 4.6.2.10. br_on_cast_fail l rt 1 rt 2 Let f be the topmost frame . Assert: Due to validation , a reference value is on the top of the stack. Pop the value ref from the stack. Push the value ref to the stack. If ref is valid with type clos f . module ( rt 2 ) , then: Do nothing. Else: Execute the instruction ( br l ) . s ; f ; ref ( br_on_cast_fail l rt 1 rt 2 ) s ; f ; ref ( br_on_cast_fail l rt 1 rt 2 ) ↪ ↪ ref ref ( br l ) if s ⊢ ref : clos f . module ( rt 2 ) otherwise 4.6.2.11. return If the first non-value entry of the stack is a frame , then: Let f be the topmost frame . Let n be the arity of f Assert: Due to validation , there are at least n values on the top of the stack. Pop the values val n from the stack. Pop all values val ′ ∗ from the top of the stack. Pop the frame from the stack. Push the values val n to the stack. Else if the first non-value entry of the stack is a label , then: Pop all values val ∗ from the top of the stack. Pop the label from the stack. Push the values val ∗ to the stack. Execute the instruction return . Else: Assert: Due to validation , the first non-value entry of the stack is a handler . Pop all values val ∗ from the top of the stack. Pop the handler from the stack. Push the values val ∗ to the stack. Execute the instruction return . ( frame n { f } val ′ ∗ val n return instr ∗ ) ( label n { instr ′ ∗ } val ∗ return instr ∗ ) ( handler n { catch ∗ } val ∗ return instr ∗ ) ↪ ↪ ↪ val n val ∗ return val ∗ return 4.6.2.12. call x Let z be the current state. Assert: Due to validation , x < ∣z . module . funcs ∣ . Let a be the address z . module . funcs [ x ] . Assert: Due to validation , a < ∣z . funcs ∣ . Push the value ( ref . func a ) to the stack. Execute the instruction ( call_ref z . funcs [ a ] . type ) . z ; ( call x ) ↪ ( ref . func a ) ( call_ref z . funcs [ a ] . type ) if z . module . funcs [ x ] = a 4.6.2.13. call_ref x Todo (*) Prose not spliced, for the prose merges the two cases of null and non-null references. Assert: due to validation , a null or function reference is on the top of the stack. Pop the reference value r from the stack. If r is ref . null ht , then: Trap. Assert: due to validation , r is a function reference . Let ref . func a be the reference r . Invoke the function instance at address a . z ; ( ref . null ) ( call_ref y ) ↪ z ; trap Note The formal rule for calling a non-null function reference is described below . 4.6.2.14. call_indirect x y Execute the instruction ( table . get x ) . Execute the instruction ( ref . cast ( ref null y )) . Execute the instruction ( call_ref y ) . ( call_indirect x y ) ↪ ( table . get x ) ( ref . cast ( ref null y )) ( call_ref y ) 4.6.2.15. return_call x Let z be the current state. Assert: Due to validation , x < ∣z . module . funcs ∣ . Let a be the address z . module . funcs [ x ] . Assert: Due to validation , a < ∣z . funcs ∣ . Push the value ( ref . func a ) to the stack. Execute the instruction ( return_call_ref z . funcs [ a ] . type ) . z ; ( return_call x ) ↪ ( ref . func a ) ( return_call_ref z . funcs [ a ] . type ) if z . module . funcs [ x ] = a 4.6.2.16. return_call_ref y Let z be the current state. If the first non-value entry of the stack is a label , then: Pop all values val ∗ from the top of the stack. Pop the label from the stack. Push the values val ∗ to the stack. Execute the instruction ( return_call_ref y ) . Else if the first non-value entry of the stack is a handler , then: Pop all values val ∗ from the top of the stack. Pop the handler from the stack. Push the values val ∗ to the stack. Execute the instruction ( return_call_ref y ) . Else: Assert: Due to validation , the first non-value entry of the stack is a frame . Assert: Due to validation , a value is on the top of the stack. Pop the value val ′′ from the stack. If val ′′ = ref . null , then: Trap. Assert: Due to validation , val ′′ is some ref . func funcaddr . Let ( ref . func a ) be the destructuring of val ′′ . Assert: Due to validation , a < ∣z . funcs ∣ . Assert: Due to validation , the expansion of z . funcs [ a ] . type is some func resulttype → resulttype . Let ( func t 1 n → t 2 m ) be the destructuring of the expansion of z . funcs [ a ] . type . Assert: Due to validation , there are at least n values on the top of the stack. Pop the values val n from the stack. Pop all values val ′ ∗ from the top of the stack. Pop the frame from the stack. Push the values val n to the stack. Push the value ( ref . func a ) to the stack. Execute the instruction ( call_ref y ) . z ; ( label k { instr ′ ∗ } val ∗ ( return_call_ref y ) instr ∗ ) z ; ( handler k { catch ∗ } val ∗ ( return_call_ref y ) instr ∗ ) z ; ( frame k { f } val ∗ ( ref . null ) ( return_call_ref y ) instr ∗ ) z ; ( frame k { f } val ′ ∗ val n ( ref . func a ) ( return_call_ref y ) instr ∗ ) ↪ ↪ ↪ ↪ val ∗ ( return_call_ref y ) val ∗ ( return_call_ref y ) trap val n ( ref . func a ) ( call_ref y ) if z . funcs [ a ] . type ≈ func t 1 n → t 2 m 4.6.2.17. return_call_indirect x y Execute the instruction ( table . get x ) . Execute the instruction ( ref . cast ( ref null y )) . Execute the instruction ( return_call_ref y ) . ( return_call_indirect x y ) ↪ ( table . get x ) ( ref . cast ( ref null y )) ( return_call_ref y ) 4.6.2.18. throw x Let z be the current state. Assert: Due to validation , x < ∣z . module . tags ∣ . Assert: Due to validation , the expansion of z . tags [ x ] . type is some func resulttype → resulttype . Let ( func t n → resulttype 0 ) be the destructuring of the expansion of z . tags [ x ] . type . Assert: Due to validation , resulttype 0 = ϵ . Let a be the length of z . exns . Assert: Due to validation , there are at least n values on the top of the stack. Pop the values val n from the stack. Let exn be the exception instance { tag z . module . tags [ x ] , fields val n } . Append exn to z . exns . Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . z ; val n ( throw x ) ↪ z [ . exns = ⊕ exn ] ; ( ref . exn a ) throw_ref if z . tags [ x ] . type ≈ func t n → ϵ ∧ a = ∣ z . exns ∣ ∧ exn = { tag z . module . tags [ x ] , fields val n } 4.6.2.19. throw_ref Let z be the current state. Assert: Due to validation , a value is on the top of the stack. Pop the value val ′ from the stack. If val ′ = ref . null , then: Trap. If val ′ is some ref . exn exnaddr , then: Let ( ref . exn a ) be the destructuring of val ′ . Pop all values val ∗ from the top of the stack. If val ∗ = ϵ , then: Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else if the first non-value entry of the stack is a label , then: Pop the label from the stack. Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else: If the first non-value entry of the stack is a frame , then: Pop the frame from the stack. Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else if the first non-value entry of the stack is not a handler , then: Throw the exception val ′ as a result. Else: Let H be the topmost handler . Let n be the arity of H Let catch ′′ ∗ be the catch handler of H If catch ′′ ∗ = ϵ , then: Pop the handler from the stack. Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else if a ≥ ∣z . exns ∣ , then: Let catch 0 catch ′ ∗ be catch ′′ ∗ . If catch 0 is some catch_all labelidx , then: Let ( catch_all l ) be the destructuring of catch 0 . Pop the handler from the stack. Execute the instruction ( br l ) . Else if catch 0 is not some catch_all_ref labelidx , then: Let catch catch ′ ∗ be catch ′′ ∗ . Pop the handler from the stack. Let H ′ be the handler whose arity is n and whose catch handler is catch ′ ∗ . Push the handler H ′ . Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else: Let ( catch_all_ref l ) be the destructuring of catch 0 . Pop the handler from the stack. Push the value ( ref . exn a ) to the stack. Execute the instruction ( br l ) . Else: Let val ∗ be z . exns [ a ] . fields . Let catch 0 catch ′ ∗ be catch ′′ ∗ . If catch 0 is some catch tagidx labelidx , then: Let ( catch x l ) be the destructuring of catch 0 . If x < ∣z . module . tags ∣ and z . exns [ a ] . tag = z . module . tags [ x ] , then: Pop the handler from the stack. Push the values val ∗ to the stack. Execute the instruction ( br l ) . Else: Let catch catch ′ ∗ be catch ′′ ∗ . Pop the handler from the stack. Let H ′ be the handler whose arity is n and whose catch handler is catch ′ ∗ . Push the handler H ′ . Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else if catch 0 is some catch_ref tagidx labelidx , then: Let ( catch_ref x l ) be the destructuring of catch 0 . If x ≥ ∣z . module . tags ∣ or z . exns [ a ] . tag = z . module . tags [ x ] , then: Let catch catch ′ ∗ be catch ′′ ∗ . Pop the handler from the stack. Let H ′ be the handler whose arity is n and whose catch handler is catch ′ ∗ . Push the handler H ′ . Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else: Pop the handler from the stack. Push the values val ∗ to the stack. Push the value ( ref . exn a ) to the stack. Execute the instruction ( br l ) . Else: If catch 0 is some catch_all labelidx , then: Let ( catch_all l ) be the destructuring of catch 0 . Pop the handler from the stack. Execute the instruction ( br l ) . Else if catch 0 is not some catch_all_ref labelidx , then: Let catch catch ′ ∗ be catch ′′ ∗ . Pop the handler from the stack. Let H be the handler whose arity is n and whose catch handler is catch ′ ∗ . Push the handler H . Push the value ( ref . exn a ) to the stack. Execute the instruction throw_ref . Else: Let ( catch_all_ref l ) be the destructuring of catch 0 . Pop the handler from the stack. Push the value ( ref . exn a ) to the stack. Execute the instruction ( br l ) . Else: Assert: Due to validation , the first non-value entry of the stack is not a label . Assert: Due to validation , the first non-value entry of the stack is not a frame . Assert: Due to validation , the first non-value entry of the stack is not a handler . Throw the exception val ′ as a result. z ; ( ref . null ) throw_ref z ; val ∗ ( ref . exn a ) throw_ref instr ∗ z ; ( label n { instr ′ ∗ } ( ref . exn a ) throw_ref ) z ; ( frame n { f } ( ref . exn a ) throw_ref ) z ; ( handler n { ϵ } ( ref . exn a ) throw_ref ) z ; ( handler n {( catch x l ) catch ′ ∗ } ( ref . exn a ) throw_ref ) z ; ( handler n {( catch_ref x l ) catch ′ ∗ } ( ref . exn a ) throw_ref ) z ; ( handler n {( catch_all l ) catch ′ ∗ } ( ref . exn a ) throw_ref ) z ; ( handler n {( catch_all_ref l ) catch ′ ∗ } ( ref . exn a ) throw_ref ) z ; ( handler n { catch catch ′ ∗ } ( ref . exn a ) throw_ref ) ↪ ↪ ↪ ↪ ↪ ↪ ↪ ↪ ↪ ↪ trap ( ref . exn a ) throw_ref if val ∗ = ϵ ∨ instr ∗ = ϵ ( ref . exn a ) throw_ref ( ref . exn a ) throw_ref ( ref . exn a ) throw_ref val ∗ ( br l ) if z . exns [ a ] . tag = z . module . tags [ x ] ∧ val ∗ = z . exns [ a ] . fields val ∗ ( ref . exn a ) ( br l ) if z . exns [ a ] . tag = z . module . tags [ x ] ∧ val ∗ = z . exns [ a ] . fields ( br l ) ( ref . exn a ) ( br l ) ( handler n { catch ′ ∗ } ( ref . exn a ) throw_ref ) otherwise 4.6.2.20. try_table bt catch ∗ instr ∗ Let z be the current state. Let t 1 m → localidx 0 ∗ t 2 n be the destructuring of instrtype z ( bt ) . Assert: Due to validation , localidx 0 ∗ = ϵ . Assert: Due to validation , there are at least m values on the top of the stack. Pop the values val m from the stack. Let H be the handler whose arity is n and whose catch handler is catch ∗ . Push the handler H . Let L be the label whose arity is n and whose continuation is the end of the block. Enter the block val m instr ∗ with the label L . z ; val m ( try_table bt catch ∗ instr ∗ ) ↪ ( handler n { catch ∗ } ( label n { ϵ } val m instr ∗ )) if instrtype z ( bt ) = t 1 m → t 2 n 4.6.3. Blocks The following auxiliary rules define the semantics of executing an instruction sequence that forms a block . 4.6.3.1. Entering instr ∗ with label L and values val ∗ Push L to the stack. Push the values val ∗ to the stack. Jump to the start of the instruction sequence instr ∗ . Note No formal reduction rule is needed for entering an instruction sequence, because the label L is embedded in the administrative instruction that structured control instructions reduce to directly. 4.6.3.2. Exiting instr ∗ with label L When the end of a block is reached without a jump, exception , or trap aborting it, then the following steps are performed. Pop all values val ∗ from the top of the stack. Assert: due to validation , the label L is now on the top of the stack. Pop the label from the stack. Push val ∗ back to the stack. Jump to the position after the end of the structured control instruction associated with the label L . ( label n { instr ∗ } val ∗ ) ↪ val ∗ 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. 4.6.4. Exception Handling The following auxiliary rules define the semantics of entering and exiting try_table blocks. 4.6.4.1. Entering instr ∗ with label L and exception handler H Push H to the stack. Push L onto the stack. Jump to the start of the instruction sequence instr ∗ . 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. 4.6.4.2. Exiting an exception handler When the end of a try_table block is reached without a jump, exception , or trap , then the following steps are performed. Let m be the number of values on the top of the stack. Pop the values val m from the stack. Assert: due to validation , a handler and a label are now on the top of the stack. Pop the label from the stack. Pop the handler H from the stack. Push val m back to the stack. Jump to the position after the end of the administrative instruction associated with the handler H . ( handler n { catch ∗ } val ∗ ) ↪ val ∗ 4.6.5. Function Calls The following auxiliary rules define the semantics of invoking a function instance through one of the call instructions and returning from it. 4.6.5.1. Invocation of function reference ( ref . func a ) Let z be the current state. Assert: due to validation , z. funcs [ a ] exists. Let fi be the function instance , z. funcs [ a ] . Let func [ t 1 n ] → [ t 2 m ] be the composite type expand ( fi . type ) . Let func x ( local t ) ∗ instr ∗ be the function fi . code . Assert: due to validation , n values are on the top of the stack. Pop the values val n from the stack. Let f be the frame { locals val n ( default t ) ∗ , module fi . module } . Push the activation of f with arity m to the stack. Let L be the label whose arity is m and whose continuation is the end of the function. Enter the instruction sequence instr ∗ with label L and no values. z ; val n ( ref . func a ) ( call_ref y ) ↪ z ; ( frame m { f } ( label m { ϵ } instr ∗ )) if z . funcs [ a ] = fi ∧ fi . type ≈ func t 1 n → t 2 m ∧ fi . code = func x ( local t ) ∗ ( instr ∗ ) ∧ f = { locals val n ( default t ) ∗ , module fi . module } Note For non-defaultable types, the respective local is left uninitialized by these rules. 4.6.5.2. Returning from a function When the end of a function is reached without a jump (including through return ), or an exception or trap aborting it, then the following steps are performed. Let F be the current frame . Let n be the arity of the activation of F . Assert: due to validation , there are n values on the top of the stack. Pop the results val n from the stack. Assert: due to validation , the frame F is now on the top of the stack. Pop the frame from the stack. Push val n back to the stack. Jump to the instruction after the original call. ( frame n { f } val n ) ↪ val n 4.6.5.3. 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. s ; f ; val n ( ref . func a ) ( call_ref y ) s ; f ; val n ( ref . func a ) ( call_ref y ) ↪ ↪ s ′ ; f ; result if ( s ; f ) . funcs [ a ] = fi ∧ fi . type ≈ func t 1 n → t 2 m ∧ fi . code = hf ∧ ( s ′ , result ) ∈ hf ( s , val n ) s ; f ; ( ref . func a ) ( call_ref y ) if ( s ; f ) . funcs [ a ] = fi ∧ fi . type ≈ func t 1 n → t 2 m ∧ fi . code = hf ∧ ⊥ ∈ hf ( s , val n ) Here, hf ( s , val n ) denotes the implementation-defined execution of host function hf in current store s with arguments val n . It yields a set of possible outcomes, where each element is either a pair of a modified store s ′ 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 s , and given arguments val n matching the ascribed parameter types t 1 n , 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 s ′ that is an extension of s and a result matching the ascribed return types t 2 m . 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. 4.6.6. Variable Instructions 4.6.6.1. local . get x Let z be the current state. Assert: Due to validation, z . locals [ x ] is defined. Let val be z . locals [ x ] . Push the value val to the stack. z ; ( local . get x ) ↪ val if z . locals [ x ] = val 4.6.6.2. local . set x Let z be the current state. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Replace z . locals [ x ] with val . z ; val ( local . set x ) ↪ z [ . locals [ x ] = val ] ; ϵ 4.6.6.3. local . tee x Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Push the value val to the stack. Push the value val to the stack. Execute the instruction ( local . set x ) . val ( local . tee x ) ↪ val val ( local . set x ) 4.6.6.4. global . get x Let z be the current state. Let val be the value z . globals [ x ] . value . Push the value val to the stack. z ; ( global . get x ) ↪ val if z . globals [ x ] . value = val 4.6.6.5. global . set x Let z be the current state. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Replace z . globals [ x ] . value with val . z ; val ( global . set x ) ↪ z [ . globals [ x ] . value = val ] ; ϵ 4.6.7. Table Instructions 4.6.7.1. table . get x Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i ≥ ∣z . tables [ x ] . refs ∣ , then: Trap. Push the value z . tables [ x ] . refs [ i ] to the stack. z ; ( at . const i ) ( table . get x ) z ; ( at . const i ) ( table . get x ) ↪ ↪ trap z . tables [ x ] . refs [ i ] if i ≥ ∣ z . tables [ x ] . refs ∣ if i < ∣ z . tables [ x ] . refs ∣ 4.6.7.2. table . set x Let z be the current state. Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref from the stack. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i ≥ ∣z . tables [ x ] . refs ∣ , then: Trap. Replace z . tables [ x ] . refs [ i ] with ref . z ; ( at . const i ) ref ( table . set x ) z ; ( at . const i ) ref ( table . set x ) ↪ ↪ z ; trap z [ . tables [ x ] . refs [ i ] = ref ] ; ϵ if i ≥ ∣ z . tables [ x ] . refs ∣ if i < ∣ z . tables [ x ] . refs ∣ 4.6.7.3. table . size x Let z be the current state. Let ( at lim rt ) be the destructuring of z . tables [ x ] . type . Let n be the length of z . tables [ x ] . refs . Push the value ( at . const n ) to the stack. z ; ( table . size x ) ↪ ( at . const n ) if ∣ z . tables [ x ] . refs ∣ = n ∧ z . tables [ x ] . type = at lim rt 4.6.7.4. table . grow x Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const n ) from the stack. Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref from the stack. Either: Let ti be the table instance growtable ( z . tables [ x ] , n , ref ) . Push the value ( at . const ∣z . tables [ x ] . refs ∣ ) to the stack. Replace z . tables [ x ] with ti . Or: Push the value ( at . const signed ∣ at ∣ − 1 ( − 1 ) ) to the stack. z ; ref ( at . const n ) ( table . grow x ) z ; ref ( at . const n ) ( table . grow x ) ↪ ↪ z [ . tables [ x ] = ti ] ; ( at . const ∣ z . tables [ x ] . refs ∣ ) if ti = growtable ( z . tables [ x ] , n , ref ) z ; ( at . const signed ∣ at ∣ − 1 ( − 1 ) ) Note The table . grow 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 . 4.6.7.5. table . fill x Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const n ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Assert: Due to validation, a value of number type at is on the top of the stack. Pop the value ( numtype 0 . const i ) from the stack. If i + n > ∣z . tables [ x ] . refs ∣ , then: Trap. If n = 0 , then: Do nothing. Else: Push the value ( at . const i ) to the stack. Push the value val to the stack. Execute the instruction ( table . set x ) . Push the value ( at . const i + 1 ) to the stack. Push the value val to the stack. Push the value ( at . const n − 1 ) to the stack. Execute the instruction ( table . fill x ) . z ; ( at . const i ) val ( at . const n ) ( table . fill x ) z ; ( at . const i ) val ( at . const n ) ( table . fill x ) z ; ( at . const i ) val ( at . const n ) ( table . fill x ) ( at . const i ) val ( table . set x ) ( at . const i + 1 ) val ( at . const n − 1 ) ( table . fill x ) otherwise ↪ ↪ ↪ trap ϵ if i + n > ∣ z . tables [ x ] . refs ∣ otherwise, if n = 0 4.6.7.6. table . copy x 1 x 2 Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const n ) from the stack. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at 2 . const i 2 ) from the stack. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at 1 . const i 1 ) from the stack. If i 1 + n > ∣z . tables [ x 1 ] . refs ∣ , then: Trap. If i 2 + n > ∣z . tables [ x 2 ] . refs ∣ , then: Trap. If n = 0 , then: Do nothing. Else: If i 1 ≤ i 2 , then: Push the value ( at 1 . const i 1 ) to the stack. Push the value ( at 2 . const i 2 ) to the stack. Execute the instruction ( table . get x 2 ) . Execute the instruction ( table . set x 1 ) . Push the value ( at 1 . const i 1 + 1 ) to the stack. Push the value ( at 2 . const i 2 + 1 ) to the stack. Else: Push the value ( at 1 . const i 1 + n − 1 ) to the stack. Push the value ( at 2 . const i 2 + n − 1 ) to the stack. Execute the instruction ( table . get x 2 ) . Execute the instruction ( table . set x 1 ) . Push the value ( at 1 . const i 1 ) to the stack. Push the value ( at 2 . const i 2 ) to the stack. Push the value ( at . const n − 1 ) to the stack. Execute the instruction ( table . copy x 1 x 2 ) . z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( table . copy x 1 x 2 ) if i 1 + n > ∣ z . tables [ x 1 ] . refs ∣ ∨ i 2 + n > ∣ z . tables [ x 2 ] . refs ∣ z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( table . copy x y ) z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( table . copy x y ) ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( table . get y ) ( table . set x ) ( at 1 . const i 1 + 1 ) ( at 2 . const i 2 + 1 ) ( at ′ . const n − 1 ) ( table . copy x y ) otherwise, if i 1 ≤ i 2 z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( table . copy x y ) ( at 1 . const i 1 + n − 1 ) ( at 2 . const i 2 + n − 1 ) ( table . get y ) ( table . set x ) ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n − 1 ) ( table . copy x y ) otherwise ↪ ↪ ↪ ↪ trap ϵ otherwise, if n = 0 4.6.7.7. table . init x y Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const j ) from the stack. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i + n > ∣z . tables [ x ] . refs ∣ , then: Trap. If j + n > ∣z . elems [ y ] . refs ∣ , then: Trap. If n = 0 , then: Do nothing. Else: Assert: Due to validation, j < ∣z . elems [ y ] . refs ∣ . Push the value ( at . const i ) to the stack. Push the value z . elems [ y ] . refs [ j ] to the stack. Execute the instruction ( table . set x ) . Push the value ( at . const i + 1 ) to the stack. Push the value ( i 32 . const j + 1 ) to the stack. Push the value ( i 32 . const n − 1 ) to the stack. Execute the instruction ( table . init x y ) . z ; ( at . const i ) ( i 32 . const j ) ( i 32 . const n ) ( table . init x y ) if i + n > ∣ z . tables [ x ] . refs ∣ ∨ j + n > ∣ z . elems [ y ] . refs ∣ z ; ( at . const i ) ( i 32 . const j ) ( i 32 . const n ) ( table . init x y ) z ; ( at . const i ) ( i 32 . const j ) ( i 32 . const n ) ( table . init x y ) ( at . const i ) z . elems [ y ] . refs [ j ] ( table . set x ) ( at . const i + 1 ) ( i 32 . const j + 1 ) ( i 32 . const n − 1 ) ( table . init x y ) otherwise ↪ ↪ ↪ trap ϵ otherwise, if n = 0 4.6.7.8. elem . drop x Let z be the current state. Replace z . elems [ x ] . refs with ϵ . z ; ( elem . drop x ) ↪ z [ . elems [ x ] . refs = ϵ ] ; ϵ 4.6.8. Memory Instructions Note The alignment memarg . align 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 2 memarg . align = 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. 4.6.8.1. nt . load loadop ? x ao Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If loadop ? is not defined, then: If i + ao . offset + ∣ nt ∣ /8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let c be the result for which bytes nt ( c ) = z . mems [ x ] . bytes [ i + ao . offset : ∣ nt ∣ /8 ] . Push the value ( nt . const c ) to the stack. Else: Assert: Due to validation, nt is i N . Let loadop 0 be loadop ? . Let n _ sx be the destructuring of loadop 0 . If i + ao . offset + n/8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let c be the result for which bytes i n ( c ) = z . mems [ x ] . bytes [ i + ao . offset : n/8 ] . Push the value ( nt . const extend n , ∣ nt ∣ sx ( c ) ) to the stack. z ; ( at . const i ) ( nt . load x ao ) z ; ( at . const i ) ( nt . load x ao ) z ; ( at . const i ) ( i N . load n _ sx x ao ) z ; ( at . const i ) ( i N . load n _ sx x ao ) ↪ ↪ ↪ ↪ trap if i + ao . offset + ∣ nt ∣ /8 > ∣ z . mems [ x ] . bytes ∣ ( nt . const c ) if bytes nt ( c ) = z . mems [ x ] . bytes [ i + ao . offset : ∣ nt ∣ /8 ] trap if i + ao . offset + n /8 > ∣ z . mems [ x ] . bytes ∣ ( i N . const extend n , ∣ i N ∣ sx ( c ) ) if bytes i n ( c ) = z . mems [ x ] . bytes [ i + ao . offset : n /8 ] 4.6.8.2. v 128 . load K x M _ sx x ao Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i + ao . offset + K ⋅ M/8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let j M be the result for which ( bytes i K ( j M ) = z . mems [ x ] . bytes [ i + ao . offset + k ⋅ K/8 : K/8 ] ) k < M . Let i N be the result for which N = K ⋅ 2 . Let c be lanes i N x M − 1 ( extend K , N sx ( j ) M ) . Push the value ( v 128 . const c ) to the stack. z ; ( at . const i ) ( v 128 . load K x M _ sx x ao ) z ; ( at . const i ) ( v 128 . load K x M _ sx x ao ) if ( bytes i K ( j ) = z . mems [ x ] . bytes [ i + ao . offset + k ⋅ K /8 : K /8 ] ) k < M ∧ c = lanes i N x M − 1 ( extend K , N sx ( j ) M ) ∧ N = K ⋅ 2 ↪ ↪ trap ( v 128 . const c ) if i + ao . offset + K ⋅ M /8 > ∣ z . mems [ x ] . bytes ∣ 4.6.8.3. v 128 . load N _ splat x ao Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i + ao . offset + N/8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let M be 128/N . Let i N be the result for which ∣ i N ∣ = N . Let j be the result for which bytes i N ( j ) = z . mems [ x ] . bytes [ i + ao . offset : N/8 ] . Let c be lanes i N x M − 1 ( j M ) . Push the value ( v 128 . const c ) to the stack. z ; ( at . const i ) ( v 128 . load N _ splat x ao ) z ; ( at . const i ) ( v 128 . load N _ splat x ao ) ↪ ↪ trap ( v 128 . const c ) if bytes i N ( j ) = z . mems [ x ] . bytes [ i + ao . offset : N /8 ] ∧ N = ∣ i N ∣ ∧ M = 128/ N ∧ c = lanes i N x M − 1 ( j M ) if i + ao . offset + N /8 > ∣ z . mems [ x ] . bytes ∣ 4.6.8.4. v 128 . load N _ zero x ao Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i + ao . offset + N/8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let j be the result for which bytes i N ( j ) = z . mems [ x ] . bytes [ i + ao . offset : N/8 ] . Let c be extend N , 128 u ( j ) . Push the value ( v 128 . const c ) to the stack. z ; ( at . const i ) ( v 128 . load N _ zero x ao ) z ; ( at . const i ) ( v 128 . load N _ zero x ao ) ↪ ↪ trap ( v 128 . const c ) if bytes i N ( j ) = z . mems [ x ] . bytes [ i + ao . offset : N /8 ] ∧ c = extend N , 128 u ( j ) if i + ao . offset + N /8 > ∣ z . mems [ x ] . bytes ∣ 4.6.8.5. v 128 . load N _ lane x ao j Let z be the current state. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Assert: Due to validation , a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i + ao . offset + N/8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let M be ∣ v 128 ∣ /N . Let i N be the result for which ∣ i N ∣ = N . Let k be the result for which bytes i N ( k ) = z . mems [ x ] . bytes [ i + ao . offset : N/8 ] . Let c be lanes i N x M − 1 ( lanes i N x M ( c 1 ) [ [ j ] = k ]) . Push the value ( v 128 . const c ) to the stack. z ; ( at . const i ) ( v 128 . const c 1 ) ( v 128 . load N _ lane x ao j ) z ; ( at . const i ) ( v 128 . const c 1 ) ( v 128 . load N _ lane x ao j ) ↪ ↪ trap ( v 128 . const c ) if bytes i N ( k ) = z . mems [ x ] . bytes [ i + ao . offset : N /8 ] ∧ N = ∣ i N ∣ ∧ M = ∣ v 128 ∣ / N ∧ c = lanes i N x M − 1 ( lanes i N x M ( c 1 ) [ [ j ] = k ]) if i + ao . offset + N /8 > ∣ z . mems [ x ] . bytes ∣ 4.6.8.6. nt . store storeop ? x ao Let z be the current state. Assert: Due to validation , a number value is on the top of the stack. Pop the value ( nt ′ . const c ) from the stack. Assert: Due to validation , a value is on the top of the stack. Pop the value ( at . const i ) from the stack. Assert: Due to validation , nt = nt ′ . If storeop ? is not defined, then: If i + ao . offset + ∣ nt ′ ∣ /8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let b ∗ be bytes nt ′ ( c ) . Replace z . mems [ x ] . bytes [ i + ao . offset : ∣ nt ′ ∣ /8 ] with b ∗ . Else: Assert: Due to validation , nt ′ is i N . Let n be storeop ? . If i + ao . offset + n/8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let b ∗ be bytes i n ( wrap ∣ nt ′ ∣ , n ( c )) . Replace z . mems [ x ] . bytes [ i + ao . offset : n/8 ] with b ∗ . z ; ( at . const i ) ( nt . const c ) ( nt . store x ao ) z ; ( at . const i ) ( nt . const c ) ( nt . store x ao ) z ; ( at . const i ) ( i N . const c ) ( i N . store n x ao ) z ; ( at . const i ) ( i N . const c ) ( i N . store n x ao ) z ; ( at . const i ) ( v 128 . const c ) ( v 128 . store x ao ) z ; ( at . const i ) ( v 128 . const c ) ( v 128 . store x ao ) ↪ ↪ ↪ ↪ ↪ ↪ z ; trap if i + ao . offset + ∣ nt ∣ /8 > ∣ z . mems [ x ] . bytes ∣ z [ . mems [ x ] . bytes [ i + ao . offset : ∣ nt ∣ /8 ] = b ∗ ] ; ϵ if b ∗ = bytes nt ( c ) z ; trap if i + ao . offset + n /8 > ∣ z . mems [ x ] . bytes ∣ z [ . mems [ x ] . bytes [ i + ao . offset : n /8 ] = b ∗ ] ; ϵ if b ∗ = bytes i n ( wrap ∣ i N ∣ , n ( c )) z ; trap if i + ao . offset + ∣ v 128 ∣ /8 > ∣ z . mems [ x ] . bytes ∣ z [ . mems [ x ] . bytes [ i + ao . offset : ∣ v 128 ∣ /8 ] = b ∗ ] ; ϵ if b ∗ = bytes v 128 ( c ) 4.6.8.7. v 128 . store N _ lane x ao j Let z be the current state. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c ) from the stack. Assert: Due to validation , a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i + ao . offset + N/8 > ∣z . mems [ x ] . bytes ∣ , then: Trap. Let M be 128/N . Let i N be the result for which ∣ i N ∣ = N . Assert: Due to validation , j < ∣ lanes i N x M ( c ) ∣ . Let b ∗ be bytes i N ( lanes i N x M ( c ) [ j ]) . Replace z . mems [ x ] . bytes [ i + ao . offset : N/8 ] with b ∗ . z ; ( at . const i ) ( v 128 . const c ) ( v 128 . store N _ lane x ao j ) z ; ( at . const i ) ( v 128 . const c ) ( v 128 . store N _ lane x ao j ) ↪ ↪ z ; trap if i + ao . offset + N /8 > ∣ z . mems [ x ] . bytes ∣ z [ . mems [ x ] . bytes [ i + ao . offset : N /8 ] = b ∗ ] ; ϵ if N = ∣ i N ∣ ∧ M = 128/ N ∧ b ∗ = bytes i N ( lanes i N x M ( c ) [ j ]) 4.6.8.8. memory . size x Let z be the current state. Let ( at lim page ) be the destructuring of z . mems [ x ] . type . Let n ⋅ 64 Ki be the length of z . mems [ x ] . bytes . Push the value ( at . const n ) to the stack. z ; ( memory . size x ) ↪ ( at . const n ) if n ⋅ 64 Ki = ∣ z . mems [ x ] . bytes ∣ ∧ z . mems [ x ] . type = at lim page 4.6.8.9. memory . grow x Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const n ) from the stack. Either: Let mi be the memory instance growmem ( z . mems [ x ] , n ) . Push the value ( at . const ∣z . mems [ x ] . bytes ∣ / ( 64 Ki )) to the stack. Replace z . mems [ x ] with mi . Or: Push the value ( at . const signed ∣ at ∣ − 1 ( − 1 ) ) to the stack. z ; ( at . const n ) ( memory . grow x ) z ; ( at . const n ) ( memory . grow x ) ↪ ↪ z [ . mems [ x ] = mi ] ; ( at . const ∣ z . mems [ x ] . bytes ∣ /64 Ki ) if mi = growmem ( z . mems [ x ] , n ) z ; ( at . const signed ∣ at ∣ − 1 ( − 1 ) ) Note The memory . grow 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 . 4.6.8.10. memory . fill x Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const n ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Assert: Due to validation, a value of number type at is on the top of the stack. Pop the value ( numtype 0 . const i ) from the stack. If i + n > ∣z . mems [ x ] . bytes ∣ , then: Trap. If n = 0 , then: Do nothing. Else: Push the value ( at . const i ) to the stack. Push the value val to the stack. Execute the instruction ( i 32 . store 8 x ) . Push the value ( at . const i + 1 ) to the stack. Push the value val to the stack. Push the value ( at . const n − 1 ) to the stack. Execute the instruction ( memory . fill x ) . z ; ( at . const i ) val ( at . const n ) ( memory . fill x ) z ; ( at . const i ) val ( at . const n ) ( memory . fill x ) z ; ( at . const i ) val ( at . const n ) ( memory . fill x ) ( at . const i ) val ( i 32 . store 8 x ) ( at . const i + 1 ) val ( at . const n − 1 ) ( memory . fill x ) otherwise ↪ ↪ ↪ trap ϵ if i + n > ∣ z . mems [ x ] . bytes ∣ otherwise, if n = 0 4.6.8.11. memory . copy x 1 x 2 Let z be the current state. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const n ) from the stack. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at 2 . const i 2 ) from the stack. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at 1 . const i 1 ) from the stack. If i 1 + n > ∣z . mems [ x 1 ] . bytes ∣ , then: Trap. If i 2 + n > ∣z . mems [ x 2 ] . bytes ∣ , then: Trap. If n = 0 , then: Do nothing. Else: If i 1 ≤ i 2 , then: Push the value ( at 1 . const i 1 ) to the stack. Push the value ( at 2 . const i 2 ) to the stack. Execute the instruction ( i 32 . load 8 _ u x 2 ) . Execute the instruction ( i 32 . store 8 x 1 ) . Push the value ( at 1 . const i 1 + 1 ) to the stack. Push the value ( at 2 . const i 2 + 1 ) to the stack. Else: Push the value ( at 1 . const i 1 + n − 1 ) to the stack. Push the value ( at 2 . const i 2 + n − 1 ) to the stack. Execute the instruction ( i 32 . load 8 _ u x 2 ) . Execute the instruction ( i 32 . store 8 x 1 ) . Push the value ( at 1 . const i 1 ) to the stack. Push the value ( at 2 . const i 2 ) to the stack. Push the value ( at . const n − 1 ) to the stack. Execute the instruction ( memory . copy x 1 x 2 ) . z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( memory . copy x 1 x 2 ) if i 1 + n > ∣ z . mems [ x 1 ] . bytes ∣ ∨ i 2 + n > ∣ z . mems [ x 2 ] . bytes ∣ z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( memory . copy x 1 x 2 ) z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( memory . copy x 1 x 2 ) ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( i 32 . load 8 _ u x 2 ) ( i 32 . store 8 x 1 ) ( at 1 . const i 1 + 1 ) ( at 2 . const i 2 + 1 ) ( at ′ . const n − 1 ) ( memory . copy x 1 x 2 ) otherwise, if i 1 ≤ i 2 z ; ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n ) ( memory . copy x 1 x 2 ) ( at 1 . const i 1 + n − 1 ) ( at 2 . const i 2 + n − 1 ) ( i 32 . load 8 _ u x 2 ) ( i 32 . store 8 x 1 ) ( at 1 . const i 1 ) ( at 2 . const i 2 ) ( at ′ . const n − 1 ) ( memory . copy x 1 x 2 ) otherwise ↪ ↪ ↪ ↪ trap ϵ otherwise, if n = 0 4.6.8.12. memory . init x y Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const j ) from the stack. Assert: Due to validation, a number value is on the top of the stack. Pop the value ( at . const i ) from the stack. If i + n > ∣z . mems [ x ] . bytes ∣ , then: Trap. If j + n > ∣z . datas [ y ] . bytes ∣ , then: Trap. If n = 0 , then: Do nothing. Else: Assert: Due to validation, j < ∣z . datas [ y ] . bytes ∣ . Push the value ( at . const i ) to the stack. Push the value ( i 32 . const z . datas [ y ] . bytes [ j ]) to the stack. Execute the instruction ( i 32 . store 8 x ) . Push the value ( at . const i + 1 ) to the stack. Push the value ( i 32 . const j + 1 ) to the stack. Push the value ( i 32 . const n − 1 ) to the stack. Execute the instruction ( memory . init x y ) . z ; ( at . const i ) ( i 32 . const j ) ( i 32 . const n ) ( memory . init x y ) if i + n > ∣ z . mems [ x ] . bytes ∣ ∨ j + n > ∣ z . datas [ y ] . bytes ∣ z ; ( at . const i ) ( i 32 . const j ) ( i 32 . const n ) ( memory . init x y ) z ; ( at . const i ) ( i 32 . const j ) ( i 32 . const n ) ( memory . init x y ) ( at . const i ) ( i 32 . const z . datas [ y ] . bytes [ j ]) ( i 32 . store 8 x ) ( at . const i + 1 ) ( i 32 . const j + 1 ) ( i 32 . const n − 1 ) ( memory . init x y ) otherwise ↪ ↪ ↪ trap ϵ otherwise, if n = 0 4.6.8.13. data . drop x Let z be the current state. Replace z . datas [ x ] . bytes with ϵ . z ; ( data . drop x ) ↪ z [ . datas [ x ] . bytes = ϵ ] ; ϵ 4.6.9. Reference Instructions 4.6.9.1. ref . null ht Push the value ref . null to the stack. z ; ( ref . null ht ) ↪ ref . null 4.6.9.2. ref . func x Let z be the current state. Assert: Due to validation, x < ∣z . module . funcs ∣ . Push the value ( ref . func z . module . funcs [ x ]) to the stack. z ; ( ref . func x ) ↪ ( ref . func z . module . funcs [ x ]) 4.6.9.3. ref . is_null Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref from the stack. If ref = ref . null , then: Push the value ( i 32 . const 1 ) to the stack. Else: Push the value ( i 32 . const 0 ) to the stack. ref ref . is_null ref ref . is_null ↪ ↪ ( i 32 . const 1 ) ( i 32 . const 0 ) if ref = ref . null otherwise 4.6.9.4. ref . as_non_null Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref from the stack. If ref = ref . null , then: Trap. Push the value ref to the stack. ref ref . as_non_null ref ref . as_non_null ↪ ↪ trap ref if ref = ref . null otherwise 4.6.9.5. ref . eq Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref 2 from the stack. Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref 1 from the stack. If ref 1 = ref . null and ref 2 = ref . null , then: Push the value ( i 32 . const 1 ) to the stack. Else if ref 1 = ref 2 , then: Push the value ( i 32 . const 1 ) to the stack. Else: Push the value ( i 32 . const 0 ) to the stack. ref 1 ref 2 ref . eq ref 1 ref 2 ref . eq ref 1 ref 2 ref . eq ↪ ↪ ↪ ( i 32 . const 1 ) ( i 32 . const 1 ) ( i 32 . const 0 ) if ref 1 = ref . null ∧ ref 2 = ref . null otherwise, if ref 1 = ref 2 otherwise 4.6.9.6. ref . test rt Let f be the topmost frame . Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref from the stack. If ref is valid with type clos f . module ( rt ) , then: Push the value ( i 32 . const 1 ) to the stack. Else: Push the value ( i 32 . const 0 ) to the stack. s ; f ; ref ( ref . test rt ) s ; f ; ref ( ref . test rt ) ↪ ↪ ( i 32 . const 1 ) ( i 32 . const 0 ) if s ⊢ ref : clos f . module ( rt ) otherwise 4.6.9.7. ref . cast rt Let f be the topmost frame . Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref from the stack. If not ref is valid with type clos f . module ( rt ) , then: Trap. Push the value ref to the stack. s ; f ; ref ( ref . cast rt ) s ; f ; ref ( ref . cast rt ) ↪ ↪ ref trap if s ⊢ ref : clos f . module ( rt ) otherwise 4.6.9.8. ref . i 31 Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Push the value ( ref . i 31 wrap 32 , 31 ( i )) to the stack. ( i 32 . const i ) ref . i 31 ↪ ( ref . i 31 wrap 32 , 31 ( i )) 4.6.9.9. i 31 . get _ sx Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Trap. Assert: Due to validation, val is some ref . i 31 u 31 . Let ( ref . i 31 i ) be the destructuring of val . Push the value ( i 32 . const extend 31 , 32 sx ( i ) ) to the stack. ( ref . null ) ( i 31 . get _ sx ) ( ref . i 31 i ) ( i 31 . get _ sx ) ↪ ↪ trap ( i 32 . const extend 31 , 32 sx ( i ) ) 4.6.9.10. struct . new x Let z be the current state. Assert: Due to validation, the expansion of z . types [ x ] is some struct list ( fieldtype ) . Let ( struct list 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) n be list 0 . Let a be the length of z . structs . Assert: Due to validation, there are at least n values on the top of the stack. Pop the values val n from the stack. Let si be the structure instance { type z . types [ x ] , fields pack zt ( val ) n } . Push the value ( ref . struct a ) to the stack. Append si to z . structs . z ; val n ( struct . new x ) ↪ z [ . structs = ⊕ si ] ; ( ref . struct a ) if z . types [ x ] ≈ struct ( mut ? zt ) n ∧ a = ∣ z . structs ∣ ∧ si = { type z . types [ x ] , fields ( pack zt ( val ) ) n } 4.6.9.11. struct . new_default x Let z be the current state. Assert: Due to validation, the expansion of z . types [ x ] is some struct list ( fieldtype ) . Let ( struct list 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) ∗ be list 0 . Assert: Due to validation, for all zt in zt ∗ , default unpack ( zt ) is defined. Let val ∗ be the value sequence ϵ . For each zt in zt ∗ , do: Let val be default unpack ( zt ) . Append val to val ∗ . Assert: Due to validation, ∣ val ∗ ∣ = ∣ zt ∗ ∣ . Push the values val ∗ to the stack. Execute the instruction ( struct . new x ) . z ; ( struct . new_default x ) ↪ val ∗ ( struct . new x ) if z . types [ x ] ≈ struct ( mut ? zt ) ∗ ∧ ( default unpack ( zt ) = val ) ∗ 4.6.9.12. struct . get _ sx ? x i Let z be the current state. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Trap. Assert: Due to validation, val is some ref . struct structaddr . Let ( ref . struct a ) be the destructuring of val . Assert: Due to validation, i < ∣z . structs [ a ] . fields ∣ . Assert: Due to validation, a < ∣z . structs ∣ . Assert: Due to validation, the expansion of z . types [ x ] is some struct list ( fieldtype ) . Let ( struct list 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) ∗ be list 0 . Assert: Due to validation, i < ∣ zt ∗ ∣ . Push the value unpack zt ∗ [ i ] sx ? ( z . structs [ a ] . fields [ i ]) to the stack. z ; ( ref . null ) ( struct . get _ sx ? x i ) z ; ( ref . struct a ) ( struct . get _ sx ? x i ) ↪ ↪ trap unpack zt ∗ [ i ] sx ? ( z . structs [ a ] . fields [ i ]) if z . types [ x ] ≈ struct ( mut ? zt ) ∗ 4.6.9.13. struct . set x i Let z be the current state. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val ′ from the stack. If val ′ = ref . null , then: Trap. Assert: Due to validation, val ′ is some ref . struct structaddr . Let ( ref . struct a ) be the destructuring of val ′ . Assert: Due to validation, the expansion of z . types [ x ] is some struct list ( fieldtype ) . Let ( struct list 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) ∗ be list 0 . Assert: Due to validation, i < ∣ zt ∗ ∣ . Replace z . structs [ a ] . fields [ i ] with pack zt ∗ [ i ] ( val ) . z ; ( ref . null ) val ( struct . set x i ) z ; ( ref . struct a ) val ( struct . set x i ) ↪ ↪ z ; trap z [ . structs [ a ] . fields [ i ] = pack zt ∗ [ i ] ( val )] ; ϵ if z . types [ x ] ≈ struct ( mut ? zt ) ∗ 4.6.9.14. array . new x Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Push the values val n to the stack. Execute the instruction ( array . new_fixed x n ) . val ( i 32 . const n ) ( array . new x ) ↪ val n ( array . new_fixed x n ) 4.6.9.15. array . new_default x Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, the expansion of z . types [ x ] is some array fieldtype . Let ( array fieldtype 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) be the destructuring of fieldtype 0 . Assert: Due to validation, default unpack ( zt ) is defined. Let val be default unpack ( zt ) . Push the values val n to the stack. Execute the instruction ( array . new_fixed x n ) . z ; ( i 32 . const n ) ( array . new_default x ) ↪ val n ( array . new_fixed x n ) if z . types [ x ] ≈ array ( mut ? zt ) ∧ default unpack ( zt ) = val 4.6.9.16. array . new_fixed x n Let z be the current state. Assert: Due to validation, the expansion of z . types [ x ] is some array fieldtype . Let ( array fieldtype 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) be the destructuring of fieldtype 0 . Let a be the length of z . arrays . Assert: Due to validation, there are at least n values on the top of the stack. Pop the values val n from the stack. Let ai be the array instance { type z . types [ x ] , fields pack zt ( val ) n } . Push the value ( ref . array a ) to the stack. Append ai to z . arrays . z ; val n ( array . new_fixed x n ) ↪ z [ . arrays = ⊕ ai ] ; ( ref . array a ) if z . types [ x ] ≈ array ( mut ? zt ) ∧ a = ∣ z . arrays ∣ ∧ ai = { type z . types [ x ] , fields ( pack zt ( val ) ) n } 4.6.9.17. array . new_data x y Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Assert: Due to validation, the expansion of z . types [ x ] is some array fieldtype . Let ( array fieldtype 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) be the destructuring of fieldtype 0 . If i + n ⋅ ∣ zt ∣ /8 > ∣z . datas [ y ] . bytes ∣ , then: Trap. Let byte ∗ ∗ be the result for which each byte ∗ has length ∣ zt ∣ /8 , and the concatenation of byte ∗ ∗ is z . datas [ y ] . bytes [ i : n ⋅ ∣ zt ∣ /8 ] . Let c n be the result for which ( bytes zt ( c n ) = byte ∗ ) ∗ . Push the values unpack ( zt ) . const unpack zt ( c ) n to the stack. Execute the instruction ( array . new_fixed x n ) . z ; ( i 32 . const i ) ( i 32 . const n ) ( array . new_data x y ) z ; ( i 32 . const i ) ( i 32 . const n ) ( array . new_data x y ) ↪ ↪ trap if z . types [ x ] ≈ array ( mut ? zt ) ∧ i + n ⋅ ∣ zt ∣ /8 > ∣ z . datas [ y ] . bytes ∣ ( unpack ( zt ) . const unpack zt ( c ) ) n ( array . new_fixed x n ) if z . types [ x ] ≈ array ( mut ? zt ) ∧ ⨁ bytes zt ( c ) n = z . datas [ y ] . bytes [ i : n ⋅ ∣ zt ∣ /8 ] 4.6.9.18. array . new_elem x y Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. If i + n > ∣z . elems [ y ] . refs ∣ , then: Trap. Let ref n be z . elems [ y ] . refs [ i : n ] . Push the values ref n to the stack. Execute the instruction ( array . new_fixed x n ) . z ; ( i 32 . const i ) ( i 32 . const n ) ( array . new_elem x y ) z ; ( i 32 . const i ) ( i 32 . const n ) ( array . new_elem x y ) ↪ ↪ trap ref n ( array . new_fixed x n ) if ref n = z . elems [ y ] . refs [ i : n ] if i + n > ∣ z . elems [ y ] . refs ∣ 4.6.9.19. array . get _ sx ? x Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Trap. Assert: Due to validation, val is some ref . array arrayaddr . Let ( ref . array a ) be the destructuring of val . Assert: Due to validation, a < ∣z . arrays ∣ . If i ≥ ∣z . arrays [ a ] . fields ∣ , then: Trap. Assert: Due to validation, the expansion of z . types [ x ] is some array fieldtype . Let ( array fieldtype 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) be the destructuring of fieldtype 0 . Push the value unpack zt sx ? ( z . arrays [ a ] . fields [ i ]) to the stack. z ; ( ref . null ) ( i 32 . const i ) ( array . get _ sx ? x ) z ; ( ref . array a ) ( i 32 . const i ) ( array . get _ sx ? x ) z ; ( ref . array a ) ( i 32 . const i ) ( array . get _ sx ? x ) ↪ ↪ ↪ trap trap unpack zt sx ? ( z . arrays [ a ] . fields [ i ]) if z . types [ x ] ≈ array ( mut ? zt ) if i ≥ ∣ z . arrays [ a ] . fields ∣ 4.6.9.20. array . set x Let z be the current state. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val ′ from the stack. If val ′ = ref . null , then: Trap. Assert: Due to validation, val ′ is some ref . array arrayaddr . Let ( ref . array a ) be the destructuring of val ′ . If a < ∣z . arrays ∣ and i ≥ ∣z . arrays [ a ] . fields ∣ , then: Trap. Assert: Due to validation, the expansion of z . types [ x ] is some array fieldtype . Let ( array fieldtype 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) be the destructuring of fieldtype 0 . Replace z . arrays [ a ] . fields [ i ] with pack zt ( val ) . z ; ( ref . null ) ( i 32 . const i ) val ( array . set x ) z ; ( ref . array a ) ( i 32 . const i ) val ( array . set x ) z ; ( ref . array a ) ( i 32 . const i ) val ( array . set x ) ↪ ↪ ↪ z ; trap z ; trap z [ . arrays [ a ] . fields [ i ] = pack zt ( val )] ; ϵ if z . types [ x ] ≈ array ( mut ? zt ) if i ≥ ∣ z . arrays [ a ] . fields ∣ 4.6.9.21. array . len Let z be the current state. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Trap. Assert: Due to validation, val is some ref . array arrayaddr . Let ( ref . array a ) be the destructuring of val . Assert: Due to validation, a < ∣z . arrays ∣ . Push the value ( i 32 . const ∣z . arrays [ a ] . fields ∣ ) to the stack. z ; ( ref . null ) array . len z ; ( ref . array a ) array . len ↪ ↪ trap ( i 32 . const ∣ z . arrays [ a ] . fields ∣ ) 4.6.9.22. array . fill x Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val ′ from the stack. If val ′ = ref . null , then: Trap. Assert: Due to validation, val ′ is some ref . array arrayaddr . Let ( ref . array a ) be the destructuring of val ′ . If a ≥ ∣z . arrays ∣ , then: Do nothing. Else if i + n > ∣z . arrays [ a ] . fields ∣ , then: Trap. If n = 0 , then: Do nothing. Else: Push the value ( ref . array a ) to the stack. Push the value ( i 32 . const i ) to the stack. Push the value val to the stack. Execute the instruction ( array . set x ) . Push the value ( ref . array a ) to the stack. Push the value ( i 32 . const i + 1 ) to the stack. Push the value val to the stack. Push the value ( i 32 . const n − 1 ) to the stack. Execute the instruction ( array . fill x ) . z ; ( ref . null ) ( i 32 . const i ) val ( i 32 . const n ) ( array . fill x ) z ; ( ref . array a ) ( i 32 . const i ) val ( i 32 . const n ) ( array . fill x ) z ; ( ref . array a ) ( i 32 . const i ) val ( i 32 . const n ) ( array . fill x ) z ; ( ref . array a ) ( i 32 . const i ) val ( i 32 . const n ) ( array . fill x ) ( ref . array a ) ( i 32 . const i ) val ( array . set x ) ( ref . array a ) ( i 32 . const i + 1 ) val ( i 32 . const n − 1 ) ( array . fill x ) otherwise ↪ ↪ ↪ ↪ trap trap ϵ if i + n > ∣ z . arrays [ a ] . fields ∣ otherwise, if n = 0 4.6.9.23. array . copy x 1 x 2 Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i 2 ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i 1 ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val ′ from the stack. If val ′ = ref . null and val is reference value, then: Trap. If val = ref . null and val ′ is reference value, then: Trap. If val ′ is some ref . array arrayaddr , then: Let ( ref . array a 1 ) be the destructuring of val ′ . If val is some ref . array arrayaddr , then: If a 1 < ∣z . arrays ∣ and i 1 + n > ∣z . arrays [ a 1 ] . fields ∣ , then: Trap. Let ( ref . array a 2 ) be the destructuring of val . If a 2 ≥ ∣z . arrays ∣ , then: Do nothing. Else if i 2 + n > ∣z . arrays [ a 2 ] . fields ∣ , then: Trap. If n = 0 , then: Do nothing. Else: Assert: Due to validation, the expansion of z . types [ x 2 ] is some array fieldtype . Let ( array fieldtype 0 ) be the destructuring of the expansion of z . types [ x 2 ] . Let ( mut ? zt 2 ) be the destructuring of fieldtype 0 . Let sx ? be sx ( zt 2 ) . Push the value ( ref . array a 1 ) to the stack. If i 1 ≤ i 2 , then: Push the value ( i 32 . const i 1 ) to the stack. Push the value ( ref . array a 2 ) to the stack. Push the value ( i 32 . const i 2 ) to the stack. Execute the instruction ( array . get _ sx ? x 2 ) . Execute the instruction ( array . set x 1 ) . Push the value ( ref . array a 1 ) to the stack. Push the value ( i 32 . const i 1 + 1 ) to the stack. Push the value ( ref . array a 2 ) to the stack. Push the value ( i 32 . const i 2 + 1 ) to the stack. Else: Push the value ( i 32 . const i 1 + n − 1 ) to the stack. Push the value ( ref . array a 2 ) to the stack. Push the value ( i 32 . const i 2 + n − 1 ) to the stack. Execute the instruction ( array . get _ sx ? x 2 ) . Execute the instruction ( array . set x 1 ) . Push the value ( ref . array a 1 ) to the stack. Push the value ( i 32 . const i 1 ) to the stack. Push the value ( ref . array a 2 ) to the stack. Push the value ( i 32 . const i 2 ) to the stack. Push the value ( i 32 . const n − 1 ) to the stack. Execute the instruction ( array . copy x 1 x 2 ) . z ; ( ref . null ) ( i 32 . const i 1 ) ref ( i 32 . const i 2 ) ( i 32 . const n ) ( array . copy x 1 x 2 ) z ; ref ( i 32 . const i 1 ) ( ref . null ) ( i 32 . const i 2 ) ( i 32 . const n ) ( array . copy x 1 x 2 ) z ; ( ref . array a 1 ) ( i 32 . const i 1 ) ( ref . array a 2 ) ( i 32 . const i 2 ) ( i 32 . const n ) ( array . copy x 1 x 2 ) if i 1 + n > ∣ z . arrays [ a 1 ] . fields ∣ z ; ( ref . array a 1 ) ( i 32 . const i 1 ) ( ref . array a 2 ) ( i 32 . const i 2 ) ( i 32 . const n ) ( array . copy x 1 x 2 ) if i 2 + n > ∣ z . arrays [ a 2 ] . fields ∣ z ; ( ref . array a 1 ) ( i 32 . const i 1 ) ( ref . array a 2 ) ( i 32 . const i 2 ) ( i 32 . const n ) ( array . copy x 1 x 2 ) otherwise, if n = 0 z ; ( ref . array a 1 ) ( i 32 . const i 1 ) ( ref . array a 2 ) ( i 32 . const i 2 ) ( i 32 . const n ) ( array . copy x 1 x 2 ) ( ref . array a 1 ) ( i 32 . const i 1 ) ( ref . array a 2 ) ( i 32 . const i 2 ) ( array . get _ sx ? x 2 ) ( array . set x 1 ) ( ref . array a 1 ) ( i 32 . const i 1 + 1 ) ( ref . array a 2 ) ( i 32 . const i 2 + 1 ) ( i 32 . const n − 1 ) ( array . copy x 1 x 2 ) otherwise, if z . types [ x 2 ] ≈ array ( mut ? zt 2 ) ∧ i 1 ≤ i 2 ∧ sx ? = sx ( zt 2 ) z ; ( ref . array a 1 ) ( i 32 . const i 1 ) ( ref . array a 2 ) ( i 32 . const i 2 ) ( i 32 . const n ) ( array . copy x 1 x 2 ) ( ref . array a 1 ) ( i 32 . const i 1 + n − 1 ) ( ref . array a 2 ) ( i 32 . const i 2 + n − 1 ) ( array . get _ sx ? x 2 ) ( array . set x 1 ) ( ref . array a 1 ) ( i 32 . const i 1 ) ( ref . array a 2 ) ( i 32 . const i 2 ) ( i 32 . const n − 1 ) ( array . copy x 1 x 2 ) otherwise, if z . types [ x 2 ] ≈ array ( mut ? zt 2 ) ∧ sx ? = sx ( zt 2 ) ↪ ↪ ↪ ↪ ↪ ↪ ↪ trap trap trap trap ϵ Where: sx ( consttype ) sx ( packtype ) = = ϵ s 4.6.9.24. array . init_data x y Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const j ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Trap. Assert: Due to validation, val is some ref . array arrayaddr . Let ( ref . array a ) be the destructuring of val . If a < ∣z . arrays ∣ and i + n > ∣z . arrays [ a ] . fields ∣ , then: Trap. If the expansion of z . types [ x ] is some array fieldtype , then: Let ( array fieldtype 0 ) be the destructuring of the expansion of z . types [ x ] . Let ( mut ? zt ) be the destructuring of fieldtype 0 . If j + n ⋅ ∣ zt ∣ /8 > ∣z . datas [ y ] . bytes ∣ , then: Trap. If n = 0 , then: Do nothing. Else: Let c be the result for which bytes zt ( c ) = z . datas [ y ] . bytes [ j : ∣ zt ∣ /8 ] . Push the value ( ref . array a ) to the stack. Push the value ( i 32 . const i ) to the stack. Push the value unpack ( zt ) . const unpack zt ( c ) to the stack. Execute the instruction ( array . set x ) . Push the value ( ref . array a ) to the stack. Push the value ( i 32 . const i + 1 ) to the stack. Push the value ( i 32 . const j + ∣ zt ∣ /8 ) to the stack. Push the value ( i 32 . const n − 1 ) to the stack. Execute the instruction ( array . init_data x y ) . Else if n = 0 , then: Do nothing. z ; ( ref . null ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_data x y ) z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_data x y ) if i + n > ∣ z . arrays [ a ] . fields ∣ z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_data x y ) if z . types [ x ] ≈ array ( mut ? zt ) ∧ j + n ⋅ ∣ zt ∣ /8 > ∣ z . datas [ y ] . bytes ∣ z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_data x y ) otherwise, if n = 0 z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_data x y ) ( ref . array a ) ( i 32 . const i ) ( unpack ( zt ) . const unpack zt ( c )) ( array . set x ) ( ref . array a ) ( i 32 . const i + 1 ) ( i 32 . const j + ∣ zt ∣ /8 ) ( i 32 . const n − 1 ) ( array . init_data x y ) otherwise, if z . types [ x ] ≈ array ( mut ? zt ) ∧ bytes zt ( c ) = z . datas [ y ] . bytes [ j : ∣ zt ∣ /8 ] ↪ ↪ ↪ ↪ ↪ trap trap trap ϵ 4.6.9.25. array . init_elem x y Let z be the current state. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const n ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const j ) from the stack. Assert: Due to validation, a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Trap. Assert: Due to validation, val is some ref . array arrayaddr . Let ( ref . array a ) be the destructuring of val . If a < ∣z . arrays ∣ and i + n > ∣z . arrays [ a ] . fields ∣ , then: Trap. If j + n > ∣z . elems [ y ] . refs ∣ , then: Trap. If n = 0 , then: Do nothing. Else if j < ∣z . elems [ y ] . refs ∣ , then: Let ref be the reference value z . elems [ y ] . refs [ j ] . Push the value ( ref . array a ) to the stack. Push the value ( i 32 . const i ) to the stack. Push the value ref to the stack. Execute the instruction ( array . set x ) . Push the value ( ref . array a ) to the stack. Push the value ( i 32 . const i + 1 ) to the stack. Push the value ( i 32 . const j + 1 ) to the stack. Push the value ( i 32 . const n − 1 ) to the stack. Execute the instruction ( array . init_elem x y ) . z ; ( ref . null ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_elem x y ) z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_elem x y ) if i + n > ∣ z . arrays [ a ] . fields ∣ z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_elem x y ) if j + n > ∣ z . elems [ y ] . refs ∣ z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_elem x y ) otherwise, if n = 0 z ; ( ref . array a ) ( i 32 . const i ) ( i 32 . const j ) ( i 32 . const n ) ( array . init_elem x y ) ( ref . array a ) ( i 32 . const i ) ref ( array . set x ) ( ref . array a ) ( i 32 . const i + 1 ) ( i 32 . const j + 1 ) ( i 32 . const n − 1 ) ( array . init_elem x y ) otherwise, if ref = z . elems [ y ] . refs [ j ] ↪ ↪ ↪ ↪ ↪ trap trap trap ϵ 4.6.9.26. any . convert_extern Assert: Due to validation, a value is on the top of the stack. Pop the value val from the stack. If val = ref . null , then: Push the value ref . null to the stack. If val is some ref . extern ref , then: Let ( ref . extern ref ) be the destructuring of val . Push the value ref to the stack. ( ref . null ) any . convert_extern ( ref . extern ref ) any . convert_extern ↪ ↪ ref . null ref 4.6.9.27. extern . convert_any Assert: Due to validation, a reference value is on the top of the stack. Pop the value ref from the stack. If ref = ref . null , then: Push the value ref . null to the stack. Else: Push the value ( ref . extern ref ) to the stack. ref extern . convert_any ref extern . convert_any ↪ ↪ ref . null ( ref . extern ref ) if ref = ref . null otherwise 4.6.10. Numeric Instructions Numeric instructions are defined in terms of the generic numeric operators . The mapping of numeric instructions to their underlying operators is expressed by the following definition: op i N ( i 1 , … , i k ) op f N ( z 1 , … , z k ) = = i op N ( i 1 , … , i k ) f op N ( z 1 , … , z k ) And for conversion operators : cvtop t 1 , t 2 sx ? ( c ) = cvtop ∣ t 1 ∣ , ∣ t 2 ∣ sx ? ( c ) 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 i 32 . add applied to operands i 1 , i 2 invokes add i 32 ( i 1 , i 2 ) , which maps to the generic iadd 32 ( i 1 , i 2 ) via the above definition. Similarly, i 64 . trunc _ f 32 _s applied to z invokes trunc f 32 , i 64 s ( z ) , which maps to the generic trunc s 32 , 64 ( z ) . 4.6.10.1. nt . const c Push the value ( nt . const c ) to the stack. Note No formal reduction rule is required for this instruction, since const instructions already are values . 4.6.10.2. nt . unop Assert: Due to validation , a value of number type nt is on the top of the stack. Pop the value ( numtype 0 . const c 1 ) from the stack. If unop nt ( c 1 ) is empty, then: Trap. Let c be an element of unop nt ( c 1 ) . Push the value ( nt . const c ) to the stack. ( nt . const c 1 ) ( nt . unop ) ( nt . const c 1 ) ( nt . unop ) ↪ ↪ ( nt . const c ) trap if c ∈ unop nt ( c 1 ) if unop nt ( c 1 ) = ϵ 4.6.10.3. nt . binop Assert: Due to validation , a value of number type nt is on the top of the stack. Pop the value ( numtype 0 . const c 2 ) from the stack. Assert: Due to validation , a number value is on the top of the stack. Pop the value ( numtype 0 . const c 1 ) from the stack. If binop nt ( c 1 , c 2 ) is empty, then: Trap. Let c be an element of binop nt ( c 1 , c 2 ) . Push the value ( nt . const c ) to the stack. ( nt . const c 1 ) ( nt . const c 2 ) ( nt . binop ) ( nt . const c 1 ) ( nt . const c 2 ) ( nt . binop ) ↪ ↪ ( nt . const c ) trap if c ∈ binop nt ( c 1 , c 2 ) if binop nt ( c 1 , c 2 ) = ϵ 4.6.10.4. nt . testop Assert: Due to validation , a value of number type nt is on the top of the stack. Pop the value ( numtype 0 . const c 1 ) from the stack. Let c be testop nt ( c 1 ) . Push the value ( i 32 . const c ) to the stack. ( nt . const c 1 ) ( nt . testop ) ↪ ( i 32 . const c ) if c = testop nt ( c 1 ) 4.6.10.5. nt . relop Assert: Due to validation , a value of number type nt is on the top of the stack. Pop the value ( numtype 0 . const c 2 ) from the stack. Assert: Due to validation , a number value is on the top of the stack. Pop the value ( numtype 0 . const c 1 ) from the stack. Let c be relop nt ( c 1 , c 2 ) . Push the value ( i 32 . const c ) to the stack. ( nt . const c 1 ) ( nt . const c 2 ) ( nt . relop ) ↪ ( i 32 . const c ) if c = relop nt ( c 1 , c 2 ) 4.6.10.6. nt 2 . cvtop _ nt 1 Assert: Due to validation , a value of number type nt 1 is on the top of the stack. Pop the value ( numtype 0 . const c 1 ) from the stack. If cvtop nt 1 , nt 2 ( c 1 ) is empty, then: Trap. Let c be an element of cvtop nt 1 , nt 2 ( c 1 ) . Push the value ( nt 2 . const c ) to the stack. ( nt 1 . const c 1 ) ( nt 2 . cvtop _ nt 1 ) ( nt 1 . const c 1 ) ( nt 2 . cvtop _ nt 1 ) ↪ ↪ ( nt 2 . const c ) trap if c ∈ cvtop nt 1 , nt 2 ( c 1 ) if cvtop nt 1 , nt 2 ( c 1 ) = ϵ 4.6.11. Vector Instructions Vector instructions that operate bitwise are handled as integer operations of respective bit width. op v N ( i 1 , … , i k ) = i op N ( i 1 , … , i k ) Most other vector instructions are defined in terms of numeric operators that are applied lane-wise according to the given shape . op t x N ( n 1 , … , n k ) = lanes t x N − 1 ( op t ( i 1 , … , i k ) ∗ ) ( if i 1 ∗ = lanes t x N ( n 1 ) ∧ ⋯ ∧ i k ∗ = lanes t x N ( n k ) Note For example, the result of instruction i32x4 . add applied to operands v 1 , v 2 invokes add i32x4 ( v 1 , v 2 ) , which maps to lanes i32x4 − 1 ( add i 32 ( i 1 , i 2 ) ∗ ) , where i 1 ∗ and i 2 ∗ are sequences resulting from invoking lanes i32x4 ( v 1 ) and lanes i32x4 ( v 2 ) respectively. For non-deterministic operators this definition is generalized to sets: op t x N ( n 1 , … , n k ) = { lanes t x N − 1 ( i ∗ ) ∣ i ∗ ∈ × ( op t ( i 1 , … , i k ) ∗ ) ∧ i 1 ∗ = lanes t x N ( n 1 ) ∧ ⋯ ∧ i k ∗ = lanes t x N ( n k )} where × { x ∗ } N transforms a sequence of N sets of values into a set of sequences of N values by computing the set product: × ( S 1 … S N ) = { x 1 … x N ∣ x 1 ∈ S 1 ∧ ⋯ ∧ x N ∈ S N } The remaining vector operators use individual definitions . 4.6.11.1. v 128 . const c Push the value ( v 128 . const c ) to the stack. Note No formal reduction rule is required for this instruction, since const instructions are already values . 4.6.11.2. v 128 . vvunop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Assert: Due to validation , ∣ vvunop v 128 ( c 1 ) ∣ > 0 . Let c be an element of vvunop v 128 ( c 1 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . vvunop ) ↪ ( v 128 . const c ) if c ∈ vvunop v 128 ( c 1 ) 4.6.11.3. v 128 . vvbinop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Assert: Due to validation , ∣ vvbinop v 128 ( c 1 , c 2 ) ∣ > 0 . Let c be an element of vvbinop v 128 ( c 1 , c 2 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( v 128 . vvbinop ) ↪ ( v 128 . const c ) if c ∈ vvbinop v 128 ( c 1 , c 2 ) 4.6.11.4. v 128 . vvternop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 3 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Assert: Due to validation , ∣ vvternop v 128 ( c 1 , c 2 , c 3 ) ∣ > 0 . Let c be an element of vvternop v 128 ( c 1 , c 2 , c 3 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( v 128 . const c 3 ) ( v 128 . vvternop ) ↪ ( v 128 . const c ) if c ∈ vvternop v 128 ( c 1 , c 2 , c 3 ) 4.6.11.5. v 128 . any_true Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be inez ∣ v 128 ∣ ( c 1 ) . Push the value ( i 32 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . any_true ) ↪ ( i 32 . const c ) if c = inez ∣ v 128 ∣ ( c 1 ) 4.6.11.6. sh . vunop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. If vunop sh ( c 1 ) is empty, then: Trap. Let c be an element of vunop sh ( c 1 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( sh . vunop ) ( v 128 . const c 1 ) ( sh . vunop ) ↪ ↪ ( v 128 . const c ) trap if c ∈ vunop sh ( c 1 ) if vunop sh ( c 1 ) = ϵ 4.6.11.7. sh . vbinop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. If vbinop sh ( c 1 , c 2 ) is empty, then: Trap. Let c be an element of vbinop sh ( c 1 , c 2 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( sh . vbinop ) ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( sh . vbinop ) ↪ ↪ ( v 128 . const c ) trap if c ∈ vbinop sh ( c 1 , c 2 ) if vbinop sh ( c 1 , c 2 ) = ϵ 4.6.11.8. sh . vternop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 3 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. If vternop sh ( c 1 , c 2 , c 3 ) is empty, then: Trap. Let c be an element of vternop sh ( c 1 , c 2 , c 3 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( v 128 . const c 3 ) ( sh . vternop ) ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( v 128 . const c 3 ) ( sh . vternop ) ↪ ↪ ( v 128 . const c ) trap if c ∈ vternop sh ( c 1 , c 2 , c 3 ) if vternop sh ( c 1 , c 2 , c 3 ) = ϵ 4.6.11.9. i N x M . all_true Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let i ∗ be lanes i N x M ( c 1 ) . Let c be Π inez N ( i ) ∗ . Push the value ( i 32 . const c ) to the stack. ( v 128 . const c 1 ) ( i N x M . all_true ) ↪ ( i 32 . const c ) if i ∗ = lanes i N x M ( c 1 ) ∧ c = Π ( inez N ( i ) ∗ ) 4.6.11.10. sh . vrelop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be vrelop sh ( c 1 , c 2 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( sh . vrelop ) ↪ ( v 128 . const c ) if c = vrelop sh ( c 1 , c 2 ) 4.6.11.11. sh . vshiftop Assert: Due to validation , a value of number type i 32 is on the top of the stack. Pop the value ( i 32 . const i ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be vshiftop sh ( c 1 , i ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( i 32 . const i ) ( sh . vshiftop ) ↪ ( v 128 . const c ) if c = vshiftop sh ( c 1 , i ) 4.6.11.12. sh . bitmask Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be bitmask sh ( c 1 ) . Push the value ( i 32 . const c ) to the stack. ( v 128 . const c 1 ) ( sh . bitmask ) ↪ ( i 32 . const c ) if c = bitmask sh ( c 1 ) 4.6.11.13. sh . swizzlop Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be swizzlop sh ( c 1 , c 2 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( sh . swizzlop ) ↪ ( v 128 . const c ) if c = swizzlop sh ( c 1 , c 2 ) 4.6.11.14. sh . shuffle i ∗ Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be shuffle sh ( i ∗ , c 1 , c 2 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( sh . shuffle i ∗ ) ↪ ( v 128 . const c ) if c = shuffle sh ( i ∗ , c 1 , c 2 ) 4.6.11.15. i N x M . splat Assert: Due to validation , a value is on the top of the stack. Pop the value ( numtype 0 . const c 1 ) from the stack. Assert: Due to validation , numtype 0 = unpack ( i N ) . Let c be lanes i N x M − 1 ( pack i N ( c 1 ) M ) . Push the value ( v 128 . const c ) to the stack. ( unpack ( i N ) . const c 1 ) ( i N x M . splat ) ↪ ( v 128 . const c ) if c = lanes i N x M − 1 ( pack i N ( c 1 ) M ) 4.6.11.16. lanetype x M . extract_lane _ sx ′ ? i Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. If sx ′ ? is not defined, then: Assert: Due to validation , lanetype is number type. Assert: Due to validation , i < ∣ lanes lanetype x M ( c 1 ) ∣ . Let c 2 be lanes lanetype x M ( c 1 ) [ i ] . Push the value ( lanetype . const c 2 ) to the stack. Else: Assert: Due to validation , lanetype is packed type. Let sx be sx ′ ? . Assert: Due to validation , i < ∣ lanes lanetype x M ( c 1 ) ∣ . Let c 2 be extend ∣ lanetype ∣ , 32 sx ( lanes lanetype x M ( c 1 ) [ i ]) . Push the value ( i 32 . const c 2 ) to the stack. ( v 128 . const c 1 ) ( nt x M . extract_lane i ) ( v 128 . const c 1 ) ( pt x M . extract_lane _ sx i ) ↪ ↪ ( nt . const c 2 ) ( i 32 . const c 2 ) if c 2 = lanes nt x M ( c 1 ) [ i ] if c 2 = extend ∣ pt ∣ , 32 sx ( lanes pt x M ( c 1 ) [ i ]) 4.6.11.17. i N x M . replace_lane i Assert: Due to validation , a value is on the top of the stack. Pop the value ( numtype 0 . const c 2 ) from the stack. Assert: Due to validation , numtype 0 = unpack ( i N ) . Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be lanes i N x M − 1 ( lanes i N x M ( c 1 ) [ [ i ] = pack i N ( c 2 )]) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( unpack ( i N ) . const c 2 ) ( i N x M . replace_lane i ) if c = lanes i N x M − 1 ( lanes i N x M ( c 1 ) [ [ i ] = pack i N ( c 2 )]) ↪ ( v 128 . const c ) 4.6.11.18. sh 2 . vextunop _ sh 1 Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be vextunop sh 1 , sh 2 ( c 1 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( sh 2 . vextunop _ sh 1 ) ↪ ( v 128 . const c ) if vextunop sh 1 , sh 2 ( c 1 ) = c 4.6.11.19. sh 2 . vextbinop _ sh 1 Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be vextbinop sh 1 , sh 2 ( c 1 , c 2 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( sh 2 . vextbinop _ sh 1 ) ↪ ( v 128 . const c ) if vextbinop sh 1 , sh 2 ( c 1 , c 2 ) = c 4.6.11.20. sh 2 . vextternop _ sh 1 Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 3 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be vextternop sh 1 , sh 2 ( c 1 , c 2 , c 3 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( v 128 . const c 3 ) ( sh 2 . vextternop _ sh 1 ) ↪ ( v 128 . const c ) if vextternop sh 1 , sh 2 ( c 1 , c 2 , c 3 ) = c 4.6.11.21. sh 2 . narrow _ sh 1 _ sx Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 2 ) from the stack. Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be narrow sh 1 , sh 2 sx ( c 1 , c 2 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( v 128 . const c 2 ) ( sh 2 . narrow _ sh 1 _ sx ) ↪ ( v 128 . const c ) if c = narrow sh 1 , sh 2 sx ( c 1 , c 2 ) 4.6.11.22. sh 2 . vcvtop _ sh 1 Assert: Due to validation , a value of vector type v 128 is on the top of the stack. Pop the value ( v 128 . const c 1 ) from the stack. Let c be vcvtop sh 1 , sh 2 ( vcvtop , c 1 ) . Push the value ( v 128 . const c ) to the stack. ( v 128 . const c 1 ) ( sh 2 . vcvtop _ sh 1 ) ↪ ( v 128 . const c ) if c = vcvtop sh 1 , sh 2 ( vcvtop , c 1 ) 4.6.12. Expressions An expression is evaluated relative to a current frame pointing to its containing module instance . 4.6.12.1. eval_expr instr ∗ Execute the sequence instr ∗ . Pop the value val from the stack. Return val . z ; instr ∗ ↪ ∗ z ′ ; val ∗ if z ; instr ∗ ↪ ∗ z ′ ; 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 s , as defined by the following auxiliary functions. 4.7.1.1. Tags 4.7.1.2. alloctag ( s , tagtype ) Let taginst be the tag instance { type tagtype } . Let a be the length of s . tags . Append taginst to s . tags . Return a . alloctag ( s , tagtype ) if taginst = { type tagtype } = ( s ⊕ { tags taginst } , ∣ s . tags ∣ ) 4.7.1.3. Globals 4.7.1.4. allocglobal ( s , globaltype , val ) Let globalinst be the global instance { type globaltype , value val } . Let a be the length of s . globals . Append globalinst to s . globals . Return a . allocglobal ( s , globaltype , val ) if globalinst = { type globaltype , value val } = ( s ⊕ { globals globalinst } , ∣ s . globals ∣ ) 4.7.1.5. Memories 4.7.1.6. allocmem ( s , at [ i .. j ? ] page ) Let meminst be the memory instance { type ( at [ i .. j ? ] page ) , bytes 0x00 i ⋅ 64 Ki } . Let a be the length of s . mems . Append meminst to s . mems . Return a . allocmem ( s , at [ i .. j ? ] page ) if meminst = { type ( at [ i .. j ? ] page ) , bytes ( 0x00 ) i ⋅ 64 Ki } = ( s ⊕ { mems meminst } , ∣ s . mems ∣ ) 4.7.1.7. Tables 4.7.1.8. alloctable ( s , at [ i .. j ? ] rt , ref ) Let tableinst be the table instance { type ( at [ i .. j ? ] rt ) , refs ref i } . Let a be the length of s . tables . Append tableinst to s . tables . Return a . alloctable ( s , at [ i .. j ? ] rt , ref ) if tableinst = { type ( at [ i .. j ? ] rt ) , refs ref i } = ( s ⊕ { tables tableinst } , ∣ s . tables ∣ ) 4.7.1.9. Functions 4.7.1.10. allocfunc ( s , deftype , code , moduleinst ) Let funcinst be the function instance { type deftype , module moduleinst , code code } . Let a be the length of s . funcs . Append funcinst to s . funcs . Return a . allocfunc ( s , deftype , code , moduleinst ) if funcinst = { type deftype , module moduleinst , code code } = ( s ⊕ { funcs funcinst } , ∣ s . funcs ∣ ) 4.7.1.11. Data segments 4.7.1.12. allocdata ( s , ok , byte ∗ ) Let datainst be the data instance { bytes byte ∗ } . Let a be the length of s . datas . Append datainst to s . datas . Return a . allocdata ( s , ok , byte ∗ ) if datainst = { bytes byte ∗ } = ( s ⊕ { datas datainst } , ∣ s . datas ∣ ) 4.7.1.13. Element segments 4.7.1.14. allocelem ( s , elemtype , ref ∗ ) Let eleminst be the element instance { type elemtype , refs ref ∗ } . Let a be the length of s . elems . Append eleminst to s . elems . Return a . allocelem ( s , elemtype , ref ∗ ) if eleminst = { type elemtype , refs ref ∗ } = ( s ⊕ { elems eleminst } , ∣ s . elems ∣ ) 4.7.1.15. Growing memories 4.7.1.16. growmem ( meminst , n ) Let { type ( at [ i .. j ? ] page ) , bytes b ∗ } be the destructuring of meminst . Let i ′ be ∣ b ∗ ∣ / ( 64 Ki ) + n . If not ( i ′ ≤ j ) ? , then: Fail. If i ′ ≤ 2 ∣ at ∣ − 16 , then: Let meminst ′ be the memory instance { type ( at [ i ′ .. j ? ] page ) , bytes b ∗ 0x00 n ⋅ 64 Ki } . Return meminst ′ . Fail. growmem ( meminst , n ) = meminst ′ if meminst = { type ( at [ i .. j ? ] page ) , bytes b ∗ } ∧ meminst ′ = { type ( at [ i ′ .. j ? ] page ) , bytes b ∗ ( 0x00 ) n ⋅ 64 Ki } ∧ i ′ = ∣ b ∗ ∣ / ( 64 Ki ) + n ∧ ( i ′ ≤ j ) ? ∧ i ′ ≤ 2 ∣ at ∣ − 16 4.7.1.17. Growing tables 4.7.1.18. growtable ( tableinst , n , r ) Let { type ( at [ i .. j ? ] rt ) , refs r ′ ∗ } be the destructuring of tableinst . Let i ′ be ∣ r ′ ∗ ∣ + n . If not ( i ′ ≤ j ) ? , then: Fail. If i ′ ≤ 2 ∣ at ∣ − 1 , then: Let tableinst ′ be the table instance { type ( at [ i ′ .. j ? ] rt ) , refs r ′ ∗ r n } . Return tableinst ′ . Fail. growtable ( tableinst , n , r ) = tableinst ′ if tableinst = { type ( at [ i .. j ? ] rt ) , refs r ′ ∗ } ∧ tableinst ′ = { type ( at [ i ′ .. j ? ] rt ) , refs r ′ ∗ r n } ∧ i ′ = ∣ r ′ ∗ ∣ + n ∧ ( i ′ ≤ j ) ? ∧ i ′ ≤ 2 ∣ at ∣ − 1 4.7.1.19. Modules 4.7.1.20. allocmodule ( s , module , externaddr ∗ , val g ∗ , ref t ∗ , ref e ∗ ∗ ) Let ( module type ∗ import ∗ tag ∗ global ∗ mem ∗ table ∗ func ∗ data ∗ elem ∗ start ? export ∗ ) be the destructuring of module . Let aa i ∗ be tags ( externaddr ∗ ) . Let ga i ∗ be globals ( externaddr ∗ ) . Let fa i ∗ be funcs ( externaddr ∗ ) . Let ma i ∗ be mems ( externaddr ∗ ) . Let ta i ∗ be tables ( externaddr ∗ ) . Let fa ∗ be ∣s . funcs ∣ + i f for all i f from 0 to ∣ func ∗ ∣ − 1 . Let tagtype ∗ be the tag type sequence ϵ . For each tag in tag ∗ , do: Let ( tag tagtype ) be the destructuring of tag . Append tagtype to tagtype ∗ . Let byte ∗ ∗ be the byte sequence sequence ϵ . For each data in data ∗ , do: Let ( data byte ∗ datamode ) be the destructuring of data . Append byte ∗ to byte ∗ ∗ . Let globaltype ∗ be the global type sequence ϵ . For each global in global ∗ , do: Let ( global globaltype expr g ) be the destructuring of global . Append globaltype to globaltype ∗ . Let tabletype ∗ be the table type sequence ϵ . For each table in table ∗ , do: Let ( table tabletype expr t ) be the destructuring of table . Append tabletype to tabletype ∗ . Let memtype ∗ be the memory type sequence ϵ . For each mem in mem ∗ , do: Let ( memory memtype ) be the destructuring of mem . Append memtype to memtype ∗ . Let dt ∗ be alloctype ∗ ( type ∗ ) . Let elemtype ∗ be the reference type sequence ϵ . For each elem in elem ∗ , do: Let ( elem elemtype expr e ∗ elemmode ) be the destructuring of elem . Append elemtype to elemtype ∗ . Let expr f ∗ be the expression sequence ϵ . Let local ∗ ∗ be the local sequence sequence ϵ . Let x ∗ be the type index sequence ϵ . For each func in func ∗ , do: Let ( func x local ∗ expr f ) be the destructuring of func . Append expr f to expr f ∗ . Append local ∗ to local ∗ ∗ . Append x to x ∗ . Let aa ∗ be ϵ . For each tagtype in tagtype ∗ , do: Let aa be the tag address alloctag ( s , tagtype [ := dt ∗ ] ) . Append aa to aa ∗ . Let ga ∗ be ϵ . For each globaltype in globaltype ∗ and val g in val g ∗ , do: Let ga be the global address allocglobal ( s , globaltype [ := dt ∗ ] , val g ) . Append ga to ga ∗ . Let ma ∗ be ϵ . For each memtype in memtype ∗ , do: Let ma be the memory address allocmem ( s , memtype [ := dt ∗ ] ) . Append ma to ma ∗ . Let ta ∗ be ϵ . For each tabletype in tabletype ∗ and ref t in ref t ∗ , do: Let ta be the table address alloctable ( s , tabletype [ := dt ∗ ] , ref t ) . Append ta to ta ∗ . Let xi ∗ be ϵ . For each export in export ∗ , do: Let xi be the export instance allocexport ( moduleinst , export ) . Append xi to xi ∗ . Let da ∗ be ϵ . For each byte ∗ in byte ∗ ∗ , do: Let da be the data address allocdata ( s , ok , byte ∗ ) . Append da to da ∗ . Let ea ∗ be ϵ . For each elemtype in elemtype ∗ and ref e ∗ in ref e ∗ ∗ , do: Let ea be the elem address allocelem ( s , elemtype [ := dt ∗ ] , ref e ∗ ) . Append ea to ea ∗ . Let moduleinst be the module instance { 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 ∗ } . Let funcaddr 0 ∗ be ϵ . For each expr f in expr f ∗ and local ∗ in local ∗ ∗ and x in x ∗ , do: Let funcaddr 0 be the function address allocfunc ( s , dt ∗ [ x ] , func x local ∗ expr f , moduleinst ) . Append funcaddr 0 to funcaddr 0 ∗ . Assert: Due to validation, funcaddr 0 ∗ = fa ∗ . Return moduleinst . allocmodule ( s , module , externaddr ∗ , val g ∗ , ref t ∗ , ( ref e ∗ ) ∗ ) if module = module type ∗ import ∗ tag ∗ global ∗ mem ∗ table ∗ func ∗ data ∗ elem ∗ start ? export ∗ ∧ tag ∗ = ( tag tagtype ) ∗ ∧ global ∗ = ( global globaltype expr g ) ∗ ∧ mem ∗ = ( memory memtype ) ∗ ∧ table ∗ = ( table tabletype expr t ) ∗ ∧ func ∗ = ( func x local ∗ expr f ) ∗ ∧ data ∗ = ( data byte ∗ datamode ) ∗ ∧ elem ∗ = ( elem elemtype expr e ∗ elemmode ) ∗ ∧ aa i ∗ = tags ( externaddr ∗ ) ∧ ga i ∗ = globals ( externaddr ∗ ) ∧ ma i ∗ = mems ( externaddr ∗ ) ∧ ta i ∗ = tables ( externaddr ∗ ) ∧ fa i ∗ = funcs ( externaddr ∗ ) ∧ dt ∗ = alloctype ∗ ( type ∗ ) ∧ fa ∗ = ( ∣ s . funcs ∣ + i f ) i f < ∣ func ∗ ∣ ∧ ( s 1 , aa ∗ ) = alloctag ∗ ( s , tagtype [ := dt ∗ ] ∗ ) ∧ ( s 2 , ga ∗ ) = allocglobal ∗ ( s 1 , globaltype [ := dt ∗ ] ∗ , val g ∗ ) ∧ ( s 3 , ma ∗ ) = allocmem ∗ ( s 2 , memtype [ := dt ∗ ] ∗ ) ∧ ( s 4 , ta ∗ ) = alloctable ∗ ( s 3 , tabletype [ := dt ∗ ] ∗ , ref t ∗ ) ∧ ( s 5 , da ∗ ) = allocdata ∗ ( s 4 , ok ∣ data ∗ ∣ , ( byte ∗ ) ∗ ) ∧ ( s 6 , ea ∗ ) = allocelem ∗ ( s 5 , elemtype [ := dt ∗ ] ∗ , ( ref e ∗ ) ∗ ) ∧ ( s 7 , fa ∗ ) = allocfunc ∗ ( s 6 , dt ∗ [ x ] ∗ , ( func x local ∗ expr f ) ∗ , moduleinst ∣ func ∗ ∣ ) ∧ xi ∗ = allocexport ∗ ({ tags aa i ∗ aa ∗ , globals ga i ∗ ga ∗ , mems ma i ∗ ma ∗ , tables ta i ∗ ta ∗ , funcs fa i ∗ fa ∗ } , export ∗ ) ∧ 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 ∗ } = ( s 7 , moduleinst ) Here, the notation allocx ∗ is shorthand for multiple allocations of object kind X , defined as follows: allocX ∗ ( s , ϵ , ϵ ) allocX ∗ ( s , X X ′ ∗ , Y Y ′ ∗ ) = = ( s , ϵ ) ( s 2 , a a ′ ∗ ) if ( s 1 , a ) = allocX ( X , Y , s , X , Y ) ∧ ( s 2 , a ′ ∗ ) = allocX ∗ ( s 1 , X ′ ∗ , Y ′ ∗ ) For types, however, allocation is defined in terms of rolling and substitution of all preceding types to produce a list of closed defined types : 4.7.1.21. alloctype ∗ ( type ′′ ∗ ) If type ′′ ∗ = ϵ , then: Return ϵ . Let type ′ ∗ type be type ′′ ∗ . Let ( type rectype ) be the destructuring of type . Let deftype ′ ∗ be alloctype ∗ ( type ′ ∗ ) . Let x be the length of deftype ′ ∗ . Let deftype ∗ be roll x ∗ ( rectype ) [ := deftype ′ ∗ ] . Return deftype ′ ∗ deftype ∗ . alloctype ∗ ( ϵ ) alloctype ∗ ( type ′ ∗ type ) = = ϵ deftype ′ ∗ deftype ∗ if deftype ′ ∗ = alloctype ∗ ( type ′ ∗ ) ∧ type = type rectype ∧ deftype ∗ = roll x ∗ ( rectype ) [ := deftype ′ ∗ ] ∧ x = ∣ deftype ′ ∗ ∣ Finally, export instances are produced with the help of the following definition: 4.7.1.22. allocexport ( moduleinst , export name externidx ) If externidx is some tag tagidx , then: Let ( tag x ) be the destructuring of externidx . Return { name name , addr ( tag moduleinst . tags [ x ])} . If externidx is some global globalidx , then: Let ( global x ) be the destructuring of externidx . Return { name name , addr ( global moduleinst . globals [ x ])} . If externidx is some memory memidx , then: Let ( memory x ) be the destructuring of externidx . Return { name name , addr ( mem moduleinst . mems [ x ])} . If externidx is some table tableidx , then: Let ( table x ) be the destructuring of externidx . Return { name name , addr ( table moduleinst . tables [ x ])} . Assert: Due to validation, externidx is some func funcidx . Let ( func x ) be the destructuring of externidx . Return { name name , addr ( func moduleinst . funcs [ x ])} . allocexport ( moduleinst , export name ( tag x )) allocexport ( moduleinst , export name ( global x )) allocexport ( moduleinst , export name ( memory x )) allocexport ( moduleinst , export name ( table x )) allocexport ( moduleinst , export name ( func x )) = = = = = { name name , addr ( tag moduleinst . tags [ x ])} { name name , addr ( global moduleinst . globals [ x ])} { name name , addr ( mem moduleinst . mems [ x ])} { name name , addr ( table moduleinst . tables [ x ])} { name name , addr ( func moduleinst . funcs [ x ])} 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 s , 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. 4.7.2.1. instantiate ( s , module , externaddr ∗ ) If module is not valid , then: Fail. Let xt i ∗ → xt e ∗ be the destructuring of the type of module . Let ( module type ∗ import ∗ tag ∗ global ∗ mem ∗ table ∗ func ∗ data ∗ elem ∗ start ? export ∗ ) be the destructuring of module . If ∣ externaddr ∗ ∣ = ∣ xt i ∗ ∣ , then: Fail. For all externaddr in externaddr ∗ , and corresponding xt i in xt i ∗ : If externaddr is not valid with type xt i , then: Fail. Let instr d ∗ be the concatenation of rundata i d ( data ∗ [ i d ] ) i d < ∣ data ∗ ∣ . Let instr e ∗ be the concatenation of runelem i e ( elem ∗ [ i e ] ) i e < ∣ elem ∗ ∣ . Let moduleinst 0 be the module instance { types alloctype ∗ ( type ∗ ) , globals globals ( externaddr ∗ ) , funcs funcs ( externaddr ∗ ) ( ∣s . funcs ∣ + i f ) i f < ∣ func ∗ ∣ } . Let expr t ∗ be the expression sequence ϵ . For each table in table ∗ , do: Let ( table tabletype expr t ) be the destructuring of table . Append expr t to expr t ∗ . Let expr g ∗ be the expression sequence ϵ . Let globaltype ∗ be the global type sequence ϵ . For each global in global ∗ , do: Let ( global globaltype expr g ) be the destructuring of global . Append expr g to expr g ∗ . Append globaltype to globaltype ∗ . Let expr e ∗ ∗ be the expression sequence sequence ϵ . For each elem in elem ∗ , do: Let ( elem reftype expr e ∗ elemmode ) be the destructuring of elem . Append expr e ∗ to expr e ∗ ∗ . Let z be the state ( s , { module moduleinst 0 }) . Let F be the frame z . frame . Push the frame F . Let val g ∗ be evalglobal ∗ ( z , globaltype ∗ , expr g ∗ ) . Let ref t ∗ be evalexpr ∗ ( z , expr t ∗ ) . Let ref e ∗ ∗ be evalexpr ∗ ∗ ( z , expr e ∗ ∗ ) . Pop the frame from the stack. Let ( s , f ) be the destructuring of z . Let moduleinst be allocmodule ( s , module , externaddr ∗ , val g ∗ , ref t ∗ , ref e ∗ ∗ ) . Let F ′ be the frame { module moduleinst } . Push the frame F ′ . Execute the sequence instr e ∗ . Execute the sequence instr d ∗ . If start ? is defined, then: Let ( start x ) be start ? . Let instr s be the instruction ( call x ) . Execute the instruction instr s . Pop the frame from the stack. Return moduleinst . instantiate ( s , module , externaddr ∗ ) if ⊢ module : xt i ∗ → xt e ∗ ∧ ( s ⊢ externaddr : xt i ) ∗ ∧ module = module type ∗ import ∗ tag ∗ global ∗ mem ∗ table ∗ func ∗ data ∗ elem ∗ start ? export ∗ ∧ global ∗ = ( global globaltype expr g ) ∗ ∧ table ∗ = ( table tabletype expr t ) ∗ ∧ data ∗ = ( data byte ∗ datamode ) ∗ ∧ elem ∗ = ( elem reftype expr e ∗ elemmode ) ∗ ∧ start ? = ( start x ) ? ∧ moduleinst 0 = { types alloctype ∗ ( type ∗ ) , globals globals ( externaddr ∗ ) , funcs funcs ( externaddr ∗ ) ( ∣ s . funcs ∣ + i f ) i f < ∣ func ∗ ∣ } ∧ z = s ; { module moduleinst 0 } ∧ ( z ′ , val g ∗ ) = evalglobal ∗ ( z , globaltype ∗ , expr g ∗ ) ∧ ( z ′′ , ref t ∗ ) = evalexpr ∗ ( z ′ , expr t ∗ ) ∧ ( z ′′′ , ref e ∗ ∗ ) = evalexpr ∗ ∗ ( z ′′ , expr e ∗ ∗ ) ∧ z ′′′ = s ′′′ ; f ∧ ( s ′′′′ , moduleinst ) = allocmodule ( s ′′′ , module , externaddr ∗ , val g ∗ , ref t ∗ , ( ref e ∗ ) ∗ ) ∧ instr d ∗ = ⨁ rundata i d ( data ∗ [ i d ] ) i d < ∣ data ∗ ∣ ∧ instr e ∗ = ⨁ runelem i e ( elem ∗ [ i e ] ) i e < ∣ elem ∗ ∣ ∧ instr s ? = ( call x ) ? = s ′′′′ ; { module moduleinst } ; instr e ∗ instr d ∗ instr s ? where: 4.7.2.2. evalexpr ∗ ( z , expr ′′ ∗ ) If expr ′′ ∗ = ϵ , then: Return ϵ . Let expr expr ′ ∗ be expr ′′ ∗ . Let ref be the result of evaluating expr with state z . Let ref ′ ∗ be evalexpr ∗ ( z , expr ′ ∗ ) . Return ref ref ′ ∗ . evalexpr ∗ ( z , ϵ ) evalexpr ∗ ( z , expr expr ′ ∗ ) = = ( z , ϵ ) ( z ′′ , ref ref ′ ∗ ) if z ; expr ↪ ∗ z ′ ; ref ∧ ( z ′′ , ref ′ ∗ ) = evalexpr ∗ ( z ′ , expr ′ ∗ ) and: 4.7.2.3. evalglobal ∗ ( z , globaltype ∗ , expr ′′ ∗ ) If expr ′′ ∗ = ϵ , then: Assert: Due to validation, globaltype ∗ = ϵ . Return ϵ . Else: Let expr expr ′ ∗ be expr ′′ ∗ . Assert: Due to validation, ∣ globaltype ∗ ∣ ≥ 1 . Let gt gt ′ ∗ be globaltype ∗ . Let val be the result of evaluating expr with state z . Let ( s , f ) be the destructuring of z . Let a be allocglobal ( s , gt , val ) . Append a to f . module . globals . Let val ′ ∗ be evalglobal ∗ (( s , f ) , gt ′ ∗ , expr ′ ∗ ) . Return val val ′ ∗ . evalglobal ∗ ( z , ϵ , ϵ ) evalglobal ∗ ( z , gt gt ′ ∗ , expr expr ′ ∗ ) = = ( z , ϵ ) ( z ′′ , val val ′ ∗ ) if z ; expr ↪ ∗ z ′ ; val ∧ z ′ = s ; f ∧ ( s ′ , a ) = allocglobal ( s , gt , val ) ∧ ( z ′′ , val ′ ∗ ) = evalglobal ∗ (( s ′ ; f [ . module . globals = ⊕ a ]) , gt ′ ∗ , expr ′ ∗ ) 4.7.2.4. rundata x ( data b n datamode ) If datamode = passive , then: Return ϵ . Assert: Due to validation, datamode is some active memidx expr . Let ( active y instr ∗ ) be the destructuring of datamode . Return instr ∗ ( i 32 . const 0 ) ( i 32 . const n ) ( memory . init y x ) ( data . drop x ) . 4.7.2.5. runelem x ( elem rt e n elemmode ) If elemmode = passive , then: Return ϵ . If elemmode = declare , then: Return ( elem . drop x ) . Assert: Due to validation, elemmode is some active tableidx expr . Let ( active y instr ∗ ) be the destructuring of elemmode . Return instr ∗ ( i 32 . const 0 ) ( i 32 . const n ) ( table . init y x ) ( elem . drop x ) . rundata x ( data b n ( passive )) rundata x ( data b n ( active y instr ∗ )) instr ∗ ( i 32 . const 0 ) ( i 32 . const n ) ( memory . init y x ) ( data . drop x ) runelem x ( elem rt e n ( passive )) runelem x ( elem rt e n ( declare )) runelem x ( elem rt e n ( active y instr ∗ )) instr ∗ ( i 32 . const 0 ) ( i 32 . const n ) ( table . init y x ) ( elem . drop x ) = = = = = ϵ ϵ ( elem . drop x ) 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 s ′ 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 s 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 invocation, then no failure other than traps or exceptions can occur. 4.7.3.1. invoke ( s , funcaddr , val ∗ ) Assert: Due to validation, the expansion of s . funcs [ funcaddr ] . type is some func resulttype → resulttype . Let ( func t 1 ∗ → t 2 ∗ ) be the destructuring of the expansion of s . funcs [ funcaddr ] . type . If ∣ t 1 ∗ ∣ = ∣ val ∗ ∣ , then: Fail. For all t 1 in t 1 ∗ , and corresponding val in val ∗ : If val is not valid with type t 1 , then: Fail. Let k be the length of t 2 ∗ . Let F be the frame { module { }} whose arity is k . Push the frame F . Push the values val ∗ to the stack. Push the value ( ref . func funcaddr ) to the stack. Execute the instruction ( call_ref s . funcs [ funcaddr ] . type ) . Pop the values val ′ k from the stack. Pop the frame from the stack. Return val ′ k . invoke ( s , funcaddr , val ∗ ) = s ; { module { }} ; val ∗ ( ref . func funcaddr ) ( call_ref s . funcs [ funcaddr ] . type ) if s . funcs [ funcaddr ] . type ≈ func t 1 ∗ → t 2 ∗ ∧ ( s ⊢ val : t 1 ) ∗ 5. Binary Format 5.1. Conventions The binary format for WebAssembly modules is a dense linear encoding of their abstract syntax . [ 1 ] 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 recommended Media Type is “ application/wasm ”. [ 1 ] 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. 5.1.1. Grammar The following conventions are adopted in defining grammar rules for the binary format. They mirror the conventions 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 . B n is a sequence of n ≥ 0 iterations of B . B ∗ is a possibly empty sequence of iterations of B . (This is a shorthand for B n used where n is not relevant.) B ? is an optional occurrence of B . (This is a shorthand for B n where n ≤ 1 .) x : B denotes the same language as the nonterminal B , but also binds the variable x to the attribute synthesized for B . A pattern may also be used instead of a variable, e.g., 7 : B . Productions are written sym ::= B 1 ⇒ A 1 ∣ … ∣ B n ⇒ A n , where each A i is the attribute that is synthesized for sym in the given case, usually from attribute variables bound in B i . Large productions may be split into multiple definitions, indicated by ending the first one with explicit ellipses, sym ::= B 1 , and starting continuations with ellipses, sym ::= … ∣ B 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. (This is a shorthand for a side condition requiring multiple different variables to be equal.) Note For example, the binary grammar for number types is given as follows: numtype ::= ∣ ∣ ∣ 0x7C 0x7D 0x7E 0x7F ⇒ ⇒ ⇒ ⇒ f 64 f 32 i 64 i 32 Consequently, the byte 0x7F encodes the type i 32 , 0x7E encodes the type i 64 , 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: limits ::= ∣ ∣ ∣ 0x00 n : u 64 0x01 n : u 64 m : u 64 0x04 n : u 64 0x05 n : u 64 m : u 64 ⇒ ⇒ ⇒ ⇒ ( i 32 , [ n .. ϵ ]) ( i 32 , [ n .. m ]) ( i 64 , [ n .. ϵ ]) ( i 64 , [ n .. m ]) That is, a limits pair is encoded as either the byte 0x00 followed by the encoding of a u 64 value, or the byte 0x01 followed by two such encodings. The variables n and m name the attributes of the respective u 64 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.2. Auxiliary Notation When dealing with binary encodings the following notation is also used: ϵ denotes the empty byte sequence. ∣∣ B ∣∣ is the length of the byte sequence generated from the production B in a derivation. 5.1.3. Lists Lists are encoded with their u 32 length followed by the encoding of their element sequence. list ( X ) ::= n : u 32 ( el : X ) n ⇒ el n 5.2. Values 5.2.1. Bytes Bytes encode themselves. byte ::= 0x00 ∣ … ∣ 0xFF 5.2.2. Integers All integers are encoded using the LEB128 variable-length integer encoding, in either unsigned or signed variant. Unsigned integers are encoded in unsigned LEB128 format. As an additional constraint, the total number of bytes encoding a u N value must not exceed ceil ( N/7 ) bytes. u N ::= ∣ n : byte n : byte m : u ( N − 7 ) ⇒ ⇒ n 2 7 ⋅ m + ( n − 2 7 ) if n < 2 7 ∧ n < 2 N if n ≥ 2 7 ∧ N > 7 Signed integers are encoded in signed LEB128 format, which uses a two’s complement representation. As an additional constraint, the total number of bytes encoding an s N value must not exceed ceil ( N/7 ) bytes. s N ::= ∣ ∣ n : byte n : byte n : byte i : s ( N − 7 ) ⇒ ⇒ ⇒ n n − 2 7 2 7 ⋅ i + ( n − 2 7 ) if n < 2 6 ∧ n < 2 N − 1 if 2 6 ≤ n < 2 7 ∧ n ≥ 2 7 − 2 N − 1 if n ≥ 2 7 ∧ N > 7 Uninterpreted integers are encoded as signed integers. i N ::= i : s N ⇒ signed N − 1 ( i ) Note The side conditions N > 7 in the productions for non-terminal bytes of the u N and s N 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 u 8 . Similarly, either of 0x7E and 0xFE 0x7F and 0xFE 0xFF 0x7F are well-formed encodings of the value − 2 as an s 16 . The side conditions on the value n 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 u 8 encoding. Similarly, both 0x83 0x3E and 0xFF 0x7B are malformed as s 8 encodings. 5.2.3. Floating-Point Floating-point values are encoded directly by their [IEEE-754-2019] (Section 3.4) bit pattern in little endian byte order: f N ::= b ∗ : byte N /8 ⇒ bytes f N − 1 ( b ∗ ) 5.2.4. Names Names are encoded as a list of bytes containing the [UNICODE] (Section 3.9) UTF-8 encoding of the name’s character sequence. name ::= b ∗ : list ( byte ) ⇒ name if utf 8 ( name ) = b ∗ The auxiliary utf 8 function expressing this encoding is defined as follows: utf 8 ( ch ∗ ) utf 8 ( ch ) utf 8 ( ch ) utf 8 ( ch ) utf 8 ( ch ) = = = = = ⨁ utf 8 ( ch ) ∗ b b 1 b 2 b 1 b 2 b 3 b 1 b 2 b 3 b 4 if ch < U + 80 ∧ ch = b if U + 80 ≤ ch < U + 0800 ∧ ch = 2 6 ⋅ ( b 1 − 0xC0 ) + cont ( b 2 ) if U + 0800 ≤ ch < U + D800 ∨ U + E000 ≤ ch < U + 10000 ∧ ch = 2 12 ⋅ ( b 1 − 0xE0 ) + 2 6 ⋅ cont ( b 2 ) + cont ( b 3 ) if U + 10000 ≤ ch < U + 11000 ∧ ch = 2 18 ⋅ ( b 1 − 0xF0 ) + 2 12 ⋅ cont ( b 2 ) + 2 6 ⋅ cont ( b 3 ) + cont ( b 4 ) where cont ( b ) = b − 0x80 if ( 0x80 < b < 0xC0 ) Note Unlike in some other formats, name strings are not 0-terminated. 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 N values, such that they can unambiguously occur in the same place as (positive) type indices. 5.3.1. Number Types Number types are encoded by a single byte. numtype ::= ∣ ∣ ∣ 0x7C 0x7D 0x7E 0x7F ⇒ ⇒ ⇒ ⇒ f 64 f 32 i 64 i 32 5.3.2. Vector Types Vector types are also encoded by a single byte. vectype ::= 0x7B ⇒ v 128 5.3.3. Heap Types Heap types are encoded as either a single byte, or as a type index encoded as a positive signed integer . absheaptype heaptype ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ::= ∣ 0x69 0x6A 0x6B 0x6C 0x6D 0x6E 0x6F 0x70 0x71 0x72 0x73 0x74 ht : absheaptype x : s 33 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ exn array struct i 31 eq any extern func none noextern nofunc noexn ht x if x ≥ 0 Note The heap type bot cannot occur in a module. 5.3.4. Reference Types Reference types are either encoded by a single byte followed by a heap type , or, as a short form, directly as an abstract heap type . reftype ::= ∣ ∣ 0x63 ht : heaptype 0x64 ht : heaptype ht : absheaptype ⇒ ⇒ ⇒ ref null ht ref ht ref null ht 5.3.5. Value Types Value types are encoded with their respective encoding as a number type , vector type , or reference type . valtype ::= ∣ ∣ nt : numtype vt : vectype rt : reftype ⇒ ⇒ ⇒ nt vt rt Note The value type bot cannot occur in a module. Value types can occur in contexts where type indices are also allowed, such as in the case of block types . Thus, the binary format for types corresponds to the signed LEB128 encoding of small negative s N values, so that they can coexist with (positive) type indices in the future. 5.3.6. Result Types Result types are encoded by the respective lists of value types . resulttype ::= t ∗ : list ( valtype ) ⇒ t ∗ 5.3.7. Composite Types Composite types are encoded by a distinct byte followed by a type encoding of the respective form. mut comptype fieldtype storagetype packtype ::= ∣ ::= ∣ ∣ ::= ::= ∣ ::= ∣ 0x00 0x01 0x5E ft : fieldtype 0x5F ft ∗ : list ( fieldtype ) 0x60 t 1 ∗ : resulttype t 2 ∗ : resulttype zt : storagetype mut ? : mut t : valtype pt : packtype 0x77 0x78 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ϵ mut array ft struct ft ∗ func t 1 ∗ → t 2 ∗ mut ? zt t pt i 16 i 8 5.3.8. Recursive Types Recursive types are encoded by the byte 0x4E followed by a list of sub types . Additional shorthands are recognized for unary recursions and sub types without super types. rectype subtype ::= ∣ ::= ∣ ∣ 0x4E st ∗ : list ( subtype ) st : subtype 0x4F x ∗ : list ( typeidx ) ct : comptype 0x50 x ∗ : list ( typeidx ) ct : comptype ct : comptype ⇒ ⇒ ⇒ ⇒ ⇒ rec st ∗ rec st sub final x ∗ ct sub x ∗ ct sub final ϵ ct 5.3.9. Limits Limits are encoded with a preceding flag indicating whether a maximum is present, and a flag for the address type . limits ::= ∣ ∣ ∣ 0x00 n : u 64 0x01 n : u 64 m : u 64 0x04 n : u 64 0x05 n : u 64 m : u 64 ⇒ ⇒ ⇒ ⇒ ( i 32 , [ n .. ϵ ]) ( i 32 , [ n .. m ]) ( i 64 , [ n .. ϵ ]) ( i 64 , [ n .. m ]) 5.3.10. Tag Types Tag types are encoded by a type index denoting a function type . tagtype ::= 0x00 x : typeidx ⇒ x Note In future versions of WebAssembly, the preceding zero byte may encode additional attributes. 5.3.11. Global Types Global types are encoded by their value type and a flag for their mutability . globaltype ::= t : valtype mut ? : mut ⇒ mut ? t 5.3.12. Memory Types Memory types are encoded with their limits . memtype ::= ( at , lim ) : limits ⇒ at lim page 5.3.13. Table Types Table types are encoded with their limits and the encoding of their element reference type . tabletype ::= rt : reftype ( at , lim ) : limits ⇒ at lim rt 5.3.14. External Types External types are encoded by a distiguishing byte followed by an encoding of the respective form of type. externtype ::= ∣ ∣ ∣ ∣ 0x00 x : typeidx 0x01 tt : tabletype 0x02 mt : memtype 0x03 gt : globaltype 0x04 jt : tagtype ⇒ ⇒ ⇒ ⇒ ⇒ func x table tt mem mt global gt tag jt 5.4. Instructions Instructions are encoded by opcodes . Each opcode is represented by a single byte, and is followed by the instruction’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. 5.4.1. Parametric Instructions Parametric instructions are represented by single byte codes, possibly followed by a type annotation. instr ::= ∣ ∣ ∣ ∣ 0x00 0x01 0x1A 0x1B 0x1C t ∗ : list ( valtype ) ⇒ ⇒ ⇒ ⇒ ⇒ unreachable nop drop select select t ∗ 5.4.2. Control Instructions Control instructions have varying encodings. For structured instructions, the instruction sequences forming nested blocks are delimited with explicit opcodes for end and else . 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 . blocktype instr catch castop ::= ∣ ∣ ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ::= ∣ ∣ ∣ ::= ∣ ∣ ∣ 0x40 t : valtype i : s 33 … 0x02 bt : blocktype ( in : instr ) ∗ 0x0B 0x03 bt : blocktype ( in : instr ) ∗ 0x0B 0x04 bt : blocktype ( in : instr ) ∗ 0x0B 0x04 bt : blocktype ( in 1 : instr ) ∗ 0x05 ( in 2 : instr ) ∗ 0x0B 0x08 x : tagidx 0x0A 0x0C l : labelidx 0x0D l : labelidx 0x0E l ∗ : list ( labelidx ) l n : labelidx 0x0F 0x10 x : funcidx 0x11 y : typeidx x : tableidx 0x12 x : funcidx 0x13 y : typeidx x : tableidx 0x14 x : typeidx 0x15 x : typeidx 0x1F bt : blocktype c ∗ : list ( catch ) ( in : instr ) ∗ 0x0B 0xD5 l : labelidx 0xD6 l : labelidx 0xFB 24 : u 32 ( null 1 ? , null 2 ? ) : castop l : labelidx ht 1 : heaptype ht 2 : heaptype 0xFB 25 : u 32 ( null 1 ? , null 2 ? ) : castop l : labelidx ht 1 : heaptype ht 2 : heaptype 0x00 x : tagidx l : labelidx 0x01 x : tagidx l : labelidx 0x02 l : labelidx 0x03 l : labelidx 0x00 0x01 0x02 0x03 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ϵ t i block bt in ∗ loop bt in ∗ if bt in ∗ else ϵ if bt in 1 ∗ else in 2 ∗ throw x throw_ref br l br_if l br_table l ∗ l n return call x call_indirect x y return_call x return_call_indirect x y call_ref x return_call_ref x try_table bt c ∗ in ∗ br_on_null l br_on_non_null l br_on_cast l ( ref null 1 ? ht 1 ) ( ref null 2 ? ht 2 ) br_on_cast_fail l ( ref null 1 ? ht 1 ) ( ref null 2 ? ht 2 ) catch x l catch_ref x l catch_all l catch_all_ref l ( ϵ , ϵ ) ( null , ϵ ) ( ϵ , null ) ( null , null ) if i ≥ 0 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. 5.4.3. Variable Instructions Variable instructions are represented by byte codes followed by the encoding of the respective index . instr ::= ∣ ∣ ∣ ∣ ∣ … 0x20 x : localidx 0x21 x : localidx 0x22 x : localidx 0x23 x : globalidx 0x24 x : globalidx ⇒ ⇒ ⇒ ⇒ ⇒ local . get x local . set x local . tee x global . get x global . set x 5.4.4. Table Instructions Table instructions are represented either by a single byte or a one byte prefix followed by a variable-length unsigned integer . instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0x25 x : tableidx 0x26 x : tableidx 0xFC 12 : u 32 y : elemidx x : tableidx 0xFC 13 : u 32 x : elemidx 0xFC 14 : u 32 x 1 : tableidx x 2 : tableidx 0xFC 15 : u 32 x : tableidx 0xFC 16 : u 32 x : tableidx 0xFC 17 : u 32 x : tableidx ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ table . get x table . set x table . init x y elem . drop x table . copy x 1 x 2 table . grow x table . size x table . fill x 5.4.5. Memory Instructions Each variant of memory instruction is encoded with a different byte code. Loads and stores are followed by the encoding of their memarg immediate, which includes the memory index if bit 6 of the flags field containing alignment is set; the memory index defaults to 0 otherwise. memarg instr ::= ∣ ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ n : u 32 m : u 64 n : u 32 x : memidx m : u 64 … 0x28 ( x , ao ) : memarg 0x29 ( x , ao ) : memarg 0x2A ( x , ao ) : memarg 0x2B ( x , ao ) : memarg 0x2C ( x , ao ) : memarg 0x2D ( x , ao ) : memarg 0x2E ( x , ao ) : memarg 0x2F ( x , ao ) : memarg 0x30 ( x , ao ) : memarg 0x31 ( x , ao ) : memarg 0x32 ( x , ao ) : memarg 0x33 ( x , ao ) : memarg 0x34 ( x , ao ) : memarg 0x35 ( x , ao ) : memarg 0x36 ( x , ao ) : memarg 0x37 ( x , ao ) : memarg 0x38 ( x , ao ) : memarg 0x39 ( x , ao ) : memarg 0x3A ( x , ao ) : memarg 0x3B ( x , ao ) : memarg 0x3C ( x , ao ) : memarg 0x3D ( x , ao ) : memarg 0x3E ( x , ao ) : memarg 0x3F x : memidx 0x40 x : memidx 0xFC 8 : u 32 y : dataidx x : memidx 0xFC 9 : u 32 x : dataidx 0xFC 10 : u 32 x 1 : memidx x 2 : memidx 0xFC 11 : u 32 x : memidx ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ( 0 , { align n , offset m }) ( x , { align ( n − 2 6 ) , offset m }) i 32 . load x ao i 64 . load x ao f 32 . load x ao f 64 . load x ao i 32 . load 8 _ s x ao i 32 . load 8 _ u x ao i 32 . load 16 _ s x ao i 32 . load 16 _ u x ao i 64 . load 8 _ s x ao i 64 . load 8 _ u x ao i 64 . load 16 _ s x ao i 64 . load 16 _ u x ao i 64 . load 32 _ s x ao i 64 . load 32 _ u x ao i 32 . store x ao i 64 . store x ao f 32 . store x ao f 64 . store x ao i 32 . store 8 x ao i 32 . store 16 x ao i 64 . store 8 x ao i 64 . store 16 x ao i 64 . store 32 x ao memory . size x memory . grow x memory . init x y data . drop x memory . copy x 1 x 2 memory . fill x if n < 2 6 if 2 6 ≤ n < 2 7 5.4.6. Reference Instructions Generic reference instructions are represented by single byte codes, others use prefixes and type operands. instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xD0 ht : heaptype 0xD1 0xD2 x : funcidx 0xD3 0xD4 0xFB 20 : u 32 ht : heaptype 0xFB 21 : u 32 ht : heaptype 0xFB 22 : u 32 ht : heaptype 0xFB 23 : u 32 ht : heaptype ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ref . null ht ref . is_null ref . func x ref . eq ref . as_non_null ref . test ( ref ht ) ref . test ( ref null ht ) ref . cast ( ref ht ) ref . cast ( ref null ht ) 5.4.7. Aggregate Instructions Aggregate instructions all use a prefix. instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFB 0 : u 32 x : typeidx 0xFB 1 : u 32 x : typeidx 0xFB 2 : u 32 x : typeidx i : fieldidx 0xFB 3 : u 32 x : typeidx i : fieldidx 0xFB 4 : u 32 x : typeidx i : fieldidx 0xFB 5 : u 32 x : typeidx i : fieldidx 0xFB 6 : u 32 x : typeidx 0xFB 7 : u 32 x : typeidx 0xFB 8 : u 32 x : typeidx n : u 32 0xFB 9 : u 32 x : typeidx y : dataidx 0xFB 10 : u 32 x : typeidx y : elemidx 0xFB 11 : u 32 x : typeidx 0xFB 12 : u 32 x : typeidx 0xFB 13 : u 32 x : typeidx 0xFB 14 : u 32 x : typeidx 0xFB 15 : u 32 0xFB 16 : u 32 x : typeidx 0xFB 17 : u 32 x 1 : typeidx x 2 : typeidx 0xFB 18 : u 32 x : typeidx y : dataidx 0xFB 19 : u 32 x : typeidx y : elemidx 0xFB 26 : u 32 0xFB 27 : u 32 0xFB 28 : u 32 0xFB 29 : u 32 0xFB 30 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ struct . new x struct . new_default x struct . get x i struct . get _ s x i struct . get _ u x i struct . set x i array . new x array . new_default x array . new_fixed x n array . new_data x y array . new_elem x y array . get x array . get _ s x array . get _ u x array . set x array . len array . fill x array . copy x 1 x 2 array . init_data x y array . init_elem x y any . convert_extern extern . convert_any ref . i 31 i 31 . get _ s i 31 . get _ u 5.4.8. Numeric Instructions All variants of numeric instructions are represented by separate byte codes. The const instructions are followed by the respective literal. instr ::= ∣ ∣ ∣ ∣ … 0x41 i : i 32 0x42 i : i 64 0x43 p : f 32 0x44 p : f 64 ⇒ ⇒ ⇒ ⇒ i 32 . const i i 64 . const i f 32 . const p f 64 . const p All other numeric instructions are plain opcodes without any immediates. instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0x45 0x46 0x47 0x48 0x49 0x4A 0x4B 0x4C 0x4D 0x4E 0x4F 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . eqz i 32 . eq i 32 . ne i 32 . lt _ s i 32 . lt _ u i 32 . gt _ s i 32 . gt _ u i 32 . le _ s i 32 . le _ u i 32 . ge _ s i 32 . ge _ u i 64 . eqz i 64 . eq i 64 . ne i 64 . lt _ s i 64 . lt _ u i 64 . gt _ s i 64 . gt _ u i 64 . le _ s i 64 . le _ u i 64 . ge _ s i 64 . ge _ u instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0x5B 0x5C 0x5D 0x5E 0x5F 0x60 0x61 0x62 0x63 0x64 0x65 0x66 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 32 . eq f 32 . ne f 32 . lt f 32 . gt f 32 . le f 32 . ge f 64 . eq f 64 . ne f 64 . lt f 64 . gt f 64 . le f 64 . ge instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0x67 0x68 0x69 0x6A 0x6B 0x6C 0x6D 0x6E 0x6F 0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x7B 0x7C 0x7D 0x7E 0x7F 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8A ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . clz i 32 . ctz i 32 . popcnt i 32 . add i 32 . sub i 32 . mul i 32 . div _ s i 32 . div _ u i 32 . rem _ s i 32 . rem _ u i 32 . and i 32 . or i 32 . xor i 32 . shl i 32 . shr _ s i 32 . shr _ u i 32 . rotl i 32 . rotr i 64 . clz i 64 . ctz i 64 . popcnt i 64 . add i 64 . sub i 64 . mul i 64 . div _ s i 64 . div _ u i 64 . rem _ s i 64 . rem _ u i 64 . and i 64 . or i 64 . xor i 64 . shl i 64 . shr _ s i 64 . shr _ u i 64 . rotl i 64 . rotr instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0x8B 0x8C 0x8D 0x8E 0x8F 0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9A 0x9B 0x9C 0x9D 0x9E 0x9F 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 0xA6 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 32 . abs f 32 . neg f 32 . ceil f 32 . floor f 32 . trunc f 32 . nearest f 32 . sqrt f 32 . add f 32 . sub f 32 . mul f 32 . div f 32 . min f 32 . max f 32 . copysign f 64 . abs f 64 . neg f 64 . ceil f 64 . floor f 64 . trunc f 64 . nearest f 64 . sqrt f 64 . add f 64 . sub f 64 . mul f 64 . div f 64 . min f 64 . max f 64 . copysign instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xA7 0xA8 0xA9 0xAA 0xAB 0xAC 0xAD 0xAE 0xAF 0xB0 0xB1 0xB2 0xB3 0xB4 0xB5 0xB6 0xB7 0xB8 0xB9 0xBA 0xBB 0xBC 0xBD 0xBE 0xBF ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . wrap _ i 64 i 32 . trunc _ s _ f 32 i 32 . trunc _ u _ f 32 i 32 . trunc _ s _ f 64 i 32 . trunc _ u _ f 64 i 64 . extend _ s _ i 32 i 64 . extend _ u _ i 32 i 64 . trunc _ s _ f 32 i 64 . trunc _ u _ f 32 i 64 . trunc _ s _ f 64 i 64 . trunc _ u _ f 64 f 32 . convert _ s _ i 32 f 32 . convert _ u _ i 32 f 32 . convert _ s _ i 64 f 32 . convert _ u _ i 64 f 32 . demote _ f 64 f 64 . convert _ s _ i 32 f 64 . convert _ u _ i 32 f 64 . convert _ s _ i 64 f 64 . convert _ u _ i 64 f 64 . promote _ f 32 i 32 . reinterpret _ f 32 i 64 . reinterpret _ f 64 f 32 . reinterpret _ i 32 f 64 . reinterpret _ i 64 instr ::= ∣ ∣ ∣ ∣ ∣ … 0xC0 0xC1 0xC2 0xC3 0xC4 ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . extend 8 _ s i 32 . extend 16 _ s i 64 . extend 8 _ s i 64 . extend 16 _ s i 64 . extend 32 _ s The saturating truncation instructions all have a one byte prefix, whereas the actual opcode is encoded by a variable-length unsigned integer . instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFC 0 : u 32 0xFC 1 : u 32 0xFC 2 : u 32 0xFC 3 : u 32 0xFC 4 : u 32 0xFC 5 : u 32 0xFC 6 : u 32 0xFC 7 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . trunc_sat _ s _ f 32 i 32 . trunc_sat _ u _ f 32 i 32 . trunc_sat _ s _ f 64 i 32 . trunc_sat _ u _ f 64 i 64 . trunc_sat _ s _ f 32 i 64 . trunc_sat _ u _ f 32 i 64 . trunc_sat _ s _ f 64 i 64 . trunc_sat _ u _ f 64 5.4.9. Vector Instructions All variants of vector instructions are represented by separate byte codes. They all have a one byte prefix, whereas the actual opcode is encoded by a variable-length unsigned integer . Vector loads and stores are followed by the encoding of their memarg immediate. laneidx instr ::= ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ l : byte … 0xFD 0 : u 32 ( x , ao ) : memarg 0xFD 1 : u 32 ( x , ao ) : memarg 0xFD 2 : u 32 ( x , ao ) : memarg 0xFD 3 : u 32 ( x , ao ) : memarg 0xFD 4 : u 32 ( x , ao ) : memarg 0xFD 5 : u 32 ( x , ao ) : memarg 0xFD 6 : u 32 ( x , ao ) : memarg 0xFD 7 : u 32 ( x , ao ) : memarg 0xFD 8 : u 32 ( x , ao ) : memarg 0xFD 9 : u 32 ( x , ao ) : memarg 0xFD 10 : u 32 ( x , ao ) : memarg 0xFD 11 : u 32 ( x , ao ) : memarg 0xFD 84 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 85 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 86 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 87 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 88 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 89 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 90 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 91 : u 32 ( x , ao ) : memarg i : laneidx 0xFD 92 : u 32 ( x , ao ) : memarg 0xFD 93 : u 32 ( x , ao ) : memarg ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ l v 128 . load x ao v 128 . load 8 x 8 _ s x ao v 128 . load 8 x 8 _ u x ao v 128 . load 16 x 4 _ s x ao v 128 . load 16 x 4 _ u x ao v 128 . load 32 x 2 _ s x ao v 128 . load 32 x 2 _ u x ao v 128 . load 8 _ splat x ao v 128 . load 16 _ splat x ao v 128 . load 32 _ splat x ao v 128 . load 64 _ splat x ao v 128 . store x ao v 128 . load 8 _ lane x ao i v 128 . load 16 _ lane x ao i v 128 . load 32 _ lane x ao i v 128 . load 64 _ lane x ao i v 128 . store 8 _ lane x ao i v 128 . store 16 _ lane x ao i v 128 . store 32 _ lane x ao i v 128 . store 64 _ lane x ao i v 128 . load 32 _ zero x ao v 128 . load 64 _ zero x ao The const instruction for vectors is followed by 16 immediate bytes, which are converted into an u 128 in littleendian byte order: instr ::= ∣ … 0xFD 12 : u 32 ( b : byte ) 16 ⇒ v 128 . const bytes i 128 − 1 ( ( b ) 16 ) The shuffle instruction is also followed by the encoding of 16 laneidx immediates. instr ::= ∣ ∣ ∣ … 0xFD 13 : u 32 ( l : laneidx ) 16 0xFD 14 : u 32 0xFD 256 : u 32 ⇒ ⇒ ⇒ i 8 x 16 . shuffle l 16 i 8 x 16 . swizzle i 8 x 16 . relaxed_swizzle Lane instructions are followed by the encoding of a laneidx immediate. instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 21 : u 32 l : laneidx 0xFD 22 : u 32 l : laneidx 0xFD 23 : u 32 l : laneidx 0xFD 24 : u 32 l : laneidx 0xFD 25 : u 32 l : laneidx 0xFD 26 : u 32 l : laneidx 0xFD 27 : u 32 l : laneidx 0xFD 28 : u 32 l : laneidx 0xFD 29 : u 32 l : laneidx 0xFD 30 : u 32 l : laneidx 0xFD 31 : u 32 l : laneidx 0xFD 32 : u 32 l : laneidx 0xFD 33 : u 32 l : laneidx 0xFD 34 : u 32 l : laneidx ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 8 x 16 . extract_lane _ s l i 8 x 16 . extract_lane _ u l i 8 x 16 . replace_lane l i 16 x 8 . extract_lane _ s l i 16 x 8 . extract_lane _ u l i 16 x 8 . replace_lane l i 32 x 4 . extract_lane l i 32 x 4 . replace_lane l i 64 x 2 . extract_lane l i 64 x 2 . replace_lane l f 32 x 4 . extract_lane l f 32 x 4 . replace_lane l f 64 x 2 . extract_lane l f 64 x 2 . replace_lane l All other vector instructions are plain opcodes without any immediates. instr ::= ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 15 : u 32 0xFD 16 : u 32 0xFD 17 : u 32 0xFD 18 : u 32 0xFD 19 : u 32 0xFD 20 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 8 x 16 . splat i 16 x 8 . splat i 32 x 4 . splat i 64 x 2 . splat f 32 x 4 . splat f 64 x 2 . splat instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 35 : u 32 0xFD 36 : u 32 0xFD 37 : u 32 0xFD 38 : u 32 0xFD 39 : u 32 0xFD 40 : u 32 0xFD 41 : u 32 0xFD 42 : u 32 0xFD 43 : u 32 0xFD 44 : u 32 0xFD 45 : u 32 0xFD 46 : u 32 0xFD 47 : u 32 0xFD 48 : u 32 0xFD 49 : u 32 0xFD 50 : u 32 0xFD 51 : u 32 0xFD 52 : u 32 0xFD 53 : u 32 0xFD 54 : u 32 0xFD 55 : u 32 0xFD 56 : u 32 0xFD 57 : u 32 0xFD 58 : u 32 0xFD 59 : u 32 0xFD 60 : u 32 0xFD 61 : u 32 0xFD 62 : u 32 0xFD 63 : u 32 0xFD 64 : u 32 0xFD 214 : u 32 0xFD 215 : u 32 0xFD 216 : u 32 0xFD 217 : u 32 0xFD 218 : u 32 0xFD 219 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 8 x 16 . eq i 8 x 16 . ne i 8 x 16 . lt _ s i 8 x 16 . lt _ u i 8 x 16 . gt _ s i 8 x 16 . gt _ u i 8 x 16 . le _ s i 8 x 16 . le _ u i 8 x 16 . ge _ s i 8 x 16 . ge _ u i 16 x 8 . eq i 16 x 8 . ne i 16 x 8 . lt _ s i 16 x 8 . lt _ u i 16 x 8 . gt _ s i 16 x 8 . gt _ u i 16 x 8 . le _ s i 16 x 8 . le _ u i 16 x 8 . ge _ s i 16 x 8 . ge _ u i 32 x 4 . eq i 32 x 4 . ne i 32 x 4 . lt _ s i 32 x 4 . lt _ u i 32 x 4 . gt _ s i 32 x 4 . gt _ u i 32 x 4 . le _ s i 32 x 4 . le _ u i 32 x 4 . ge _ s i 32 x 4 . ge _ u i 64 x 2 . eq i 64 x 2 . ne i 64 x 2 . lt _ s i 64 x 2 . gt _ s i 64 x 2 . le _ s i 64 x 2 . ge _ s instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 65 : u 32 0xFD 66 : u 32 0xFD 67 : u 32 0xFD 68 : u 32 0xFD 69 : u 32 0xFD 70 : u 32 0xFD 71 : u 32 0xFD 72 : u 32 0xFD 73 : u 32 0xFD 74 : u 32 0xFD 75 : u 32 0xFD 76 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 32 x 4 . eq f 32 x 4 . ne f 32 x 4 . lt f 32 x 4 . gt f 32 x 4 . le f 32 x 4 . ge f 64 x 2 . eq f 64 x 2 . ne f 64 x 2 . lt f 64 x 2 . gt f 64 x 2 . le f 64 x 2 . ge instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 77 : u 32 0xFD 78 : u 32 0xFD 79 : u 32 0xFD 80 : u 32 0xFD 81 : u 32 0xFD 82 : u 32 0xFD 83 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ v 128 . not v 128 . and v 128 . andnot v 128 . or v 128 . xor v 128 . bitselect v 128 . any_true instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 96 : u 32 0xFD 97 : u 32 0xFD 98 : u 32 0xFD 99 : u 32 0xFD 100 : u 32 0xFD 101 : u 32 0xFD 102 : u 32 0xFD 107 : u 32 0xFD 108 : u 32 0xFD 109 : u 32 0xFD 110 : u 32 0xFD 111 : u 32 0xFD 112 : u 32 0xFD 113 : u 32 0xFD 114 : u 32 0xFD 115 : u 32 0xFD 118 : u 32 0xFD 119 : u 32 0xFD 120 : u 32 0xFD 121 : u 32 0xFD 123 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 8 x 16 . abs i 8 x 16 . neg i 8 x 16 . popcnt i 8 x 16 . all_true i 8 x 16 . bitmask i 8 x 16 . narrow _ i 16 x 8 _ s i 8 x 16 . narrow _ i 16 x 8 _ u i 8 x 16 . shl i 8 x 16 . shr _ s i 8 x 16 . shr _ u i 8 x 16 . add i 8 x 16 . add_sat _ s i 8 x 16 . add_sat _ u i 8 x 16 . sub i 8 x 16 . sub_sat _ s i 8 x 16 . sub_sat _ u i 8 x 16 . min _ s i 8 x 16 . min _ u i 8 x 16 . max _ s i 8 x 16 . max _ u i 8 x 16 . avgr _ u instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 124 : u 32 0xFD 125 : u 32 0xFD 128 : u 32 0xFD 129 : u 32 0xFD 131 : u 32 0xFD 132 : u 32 0xFD 133 : u 32 0xFD 134 : u 32 0xFD 135 : u 32 0xFD 136 : u 32 0xFD 137 : u 32 0xFD 138 : u 32 0xFD 139 : u 32 0xFD 140 : u 32 0xFD 141 : u 32 0xFD 130 : u 32 0xFD 142 : u 32 0xFD 143 : u 32 0xFD 144 : u 32 0xFD 145 : u 32 0xFD 146 : u 32 0xFD 147 : u 32 0xFD 149 : u 32 0xFD 150 : u 32 0xFD 151 : u 32 0xFD 152 : u 32 0xFD 153 : u 32 0xFD 155 : u 32 0xFD 273 : u 32 0xFD 156 : u 32 0xFD 157 : u 32 0xFD 158 : u 32 0xFD 159 : u 32 0xFD 274 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 16 x 8 . extadd_pairwise _ s _ i 8 x 16 i 16 x 8 . extadd_pairwise _ u _ i 8 x 16 i 16 x 8 . abs i 16 x 8 . neg i 16 x 8 . all_true i 16 x 8 . bitmask i 16 x 8 . narrow _ i 32 x 4 _ s i 16 x 8 . narrow _ i 32 x 4 _ u i 16 x 8 . extend _ low _ s _ i 8 x 16 i 16 x 8 . extend _ high _ s _ i 8 x 16 i 16 x 8 . extend _ low _ u _ i 8 x 16 i 16 x 8 . extend _ high _ u _ i 8 x 16 i 16 x 8 . shl i 16 x 8 . shr _ s i 16 x 8 . shr _ u i 16 x 8 . q 15 mulr_sat _ s i 16 x 8 . add i 16 x 8 . add_sat _ s i 16 x 8 . add_sat _ u i 16 x 8 . sub i 16 x 8 . sub_sat _ s i 16 x 8 . sub_sat _ u i 16 x 8 . mul i 16 x 8 . min _ s i 16 x 8 . min _ u i 16 x 8 . max _ s i 16 x 8 . max _ u i 16 x 8 . avgr _ u i 16 x 8 . relaxed_q15mulr _ s i 16 x 8 . extmul _ low _ s _ i 8 x 16 i 16 x 8 . extmul _ high _ s _ i 8 x 16 i 16 x 8 . extmul _ low _ u _ i 8 x 16 i 16 x 8 . extmul _ high _ u _ i 8 x 16 i 16 x 8 . relaxed_dot _ s _ i 8 x 16 instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 126 : u 32 0xFD 127 : u 32 0xFD 160 : u 32 0xFD 161 : u 32 0xFD 163 : u 32 0xFD 164 : u 32 0xFD 167 : u 32 0xFD 168 : u 32 0xFD 169 : u 32 0xFD 170 : u 32 0xFD 171 : u 32 0xFD 172 : u 32 0xFD 173 : u 32 0xFD 174 : u 32 0xFD 177 : u 32 0xFD 181 : u 32 0xFD 182 : u 32 0xFD 183 : u 32 0xFD 184 : u 32 0xFD 185 : u 32 0xFD 186 : u 32 0xFD 188 : u 32 0xFD 189 : u 32 0xFD 190 : u 32 0xFD 191 : u 32 0xFD 275 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 x 4 . extadd_pairwise _ s _ i 16 x 8 i 32 x 4 . extadd_pairwise _ u _ i 16 x 8 i 32 x 4 . abs i 32 x 4 . neg i 32 x 4 . all_true i 32 x 4 . bitmask i 32 x 4 . extend _ low _ s _ i 16 x 8 i 32 x 4 . extend _ high _ s _ i 16 x 8 i 32 x 4 . extend _ low _ u _ i 16 x 8 i 32 x 4 . extend _ high _ u _ i 16 x 8 i 32 x 4 . shl i 32 x 4 . shr _ s i 32 x 4 . shr _ u i 32 x 4 . add i 32 x 4 . sub i 32 x 4 . mul i 32 x 4 . min _ s i 32 x 4 . min _ u i 32 x 4 . max _ s i 32 x 4 . max _ u i 32 x 4 . dot _ s _ i 16 x 8 i 32 x 4 . extmul _ low _ s _ i 16 x 8 i 32 x 4 . extmul _ high _ s _ i 16 x 8 i 32 x 4 . extmul _ low _ u _ i 16 x 8 i 32 x 4 . extmul _ high _ u _ i 16 x 8 i 32 x 4 . relaxed_dot_add _ s _ i 16 x 8 instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 192 : u 32 0xFD 193 : u 32 0xFD 195 : u 32 0xFD 196 : u 32 0xFD 199 : u 32 0xFD 200 : u 32 0xFD 201 : u 32 0xFD 202 : u 32 0xFD 203 : u 32 0xFD 204 : u 32 0xFD 205 : u 32 0xFD 206 : u 32 0xFD 209 : u 32 0xFD 213 : u 32 0xFD 220 : u 32 0xFD 221 : u 32 0xFD 222 : u 32 0xFD 223 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 64 x 2 . abs i 64 x 2 . neg i 64 x 2 . all_true i 64 x 2 . bitmask i 64 x 2 . extend _ low _ s _ i 32 x 4 i 64 x 2 . extend _ high _ s _ i 32 x 4 i 64 x 2 . extend _ low _ u _ i 32 x 4 i 64 x 2 . extend _ high _ u _ i 32 x 4 i 64 x 2 . shl i 64 x 2 . shr _ s i 64 x 2 . shr _ u i 64 x 2 . add i 64 x 2 . sub i 64 x 2 . mul i 64 x 2 . extmul _ low _ s _ i 32 x 4 i 64 x 2 . extmul _ high _ s _ i 32 x 4 i 64 x 2 . extmul _ low _ u _ i 32 x 4 i 64 x 2 . extmul _ high _ u _ i 32 x 4 instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 103 : u 32 0xFD 104 : u 32 0xFD 105 : u 32 0xFD 106 : u 32 0xFD 224 : u 32 0xFD 225 : u 32 0xFD 227 : u 32 0xFD 228 : u 32 0xFD 229 : u 32 0xFD 230 : u 32 0xFD 231 : u 32 0xFD 232 : u 32 0xFD 233 : u 32 0xFD 234 : u 32 0xFD 235 : u 32 0xFD 269 : u 32 0xFD 270 : u 32 0xFD 261 : u 32 0xFD 262 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 32 x 4 . ceil f 32 x 4 . floor f 32 x 4 . trunc f 32 x 4 . nearest f 32 x 4 . abs f 32 x 4 . neg f 32 x 4 . sqrt f 32 x 4 . add f 32 x 4 . sub f 32 x 4 . mul f 32 x 4 . div f 32 x 4 . min f 32 x 4 . max f 32 x 4 . pmin f 32 x 4 . pmax f 32 x 4 . relaxed_min f 32 x 4 . relaxed_max f 32 x 4 . relaxed_madd f 32 x 4 . relaxed_nmadd instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 116 : u 32 0xFD 117 : u 32 0xFD 122 : u 32 0xFD 148 : u 32 0xFD 236 : u 32 0xFD 237 : u 32 0xFD 239 : u 32 0xFD 240 : u 32 0xFD 241 : u 32 0xFD 242 : u 32 0xFD 243 : u 32 0xFD 244 : u 32 0xFD 245 : u 32 0xFD 246 : u 32 0xFD 247 : u 32 0xFD 271 : u 32 0xFD 272 : u 32 0xFD 263 : u 32 0xFD 264 : u 32 0xFD 265 : u 32 0xFD 266 : u 32 0xFD 267 : u 32 0xFD 268 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 64 x 2 . ceil f 64 x 2 . floor f 64 x 2 . trunc f 64 x 2 . nearest f 64 x 2 . abs f 64 x 2 . neg f 64 x 2 . sqrt f 64 x 2 . add f 64 x 2 . sub f 64 x 2 . mul f 64 x 2 . div f 64 x 2 . min f 64 x 2 . max f 64 x 2 . pmin f 64 x 2 . pmax f 64 x 2 . relaxed_min f 64 x 2 . relaxed_max f 64 x 2 . relaxed_madd f 64 x 2 . relaxed_nmadd i 8 x 16 . relaxed_laneselect i 16 x 8 . relaxed_laneselect i 32 x 4 . relaxed_laneselect i 64 x 2 . relaxed_laneselect instr ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … 0xFD 94 : u 32 0xFD 95 : u 32 0xFD 248 : u 32 0xFD 249 : u 32 0xFD 250 : u 32 0xFD 251 : u 32 0xFD 252 : u 32 0xFD 253 : u 32 0xFD 254 : u 32 0xFD 255 : u 32 0xFD 257 : u 32 0xFD 258 : u 32 0xFD 259 : u 32 0xFD 260 : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 32 x 4 . demote _ zero _ f 64 x 2 f 64 x 2 . promote _ low _ f 32 x 4 i 32 x 4 . trunc_sat _ s _ f 32 x 4 i 32 x 4 . trunc_sat _ u _ f 32 x 4 f 32 x 4 . convert _ s _ i 32 x 4 f 32 x 4 . convert _ u _ i 32 x 4 i 32 x 4 . trunc_sat _ s _ zero _ f 64 x 2 i 32 x 4 . trunc_sat _ u _ zero _ f 64 x 2 f 64 x 2 . convert _ low _ s _ i 32 x 4 f 64 x 2 . convert _ low _ u _ i 32 x 4 i 32 x 4 . relaxed_trunc _ s _ f 32 x 4 i 32 x 4 . relaxed_trunc _ u _ f 32 x 4 i 32 x 4 . relaxed_trunc _ s _ zero _ f 64 x 2 i 32 x 4 . relaxed_trunc _ u _ zero _ f 64 x 2 5.4.10. Expressions Expressions are encoded by their instruction sequence terminated with an explicit 0x0B opcode for end . expr ::= ( in : instr ) ∗ 0x0B ⇒ in ∗ 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. typeidx funcidx tableidx memidx globalidx tagidx elemidx dataidx localidx fieldidx labelidx ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= x : u 32 x : u 32 x : u 32 x : u 32 x : u 32 x : u 32 x : u 32 x : u 32 x : u 32 x : u 32 l : u 32 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ x x x x x x x x x x l External indices are encoded by a distiguishing byte followed by an encoding of their respective value. externidx ::= ∣ ∣ ∣ ∣ 0x00 x : funcidx 0x01 x : tableidx 0x02 x : memidx 0x03 x : globalidx 0x04 x : tagidx ⇒ ⇒ ⇒ ⇒ ⇒ func x table x memory x global x tag x 5.5.2. Sections Each section consists of a one-byte section id , the u 32 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 N and contents described by the grammar X . section N ( X ) ::= ∣ N : byte len : u 32 en ∗ : X ϵ ⇒ ⇒ en ∗ ϵ if len = ∣∣ 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 . The following section ids are used: 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. 5.5.3. Custom Section Custom sections have the id 0. They are intended to be used for debugging information or third-party extensions, and are ignored by the WebAssembly semantics. Their contents consist of a name further identifying the custom section, followed by an uninterpreted sequence of bytes for custom use. customsec custom ::= ::= section 0 ( custom ) name byte ∗ 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. 5.5.4. Type Section The type section has the id 1. It decodes into the list of recursive types of a module . typesec type ::= ::= ty ∗ : section 1 ( list ( type )) qt : rectype ⇒ ⇒ ty ∗ type qt 5.5.5. Import Section The import section has the id 2. It decodes into the list of imports of a module . importsec import ::= ::= im ∗ : section 2 ( list ( import )) nm 1 : name nm 2 : name xt : externtype ⇒ ⇒ im ∗ import nm 1 nm 2 xt 5.5.6. Function Section The function section has the id 3. It decodes into a list of type indices that classify the functions defined by a module . The bodies of the respective functions are encoded separately in the code section . funcsec ::= x ∗ : section 3 ( list ( typeidx )) ⇒ x ∗ 5.5.7. Table Section The table section has the id 4. It decodes into the list of tables defined by a module . tablesec table ::= ::= ∣ tab ∗ : section 4 ( list ( table )) tt : tabletype 0x40 0x00 tt : tabletype e : expr ⇒ ⇒ ⇒ tab ∗ table tt ( ref . null ht ) table tt e if tt = at lim ( ref null ? ht ) 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. 5.5.8. Memory Section The memory section has the id 5. It decodes into the list of memories defined by a module . memsec mem ::= ::= mem ∗ : section 5 ( list ( mem )) mt : memtype ⇒ ⇒ mem ∗ memory mt 5.5.9. Global Section The global section has the id 6. It decodes into the list of globals defined by a module . globalsec global ::= ::= glob ∗ : section 6 ( list ( global )) gt : globaltype e : expr ⇒ ⇒ glob ∗ global gt e 5.5.10. Export Section The export section has the id 7. It decodes into the list of exports of a module . exportsec export ::= ::= ex ∗ : section 7 ( list ( export )) nm : name xx : externidx ⇒ ⇒ ex ∗ export nm xx 5.5.11. Start Section The start section has the id 8. It decodes into the optional start function of a module . startsec start ::= ::= start ? : section 8 ( start ) x : funcidx ⇒ ⇒ start ? ( start x ) 5.5.12. Element Section The element section has the id 9. It decodes into the list of element segments defined by a module . elemsec elemkind elem ::= ::= ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ elem ∗ : section 9 ( list ( elem )) 0x00 0 : u 32 e o : expr y ∗ : list ( funcidx ) elem ( ref func ) ( ref . func y ) ∗ ( active 0 e o ) 1 : u 32 rt : elemkind y ∗ : list ( funcidx ) elem rt ( ref . func y ) ∗ passive 2 : u 32 x : tableidx e : expr rt : elemkind y ∗ : list ( funcidx ) elem rt ( ref . func y ) ∗ ( active x e ) 3 : u 32 rt : elemkind y ∗ : list ( funcidx ) elem rt ( ref . func y ) ∗ declare 4 : u 32 e o : expr e ∗ : list ( expr ) elem ( ref null func ) e ∗ ( active 0 e o ) 5 : u 32 rt : reftype e ∗ : list ( expr ) elem rt e ∗ passive 6 : u 32 x : tableidx e o : expr rt : reftype e ∗ : list ( expr ) elem rt e ∗ ( active x e o ) 7 : u 32 rt : reftype e ∗ : list ( expr ) elem rt e ∗ declare ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ elem ∗ ref func 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. 5.5.13. Code Section The code section has the id 10. It decodes into the list of code entries that are pairs of lists of locals and expressions . They represent the body of the functions defined by a module . The types of the respective functions are encoded separately in the function section . The encoding of each code entry consists of the u 32 length of the function code in bytes, the actual function code , which in turn consists of the declaration of locals , the function body as an expression . Local declarations are compressed into a list whose entries consist of a u 32 count , a value type , denoting count locals of the same value type. codesec code func locals ::= ::= ::= ::= code ∗ : section 10 ( list ( code )) len : u 32 code : func loc ∗ ∗ : list ( locals ) e : expr n : u 32 t : valtype ⇒ ⇒ ⇒ ⇒ code ∗ code ( ⨁ loc ∗ ∗ , e ) ( local t ) n if len = ∣∣ func ∣∣ if ∣ ⨁ loc ∗ ∗ ∣ < 2 32 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. 5.5.14. Data Section The data section has the id 11. It decodes into the list of data segments defined by a module . datasec data ::= ::= ∣ ∣ data ∗ : section 11 ( list ( data )) 0 : u 32 e : expr b ∗ : list ( byte ) 1 : u 32 b ∗ : list ( byte ) 2 : u 32 x : memidx e : expr b ∗ : list ( byte ) ⇒ ⇒ ⇒ ⇒ data ∗ data b ∗ ( active 0 e ) data b ∗ passive data b ∗ ( active x e ) 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. 5.5.15. Data Count Section The data count section has the id 12. It decodes into an optional u 32 count that represents the number of data segments in the data section . If this count does not match the length of the data segment list, the module is malformed. datacntsec datacnt ::= ::= n ? : section 12 ( datacnt ) n : u 32 ⇒ ⇒ n ? n Note The data count section is used to simplify single-pass validation. Since the data section occurs after the code section, the memory . init and data . drop 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.16. Tag Section The tag section has the id 13. It decodes into the list of tags defined by a module . tagsec tag ::= ::= tag ∗ : section 13 ( list ( tag )) jt : tagtype ⇒ ⇒ tag ∗ tag jt 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. magic version module ::= ::= ::= 0x00 0x61 0x73 0x6D 0x01 0x00 0x00 0x00 magic version customsec ∗ type ∗ : typesec customsec ∗ import ∗ : importsec customsec ∗ typeidx ∗ : funcsec customsec ∗ table ∗ : tablesec customsec ∗ mem ∗ : memsec customsec ∗ tag ∗ : tagsec customsec ∗ global ∗ : globalsec customsec ∗ export ∗ : exportsec customsec ∗ start ? : startsec customsec ∗ elem ∗ : elemsec customsec ∗ n ? : datacntsec customsec ∗ ( local ∗ , expr ) ∗ : codesec customsec ∗ data ∗ : datasec customsec ∗ module type ∗ import ∗ tag ∗ global ∗ mem ∗ table ∗ func ∗ data ∗ elem ∗ start ? export ∗ if ( n = ∣ data ∗ ∣ ) ? ∧ ( n ? = ϵ ∨ dataidx ( func ∗ ) = ϵ ) ∧ ( func = func typeidx local ∗ expr ) ∗ ⇒ 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. 6. Text Format 6.1. Conventions The textual format for WebAssembly modules is a rendering of their abstract syntax into S-expressions . 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 [UNICODE] (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 [UNICODE] scalar values: ‘ module ’ , U + 0A . (All characters written literally are unambiguously drawn from the 7-bit ASCII subset of Unicode.) Nonterminal symbols are written in typewriter font: valtype , instr . T n is a sequence of n ≥ 0 iterations of T . T ∗ is a possibly empty sequence of iterations of T . (This is a shorthand for T n used where n is not relevant.) T + is a non-empty sequence of iterations of T . (This is a shorthand for T n where n ≥ 1 .) T ? is an optional occurrence of T . (This is a shorthand for T n where n ≤ 1 .) x : T denotes the same language as the nonterminal T , but also binds the variable x to the attribute synthesized for T . A pattern may also be used instead of a variable, e.g., 7 : T . Productions are written sym ::= T 1 ⇒ A 1 ∣ … ∣ T n ⇒ A n , where each A i is the attribute that is synthesized for sym in the given case, usually from attribute variables bound in T i . Large productions may be split into multiple definitions, indicated by ending the first one with explicit ellipses, sym ::= B 1 , and starting continuations with ellipses, sym ::= … ∣ B 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: numtype ::= ∣ ∣ ∣ ‘ i32 ’ ‘ i64 ’ ‘ f32 ’ ‘ f64 ’ ⇒ ⇒ ⇒ ⇒ i 32 i 64 f 32 f 64 The textual grammar for limits is defined as follows: limits ::= ∣ n : u64 n : u64 m : u64 ⇒ ⇒ [ n .. ϵ ] [ n .. m ] The variables n and m 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: sym ::= abbreviated syntax ≡ expanded 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 I 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 . It is convenient to define identifier contexts as records I with abstract syntax as follows: I ::= { types ( name ? ) ∗ tags ( name ? ) ∗ globals ( name ? ) ∗ mems ( name ? ) ∗ tables ( name ? ) ∗ funcs ( name ? ) ∗ datas ( name ? ) ∗ elems ( name ? ) ∗ locals ( name ? ) ∗ labels ( name ? ) ∗ fields ( ( name ? ) ∗ ) ∗ typedefs ( deftype ? ) ∗ } 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. 6.1.3.1. 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. list ( X ) ::= ( el : X ) ∗ ⇒ el ∗ if ∣ el ∗ ∣ < 2 32 6.2. Lexical Format 6.2.1. Characters The text format assigns meaning to source text , which consists of a sequence of characters . Characters are assumed to be represented as valid [UNICODE] (Section 2.4) scalar values . source char ::= ::= char ∗ U + 00 ∣ … ∣ U + D7FF ∣ U + E000 ∣ … ∣ U + 10FFFF 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 ASCII subset of Unicode. 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. token keyword reserved ::= ::= ::= keyword ∣ u ∣ s ∣ f ∣ string ∣ id ∣ ‘ ( ’ ∣ ‘ ) ’ ∣ reserved ( ‘ a ’ ∣ … ∣ ‘ z ’ ) idchar ∗ ( idchar ∣ string ∣ ‘ , ’ ∣ ‘ ; ’ ∣ ‘ [ ’ ∣ ‘ ] ’ ∣ ‘ { ’ ∣ ‘ } ’ ) + 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.3. White Space White space is any sequence of literal space characters, formatting characters, comments , or annotations . The allowed formatting characters correspond to a subset of the ASCII format effectors , namely, horizontal tabulation ( U + 09 ), line feed ( U + 0A ), and carriage return ( U + 0D ). space format newline ::= ::= ::= ( ‘ ’ ∣ format ∣ comment ∣ annot ) ∗ newline ∣ U + 09 U + 0A ∣ U + 0D ∣ U + 0D U + 0A The only relevance of white space is to separate tokens . It is otherwise ignored. 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. comment linecomment linechar blockcomment blockchar ::= ::= ::= ::= ::= ∣ ∣ ∣ linecomment ∣ blockcomment ‘ ;; ’ linechar ∗ ( newline ∣ eof ) c : char ‘ ( ; ’ blockchar ∗ ‘ ; ) ’ c : char ‘ ; ’ + c : char ‘ ( ’ + c : char blockcomment if c = U + 0A ∧ c = U + 0D if c = ‘ ; ’ ∧ c = ‘ ( ’ if c = ‘ ; ’ ∧ c = ‘ ) ’ if c = ‘ ; ’ ∧ c = ‘ ( ’ 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. Note Any formatting and control characters are allowed inside comments. 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 ::= ::= ‘ ( @ ’ annotid ( space ∣ token ) ∗ ‘ ) ’ 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 superimposing 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 digit hexdigit num hexnum ::= ::= ::= ∣ ∣ ::= ∣ ::= ∣ ϵ ⇒ + 1 ∣ ‘ + ’ ⇒ + 1 ∣ ‘ - ’ ⇒ − 1 ‘ 0 ’ ⇒ 0 ∣ … ∣ ‘ 9 ’ ⇒ 9 d : digit ⇒ d ‘ A ’ ⇒ 10 ∣ … ∣ ‘ F ’ ⇒ 15 ‘ a ’ ⇒ 10 ∣ … ∣ ‘ f ’ ⇒ 15 d : digit n : num ‘ _ ’ ? d : digit h : hexdigit n : hexnum ‘ _ ’ ? h : hexdigit ⇒ ⇒ ⇒ ⇒ d 10 n + d h 16 n + h The allowed syntax for integer literals depends on size and signedness. Moreover, their value must lie within the range of the respective type. u N s N ::= ∣ ::= n : num ‘ 0x ’ n : hexnum s : sign n : u N ⇒ ⇒ ⇒ n n s ⋅ n if n < 2 N if n < 2 N if − 2 N − 1 ≤ s ⋅ n < 2 N − 1 Uninterpreted integers can be written as either signed or unsigned, and are normalized to unsigned in the abstract syntax. i N ::= ∣ n : u N i : s N ⇒ ⇒ n signed N − 1 ( i ) 6.3.2. Floating-Point Floating-point values can be represented in either decimal or hexadecimal notation. frac hexfrac mant hexmant float hexfloat ::= ∣ ::= ∣ ::= ∣ ::= ∣ ::= ::= d : digit d : digit ‘ _ ’ ? p : frac h : hexdigit h : hexdigit ‘ _ ’ ? p : hexfrac p : num ‘ . ’ ? p : num ‘ . ’ q : frac p : hexnum ‘ . ’ ? p : hexnum ‘ . ’ q : hexfrac p : mant ( ‘ E ’ ∣ ‘ e ’ ) s : sign e : num ‘ 0x ’ p : hexmant ( ‘ P ’ ∣ ‘ p ’ ) s : sign e : num ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ d /10 ( d + p /10 ) /10 h /16 ( h + p /16 ) /16 p p + q p p + q p ⋅ 1 0 s ⋅ e p ⋅ 2 s ⋅ e The value of a literal must not lie outside the representable range of the corresponding [IEEE-754-2019] 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. f N f N mag ::= ∣ ::= ∣ ∣ ∣ ∣ ( + 1 ) : sign q : f N mag ( − 1 ) : sign q : f N mag q : float q : hexfloat ‘ inf ’ ‘ nan ’ ‘ nan:0x ’ n : hexnum ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ + q − q float N ( q ) float N ( q ) ∞ nan ( canon N ) nan ( n ) if float N ( q ) = ∞ if float N ( q ) = ∞ if 1 ≤ n < 2 signif ( N ) 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 ASCII control characters, quotation marks ( ‘ ’ ’ ’ ), or backslash ( ‘ \ ’ ), except when expressed with an escape sequence . string stringelem ::= ::= ∣ ‘ ’ ’ ’ ( b ∗ : stringelem ) ∗ ‘ ’ ’ ’ c : stringchar ‘ \ ’ h 1 : hexdigit h 2 : hexdigit ⇒ ⇒ ⇒ ⨁ b ∗ ∗ utf 8 ( c ) 16 h 1 + h 2 if ∣ ⨁ b ∗ ∗ ∣ < 2 32 Each character in a string literal represents the byte sequence corresponding to its UTF-8 [UNICODE] (Section 2.5) encoding, except for hexadecimal escape sequences ‘ \hh ’ , which represent raw bytes of the respective value. stringchar ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ c : char ‘ \t ’ ‘ \n ’ ‘ \r ’ ‘ \ ’ ’ ’ ‘ \ ’ ’ ‘ \\ ’ ‘ \u { ’ n : hexnum ‘ } ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ c U + 09 U + 0A U + 0D U + 22 U + 27 U + 5C n if c ≥ U + 20 ∧ c = U + 7F ∧ c = ‘ ’ ’ ’ ∧ c = ‘ \ ’ if n < 0xD800 ∨ 0xE800 ≤ n < 0x110000 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 [UNICODE] (Section 2.5) and is interpreted as a string of Unicode scalar values. name ::= b ∗ : string ⇒ c ∗ if b ∗ = utf 8 ( c ∗ ) 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 either a sequence of printable ASCII characters that does not contain a space, quotation mark, comma, semicolon, or bracket, or by a quoted name . id idchar ::= ∣ ::= ∣ ∣ ∣ ∣ ‘ $ ’ c ∗ : idchar + ‘ $ ’ c ∗ : name ‘ 0 ’ ∣ … ∣ ‘ 9 ’ ‘ A ’ ∣ … ∣ ‘ Z ’ ‘ a ’ ∣ … ∣ ‘ z ’ ‘ ! ’ ∣ ‘ # ’ ∣ ‘ $ ’ ∣ ‘ % ’ ∣ ‘ & ’ ∣ ‘ ’ ’ ∣ ‘ * ’ ∣ ‘ + ’ ∣ ‘ - ’ ∣ ‘ . ’ ∣ ‘ / ’ ‘ : ’ ∣ ‘ < ’ ∣ ‘ = ’ ∣ ‘ > ’ ∣ ‘ ? ’ ∣ ‘ @ ’ ∣ ‘ \ ’ ∣ ‘ ^ ’ ∣ ‘ _ ’ ∣ ‘ ˋ ’ ∣ ‘ | ’ ∣ ‘ ~ ’ ⇒ ⇒ c ∗ c ∗ if ∣ c ∗ ∣ > 0 Note The value of an identifier character is the Unicode codepoint denoting it. 6.3.5.1. 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. 6.4. Types 6.4.1. Number Types numtype ::= ∣ ∣ ∣ ‘ i32 ’ ‘ i64 ’ ‘ f32 ’ ‘ f64 ’ ⇒ ⇒ ⇒ ⇒ i 32 i 64 f 32 f 64 6.4.2. Vector Types vectype ::= ‘ v128 ’ ⇒ v 128 6.4.3. Heap Types absheaptype heaptype I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ::= ∣ ‘ any ’ ‘ eq ’ ‘ i31 ’ ‘ struct ’ ‘ array ’ ‘ none ’ ‘ func ’ ‘ nofunc ’ ‘ exn ’ ‘ noexn ’ ‘ extern ’ ‘ noextern ’ ht : absheaptype x : typeidx I ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ any eq i 31 struct array none func nofunc exn noexn extern noextern ht x 6.4.4. Reference Types null reftype I ::= ::= ‘ null ’ ‘ ( ’ ‘ ref ’ null ? : null ? ht : heaptype I ‘ ) ’ ⇒ ⇒ null ref null ? ht 6.4.4.1. Abbreviations There are shorthands for references to abstract heap types. reftype I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ anyref ’ ‘ eqref ’ ‘ i31ref ’ ‘ structref ’ ‘ arrayref ’ ‘ nullref ’ ‘ funcref ’ ‘ nullfuncref ’ ‘ exnref ’ ‘ nullexnref ’ ‘ externref ’ ‘ nullexternref ’ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ any ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ eq ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ i31 ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ struct ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ array ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ none ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ func ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ nofunc ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ exn ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ noexn ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ extern ’ ‘ ) ’ ‘ ( ’ ‘ ref ’ ‘ null ’ ‘ noextern ’ ‘ ) ’ 6.4.5. Value Types valtype I ::= ∣ ∣ nt : numtype vt : vectype rt : reftype I ⇒ ⇒ ⇒ nt vt rt 6.4.6. Composite Types Composite types are parsed into their respective abstract representation, paired with the local identifier context generated by their bound field or parameter identifiers: comptype I field I param I result I fieldtype I storagetype I packtype ::= ∣ ∣ ::= ::= ::= ::= ∣ ::= ∣ ::= ∣ ‘ ( ’ ‘ struct ’ ( ft , id ? ) ∗ : list ( field I ) ‘ ) ’ ‘ ( ’ ‘ array ’ ft : fieldtype I ‘ ) ’ ‘ ( ’ ‘ func ’ ( t 1 , id ? ) ∗ : list ( param I ) t 2 ∗ : list ( result I ) ‘ ) ’ ‘ ( ’ ‘ field ’ id ? : id ? ft : fieldtype I ‘ ) ’ ‘ ( ’ ‘ param ’ id ? : id ? t : valtype I ‘ ) ’ ‘ ( ’ ‘ result ’ t : valtype I ‘ ) ’ zt : storagetype I ‘ ( ’ ‘ mut ’ zt : storagetype I ‘ ) ’ t : valtype I pt : packtype ‘ i8 ’ ‘ i16 ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ( struct ft ∗ , { fields ( ( id ? ) ∗ )}) ( array ft , { fields ( ϵ )}) ( func t 1 ∗ → t 2 ∗ , { fields ( ϵ )}) ( ft , id ? ) ( t , id ? ) t zt mut zt t pt i 8 i 16 Note The optional identifier names for parameters in a function type only have documentation purpose. They cannot be referenced from anywhere. 6.4.6.1. Abbreviations Multiple anonymous structure fields or parameters or multiple results may be combined into a single declaration: field I param I result I ::= ::= ::= … ∣ ‘ ( ’ ‘ field ’ fieldtype I ∗ ‘ ) ’ … ∣ ‘ ( ’ ‘ param ’ valtype I ∗ ‘ ) ’ … ∣ ‘ ( ’ ‘ result ’ valtype I ∗ ‘ ) ’ ≡ ≡ ≡ ( ‘ ( ’ ‘ field ’ fieldtype I ‘ ) ’ ) ∗ ( ‘ ( ’ ‘ param ’ valtype I ‘ ) ’ ) ∗ ( ‘ ( ’ ‘ result ’ valtype I ‘ ) ’ ) ∗ 6.4.7. Recursive Types Recursive types are parsed into their respective abstract representation, paired with the identifier context generated by their bound identifiers: final subtype I typedef I rectype I ::= ::= ::= ::= ‘ final ’ ‘ ( ’ ‘ sub ’ fin ? : final ? x ∗ : list ( typeidx I ) ( ct , I ′ ) : comptype I ‘ ) ’ ‘ ( ’ ‘ type ’ id ? : id ? ( st , I ′ ) : subtype I ‘ ) ’ ‘ ( ’ ‘ rec ’ ( st , I ′ ) ∗ : list ( typedef I ) ‘ ) ’ ⇒ ⇒ ⇒ ⇒ final ( sub fin ? x ∗ ct , I ′ ) ( st , I ′ ⊕ { types ( id ? )}) ( rec st ∗ , ⨁ I ′ ∗ ) 6.4.7.1. Abbreviations Final sub types with no super-types can omit the ‘ sub ’ keyword and its arguments: subtype I ::= … ∣ comptype I ≡ ‘ ( ’ ‘ sub ’ ‘ final ’ comptype I ‘ ) ’ Similarly, singular recursive types can omit the ‘ rec ’ keyword: rectype I ::= … ∣ typedef I ≡ ‘ ( ’ ‘ rec ’ typedef I ‘ ) ’ 6.4.8. Address Types addrtype ::= ∣ ‘ i32 ’ ‘ i64 ’ ⇒ ⇒ i 32 i 64 6.4.8.1. Abbreviations The address type can be omitted, in which case it defaults i 32 : addrtype ::= … ∣ ϵ ≡ ‘ i32 ’ 6.4.9. Limits limits ::= ∣ n : u64 n : u64 m : u64 ⇒ ⇒ [ n .. ϵ ] [ n .. m ] 6.4.10. Tag Types tagtype I ::= ( x , I ′ ) : typeuse I ⇒ x 6.4.11. Global Types globaltype I ::= ∣ t : valtype I ‘ ( ’ ‘ mut ’ t : valtype I ‘ ) ’ ⇒ ⇒ t mut t 6.4.12. Memory Types memtype I ::= at : addrtype lim : limits ⇒ at lim page 6.4.13. Table Types tabletype I ::= at : addrtype lim : limits rt : reftype I ⇒ at lim rt 6.4.14. External Types externtype I ::= ∣ ∣ ∣ ∣ ‘ ( ’ ‘ tag ’ id ? : id ? jt : tagtype I ‘ ) ’ ‘ ( ’ ‘ global ’ id ? : id ? gt : globaltype I ‘ ) ’ ‘ ( ’ ‘ memory ’ id ? : id ? mt : memtype I ‘ ) ’ ‘ ( ’ ‘ table ’ id ? : id ? tt : tabletype I ‘ ) ’ ‘ ( ’ ‘ func ’ id ? : id ? ( x , I ′ ) : typeuse I ‘ ) ’ ⇒ ⇒ ⇒ ⇒ ⇒ ( tag jt , { tags ( id ? )}) ( global gt , { globals ( id ? )}) ( mem mt , { mems ( id ? )}) ( table tt , { tables ( id ? )}) ( func x , { funcs ( id ? )}) 6.4.15. Type Uses A type use is a reference to a type definition . Where it is required to reference a function type , it may optionally be augmented by explicit inlined parameter and result declarations. That allows binding symbolic identifiers to name the local indices of parameters. If inline declarations are given, then their types must match the referenced function type . typeuse I ::= ∣ ‘ ( ’ ‘ type ’ x : typeidx I ‘ ) ’ if I . typedefs [ x ] = ( rec st ∗ ) . i ∧ st ∗ [ i ] = sub final ( func t 1 ∗ → t 2 ∗ ) ∧ I ′ = { locals ( ϵ ) ∣ t 1 ∗ ∣ } ‘ ( ’ ‘ type ’ x : typeidx I ‘ ) ’ ( t 1 , id ? ) ∗ : param I ∗ t 2 ∗ : result I ∗ if I . typedefs [ x ] = ( rec st ∗ ) . i ∧ st ∗ [ i ] = sub final ( func t 1 ∗ → t 2 ∗ ) ∧ I ′ = { locals ( id ? ) ∗ } ∧ ⊢ I ′ : ok ⇒ ⇒ ( x , I ′ ) ( x , I ′ ) 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 I ′ ensures that the parameters do not contain duplicate identifiers. 6.4.15.1. 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 I ::= … ∣ ( t 1 , id ? ) ∗ : param I ∗ t 2 ∗ : result I ∗ if I . typedefs [ x ] = ( rec ( sub final ( func t 1 ∗ → t 2 ∗ ))) . 0 ∧ ( I . typedefs [ i ] = ( rec ( sub final ( func t 1 ∗ → t 2 ∗ ))) . 0 ) i < x ≡ ‘ ( ’ ‘ type ’ x : typeidx I ‘ ) ’ param I ∗ result I ∗ where x 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 I instrs I ::= ∣ ::= in : plaininstr I in : blockinstr I in ∗ : instr I ∗ ⇒ ⇒ ⇒ in in 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. label I ::= ∣ ∣ ϵ id : id id : id ⇒ ⇒ ⇒ ( ϵ , { labels ϵ } ⊕ I ) ( id , { labels id } ⊕ I ) ( id , { labels id } ⊕ I [ . labels [ x ] = ϵ ]) if id ∈ / I . labels if id = I . labels [ x ] 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. 6.5.2. Parametric Instructions plaininstr I ::= ∣ ∣ ∣ ‘ unreachable ’ ‘ nop ’ ‘ drop ’ ‘ select ’ ( t ∗ : result I ∗ ) ? ⇒ ⇒ ⇒ ⇒ unreachable nop drop select ( t ∗ ) ? 6.5.3. Control Instructions Structured control instructions can bind an optional symbolic label identifier . The same label identifier may optionally be repeated after the corresponding ‘ end ’ or ‘ else ’ keywords, to indicate the matching delimiters. Their block type is given as a type use , analogous to the type of functions . However, the special case of a type use that is syntactically empty or consists of only a single result is not regarded as an abbreviation for an inline function type , but is parsed directly into an optional value type . blocktype I blockinstr I catch I ::= ∣ ::= ∣ ∣ ∣ ::= ∣ ∣ ∣ t ? : result I ? ( x , I ′ ) : typeuse I if I ′ = { locals ( ϵ ) ∗ } ‘ block ’ ( id ? , I ′ ) : label I bt : blocktype I in ∗ : instrs I ′ ‘ end ’ id ′ ? : id ? if id ′ ? = ϵ ∨ id ′ ? = id ? ‘ loop ’ ( id ? , I ′ ) : label I bt : blocktype I in ∗ : instrs I ′ ‘ end ’ id ′ ? : id ? if id ′ ? = ϵ ∨ id ′ ? = id ? ‘ if ’ ( id ? , I ′ ) : label I bt : blocktype I in 1 ∗ : instrs I ′ ‘ else ’ id 1 ? : id ? in 2 ∗ : instrs I ′ ‘ end ’ id 2 ? : id ? if ( id 1 ? = ϵ ∨ id 1 ? = id ? ) ∧ ( id 2 ? = ϵ ∨ id 2 ? = id ? ) ‘ try_table ’ ( id ? , I ′ ) : label I bt : blocktype I c ∗ : catch I ∗ in ∗ : instrs I ′ ‘ end ’ id ′ ? : id ? if id ′ ? = ϵ ∨ id ′ ? = id ? ‘ ( ’ ‘ catch ’ x : tagidx I l : labelidx I ‘ ) ’ ‘ ( ’ ‘ catch_ref ’ x : tagidx I l : labelidx I ‘ ) ’ ‘ ( ’ ‘ catch_all ’ l : labelidx I ‘ ) ’ ‘ ( ’ ‘ catch_all_ref ’ l : labelidx I ‘ ) ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ t ? x block bt in ∗ loop bt in ∗ if bt in 1 ∗ else in 2 ∗ try_table bt c ∗ in ∗ catch x l catch_ref x l catch_all l catch_all_ref l Note The side condition stating that the identifier context I ′ 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. All other control instruction are represented verbatim. Note The side condition stating that the identifier context I ′ 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 annotation. 6.5.3.1. Abbreviations The ‘ else ’ keyword of an ‘ if ’ instruction can be omitted if the following instruction sequence is empty. blockinstr I ::= ∣ … ‘ if ’ label I blocktype I instrs I ‘ end ’ id ? ‘ if ’ label I blocktype I instrs I ‘ else ’ ‘ end ’ id ? ≡ Also, for backwards compatibility, the table index to ‘ call_indirect ’ and ‘ return_call_indirect ’ can be omitted, defaulting to 0 . plaininstr I ::= ∣ ∣ … ‘ call_indirect ’ typeuse I ‘ return_call_indirect ’ typeuse I ≡ ≡ ‘ call_indirect ’ ‘ 0 ’ typeuse I ‘ return_call_indirect ’ ‘ 0 ’ typeuse I 6.5.4. Variable Instructions plaininstr I ::= ∣ ∣ ∣ ∣ ∣ … ‘ local.get ’ x : localidx I ‘ local.set ’ x : localidx I ‘ local.tee ’ x : localidx I ‘ global.get ’ x : globalidx I ‘ global.set ’ x : globalidx I ⇒ ⇒ ⇒ ⇒ ⇒ local . get x local . set x local . tee x global . get x global . set x 6.5.5. Table Instructions plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ table.get ’ x : tableidx I ‘ table.set ’ x : tableidx I ‘ table.size ’ x : tableidx I ‘ table.grow ’ x : tableidx I ‘ table.fill ’ x : tableidx I ‘ table.copy ’ x 1 : tableidx I x 2 : tableidx I ‘ table.init ’ x : tableidx I y : elemidx I ‘ elem.drop ’ x : elemidx I ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ table . get x table . set x table . size x table . grow x table . fill x table . copy x 1 x 2 table . init x y elem . drop x 6.5.5.1. Abbreviations For backwards compatibility, all table indices may be omitted from table instructions, defaulting to 0 . plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ table.get ’ ‘ table.set ’ ‘ table.size ’ ‘ table.grow ’ ‘ table.fill ’ ‘ table.copy ’ ‘ table.init ’ elemidx I ≡ ≡ ≡ ≡ ≡ ≡ ≡ ‘ table.get ’ ‘ 0 ’ ‘ table.set ’ ‘ 0 ’ ‘ table.size ’ ‘ 0 ’ ‘ table.grow ’ ‘ 0 ’ ‘ table.fill ’ ‘ 0 ’ ‘ table.copy ’ ‘ 0 ’ ‘ 0 ’ ‘ table.init ’ ‘ 0 ’ elemidx I 6.5.6. Memory Instructions The offset and alignment immediates to memory instructions are optional. The offset defaults to 0 , the alignment to the storage size of the respective memory access, which is its natural alignment . Lexically, an offset or align phrase is considered a single keyword token , so no white space is allowed around the ‘ = ’ . memarg N offset align N laneidx ::= ::= ∣ ::= ∣ ::= m : offset n : align N ‘ offset = ’ m : u64 ϵ ‘ align = ’ m : u64 ϵ i : u8 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ { align n , offset m } m 0 n N i if m = 2 n plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i32.load ’ x : memidx I ao : memarg 4 ‘ i64.load ’ x : memidx I ao : memarg 8 ‘ f32.load ’ x : memidx I ao : memarg 4 ‘ f64.load ’ x : memidx I ao : memarg 8 ‘ i32.load8_s ’ x : memidx I ao : memarg 1 ‘ i32.load8_u ’ x : memidx I ao : memarg 1 ‘ i32.load16_s ’ x : memidx I ao : memarg 2 ‘ i32.load16_u ’ x : memidx I ao : memarg 2 ‘ i64.load8_s ’ x : memidx I ao : memarg 1 ‘ i64.load8_u ’ x : memidx I ao : memarg 1 ‘ i64.load16_s ’ x : memidx I ao : memarg 2 ‘ i64.load16_u ’ x : memidx I ao : memarg 2 ‘ i64.load32_s ’ x : memidx I ao : memarg 4 ‘ i64.load32_u ’ x : memidx I ao : memarg 4 ‘ v128.load ’ x : memidx I ao : memarg 16 ‘ v128.load8x8_s ’ x : memidx I ao : memarg 8 ‘ v128.load8x8_u ’ x : memidx I ao : memarg 8 ‘ v128.load16x4_s ’ x : memidx I ao : memarg 8 ‘ v128.load16x4_u ’ x : memidx I ao : memarg 8 ‘ v128.load32x2_s ’ x : memidx I ao : memarg 8 ‘ v128.load32x2_u ’ x : memidx I ao : memarg 8 ‘ v128.load8_splat ’ x : memidx I ao : memarg 1 ‘ v128.load16_splat ’ x : memidx I ao : memarg 2 ‘ v128.load32_splat ’ x : memidx I ao : memarg 4 ‘ v128.load64_splat ’ x : memidx I ao : memarg 8 ‘ v128.load32_zero ’ x : memidx I ao : memarg 4 ‘ v128.load64_zero ’ x : memidx I ao : memarg 8 ‘ v128.load8_lane ’ x : memidx I ao : memarg 1 i : laneidx ‘ v128.load16_lane ’ x : memidx I ao : memarg 2 i : laneidx ‘ v128.load32_lane ’ x : memidx I ao : memarg 4 i : laneidx ‘ v128.load64_lane ’ x : memidx I ao : memarg 8 i : laneidx ‘ i32.store ’ x : memidx I ao : memarg 4 ‘ i64.store ’ x : memidx I ao : memarg 8 ‘ f32.store ’ x : memidx I ao : memarg 4 ‘ f64.store ’ x : memidx I ao : memarg 8 ‘ i32.store8 ’ x : memidx I ao : memarg 1 ‘ i32.store16 ’ x : memidx I ao : memarg 2 ‘ i64.store8 ’ x : memidx I ao : memarg 1 ‘ i64.store16 ’ x : memidx I ao : memarg 2 ‘ i64.store32 ’ x : memidx I ao : memarg 4 ‘ v128.store ’ x : memidx I ao : memarg 16 ‘ v128.store8_lane ’ x : memidx I ao : memarg 1 i : laneidx ‘ v128.store16_lane ’ x : memidx I ao : memarg 2 i : laneidx ‘ v128.store32_lane ’ x : memidx I ao : memarg 4 i : laneidx ‘ v128.store64_lane ’ x : memidx I ao : memarg 8 i : laneidx ‘ memory.size ’ x : memidx I ‘ memory.grow ’ x : memidx I ‘ memory.fill ’ x : memidx I ‘ memory.copy ’ x 1 : memidx I x 2 : memidx I ‘ memory.init ’ x : memidx I y : dataidx I ‘ data.drop ’ x : dataidx I ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . load x ao i 64 . load x ao f 32 . load x ao f 64 . load x ao i 32 . load 8 _ s x ao i 32 . load 8 _ u x ao i 32 . load 16 _ s x ao i 32 . load 16 _ u x ao i 64 . load 8 _ s x ao i 64 . load 8 _ u x ao i 64 . load 16 _ s x ao i 64 . load 16 _ u x ao i 64 . load 32 _ s x ao i 64 . load 32 _ u x ao v 128 . load x ao v 128 . load 8 x 8 _ s x ao v 128 . load 8 x 8 _ u x ao v 128 . load 16 x 4 _ s x ao v 128 . load 16 x 4 _ u x ao v 128 . load 32 x 2 _ s x ao v 128 . load 32 x 2 _ u x ao v 128 . load 8 _ splat x ao v 128 . load 16 _ splat x ao v 128 . load 32 _ splat x ao v 128 . load 64 _ splat x ao v 128 . load 32 _ zero x ao v 128 . load 64 _ zero x ao v 128 . load 8 _ lane x ao i v 128 . load 16 _ lane x ao i v 128 . load 32 _ lane x ao i v 128 . load 64 _ lane x ao i i 32 . store x ao i 64 . store x ao f 32 . store x ao f 64 . store x ao i 32 . store 8 x ao i 32 . store 16 x ao i 64 . store 8 x ao i 64 . store 16 x ao i 64 . store 32 x ao v 128 . store x ao v 128 . store 8 _ lane x ao i v 128 . store 16 _ lane x ao i v 128 . store 32 _ lane x ao i v 128 . store 64 _ lane x ao i memory . size x memory . grow x memory . fill x memory . copy x 1 x 2 memory . init x y data . drop x 6.5.6.1. Abbreviations As an abbreviation, the memory index can be omitted in all memory instructions, defaulting to 0 . plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i32.load ’ memarg 4 ‘ i64.load ’ memarg 8 ‘ f32.load ’ memarg 4 ‘ f64.load ’ memarg 8 ‘ i32.load8_s ’ memarg 1 ‘ i32.load8_u ’ memarg 1 ‘ i32.load16_s ’ memarg 2 ‘ i32.load16_u ’ memarg 2 ‘ i64.load8_s ’ memarg 1 ‘ i64.load8_u ’ memarg 1 ‘ i64.load16_s ’ memarg 2 ‘ i64.load16_u ’ memarg 2 ‘ i64.load32_s ’ memarg 4 ‘ i64.load32_u ’ memarg 4 ‘ v128.load ’ memarg 16 ‘ v128.load8x8_s ’ memarg 8 ‘ v128.load8x8_u ’ memarg 8 ‘ v128.load16x4_s ’ memarg 8 ‘ v128.load16x4_u ’ memarg 8 ‘ v128.load32x2_s ’ memarg 8 ‘ v128.load32x2_u ’ memarg 8 ‘ v128.load8_splat ’ memarg 1 ‘ v128.load16_splat ’ memarg 2 ‘ v128.load32_splat ’ memarg 4 ‘ v128.load64_splat ’ memarg 8 ‘ v128.load32_zero ’ memarg 4 ‘ v128.load64_zero ’ memarg 8 ‘ v128.load8_lane ’ memarg 1 laneidx ‘ v128.load16_lane ’ memarg 2 laneidx ‘ v128.load32_lane ’ memarg 4 laneidx ‘ v128.load64_lane ’ memarg 8 laneidx ‘ i32.store ’ memarg 4 ‘ i64.store ’ memarg 8 ‘ f32.store ’ memarg 4 ‘ f64.store ’ memarg 8 ‘ i32.store8 ’ memarg 1 ‘ i32.store16 ’ memarg 2 ‘ i64.store8 ’ memarg 1 ‘ i64.store16 ’ memarg 2 ‘ i64.store32 ’ memarg 4 ‘ v128.store ’ memarg 16 ‘ v128.store8_lane ’ memarg 1 laneidx ‘ v128.store16_lane ’ memarg 2 laneidx ‘ v128.store32_lane ’ memarg 4 laneidx ‘ v128.store64_lane ’ memarg 8 laneidx ‘ memory.size ’ ‘ memory.grow ’ ‘ memory.fill ’ ‘ memory.copy ’ ‘ memory.init ’ dataidx I ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ≡ ‘ i32.load ’ ‘ 0 ’ memarg 4 ‘ i64.load ’ ‘ 0 ’ memarg 8 ‘ f32.load ’ ‘ 0 ’ memarg 4 ‘ f64.load ’ ‘ 0 ’ memarg 8 ‘ i32.load8_s ’ ‘ 0 ’ memarg 1 ‘ i32.load8_u ’ ‘ 0 ’ memarg 1 ‘ i32.load16_s ’ ‘ 0 ’ memarg 2 ‘ i32.load16_u ’ ‘ 0 ’ memarg 2 ‘ i64.load8_s ’ ‘ 0 ’ memarg 1 ‘ i64.load8_u ’ ‘ 0 ’ memarg 1 ‘ i64.load16_s ’ ‘ 0 ’ memarg 2 ‘ i64.load16_u ’ ‘ 0 ’ memarg 2 ‘ i64.load32_s ’ ‘ 0 ’ memarg 4 ‘ i64.load32_u ’ ‘ 0 ’ memarg 4 ‘ v128.load ’ ‘ 0 ’ memarg 16 ‘ v128.load8x8_s ’ ‘ 0 ’ memarg 8 ‘ v128.load8x8_u ’ ‘ 0 ’ memarg 8 ‘ v128.load16x4_s ’ ‘ 0 ’ memarg 8 ‘ v128.load16x4_u ’ ‘ 0 ’ memarg 8 ‘ v128.load32x2_s ’ ‘ 0 ’ memarg 8 ‘ v128.load32x2_u ’ ‘ 0 ’ memarg 8 ‘ v128.load8_splat ’ ‘ 0 ’ memarg 1 ‘ v128.load16_splat ’ ‘ 0 ’ memarg 2 ‘ v128.load32_splat ’ ‘ 0 ’ memarg 4 ‘ v128.load64_splat ’ ‘ 0 ’ memarg 8 ‘ v128.load32_zero ’ ‘ 0 ’ memarg 4 ‘ v128.load64_zero ’ ‘ 0 ’ memarg 8 ‘ v128.load8_lane ’ ‘ 0 ’ memarg 1 laneidx ‘ v128.load16_lane ’ ‘ 0 ’ memarg 2 laneidx ‘ v128.load32_lane ’ ‘ 0 ’ memarg 4 laneidx ‘ v128.load64_lane ’ ‘ 0 ’ memarg 8 laneidx ‘ i32.store ’ ‘ 0 ’ memarg 4 ‘ i64.store ’ ‘ 0 ’ memarg 8 ‘ f32.store ’ ‘ 0 ’ memarg 4 ‘ f64.store ’ ‘ 0 ’ memarg 8 ‘ i32.store8 ’ ‘ 0 ’ memarg 1 ‘ i32.store16 ’ ‘ 0 ’ memarg 2 ‘ i64.store8 ’ ‘ 0 ’ memarg 1 ‘ i64.store16 ’ ‘ 0 ’ memarg 2 ‘ i64.store32 ’ ‘ 0 ’ memarg 4 ‘ v128.store ’ ‘ 0 ’ memarg 16 ‘ v128.store8_lane ’ ‘ 0 ’ memarg 1 laneidx ‘ v128.store16_lane ’ ‘ 0 ’ memarg 2 laneidx ‘ v128.store32_lane ’ ‘ 0 ’ memarg 4 laneidx ‘ v128.store64_lane ’ ‘ 0 ’ memarg 8 laneidx ‘ memory.size ’ ‘ 0 ’ ‘ memory.grow ’ ‘ 0 ’ ‘ memory.fill ’ ‘ 0 ’ ‘ memory.copy ’ ‘ 0 ’ ‘ 0 ’ ‘ memory.init ’ ‘ 0 ’ dataidx I 6.5.7. Reference Instructions plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ ref.null ’ ht : heaptype I ‘ ref.func ’ x : funcidx I ‘ ref.is_null ’ ‘ ref.as_non_null ’ ‘ ref.eq ’ ‘ ref.test ’ rt : reftype I ‘ ref.cast ’ rt : reftype I ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ref . null ht ref . func x ref . is_null ref . as_non_null ref . eq ref . test rt ref . cast rt 6.5.8. Aggregate Instructions plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ ref.i31 ’ ‘ i31.get_s ’ ‘ i31.get_u ’ ‘ struct.new ’ x : typeidx I ‘ struct.new_default ’ x : typeidx I ‘ struct.get ’ x : typeidx I i : fieldidx I , x ‘ struct.get_s ’ x : typeidx I i : fieldidx I , x ‘ struct.get_u ’ x : typeidx I i : fieldidx I , x ‘ struct.set ’ x : typeidx I i : fieldidx I , x ‘ array.new ’ x : typeidx I ‘ array.new_default ’ x : typeidx I ‘ array.new_fixed ’ x : typeidx I n : u32 ‘ array.new_data ’ x : typeidx I y : dataidx I ‘ array.new_elem ’ x : typeidx I y : elemidx I ‘ array.get ’ x : typeidx I ‘ array.get_s ’ x : typeidx I ‘ array.get_u ’ x : typeidx I ‘ array.set ’ x : typeidx I ‘ array.len ’ ‘ array.fill ’ x : typeidx I ‘ array.copy ’ x 1 : typeidx I x 2 : typeidx I ‘ array.init_data ’ x : typeidx I y : dataidx I ‘ array.init_elem ’ x : typeidx I y : elemidx I ‘ any.convert_extern ’ ‘ extern.convert_any ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ref . i 31 i 31 . get _ s i 31 . get _ u struct . new x struct . new_default x struct . get x i struct . get _ s x i struct . get _ u x i struct . set x i array . new x array . new_default x array . new_fixed x n array . new_data x y array . new_elem x y array . get x array . get _ s x array . get _ u x array . set x array . len array . fill x array . copy x 1 x 2 array . init_data x y array . init_elem x y any . convert_extern extern . convert_any 6.5.9. Numeric Instructions plaininstr I ::= ∣ ∣ ∣ ∣ … ‘ i32.const ’ c : i32 ‘ i64.const ’ c : i64 ‘ f32.const ’ c : f32 ‘ f64.const ’ c : f64 ⇒ ⇒ ⇒ ⇒ i 32 . const c i 64 . const c f 32 . const c f 64 . const c plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i32.eqz ’ ‘ i32.eq ’ ‘ i32.ne ’ ‘ i32.lt_s ’ ‘ i32.lt_u ’ ‘ i32.gt_s ’ ‘ i32.gt_u ’ ‘ i32.le_s ’ ‘ i32.le_u ’ ‘ i32.ge_s ’ ‘ i32.ge_u ’ ‘ i32.clz ’ ‘ i32.ctz ’ ‘ i32.popcnt ’ ‘ i32.extend8_s ’ ‘ i32.extend16_s ’ ‘ i32.add ’ ‘ i32.sub ’ ‘ i32.mul ’ ‘ i32.div_s ’ ‘ i32.div_u ’ ‘ i32.rem_s ’ ‘ i32.rem_u ’ ‘ i32.and ’ ‘ i32.or ’ ‘ i32.xor ’ ‘ i32.shl ’ ‘ i32.shr_s ’ ‘ i32.shr_u ’ ‘ i32.rotl ’ ‘ i32.rotr ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . eqz i 32 . eq i 32 . ne i 32 . lt _ s i 32 . lt _ u i 32 . gt _ s i 32 . gt _ u i 32 . le _ s i 32 . le _ u i 32 . ge _ s i 32 . ge _ u i 32 . clz i 32 . ctz i 32 . popcnt i 32 . extend 8 _ s i 32 . extend 16 _ s i 32 . add i 32 . sub i 32 . mul i 32 . div _ s i 32 . div _ u i 32 . rem _ s i 32 . rem _ u i 32 . and i 32 . or i 32 . xor i 32 . shl i 32 . shr _ s i 32 . shr _ u i 32 . rotl i 32 . rotr plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i64.eqz ’ ‘ i64.eq ’ ‘ i64.ne ’ ‘ i64.lt_s ’ ‘ i64.lt_u ’ ‘ i64.gt_s ’ ‘ i64.gt_u ’ ‘ i64.le_s ’ ‘ i64.le_u ’ ‘ i64.ge_s ’ ‘ i64.ge_u ’ ‘ i64.clz ’ ‘ i64.ctz ’ ‘ i64.popcnt ’ ‘ i64.extend8_s ’ ‘ i64.extend16_s ’ ‘ i64.extend32_s ’ ‘ i64.add ’ ‘ i64.sub ’ ‘ i64.mul ’ ‘ i64.div_s ’ ‘ i64.div_u ’ ‘ i64.rem_s ’ ‘ i64.rem_u ’ ‘ i64.and ’ ‘ i64.or ’ ‘ i64.xor ’ ‘ i64.shl ’ ‘ i64.shr_s ’ ‘ i64.shr_u ’ ‘ i64.rotl ’ ‘ i64.rotr ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 64 . eqz i 64 . eq i 64 . ne i 64 . lt _ s i 64 . lt _ u i 64 . gt _ s i 64 . gt _ u i 64 . le _ s i 64 . le _ u i 64 . ge _ s i 64 . ge _ u i 64 . clz i 64 . ctz i 64 . popcnt i 64 . extend 8 _ s i 64 . extend 16 _ s i 64 . extend 32 _ s i 64 . add i 64 . sub i 64 . mul i 64 . div _ s i 64 . div _ u i 64 . rem _ s i 64 . rem _ u i 64 . and i 64 . or i 64 . xor i 64 . shl i 64 . shr _ s i 64 . shr _ u i 64 . rotl i 64 . rotr plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ f32.eq ’ ‘ f32.ne ’ ‘ f32.lt ’ ‘ f32.gt ’ ‘ f32.le ’ ‘ f32.ge ’ ‘ f32.abs ’ ‘ f32.neg ’ ‘ f32.sqrt ’ ‘ f32.ceil ’ ‘ f32.floor ’ ‘ f32.trunc ’ ‘ f32.nearest ’ ‘ f32.add ’ ‘ f32.sub ’ ‘ f32.mul ’ ‘ f32.div ’ ‘ f32.min ’ ‘ f32.max ’ ‘ f32.copysign ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 32 . eq f 32 . ne f 32 . lt f 32 . gt f 32 . le f 32 . ge f 32 . abs f 32 . neg f 32 . sqrt f 32 . ceil f 32 . floor f 32 . trunc f 32 . nearest f 32 . add f 32 . sub f 32 . mul f 32 . div f 32 . min f 32 . max f 32 . copysign plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ f64.eq ’ ‘ f64.ne ’ ‘ f64.lt ’ ‘ f64.gt ’ ‘ f64.le ’ ‘ f64.ge ’ ‘ f64.abs ’ ‘ f64.neg ’ ‘ f64.sqrt ’ ‘ f64.ceil ’ ‘ f64.floor ’ ‘ f64.trunc ’ ‘ f64.nearest ’ ‘ f64.add ’ ‘ f64.sub ’ ‘ f64.mul ’ ‘ f64.div ’ ‘ f64.min ’ ‘ f64.max ’ ‘ f64.copysign ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 64 . eq f 64 . ne f 64 . lt f 64 . gt f 64 . le f 64 . ge f 64 . abs f 64 . neg f 64 . sqrt f 64 . ceil f 64 . floor f 64 . trunc f 64 . nearest f 64 . add f 64 . sub f 64 . mul f 64 . div f 64 . min f 64 . max f 64 . copysign plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i32.wrap_i64 ’ ‘ i32.trunc_f32_s ’ ‘ i32.trunc_f32_u ’ ‘ i32.trunc_f64_s ’ ‘ i32.trunc_f64_u ’ ‘ i32.trunc_sat_f32_s ’ ‘ i32.trunc_sat_f32_u ’ ‘ i32.trunc_sat_f64_s ’ ‘ i32.trunc_sat_f64_u ’ ‘ i64.extend_i32_s ’ ‘ i64.extend_i32_u ’ ‘ i64.trunc_f32_s ’ ‘ i64.trunc_f32_u ’ ‘ i64.trunc_f64_s ’ ‘ i64.trunc_f64_u ’ ‘ i64.trunc_sat_f32_s ’ ‘ i64.trunc_sat_f32_u ’ ‘ i64.trunc_sat_f64_s ’ ‘ i64.trunc_sat_f64_u ’ ‘ f32.demote_f64 ’ ‘ f32.convert_i32_s ’ ‘ f32.convert_i32_u ’ ‘ f32.convert_i64_s ’ ‘ f32.convert_i64_u ’ ‘ f64.promote_f32 ’ ‘ f64.convert_i32_s ’ ‘ f64.convert_i32_u ’ ‘ f64.convert_i64_s ’ ‘ f64.convert_i64_u ’ ‘ i32.reinterpret_f32 ’ ‘ i64.reinterpret_f64 ’ ‘ f32.reinterpret_i32 ’ ‘ f64.reinterpret_i64 ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 . wrap _ i 64 i 32 . trunc _ s _ f 32 i 32 . trunc _ u _ f 32 i 32 . trunc _ s _ f 64 i 32 . trunc _ u _ f 64 i 32 . trunc_sat _ s _ f 32 i 32 . trunc_sat _ u _ f 32 i 32 . trunc_sat _ s _ f 64 i 32 . trunc_sat _ u _ f 64 i 64 . extend _ s _ i 32 i 64 . extend _ u _ i 32 i 64 . trunc _ s _ f 32 i 64 . trunc _ u _ f 32 i 64 . trunc _ s _ f 64 i 64 . trunc _ u _ f 64 i 64 . trunc_sat _ s _ f 32 i 64 . trunc_sat _ u _ f 32 i 64 . trunc_sat _ s _ f 64 i 64 . trunc_sat _ u _ f 64 f 32 . demote _ f 64 f 32 . convert _ s _ i 32 f 32 . convert _ u _ i 32 f 32 . convert _ s _ i 64 f 32 . convert _ u _ i 64 f 64 . promote _ f 32 f 64 . convert _ s _ i 32 f 64 . convert _ u _ i 32 f 64 . convert _ s _ i 64 f 64 . convert _ u _ i 64 i 32 . reinterpret _ f 32 i 64 . reinterpret _ f 64 f 32 . reinterpret _ i 32 f 64 . reinterpret _ i 64 6.5.10. Vector Instructions Vector constant instructions have a mandatory shape descriptor, which determines how the following values are parsed. plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ … ‘ v128.const ’ ‘ i8x16 ’ c ∗ : i8 16 ‘ v128.const ’ ‘ i16x8 ’ c ∗ : i16 8 ‘ v128.const ’ ‘ i32x4 ’ c ∗ : i32 4 ‘ v128.const ’ ‘ i64x2 ’ c ∗ : i64 2 ‘ v128.const ’ ‘ f32x4 ’ c ∗ : f32 4 ‘ v128.const ’ ‘ f64x2 ’ c ∗ : f64 2 ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ v 128 . const bytes i 128 − 1 ( ⨁ bytes i 8 ( c ) ∗ ) v 128 . const bytes i 128 − 1 ( ⨁ bytes i 16 ( c ) ∗ ) v 128 . const bytes i 128 − 1 ( ⨁ bytes i 32 ( c ) ∗ ) v 128 . const bytes i 128 − 1 ( ⨁ bytes i 64 ( c ) ∗ ) v 128 . const bytes i 128 − 1 ( ⨁ bytes f 32 ( c ) ∗ ) v 128 . const bytes i 128 − 1 ( ⨁ bytes f 64 ( c ) ∗ ) plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i8x16.shuffle ’ i ∗ : laneidx 16 ‘ i8x16.swizzle ’ ‘ i8x16.relaxed_swizzle ’ ‘ i8x16.splat ’ ‘ i16x8.splat ’ ‘ i32x4.splat ’ ‘ i64x2.splat ’ ‘ f32x4.splat ’ ‘ f64x2.splat ’ ‘ i8x16.extract_lane_s ’ i : laneidx ‘ i8x16.extract_lane_u ’ i : laneidx ‘ i16x8.extract_lane_s ’ i : laneidx ‘ i16x8.extract_lane_u ’ i : laneidx ‘ i32x4.extract_lane ’ i : laneidx ‘ i64x2.extract_lane ’ i : laneidx ‘ f32x4.extract_lane ’ i : laneidx ‘ f64x2.extract_lane ’ i : laneidx ‘ i8x16.replace_lane ’ i : laneidx ‘ i16x8.replace_lane ’ i : laneidx ‘ i32x4.replace_lane ’ i : laneidx ‘ i64x2.replace_lane ’ i : laneidx ‘ f32x4.replace_lane ’ i : laneidx ‘ f64x2.replace_lane ’ i : laneidx ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 8 x 16 . shuffle i ∗ i 8 x 16 . swizzle i 8 x 16 . relaxed_swizzle i 8 x 16 . splat i 16 x 8 . splat i 32 x 4 . splat i 64 x 2 . splat f 32 x 4 . splat f 64 x 2 . splat i 8 x 16 . extract_lane _ s i i 8 x 16 . extract_lane _ u i i 16 x 8 . extract_lane _ s i i 16 x 8 . extract_lane _ u i i 32 x 4 . extract_lane i i 64 x 2 . extract_lane i f 32 x 4 . extract_lane i f 64 x 2 . extract_lane i i 8 x 16 . replace_lane i i 16 x 8 . replace_lane i i 32 x 4 . replace_lane i i 64 x 2 . replace_lane i f 32 x 4 . replace_lane i f 64 x 2 . replace_lane i plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ v128.any_true ’ ‘ v128.not ’ ‘ v128.and ’ ‘ v128.andnot ’ ‘ v128.or ’ ‘ v128.xor ’ ‘ v128.bitselect ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ v 128 . any_true v 128 . not v 128 . and v 128 . andnot v 128 . or v 128 . xor v 128 . bitselect plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i8x16.all_true ’ ‘ i8x16.eq ’ ‘ i8x16.ne ’ ‘ i8x16.lt_s ’ ‘ i8x16.lt_u ’ ‘ i8x16.gt_s ’ ‘ i8x16.gt_u ’ ‘ i8x16.le_s ’ ‘ i8x16.le_u ’ ‘ i8x16.ge_s ’ ‘ i8x16.ge_u ’ ‘ i8x16.abs ’ ‘ i8x16.neg ’ ‘ i8x16.popcnt ’ ‘ i8x16.add ’ ‘ i8x16.add_sat_s ’ ‘ i8x16.add_sat_u ’ ‘ i8x16.sub ’ ‘ i8x16.sub_sat_s ’ ‘ i8x16.sub_sat_u ’ ‘ i8x16.min_s ’ ‘ i8x16.min_u ’ ‘ i8x16.max_s ’ ‘ i8x16.max_u ’ ‘ i8x16.avgr_u ’ ‘ i8x16.relaxed_laneselect ’ ‘ i8x16.shl ’ ‘ i8x16.shr_s ’ ‘ i8x16.shr_u ’ ‘ i8x16.bitmask ’ ‘ i8x16.narrow_i16x8_s ’ ‘ i8x16.narrow_i16x8_u ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 8 x 16 . all_true i 8 x 16 . eq i 8 x 16 . ne i 8 x 16 . lt _ s i 8 x 16 . lt _ u i 8 x 16 . gt _ s i 8 x 16 . gt _ u i 8 x 16 . le _ s i 8 x 16 . le _ u i 8 x 16 . ge _ s i 8 x 16 . ge _ u i 8 x 16 . abs i 8 x 16 . neg i 8 x 16 . popcnt i 8 x 16 . add i 8 x 16 . add_sat _ s i 8 x 16 . add_sat _ u i 8 x 16 . sub i 8 x 16 . sub_sat _ s i 8 x 16 . sub_sat _ u i 8 x 16 . min _ s i 8 x 16 . min _ u i 8 x 16 . max _ s i 8 x 16 . max _ u i 8 x 16 . avgr _ u i 8 x 16 . relaxed_laneselect i 8 x 16 . shl i 8 x 16 . shr _ s i 8 x 16 . shr _ u i 8 x 16 . bitmask i 8 x 16 . narrow _ i 16 x 8 _ s i 8 x 16 . narrow _ i 16 x 8 _ u plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i16x8.all_true ’ ‘ i16x8.eq ’ ‘ i16x8.ne ’ ‘ i16x8.lt_s ’ ‘ i16x8.lt_u ’ ‘ i16x8.gt_s ’ ‘ i16x8.gt_u ’ ‘ i16x8.le_s ’ ‘ i16x8.le_u ’ ‘ i16x8.ge_s ’ ‘ i16x8.ge_u ’ ‘ i16x8.abs ’ ‘ i16x8.neg ’ ‘ i16x8.add ’ ‘ i16x8.add_sat_s ’ ‘ i16x8.add_sat_u ’ ‘ i16x8.sub ’ ‘ i16x8.sub_sat_s ’ ‘ i16x8.sub_sat_u ’ ‘ i16x8.mul ’ ‘ i16x8.min_s ’ ‘ i16x8.min_u ’ ‘ i16x8.max_s ’ ‘ i16x8.max_u ’ ‘ i16x8.avgr_u ’ ‘ i16x8.q15mulr_sat_s ’ ‘ i16x8.relaxed_q15mulr_s ’ ‘ i16x8.relaxed_laneselect ’ ‘ i16x8.shl ’ ‘ i16x8.shr_s ’ ‘ i16x8.shr_u ’ ‘ i16x8.bitmask ’ ‘ i16x8.narrow_i32x4_s ’ ‘ i16x8.narrow_i32x4_u ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 16 x 8 . all_true i 16 x 8 . eq i 16 x 8 . ne i 16 x 8 . lt _ s i 16 x 8 . lt _ u i 16 x 8 . gt _ s i 16 x 8 . gt _ u i 16 x 8 . le _ s i 16 x 8 . le _ u i 16 x 8 . ge _ s i 16 x 8 . ge _ u i 16 x 8 . abs i 16 x 8 . neg i 16 x 8 . add i 16 x 8 . add_sat _ s i 16 x 8 . add_sat _ u i 16 x 8 . sub i 16 x 8 . sub_sat _ s i 16 x 8 . sub_sat _ u i 16 x 8 . mul i 16 x 8 . min _ s i 16 x 8 . min _ u i 16 x 8 . max _ s i 16 x 8 . max _ u i 16 x 8 . avgr _ u i 16 x 8 . q 15 mulr_sat _ s i 16 x 8 . relaxed_q15mulr _ s i 16 x 8 . relaxed_laneselect i 16 x 8 . shl i 16 x 8 . shr _ s i 16 x 8 . shr _ u i 16 x 8 . bitmask i 16 x 8 . narrow _ i 32 x 4 _ s i 16 x 8 . narrow _ i 32 x 4 _ u plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i32x4.all_true ’ ‘ i32x4.eq ’ ‘ i32x4.ne ’ ‘ i32x4.lt_s ’ ‘ i32x4.lt_u ’ ‘ i32x4.gt_s ’ ‘ i32x4.gt_u ’ ‘ i32x4.le_s ’ ‘ i32x4.le_u ’ ‘ i32x4.ge_s ’ ‘ i32x4.ge_u ’ ‘ i32x4.abs ’ ‘ i32x4.neg ’ ‘ i32x4.add ’ ‘ i32x4.sub ’ ‘ i32x4.mul ’ ‘ i32x4.min_s ’ ‘ i32x4.min_u ’ ‘ i32x4.max_s ’ ‘ i32x4.max_u ’ ‘ i32x4.relaxed_laneselect ’ ‘ i32x4.shl ’ ‘ i32x4.shr_s ’ ‘ i32x4.shr_u ’ ‘ i32x4.bitmask ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 32 x 4 . all_true i 32 x 4 . eq i 32 x 4 . ne i 32 x 4 . lt _ s i 32 x 4 . lt _ u i 32 x 4 . gt _ s i 32 x 4 . gt _ u i 32 x 4 . le _ s i 32 x 4 . le _ u i 32 x 4 . ge _ s i 32 x 4 . ge _ u i 32 x 4 . abs i 32 x 4 . neg i 32 x 4 . add i 32 x 4 . sub i 32 x 4 . mul i 32 x 4 . min _ s i 32 x 4 . min _ u i 32 x 4 . max _ s i 32 x 4 . max _ u i 32 x 4 . relaxed_laneselect i 32 x 4 . shl i 32 x 4 . shr _ s i 32 x 4 . shr _ u i 32 x 4 . bitmask plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i64x2.all_true ’ ‘ i64x2.eq ’ ‘ i64x2.ne ’ ‘ i64x2.lt_s ’ ‘ i64x2.gt_s ’ ‘ i64x2.le_s ’ ‘ i64x2.ge_s ’ ‘ i64x2.abs ’ ‘ i64x2.neg ’ ‘ i64x2.add ’ ‘ i64x2.sub ’ ‘ i64x2.mul ’ ‘ i64x2.relaxed_laneselect ’ ‘ i64x2.shl ’ ‘ i64x2.shr_s ’ ‘ i64x2.shr_u ’ ‘ i64x2.bitmask ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 64 x 2 . all_true i 64 x 2 . eq i 64 x 2 . ne i 64 x 2 . lt _ s i 64 x 2 . gt _ s i 64 x 2 . le _ s i 64 x 2 . ge _ s i 64 x 2 . abs i 64 x 2 . neg i 64 x 2 . add i 64 x 2 . sub i 64 x 2 . mul i 64 x 2 . relaxed_laneselect i 64 x 2 . shl i 64 x 2 . shr _ s i 64 x 2 . shr _ u i 64 x 2 . bitmask plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ f32x4.eq ’ ‘ f32x4.ne ’ ‘ f32x4.lt ’ ‘ f32x4.gt ’ ‘ f32x4.le ’ ‘ f32x4.ge ’ ‘ f32x4.abs ’ ‘ f32x4.neg ’ ‘ f32x4.sqrt ’ ‘ f32x4.ceil ’ ‘ f32x4.floor ’ ‘ f32x4.trunc ’ ‘ f32x4.nearest ’ ‘ f32x4.add ’ ‘ f32x4.sub ’ ‘ f32x4.mul ’ ‘ f32x4.div ’ ‘ f32x4.min ’ ‘ f32x4.max ’ ‘ f32x4.pmin ’ ‘ f32x4.pmax ’ ‘ f32x4.relaxed_min ’ ‘ f32x4.relaxed_max ’ ‘ f32x4.relaxed_madd ’ ‘ f32x4.relaxed_nmadd ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 32 x 4 . eq f 32 x 4 . ne f 32 x 4 . lt f 32 x 4 . gt f 32 x 4 . le f 32 x 4 . ge f 32 x 4 . abs f 32 x 4 . neg f 32 x 4 . sqrt f 32 x 4 . ceil f 32 x 4 . floor f 32 x 4 . trunc f 32 x 4 . nearest f 32 x 4 . add f 32 x 4 . sub f 32 x 4 . mul f 32 x 4 . div f 32 x 4 . min f 32 x 4 . max f 32 x 4 . pmin f 32 x 4 . pmax f 32 x 4 . relaxed_min f 32 x 4 . relaxed_max f 32 x 4 . relaxed_madd f 32 x 4 . relaxed_nmadd plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ f64x2.eq ’ ‘ f64x2.ne ’ ‘ f64x2.lt ’ ‘ f64x2.gt ’ ‘ f64x2.le ’ ‘ f64x2.ge ’ ‘ f64x2.abs ’ ‘ f64x2.neg ’ ‘ f64x2.sqrt ’ ‘ f64x2.ceil ’ ‘ f64x2.floor ’ ‘ f64x2.trunc ’ ‘ f64x2.nearest ’ ‘ f64x2.add ’ ‘ f64x2.sub ’ ‘ f64x2.mul ’ ‘ f64x2.div ’ ‘ f64x2.min ’ ‘ f64x2.max ’ ‘ f64x2.pmin ’ ‘ f64x2.pmax ’ ‘ f64x2.relaxed_min ’ ‘ f64x2.relaxed_max ’ ‘ f64x2.relaxed_madd ’ ‘ f64x2.relaxed_nmadd ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ f 64 x 2 . eq f 64 x 2 . ne f 64 x 2 . lt f 64 x 2 . gt f 64 x 2 . le f 64 x 2 . ge f 64 x 2 . abs f 64 x 2 . neg f 64 x 2 . sqrt f 64 x 2 . ceil f 64 x 2 . floor f 64 x 2 . trunc f 64 x 2 . nearest f 64 x 2 . add f 64 x 2 . sub f 64 x 2 . mul f 64 x 2 . div f 64 x 2 . min f 64 x 2 . max f 64 x 2 . pmin f 64 x 2 . pmax f 64 x 2 . relaxed_min f 64 x 2 . relaxed_max f 64 x 2 . relaxed_madd f 64 x 2 . relaxed_nmadd plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i16x8.extend_low_i8x16_s ’ ‘ i16x8.extend_low_i8x16_u ’ ‘ i16x8.extend_high_i8x16_s ’ ‘ i16x8.extend_high_i8x16_u ’ ‘ i32x4.extend_low_i16x8_s ’ ‘ i32x4.extend_low_i16x8_u ’ ‘ i32x4.extend_high_i16x8_s ’ ‘ i32x4.extend_high_i16x8_u ’ ‘ i32x4.trunc_sat_f32x4_s ’ ‘ i32x4.trunc_sat_f32x4_u ’ ‘ i32x4.trunc_sat_f64x2_s_zero ’ ‘ i32x4.trunc_sat_f64x2_u_zero ’ ‘ i32x4.relaxed_trunc_f32x4_s ’ ‘ i32x4.relaxed_trunc_f32x4_u ’ ‘ i32x4.relaxed_trunc_f64x2_s_zero ’ ‘ i32x4.relaxed_trunc_f64x2_u_zero ’ ‘ i64x2.extend_low_i32x4_s ’ ‘ i64x2.extend_low_i32x4_u ’ ‘ i64x2.extend_high_i32x4_s ’ ‘ i64x2.extend_high_i32x4_u ’ ‘ f32x4.demote_f64x2_zero ’ ‘ f32x4.convert_i32x4_s ’ ‘ f32x4.convert_i32x4_u ’ ‘ f64x2.promote_low_f32x4 ’ ‘ f64x2.convert_low_i32x4_s ’ ‘ f64x2.convert_low_i32x4_u ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 16 x 8 . extend _ low _ s _ i 8 x 16 i 16 x 8 . extend _ low _ u _ i 8 x 16 i 16 x 8 . extend _ high _ s _ i 8 x 16 i 16 x 8 . extend _ high _ u _ i 8 x 16 i 32 x 4 . extend _ low _ s _ i 16 x 8 i 32 x 4 . extend _ low _ u _ i 16 x 8 i 32 x 4 . extend _ high _ s _ i 16 x 8 i 32 x 4 . extend _ high _ u _ i 16 x 8 i 32 x 4 . trunc_sat _ s _ f 32 x 4 i 32 x 4 . trunc_sat _ u _ f 32 x 4 i 32 x 4 . trunc_sat _ s _ zero _ f 64 x 2 i 32 x 4 . trunc_sat _ u _ zero _ f 64 x 2 i 32 x 4 . relaxed_trunc _ s _ f 32 x 4 i 32 x 4 . relaxed_trunc _ u _ f 32 x 4 i 32 x 4 . relaxed_trunc _ s _ zero _ f 64 x 2 i 32 x 4 . relaxed_trunc _ u _ zero _ f 64 x 2 i 64 x 2 . extend _ low _ s _ i 32 x 4 i 64 x 2 . extend _ low _ u _ i 32 x 4 i 64 x 2 . extend _ high _ s _ i 32 x 4 i 64 x 2 . extend _ high _ u _ i 32 x 4 f 32 x 4 . demote _ zero _ f 64 x 2 f 32 x 4 . convert _ s _ i 32 x 4 f 32 x 4 . convert _ u _ i 32 x 4 f 64 x 2 . promote _ low _ f 32 x 4 f 64 x 2 . convert _ low _ s _ i 32 x 4 f 64 x 2 . convert _ low _ u _ i 32 x 4 plaininstr I ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ … ‘ i16x8.extadd_pairwise_i8x16_s ’ ‘ i16x8.extadd_pairwise_i8x16_u ’ ‘ i16x8.extmul_low_i8x16_s ’ ‘ i16x8.extmul_low_i8x16_u ’ ‘ i16x8.extmul_high_i8x16_s ’ ‘ i16x8.extmul_high_i8x16_u ’ ‘ i16x8.relaxed_dot_i8x16_i7x16_s ’ ‘ i32x4.extadd_pairwise_i16x8_s ’ ‘ i32x4.extadd_pairwise_i16x8_u ’ ‘ i32x4.extmul_low_i16x8_s ’ ‘ i32x4.extmul_low_i16x8_u ’ ‘ i32x4.extmul_high_i16x8_s ’ ‘ i32x4.extmul_high_i16x8_u ’ ‘ i32x4.dot_i16x8_s ’ ‘ i32x4.relaxed_dot_i8x16_i7x16_add_s ’ ⇒ i 32 x 4 . relaxed_dot_add _ s _ i 16 x 8 ∣ ‘ i64x2.extmul_low_i32x4_s ’ ‘ i64x2.extmul_low_i32x4_u ’ ‘ i64x2.extmul_high_i32x4_s ’ ‘ i64x2.extmul_high_i32x4_u ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ i 16 x 8 . extadd_pairwise _ s _ i 8 x 16 i 16 x 8 . extadd_pairwise _ u _ i 8 x 16 i 16 x 8 . extmul _ low _ s _ i 8 x 16 i 16 x 8 . extmul _ low _ u _ i 8 x 16 i 16 x 8 . extmul _ high _ s _ i 8 x 16 i 16 x 8 . extmul _ high _ u _ i 8 x 16 i 16 x 8 . relaxed_dot _ s _ i 8 x 16 i 32 x 4 . extadd_pairwise _ s _ i 16 x 8 i 32 x 4 . extadd_pairwise _ u _ i 16 x 8 i 32 x 4 . extmul _ low _ s _ i 16 x 8 i 32 x 4 . extmul _ low _ u _ i 16 x 8 i 32 x 4 . extmul _ high _ s _ i 16 x 8 i 32 x 4 . extmul _ high _ u _ i 16 x 8 i 32 x 4 . dot _ s _ i 16 x 8 i 64 x 2 . extmul _ low _ s _ i 32 x 4 i 64 x 2 . extmul _ low _ u _ i 32 x 4 i 64 x 2 . extmul _ high _ s _ i 32 x 4 i 64 x 2 . extmul _ high _ u _ i 32 x 4 6.5.11. Folded Instructions Instructions can be written as S-expressions by grouping them into folded form. In that notation, an instruction is wrapped in parentheses and optionally includes nested folded instructions to indicate its operands. In the case of block instructions , the folded form omits the ‘ end ’ delimiter. For if instructions, both branches have to be wrapped into nested S-expressions, headed by the keywords ‘ then ’ and ‘ else ’ . The set of all phrases defined by the following abbreviations recursively forms the auxiliary syntactic class foldedinstr . Such a folded instruction can appear anywhere a regular instruction can. foldedinstr I ::= ∣ ∣ ∣ ∣ ‘ ( ’ plaininstr I instrs I ‘ ) ’ instrs I plaininstr I ‘ ( ’ ‘ block ’ label I blocktype I instrs I ′ ‘ ) ’ ‘ block ’ label I blocktype I instrs I ′ ‘ end ’ ‘ ( ’ ‘ loop ’ label I blocktype I instrs I ′ ‘ ) ’ ‘ loop ’ label I blocktype I instrs I ′ ‘ end ’ ‘ ( ’ ‘ if ’ label I blocktype I foldedinstr I ∗ ‘ ( ’ ‘ then ’ in 1 ∗ : instrs I ′ ‘ ) ’ ( ‘ ( ’ ‘ else ’ in 2 ∗ : instrs I ′ ‘ ) ’ ) ? ‘ ) ’ foldedinstr I ∗ ‘ if ’ label I blocktype I in 1 ∗ : instrs I ′ ( ‘ else ’ in 2 ∗ : instrs I ′ ) ? ‘ end ’ ‘ ( ’ ‘ try_table ’ label I blocktype I catch I ∗ instrs I ′ ‘ ) ’ ‘ try_table ’ label I blocktype I catch I ∗ instrs I ′ ‘ end ’ ≡ ≡ ≡ ≡ ≡ Note For example, the instruction sequence ( local.get $x ) ( i32.const 2 ) i32.add ( i32.const 3 ) i32.mul can be folded into ( i32.mul ( i32.add ( local.get $x ) ( i32.const 2 )) ( i32.const 3 )) 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. expr I ::= in ∗ : instrs I ⇒ in ∗ 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 I . idx ids typeidx I tagidx I globalidx I memidx I tableidx I funcidx I dataidx I elemidx I localidx I labelidx I fieldidx I , x ::= ∣ ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= ::= x : u32 id : id idx I . types idx I . tags idx I . globals idx I . mems idx I . tables idx I . funcs idx I . datas idx I . elems idx I . locals idx I . labels idx I . fields [ x ] ⇒ ⇒ x x if ids [ x ] = id 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. type I ::= ( qt , I ′ ) : rectype I ⇒ ( type qt , I ′ ⊕ I ′′ ) if qt = rec st n ∧ I ′′ = { typedefs ( qt . i ) i < n } 6.6.3. Tags A tag definition can bind a symbolic tag identifier . tag I ::= ‘ ( ’ ‘ tag ’ id ? : id ? jt : tagtype I ‘ ) ’ ⇒ ( tag jt , { tags ( id ? )}) 6.6.3.1. Abbreviations Tags can be defined as imports or exports inline: import I export I ::= ∣ ::= … ‘ ( ’ ‘ tag ’ id ? ‘ ( ’ ‘ import ’ name 2 ‘ ) ’ tagtype I ‘ ) ’ ‘ ( ’ ‘ import ’ name 2 ‘ ( ’ ‘ tag ’ id ? tagtype I ‘ ) ’ ‘ ) ’ ‘ ( ’ ‘ tag ’ id ? : id ? ‘ ( ’ ‘ export ’ name ‘ ) ’ … ‘ ) ’ ‘ ( ’ ‘ tag ’ id ′ : id … ‘ ) ’ ‘ ( ’ ‘ export ’ name ‘ ( ’ ‘ tag ’ id ‘ ) ’ ‘ ) ’ if id ? = id ′ ∨ id ? = ϵ ∧ id ′ ∈ / I . 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 I ::= ‘ ( ’ ‘ global ’ id ? : id ? gt : globaltype I e : expr I ‘ ) ’ ⇒ ( global gt e , { globals ( id ? )}) 6.6.4.1. Abbreviations Globals can be defined as imports or exports inline: import I export I ::= ∣ ::= … ‘ ( ’ ‘ global ’ id ? ‘ ( ’ ‘ import ’ name 2 ‘ ) ’ globaltype I ‘ ) ’ ‘ ( ’ ‘ import ’ name 2 ‘ ( ’ ‘ global ’ id ? globaltype I ‘ ) ’ ‘ ) ’ ‘ ( ’ ‘ global ’ id ? : id ? ‘ ( ’ ‘ export ’ name ‘ ) ’ … ‘ ) ’ ‘ ( ’ ‘ global ’ id ′ : id … ‘ ) ’ ‘ ( ’ ‘ export ’ name ‘ ( ’ ‘ global ’ id ‘ ) ’ ‘ ) ’ if id ? = id ′ ∨ id ? = ϵ ∧ id ′ ∈ / I . 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 I ::= ‘ ( ’ ‘ memory ’ id ? : id ? mt : memtype I ‘ ) ’ ⇒ ( memory mt , { mems ( id ? )}) 6.6.5.1. 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 I ::= ‘ ( ’ ‘ memory ’ id ? : id ? at ? : addrtype ? ‘ ( ’ ‘ data ’ b ∗ : datastring ‘ ) ’ ‘ ) ’ ‘ ( ’ ‘ memory ’ id ′ : id at ? : addrtype ? n : u64 n : u64 ‘ ) ’ ‘ ( ’ ‘ data ’ ‘ ( ’ ‘ memory ’ id ′ : id ‘ ) ’ ‘ ( ’ at ′ : addrtype ‘ .const ’ ‘ 0 ’ ‘ ) ’ datastring ‘ ) ’ if id ? = id ′ ∨ id ? = ϵ ∧ id ′ ∈ / I . mems ∧ at ? = at ′ ∨ at ? = ϵ ∧ at ′ = i 32 ∧ n = ceil ( ∣ b ∗ ∣ /64 ⋅ Ki ) ≡ Memories can be defined as imports or exports inline: import I export I ::= ∣ ::= … ‘ ( ’ ‘ memory ’ id ? ‘ ( ’ ‘ import ’ name 2 ‘ ) ’ memtype I ‘ ) ’ ‘ ( ’ ‘ import ’ name 2 ‘ ( ’ ‘ memory ’ id ? memtype I ‘ ) ’ ‘ ) ’ ‘ ( ’ ‘ memory ’ id ? : id ? ‘ ( ’ ‘ export ’ name ‘ ) ’ … ‘ ) ’ ‘ ( ’ ‘ memory ’ id ′ : id … ‘ ) ’ ‘ ( ’ ‘ export ’ name ‘ ( ’ ‘ memory ’ id ‘ ) ’ ‘ ) ’ if id ? = id ′ ∨ id ? = ϵ ∧ id ′ ∈ / I . mems ≡ ≡ 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 I ::= ‘ ( ’ ‘ table ’ id ? : id ? tt : tabletype I e : expr I ‘ ) ’ ⇒ ( table tt e , { tables ( id ? )}) 6.6.6.1. Abbreviations A table’s initialization expression can be omitted, in which case it defaults to ref . null : table I ::= ∣ … ‘ ( ’ ‘ table ’ id ? tt : tabletype I ‘ ) ’ ‘ ( ’ ‘ table ’ id ? tt : tabletype I ‘ ( ’ ‘ ref.null ’ ht : heaptype I ‘ ) ’ ‘ ) ’ 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 I ::= ‘ ( ’ ‘ table ’ id ? : id ? at ? : addrtype ? reftype I ‘ ( ’ ‘ elem ’ ( rt , e ∗ ) : elemlist I ‘ ) ’ ‘ ) ’ ‘ ( ’ ‘ table ’ id ′ : id at ? : addrtype ? n : u64 n : u64 reftype I ‘ ) ’ ‘ ( ’ ‘ elem ’ ‘ ( ’ ‘ table ’ id ′ : id ‘ ) ’ ‘ ( ’ at ′ : addrtype ‘ .const ’ ‘ 0 ’ ‘ ) ’ elemlist I ‘ ) ’ if id ? = id ′ ∨ id ? = ϵ ∧ id ′ ∈ / I . tables ∧ at ? = at ′ ∨ at ? = ϵ ∧ at ′ = i 32 ∧ n = ∣ e ∗ ∣ ≡ Tables can be defined as imports or exports inline: import I export I ::= ∣ ::= … ‘ ( ’ ‘ table ’ id ? ‘ ( ’ ‘ import ’ name 2 ‘ ) ’ tabletype I ‘ ) ’ ‘ ( ’ ‘ import ’ name 2 ‘ ( ’ ‘ table ’ id ? tabletype I ‘ ) ’ ‘ ) ’ ‘ ( ’ ‘ table ’ id ? : id ? ‘ ( ’ ‘ export ’ name ‘ ) ’ … ‘ ) ’ ‘ ( ’ ‘ table ’ id ′ : id … ‘ ) ’ ‘ ( ’ ‘ export ’ name ‘ ( ’ ‘ table ’ id ‘ ) ’ ‘ ) ’ if id ? = id ′ ∨ id ? = ϵ ∧ id ′ ∈ / I . 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 I ::= ‘ ( ’ ‘ func ’ id ? : id ? ( x , I 1 ) : typeuse I (( loc ∗ , I 2 ) : local I ) ∗ e : expr I ′ ‘ ) ’ ( func x ( ⨁ loc ∗ ∗ ) e , { funcs ( id ? )}) if I ′ = I ⊕ I 1 ⊕ ⨁ I 2 ∗ ∧ ⊢ I ′ : ok ⇒ local I ::= ‘ ( ’ ‘ local ’ id ? : id ? t : valtype I ‘ ) ’ ( local t , { locals ( id ? )}) ⇒ Note The well-formedness condition on I ′ ensures that parameters and locals do not contain duplicate identifiers. 6.6.7.1. Abbreviations Multiple anonymous locals may be combined into a single declaration: local I ::= … ∣ ‘ ( ’ ‘ local ’ valtype I ∗ ‘ ) ’ ≡ ( ‘ ( ’ ‘ local ’ valtype I ‘ ) ’ ) ∗ Functions can be defined as imports or exports inline: import I export I ::= ∣ ::= … ‘ ( ’ ‘ func ’ id ? ‘ ( ’ ‘ import ’ name 2 ‘ ) ’ typeuse I ‘ ) ’ ‘ ( ’ ‘ import ’ name 2 ‘ ( ’ ‘ func ’ id ? typeuse I ‘ ) ’ ‘ ) ’ ‘ ( ’ ‘ func ’ id ? : id ? ‘ ( ’ ‘ export ’ name ‘ ) ’ … ‘ ) ’ ‘ ( ’ ‘ func ’ id ′ : id … ‘ ) ’ ‘ ( ’ ‘ export ’ name ‘ ( ’ ‘ func ’ id ‘ ) ’ ‘ ) ’ if id ? = id ′ ∨ id ? = ϵ ∧ id ′ ∈ / I . 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. 6.6.8. Data Segments Data segments allow for an optional memory index to identify the memory to initialize. The data is written as a string , which may be split up into a possibly empty sequence of individual string literals. data I datastring memuse I offsetexpr I ::= ∣ ::= ::= ::= ‘ ( ’ ‘ data ’ id ? : id ? b ∗ : datastring ‘ ) ’ ( data b ∗ passive , { datas ( id ? )}) ‘ ( ’ ‘ data ’ id ? : id ? x : memuse I e : offsetexpr I b ∗ : datastring ‘ ) ’ ( data b ∗ ( active x e ) , { datas ( id ? )}) b ∗ ∗ : string ∗ ‘ ( ’ ‘ memory ’ x : memidx I ‘ ) ’ ‘ ( ’ ‘ offset ’ e : expr I ‘ ) ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⨁ b ∗ ∗ x e 6.6.8.1. Abbreviations As an abbreviation, a single folded instruction may occur in place of the offset of an active segment: offsetexpr I ::= … ∣ foldedinstr I ≡ ‘ ( ’ ‘ offset ’ foldedinstr I ‘ ) ’ Also, a memory use can be omitted, defaulting to 0 . memuse I ::= … ∣ ϵ ≡ ‘ ( ’ ‘ memory ’ ‘ 0 ’ ‘ ) ’ As another abbreviation, data segments may also be specified inline with memory definitions; see the respective section. 6.6.9. Element Segments Element segments allow for an optional table index to identify the table to initialize. elem I elemlist I elemexpr I tableuse I ::= ∣ ∣ ::= ::= ::= ‘ ( ’ ‘ elem ’ id ? : id ? ( rt , e ∗ ) : elemlist I ‘ ) ’ ( elem rt e ∗ passive , { elems ( id ? )}) ‘ ( ’ ‘ elem ’ id ? : id ? x : tableuse I e ′ : offsetexpr I ( rt , e ∗ ) : elemlist I ‘ ) ’ ( elem rt e ∗ ( active x e ′ ) , { elems ( id ? )}) ‘ ( ’ ‘ elem ’ id ? : id ? ‘ declare ’ ( rt , e ∗ ) : elemlist I ‘ ) ’ ( elem rt e ∗ declare , { elems ( id ? )}) rt : reftype I e ∗ : list ( elemexpr I ) ‘ ( ’ ‘ item ’ e : expr I ‘ ) ’ ‘ ( ’ ‘ table ’ x : tableidx I ‘ ) ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ( rt , e ∗ ) e x 6.6.9.1. 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: elemexpr I ::= … ∣ foldedinstr I ≡ ‘ ( ’ ‘ item ’ foldedinstr I ‘ ) ’ Also, the element list may be written as just a sequence of function indices : elemlist I ::= … ∣ ‘ func ’ x ∗ : funcidx I ∗ ≡ ‘ ( ’ ‘ ref ’ ‘ func ’ ‘ ) ’ ( ‘ ( ’ ‘ ref.func ’ funcidx I ‘ ) ’ ) ∗ A table use can be omitted, defaulting to 0 . tableuse I ::= … ∣ ϵ ≡ ‘ ( ’ ‘ table ’ ‘ 0 ’ ‘ ) ’ Furthermore, for backwards compatibility with earlier versions of WebAssembly, if the table use is omitted, the ‘ func ’ keyword can be omitted as well. elem I ::= ∣ … ‘ ( ’ ‘ elem ’ offsetexpr I list ( funcidx I ) ‘ ) ’ ‘ ( ’ ‘ elem ’ offsetexpr I ‘ func ’ list ( funcidx I ) ‘ ) ’ ≡ As yet another abbreviation, element segments may also be specified inline with table definitions; see the respective section. 6.6.10. Start Function A start function is defined in terms of its index. start I ::= ‘ ( ’ ‘ start ’ x : funcidx I ‘ ) ’ ⇒ ( start x , { }) 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 I ::= ‘ ( ’ ‘ import ’ nm 1 : name nm 2 : name ( xt , I ′ ) : externtype I ‘ ) ’ ⇒ ( import nm 1 nm 2 xt , I ′ ) 6.6.11.1. 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. export I externidx I ::= ::= ∣ ∣ ∣ ∣ ‘ ( ’ ‘ export ’ nm : name xx : externidx I ‘ ) ’ ‘ ( ’ ‘ tag ’ x : tagidx I ‘ ) ’ ‘ ( ’ ‘ global ’ x : globalidx I ‘ ) ’ ‘ ( ’ ‘ memory ’ x : memidx I ‘ ) ’ ‘ ( ’ ‘ table ’ x : tableidx I ‘ ) ’ ‘ ( ’ ‘ func ’ x : funcidx I ‘ ) ’ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ( export nm xx , { }) tag x global x memory x table x func x 6.6.12.1. 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 . decl I ::= ∣ type I ∣ import I ∣ tag I ∣ global I ∣ mem I ∣ table I func I ∣ data I ∣ elem I ∣ start I ∣ export I module ::= ‘ ( ’ ‘ module ’ id ? ( decl , I ) ∗ : decl I ′ ∗ ‘ ) ’ module type ∗ import ∗ tag ∗ global ∗ mem ∗ table ∗ func ∗ data ∗ elem ∗ start ? export ∗ if I ′ = ⨁ I ∗ ∧ ⊢ I ′ : ok ∧ type ∗ = types ( decl ∗ ) ∧ import ∗ = imports ( decl ∗ ) ∧ tag ∗ = tags ( decl ∗ ) ∧ global ∗ = globals ( decl ∗ ) ∧ mem ∗ = mems ( decl ∗ ) ∧ table ∗ = tables ( decl ∗ ) ∧ func ∗ = funcs ( decl ∗ ) ∧ data ∗ = datas ( decl ∗ ) ∧ elem ∗ = elems ( decl ∗ ) ∧ start ? = starts ( decl ∗ ) ∧ export ∗ = exports ( decl ∗ ) ∧ ordered ( decl ∗ ) ⇒ where types ( decl ∗ ) , imports ( decl ∗ ) , tags ( decl ∗ ) , etc., extract the sequence of types , imports , tags , etc., contained in decl ∗ , respectively. The auxiliary predicate ordered checks that no imports occur after the first definition of a tag , global , memory , table , or function in a sequence of declarations: ordered ( 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 ∗ ) = ϵ = = true if imports ( decl ∗ ) = ϵ 6.6.13.1. Abbreviations In a source file, the toplevel ‘ ( ’ ‘ module ’ … ‘ ) ’ surrounding the module body may be omitted. module ::= … ∣ decl I ∗ ≡ ‘ ( ’ ‘ module ’ decl I ∗ ‘ ) ’ A Appendix A.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 . 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 i : u 64 is used to specify a symbolic name in addition to the respective value range. Booleans Interface operation that are predicates return Boolean values: bool ::= false ∣ true Exceptions and Errors Invoking an exported function may throw or propagate exceptions, expressed by an auxiliary syntactic class: exception ::= exception exnaddr The exception address exnaddr identifies the exception thrown. Failure of an interface operation is also indicated by an auxiliary syntactic class: error ::= error 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 classifications and diagnostic messages. Pre- and Post-Conditions Some operations state pre-conditions about their arguments or post-conditions about their results. It is the embedder’s responsibility to meet the pre-conditions. If it does, the post conditions are guaranteed by the semantics. In addition to pre- and post-conditions explicitly stated with each operation, the specification adopts the following conventions for runtime objects ( store , moduleinst , addresses ): Every runtime object passed as a parameter must be valid per an implicit pre-condition. Every runtime object returned as a result is valid per an implicit post-condition. 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. Store store_init ( ) : store Return the empty store . store_init ( ) = { } Modules module_decode ( byte ∗ ) : module ∣ error If there exists a derivation for the byte sequence byte ∗ as a module according to the binary grammar for modules , yielding a module m , then return m . Else, return error . module_decode ( b ∗ ) module_decode ( b ∗ ) = = m error ( if module ⟹ ∗ m : b ∗ ) ( otherwise ) module_parse ( char ∗ ) : module ∣ error If there exists a derivation for the source char ∗ as a module according to the text grammar for modules , yielding a module m , then return m . Else, return error . module_parse ( c ∗ ) module_parse ( c ∗ ) = = m error ( if module ⟹ ∗ m : c ∗ ) ( otherwise ) module_validate ( module ) : error ? If module is valid , then return nothing. Else, return error . module_validate ( m ) module_validate ( m ) = = ϵ error ( if ⊢ m : externtype ∗ → externtype ′ ∗ ) ( otherwise ) module_instantiate ( store , module , externaddr ∗ ) : ( store , moduleinst ∣ exception ∣ error ) Try instantiating module in store with external addresses externaddr ∗ as imports: If it succeeds with a module instance moduleinst , then let result be moduleinst . Else, let result be error . Return the new store paired with result . module_instantiate ( S , m , ev ∗ ) module_instantiate ( S , m , ev ∗ ) = = ( S ′ , F . module ) ( S ′ , error ) ( if instantiate ( S , m , ev ∗ ) ↪ ∗ S ′ ; F ; ϵ ) ( otherwise , if instantiate ( S , m , ev ∗ ) ↪ ∗ S ′ ; F ; result ) Note The store may be modified even in case of an error. module_imports ( module ) : ( name , name , externtype ) ∗ Pre-condition: module is valid with the external import types externtype ∗ and external export types externtype ′ ∗ . Let import ∗ be the imports of module . Assert: the length of import ∗ equals the length of externtype ∗ . For each import i in import ∗ and corresponding externtype i in externtype ∗ , do: Let import nm i1 nm i2 xt i be the deconstruction of import i . Let result i be the triple ( nm i1 , nm i2 , externtype i ) . Return the concatenation of all result i , in index order. Post-condition: each externtype i is valid under the empty context . module_imports ( m ) = ( nm 1 , nm 2 , externtype ) ∗ ( if ( import nm 1 nm 2 xt ∗ ) ∗ ∈ m ∧ ⊢ m : externtype ∗ → externtype ′ ∗ ) module_exports ( module ) : ( name , externtype ) ∗ Pre-condition: module is valid with the external import types externtype ∗ and external export types externtype ′ ∗ . Let export ∗ be the exports of module . Assert: the length of export ∗ equals the length of externtype ′ ∗ . For each export i in export ∗ and corresponding externtype i ′ in externtype ′ ∗ , do: Let export nm i externidx i be the deconstruction of export i . Let result i be the pair ( nm i , externtype i ′ ) . Return the concatenation of all result i , in index order. Post-condition: each externtype i ′ is valid under the empty context . module_exports ( m ) ma t hi t nm , externtype ′ ) ∗ = ( ( if ( export nm xt ∗ ) ∗ ∈ m ∧ ⊢ m : externtype ∗ → externtype ′ ∗ ) Module Instances instance_export ( moduleinst , name ) : externaddr ∣ error Assert: due to validity of the module instance moduleinst , all its export names are different. If there exists an exportinst i in moduleinst . exports such that name exportinst i . name equals name , then: Return the external address exportinst i . addr . Else, return error . instance_export ( m , name ) instance_export ( m , name ) = = m . exports [ i ] . addr error ( if m . exports [ i ] . name = name ) ( otherwise ) Functions func_alloc ( store , deftype , hostfunc ) : ( store , funcaddr ) Pre-condition: the defined type deftype is valid under the empty context and expands to a function type . 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 . Return the new store paired with funcaddr . func_alloc ( S , dt , code ) = ( S ′ , a ) ( if allocfunc ( S , dt , code , { }) = S ′ , 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_type ( store , funcaddr ) : deftype Let deftype be the definedn type S. funcs [ a ] . type . Return deftype . Post-condition: the returned defined type is valid and expands to a function type . func_type ( S , a ) = S . funcs [ a ] . type func_invoke ( store , funcaddr , val ∗ ) : ( store , val ∗ ∣ exception ∣ error ) Try invoking the function funcaddr in store with values val ∗ as arguments: If it succeeds with values val ′ ∗ as results, then let result be val ′ ∗ . Else if the outcome is an exception with a thrown exception ref . exn exnaddr as the result, then let result be exception exnaddr Else it has trapped, hence let result be error . Return the new store paired with result . func_invoke ( S , a , v ∗ ) func_invoke ( S , a , v ∗ ) func_invoke ( S , a , v ∗ ) = = = ( S ′ , v ′ ∗ ) ( S ′ , exception a ′ ) ( S ′ , error ) ( if invoke ( S , a , v ∗ ) ↪ ∗ S ′ ; F ; v ′ ∗ ) ( if invoke ( S , a , v ∗ ) ↪ ∗ S ′ ; F ; ( ref . exn a ′ ) throw_ref ( if invoke ( S , a , v ∗ ) ↪ ∗ S ′ ; F ; trap ) Note The store may be modified even in case of an error. Tables table_alloc ( store , tabletype , ref ) : ( store , tableaddr ) Pre-condition: the tabletype is valid under the empty context . Let tableaddr be the result of allocating a table in store with table type tabletype and initialization value ref . Return the new store paired with tableaddr . table_alloc ( S , tt , r ) = ( S ′ , a ) ( if alloctable ( S , tt , r ) = S ′ , a ) table_type ( store , tableaddr ) : tabletype Return S. tables [ a ] . type . Post-condition: the returned table type is valid under the empty context . table_type ( S , a ) = S . tables [ a ] . type table_read ( store , tableaddr , i : u 64 ) : ref ∣ error Let ti be the table instance store . tables [ tableaddr ] . If i is larger than or equal to the length of ti . refs , then return error . Else, return the reference value ti . refs [ i ] . table_read ( S , a , i ) table_read ( S , a , i ) = = r error ( if S . tables [ a ] . refs [ i ] = r ) ( otherwise ) table_write ( store , tableaddr , i : u 64 , ref ) : store ∣ error Let ti be the table instance store . tables [ tableaddr ] . If i is larger than or equal to the length of ti . refs , then return error . Replace ti . refs [ i ] with the reference value ref . Return the updated store. table_write ( S , a , i , r ) table_write ( S , a , i , r ) = = S ′ error ( if S ′ = S with tables [ a ] . refs [ i ] = r ) ( otherwise ) table_size ( store , tableaddr ) : u 64 Return the length of store . tables [ tableaddr ] . refs . table_size ( S , a ) = n ( if ∣ S . tables [ a ] . refs ∣ = n ) table_grow ( store , tableaddr , n : u 64 , ref ) : store ∣ error Try growing the table instance store . tables [ tableaddr ] by n elements with initialization value ref : If it succeeds, return the updated store. Else, return error . table_grow ( S , a , n , r ) table_grow ( S , a , n , r ) = = S ′ error ( if S ′ = S with tables [ a ] = growtable ( S . tables [ a ] , n , r )) ( otherwise ) Memories mem_alloc ( store , memtype ) : ( store , memaddr ) Pre-condition: the memtype is valid under the empty context . Let memaddr be the result of allocating a memory in store with memory type memtype . Return the new store paired with memaddr . mem_alloc ( S , mt ) = ( S ′ , a ) ( if allocmem ( S , mt ) = S ′ , a ) mem_type ( store , memaddr ) : memtype Return S. mems [ a ] . type . Post-condition: the returned memory type is valid under the empty context . mem_type ( S , a ) = S . mems [ a ] . type mem_read ( store , memaddr , i : u 64 ) : byte ∣ error Let mi be the memory instance store . mems [ memaddr ] . If i is larger than or equal to the length of mi . bytes , then return error . Else, return the byte mi . bytes [ i ] . mem_read ( S , a , i ) mem_read ( S , a , i ) = = b error ( if S . mems [ a ] . bytes [ i ] = b ) ( otherwise ) mem_write ( store , memaddr , i : u 64 , byte ) : store ∣ error Let mi be the memory instance store . mems [ memaddr ] . If i is larger than or equal to the length of mi . bytes , then return error . Replace mi . bytes [ i ] with byte . Return the updated store. mem_write ( S , a , i , b ) mem_write ( S , a , i , b ) = = S ′ error ( if S ′ = S with mems [ a ] . bytes [ i ] = b ) ( otherwise ) mem_size ( store , memaddr ) : u 64 Return the length of store . mems [ memaddr ] . bytes divided by the page size . mem_size ( S , a ) = n ( if ∣ S . mems [ a ] . bytes ∣ = n ⋅ 64 Ki ) mem_grow ( store , memaddr , n : u 64 ) : store ∣ error Try growing the memory instance store . mems [ memaddr ] by n pages : If it succeeds, return the updated store. Else, return error . mem_grow ( S , a , n ) mem_grow ( S , a , n ) = = S ′ error ( if S ′ = S with mems [ a ] = growmem ( S . mems [ a ] , n )) ( otherwise ) Tags tag_alloc ( store , tagtype ) : ( store , tagaddr ) Pre-condition: tagtype is valid . Let tagaddr be the result of allocating a tag in store with tag type tagtype . Return the new store paired with tagaddr . tag_alloc ( S , tt ) = ( S ′ , a ) ( if alloctag ( S , tt ) = S ′ , a ) tag_type ( store , tagaddr ) : tagtype Return S. tags [ a ] . type . Post-condition: the returned tag type is valid . tag_type ( S , a ) = S . tags [ a ] . type Exceptions exn_alloc ( store , tagaddr , val ∗ ) : ( store , exnaddr ) Pre-condition: tagaddr is an allocated tag address . Let exnaddr be the result of allocating an exception instance in store with tag address tagaddr and initialization values val ∗ . Return the new store paired with exnaddr . exn_alloc ( S , tagaddr , val ∗ ) = ( S ⊕ { exns exninst } , ∣ S . exns ∣ ) ( if exninst = { tag tagaddr , fields val ∗ } exn_tag ( store , exnaddr ) : tagaddr Let exninst be the exception instance store . exns [ exnaddr ] . Return the tag address exninst . tag . exn_tag ( S , a ) = exninst . tag ( if exninst = S . exns [ a ]) exn_read ( store , exnaddr ) : val ∗ Let exninst be the exception instance store . exns [ exnaddr ] . Return the values exninst . fields . exn_read ( S , a ) = exninst . fields ( if exninst = S . exns [ a ]) Globals global_alloc ( store , globaltype , val ) : ( store , globaladdr ) Pre-condition: the globaltype is valid under the empty context . Let globaladdr be the result of allocating a global in store with global type globaltype and initialization value val . Return the new store paired with globaladdr . global_alloc ( S , gt , v ) = ( S ′ , a ) ( if allocglobal ( S , gt , v ) = S ′ , a ) global_type ( store , globaladdr ) : globaltype Return S. globals [ a ] . type . Post-condition: the returned global type is valid under the empty context . global_type ( S , a ) = S . globals [ a ] . type global_read ( store , globaladdr ) : val Let gi be the global instance store . globals [ globaladdr ] . Return the value gi . value . global_read ( S , a ) = v ( if S . globals [ a ] . value = v ) global_write ( store , globaladdr , val ) : store ∣ error Let gi be the global instance store . globals [ globaladdr ] . Let mut t be the structure of the global type gi . type . If mut is empty, then return error . Replace gi . value with the value val . Return the updated store. global_write ( S , a , v ) global_write ( S , a , v ) = = S ′ error ( if S . globals [ a ] . type = mut t ∧ S ′ = S with globals [ a ] . value = v ) ( otherwise ) Values ref_type ( store , ref ) : reftype Pre-condition: the reference ref is valid under store S . Return the reference type t with which ref is valid. Post-condition: the returned reference type is valid under the empty context . ref_type ( S , r ) = t ( if S ⊢ r : t ) 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 If default valtype is not defined, then return error . Else, return the value default valtype . val_default ( t ) val_default ( t ) = = v error ( if default t = v ) ( if default t = ϵ ) Matching match_valtype ( valtype 1 , valtype 2 ) : bool Pre-condition: the value types valtype 1 and valtype 2 are valid under the empty context . If valtype 1 matches valtype 2 , then return true . Else, return false . match_reftype ( t 1 , t 2 ) match_reftype ( t 1 , t 2 ) = = true false ( if ⊢ t 1 ≤ t 2 ) ( otherwise ) match_externtype ( externtype 1 , externtype 2 ) : bool Pre-condition: the extern types externtype 1 and externtype 2 are valid under the empty context . If externtype 1 matches externtype 2 , then return true . Else, return false . match_externtype ( et 1 , et 2 ) match_externtype ( et 1 , et 2 ) = = true false ( if ⊢ et 1 ≤ et 2 ) ( otherwise ) A.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. 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: Any production in the binary or textual syntax that produces abstract syntax with a marked construct is omitted by extension. 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 annotated 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: [ ! INF ] S ; F ; ( i 32 . const n ) memory . grow x S ; F ; ( i 32 . const n ) memory . grow x ↪ ↪ S ′ ; F ; ( i 32 . const sz ) ( if ∧ ∧ D O U B L ES L A S H [ 1 e x ] F . module . mems [ x ] = a sz = ∣ S . mems [ a ] . datas ∣/64 Ki S ′ = S with mems [ a ] = growmem ( S . mems [ a ] , n )) S ; F ; ( i 32 . const signed 32 − 1 ( − 1 )) 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 ambiguous 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 execution 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 conditioning on the “current” profile is not intended and should be avoided. Defined Profiles 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. Full Profile ( FUL ) The full profile contains the complete language and all possible behaviours. It imposes no restrictions, i.e., all rules and definitions are active. All other profiles define sub-languages of this profile. Deterministic Profile ( DET ) The deterministic profile excludes all rules marked [ ! DET ] . It defines a sub-language that does not exhibit any incidental non-deterministic behaviour: All NaN values generated by floating-point instructions are canonical and positive. All relaxed vector instructions have a fixed behaviour that does not depend on the implementation. Even under this profile, the memory . grow and table . grow instructions technically remain non-deterministic , in order to be able to indicate resource exhaustion. 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. A.3 Implementation Limitations Implementations typically impose additional restrictions on a number of aspects of a WebAssembly module or execution. These may stem from: physical resource limits, constraints imposed by the embedder or its environment, limitations of selected implementation strategies. This section lists allowed limitations. Where restrictions take the form of numeric limits, no minimum requirements are given, nor are the limits assumed to be concrete, fixed numbers. However, it is expected that all implementations have “reasonably” large limits to enable common applications. Note A conforming implementation is not allowed to leave out individual features . However, designated subsets of WebAssembly may be specified in the future. Syntactic Limits Structure An implementation may impose restrictions on the following dimensions of a module: the number of types in a module the number of functions in a module , including imports the number of tables in a module , including imports the number of memories in a module , including imports the number of globals in a module , including imports the number of tags in a module , including imports the number of element segments in a module the number of data segments in a module the number of imports to a module the number of exports from a module the number of sub types in a recursive type the subtyping depth of a sub type the number of fields in a structure type the number of parameters in a function type the number of results in a function type the number of parameters in a block type the number of results in a block type the number of locals in a function the number of instructions in a function body the number of instructions in a structured control instruction the number of structured control instructions in a function the nesting depth of structured control instructions the number of label indices in a br_table instruction the number of instructions in a constant expression the number of instructions between a branch instruction and its target the nesting depth of blocks between a branch instruction and its target the length of the array in a array . new_fixed instruction the length of an element segment the length of a data segment the length of a name the range of characters in a name If the limits of an implementation are exceeded for a given module, then the implementation may reject the validation , compilation, or instantiation of that module with an implementation-specific error. Note The last item allows embedders that operate in limited environments without support for [UNICODE] to limit the names of imports and exports to common subsets like ASCII . 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 the size of an individual constant expression ’s instruction sequence the number of sections 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 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. 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 the number of handlers on the stack the number of labels dropped from the stack by a branch instruction the number of values dropped from the stack by a branch instruction If the runtime limits of an implementation are exceeded during execution of a computation, then it may terminate that computation and report an implementation-specific error to the invoking code. Some of the above limits may already be verified during validation or 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. Type Soundness The type system of WebAssembly is sound , implying both type safety and memory safety with respect to the WebAssembly semantics. For example: All types declared and derived during validation are respected at run time; e.g., every local or global variable will only contain type-correct values, every instruction will only be applied to operands of the expected type, and every function invocation always evaluates to a result of the right type (if it does not diverge, throw an exception, or trap ). No memory location will be read or written except those explicitly defined by the program, i.e., as a local , a global , an element in a table , or a location within a linear memory . There is no undefined behavior, i.e., the execution rules cover all possible cases that can occur in a valid program, and the rules are mutually consistent. Soundness also is instrumental in ensuring additional properties, most notably, encapsulation of function and module scopes: no locals can be accessed outside their own function and no module components can be accessed outside their own module unless they are explicitly exported or imported . The typing rules defining WebAssembly validation only cover the static components of a WebAssembly program. In order to state and prove soundness precisely, the typing rules must be extended to the dynamic components of the abstract runtime , that is, the store , configurations , and administrative instructions . [ 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 group of recursive types : context ::= { … , recs subtype ∗ } Types Well-formedness for extended type forms is defined as follows. The type use ( rec . i ) is valid if: The recursive type C . recs [ i ] exists. C ⊢ rec . i : ok C . recs [ i ] = st The heap type bot is always valid . C ⊢ bot : ok The value type bot is always valid . C ⊢ bot : ok The recursive type ( rec subtype ∗ ) is valid for i 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 i . The recursive type ( rec subtype ′ ∗ ) is valid for i + 1 . C ⊢ rec ϵ : ok ( i ) C ⊢ rec ( subtype 1 subtype ∗ ) : ok ( i ) C ⊢ subtype 1 : ok ( i ) C ⊢ rec subtype ∗ : ok ( i + 1 ) The sub type ( sub final ? typeuse ∗ comptype ) is valid for i if: The length of typeuse ∗ is less than or equal to 1 . For all typeuse in typeuse ∗ : The type use typeuse is valid . typeuse ≺ i is true. The sub type unroll C ( typeuse ) is of the form ( sub typeuse ′ ∗ comptype ′ ) . comptype ′ ∗ is the concatenation of all such comptype ′ . The composite type comptype is valid . For all comptype ′ in comptype ′ ∗ : The composite type comptype matches the composite type comptype ′ . C ⊢ sub final ? typeuse ∗ comptype : ok ( i ) ∣ typeuse ∗ ∣ ≤ 1 ( C ⊢ typeuse : ok ) ∗ ( typeuse ≺ i ) ∗ ( unroll C ( typeuse ) = sub typeuse ′ ∗ comptype ′ ) ∗ C ⊢ comptype : ok ( C ⊢ comptype ≤ comptype ′ ) ∗ where: unroll C ( deftype ) unroll C ( typeidx ) unroll C ( rec . i ) rec . j ≺ i typeuse ≺ i = = = = = unroll ( deftype ) unroll ( C . types [ typeidx ]) C . recs [ i ] j < i true otherwise Note The new rules for recursive types and sub types complement the ones previously given , which only allowed regular type indices as supertypes. They define validity of rolled-up recursive types, like they occur in defined types , in turn needed to define validity of contexts . None of these rules are needed in the implementation of a validator. The defined type ( rectype . i ) is valid if: Let C ′ be the same context as C , but with the sub type sequence subtype n prepended to the field recs . Under the context C ′ , the recursive type rectype is valid for 0 . The recursive type rectype is of the form ( rec subtype n ) . i is less than n . C ⊢ rectype . i : ok C , recs subtype n ⊢ rectype : ok ( 0 ) rectype = rec subtype n i < n Subtyping Inside a rolled-up recursive type , a recursive type index can match another heap type . C ⊢ rec . i ≤ struct C . recs [ i ] = sub final ? ( struct fieldtype ∗ ) C ⊢ rec . i ≤ array C . recs [ i ] = sub final ? ( array fieldtype ) C ⊢ rec . i ≤ func C . recs [ i ] = sub final ? ( func t 1 ∗ → t 2 ∗ ) C ⊢ rec . i ≤ typeuse ∗ [ j ] C . recs [ i ] = sub final ? typeuse ∗ ct Note These rules complement the previously given rules for matching heap types . They are only invoked when checking validity of rolled-up recursive types . Results Results can be classified by result types as follows. Results val ∗ For each value val i in val ∗ : The value val i is valid with some value type t i . Let t ∗ be the concatenation of all t i . Then the result is valid with result type [ t ∗ ] . S ⊢ val ∗ : [ t ∗ ] ( S ⊢ val : t ) ∗ Results ( ref . exn a ) throw_ref The value ref . exn a must be valid . Then the result is valid with result type [ t ∗ ] , for any valid closed result types . S ⊢ ( ref . exn a ) throw_ref : [ t ∗ ] S ⊢ ref . exn a : ref exn ⊢ [ t ∗ ] : ok Results trap The result is valid with result type [ t ∗ ] , for any valid closed result types . S ⊢ trap : [ t ∗ ] ⊢ [ t ∗ ] : ok Store Validity The following typing rules specify when a runtime store S is valid . A valid store must consist of tag , global , memory , table , function , data , element , structure , array , exception , and module instances that are themselves valid, relative to S . To that end, each kind of instance is classified by a respective tag , global , memory , table , function , or element , type, or just ok in the case of data structures , arrays , or exceptions . Module instances are classified by module contexts , which are regular contexts repurposed as module types describing the index spaces defined by a module. Store S Each tag instance taginst i in S. tags must be valid with some tag type tagtype i . Each global instance globalinst i in S. globals must be valid with some global type globaltype i . Each memory instance meminst i in S. mems must be valid with some memory type memtype i . Each table instance tableinst i in S. tables must be valid with some table type tabletype i . Each function instance funcinst i in S. funcs must be valid with some defined type deftype i . Each data instance datainst i in S. datas must be valid . Each element instance eleminst i in S. elems must be valid with some reference type reftype i . Each structure instance structinst i in S. structs must be valid . Each array instance arrayinst i in S. arrays must be valid . Each exception instance exninst i in S. 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 indirections 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. ⊢ S : ok ( S ⊢ taginst : tagtype ) ∗ ( S ⊢ globalinst : globaltype ) ∗ ( S ⊢ meminst : memtype ) ∗ ( S ⊢ tableinst : tabletype ) ∗ ( S ⊢ funcinst : deftype ) ∗ ( S ⊢ datainst : ok ) ∗ ( S ⊢ eleminst : reftype ) ∗ ( S ⊢ structinst : ok ) ∗ ( S ⊢ arrayinst : ok ) ∗ ( S ⊢ exninst : ok ) ∗ S = { tags taginst ∗ , globals globalinst ∗ , mems meminst ∗ , tables tableinst ∗ , funcs funcinst ∗ , datas datainst ∗ , elems eleminst ∗ , structs structinst ∗ , arrays arrayinst ∗ , exns exninst ∗ } ( S . structs [ a s ] = structinst ) ∗ (( ref . struct a s ) ≫ S + ( ref . struct a s ) ) ∗ ( S . arrays [ a a ] = arrayinst ) ∗ (( ref . array a a ) ≫ S + ( ref . array a a ) ) ∗ ( S . exns [ a e ] = exninst ) ∗ (( ref . exn a e ) ≫ S + ( ref . exn a e ) ) ∗ where val 1 ≫ S + val 2 denotes the transitive closure of the following immutable reachability relation on values : ( ref . struct a ) ( ref . array a ) ( ref . exn a ) ( ref . extern ref ) ≫ S ≫ S ≫ S ≫ S S . structs [ a ] . fields [ i ] S . arrays [ a ] . fields [ i ] S . exns [ a ] . fields [ i ] ref if expand ( S . structs [ a ] . type ) = struct ft 1 i st ft 2 ∗ if expand ( S . arrays [ a ] . type ) = array st 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. Tag Instances { type tagtype } The tag type tagtype must be valid under the empty context . Then the tag instance is valid with tag type tagtype . S ⊢ { type tagtype } : tagtype ⊢ tagtype : ok Global Instances { type mut t , value val } The global type mut t must be valid under the empty context . The value val must be valid with some value type t ′ . The value type t ′ must match the value type t . Then the global instance is valid with global type mut t . S ⊢ { type mut t , value val } : mut t ⊢ mut t : ok S ⊢ val : t ′ ⊢ t ′ ≤ t Memory Instances { type ( addrtype limits ) , bytes b ∗ } The memory type addrtype limits must be valid under the empty context . Let limits be [ n .. m ] . The length of b ∗ must equal m multiplied by the page size 64 Ki . Then the memory instance is valid with memory type addrtype limits . S ⊢ { type ( addrtype [ n .. m ]) , bytes b ∗ } : addrtype [ n .. m ] ⊢ addrtype [ n .. m ] : ok ∣ b ∗ ∣ = n ⋅ 64 Ki Table Instances { type ( addrtype limits t ) , refs ref ∗ } The table type addrtype limits t must be valid under the empty context . Let limits be [ n .. m ] . The length of ref ∗ must equal n . For each reference ref i in the table’s elements ref n : The reference ref i must be valid with some reference type t i ′ . The reference type t i ′ must match the reference type t . Then the table instance is valid with table type addrtype limits t . S ⊢ { type ( addrtype [ n .. m ] t ) , refs ref ∗ } : addrtype [ n .. m ] t ⊢ addrtype [ n .. m ] t : ok ∣ ref ∗ ∣ = n ( S ⊢ ref : t ′ ) ∗ ( ⊢ t ′ ≤ t ) ∗ Function Instances { type deftype , module moduleinst , code func } The defined type deftype must be valid under an empty context . The module instance moduleinst must be valid with some context C . Under context C : The function func must be valid with some defined type deftype ′ . The defined type deftype ′ must match deftype . Then the function instance is valid with defined type deftype . S ⊢ { type deftype , module moduleinst , code func } : deftype ⊢ deftype : ok S ⊢ moduleinst : C C ⊢ func : deftype ′ C ⊢ deftype ′ ≤ deftype Host Function Instances { type deftype , hostfunc hf } The defined type deftype must be valid under an empty context . The expansion of defined type deftype must be some function type func [ t 1 ∗ ] → [ t 2 ∗ ] . For every valid store S 1 extending S and every sequence val ∗ of values whose types coincide with t 1 ∗ : Executing hf in store S 1 with arguments val ∗ has a non-empty set of possible outcomes. For every element R of this set: Either R must be ⊥ (i.e., divergence). Or R consists of a valid store S 2 extending S 1 and a result result whose type coincides with [ t 2 ∗ ] . Then the function instance is valid with defined type deftype . S ⊢ { type deftype , hostfunc hf } : deftype ⊢ deftype : ok deftype ≈ func [ t 1 ∗ ] → [ t 2 ∗ ] ∀ S 1 , val ∗ , ⊢ S 1 : ok ∧ ⊢ S ⪯ S 1 ∧ S 1 ⊢ val ∗ : [ t 1 ∗ ] ⟹ hf ( S 1 ; val ∗ ) ⊃ ∅ ∧ ∀ R ∈ hf ( S 1 ; val ∗ ) , R = ⊥ ∨ ∃ S 2 , result , ⊢ S 2 : ok ∧ ⊢ S 1 ⪯ S 2 ∧ S 2 ⊢ result : [ t 2 ∗ ] ∧ R = ( S 2 ; result ) 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. Data Instances { bytes b ∗ } The data instance is valid. S ⊢ { bytes b ∗ } : ok Element Instances { type t , refs ref ∗ } The reference type t must be valid under the empty context . For each reference ref i in the elements ref n : The reference ref i must be valid with some reference type t i ′ . The reference type t i ′ must match the reference type t . Then the element instance is valid with reference type t . S ⊢ { type t , refs ref ∗ } : t ⊢ t : ok ( S ⊢ ref : t ′ ) ∗ ( ⊢ t ′ ≤ t ) ∗ Structure Instances { type deftype , fields fieldval ∗ } The defined type deftype must be valid under the empty context . The expansion of deftype must be a structure type struct fieldtype ∗ . The length of the sequence of field values fieldval ∗ must be the same as the length of the sequence of field types fieldtype ∗ . For each field value fieldval i in fieldval ∗ and corresponding field type fieldtype i in fieldtype ∗ : Let fieldtype i be mut storagetype i . The field value fieldval i must be valid with storage type storagetype i . Then the structure instance is valid. S ⊢ { type dt , fields fv ∗ } : ok ⊢ dt : ok expand ( dt ) = struct ( mut st ) ∗ ( S ⊢ fv : st ) ∗ Array Instances { type deftype , fields fieldval ∗ } The defined type deftype must be valid under the empty context . The expansion of deftype must be an array type array fieldtype . Let fieldtype be mut storagetype . For each field value fieldval i in fieldval ∗ : The field value fieldval i must be valid with storage type storagetype . Then the array instance is valid. S ⊢ { type dt , fields fv ∗ } : ok ⊢ dt : ok expand ( dt ) = array ( mut st ) ( S ⊢ fv : st ) ∗ Field Values fieldval If fieldval is a value val , then: The value val must be valid with value type t . Then the field value is valid with value type t . Else, fieldval is a packed value packval : Let packtype . pack i be the field value fieldval . Then the field value is valid with packed type packtype . S ⊢ pt . pack i : pt Exception Instances { tag a , fields val ∗ } The store entry S. tags [ a ] must exist. The expansion of the tag type S. tags [ a ] . type must be some function type func [ t ∗ ] → [ t ′ ∗ ] . The result type [ t ′ ∗ ] must be empty. The sequence val a st of values must have the same length as the sequence t ∗ of value types . For each value val i in val a st and corresponding value type t i in t ∗ , the value val i must be valid with type t i . Then the exception instance is valid. S ⊢ { tag a , fields val ∗ } : ok S. tags [ a ] . type ≈ func [ t ∗ ] → [ ] ( S ⊢ val : t ) ∗ Export Instances { name name , addr externaddr } The external address externaddr must be valid with some external type externtype . Then the export instance is valid. S ⊢ { name name , addr externaddr } : ok S ⊢ externaddr : externtype Module Instances moduleinst Each defined type deftype i in moduleinst . types must be valid under the empty context . For each tag address tagaddr i in moduleinst . tags , the external address tag tagaddr i must be valid with some external type tag tagtype i . For each global address globaladdr i in moduleinst . globals , the external address global globaladdr i must be valid with some external type global globaltype i . For each memory address memaddr i in moduleinst . mems , the external address mem memaddr i must be valid with some external type mem memtype i . For each table address tableaddr i in moduleinst . tables , the external address table tableaddr i must be valid with some external type table tabletype i . For each function address funcaddr i in moduleinst . funcs , the external address func funcaddr i must be valid with some external type func deftype F i . For each data address dataaddr i in moduleinst . datas , the data instance S. datas [ dataaddr i ] must be valid with ok i . For each element address elemaddr i in moduleinst . elems , the element instance S. elems [ elemaddr i ] must be valid with some reference type reftype i . Each export instance exportinst i in moduleinst . exports must be valid . For each export instance exportinst i in moduleinst . exports , the name exportinst i . name must be different from any other name occurring in moduleinst . exports . Let deftype ∗ be the concatenation of all deftype i in order. Let tagtype ∗ be the concatenation of all tagtype i in order. Let globaltype ∗ be the concatenation of all globaltype i in order. Let memtype ∗ be the concatenation of all memtype i in order. Let tabletype ∗ be the concatenation of all tabletype i in order. Let deftype F ∗ be the concatenation of all deftype F i in order. Let reftype ∗ be the concatenation of all reftype i in order. Let ok ∗ be the concatenation of all ok i in order. Let m be the length of moduleinst . funcs . Let x ∗ be the sequence of function indices from 0 to k − 1 for some k that is smaller than or equal to the number of functions in the instance. 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 x ∗ } . S ⊢ { types tags globals mems tables funcs datas elems exports deftype ∗ , tagaddr ∗ , globaladdr ∗ , memaddr ∗ , tableaddr ∗ , funcaddr ∗ , dataaddr ∗ , elemaddr ∗ , exportinst ∗ } : { types tags globals mems tables funcs datas elems refs deftype ∗ , tagtype ∗ , globaltype ∗ , memtype ∗ , tabletype ∗ , deftype F ∗ , ok ∗ , reftype ∗ , 0 … k − 1 } ( ⊢ deftype : ok ) ∗ ( S ⊢ tag tagaddr : tag tagtype ) ∗ ( S ⊢ global globaladdr : global globaltype ) ∗ ( S ⊢ func funcaddr : func deftype F ) ∗ ( S ⊢ mem memaddr : mem memtype ) ∗ ( S ⊢ table tableaddr : table tabletype ) ∗ ( S ⊢ S . datas [ dataaddr ] : ok ) ∗ ( S ⊢ S . elems [ elemaddr ] : reftype ) ∗ ( S ⊢ exportinst : ok ) ∗ ( exportinst . name ) ∗ disjoint k ≤ ∣ funcaddr ∗ ∣ Note The field C . refs from the resulting context is meant to be constructed to contain all function indices from the module. However, modules and their instances can contain more than 2 32 functions (at most 2 32 definitions plus 2 32 imports), while the highest representable function index is 2 32 − 1 . The variable k in the rule hence allows picking an upper limit for C . refs that is smaller than the total number of functions, in case that is necessary for C . refs to be syntactically well-formed. In practice, k = min ( ∣ funcaddr ∗ ∣ , 2 32 ) always is the maximally permissive choice. Configuration Validity To relate the WebAssembly type system to its execution semantics , the typing rules for instructions must be extended to configurations S ; T , which relates the store to execution threads . Configurations and threads are classified by their result type . In addition to the store S , threads are typed under a return type resulttype ? , which controls whether and with which type a return instruction is allowed. This type is absent ( ϵ ) except for instruction sequences inside an administrative frame instruction. 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 S ; T The store S must be valid . Under no allowed return type, the thread T must be valid with some result type [ t ∗ ] . Then the configuration is valid with the result type [ t ∗ ] . ⊢ S ; T : [ t ∗ ] ⊢ S : ok S ; ϵ ⊢ T : [ t ∗ ] Threads F ; instr ∗ Let resulttype ? be the current allowed return type. The frame F must be valid with a context C . Let C ′ be the same context as C , but with return set to resulttype ? . Under context C ′ , the instruction sequence instr ∗ must be valid with some type [ ] → [ t ∗ ] . Then the thread is valid with the result type [ t ∗ ] . S ; resulttype ? ⊢ F ; instr ∗ : [ t ∗ ] S ⊢ F : C S ; C , return resulttype ? ⊢ instr ∗ : [ ] → [ t ∗ ] Frames { locals val ∗ , module moduleinst } The module instance moduleinst must be valid with some module context C . Each value val i in val ∗ must be valid with some value type t i . Let t ∗ be the concatenation of all t i in order. Let C ′ be the same context as C , but with the value types t ∗ prepended to the locals list. Then the frame is valid with frame context C ′ . S ⊢ { locals val ∗ , module moduleinst } : ( C , locals t ∗ ) S ⊢ moduleinst : C ( S ⊢ val : t ) ∗ Administrative Instructions Typing rules for administrative instructions are specified as follows. In addition to the context C , typing of these instructions is defined under a given store S . To that end, all previous typing judgements C ⊢ prop are generalized to include the store, as in S ; C ⊢ prop , by implicitly adding S to all rules – S is never modified by the pre-existing rules, but it is accessed in the extra rules for administrative instructions given below. trap The instruction is valid with any valid instruction type of the form [ t 1 ∗ ] → [ t 2 ∗ ] . S ; C ⊢ trap : [ t 1 ∗ ] → [ t 2 ∗ ] C ⊢ [ t 1 ∗ ] → [ t 2 ∗ ] : ok val The value val must be valid with value type t . Then it is valid as an instruction with type [ ] → [ t ] . S ; C ⊢ val : [ ] → [ t ] S ⊢ val : t label n { instr 0 ∗ } instr ∗ The instruction sequence instr 0 ∗ must be valid with some type [ t 1 n ] → x ∗ [ t 2 ∗ ] . Let C ′ be the same context as C , but with the result type [ t 1 n ] prepended to the labels list. Under context C ′ , the instruction sequence instr ∗ must be valid with type [ ] → x ′ ∗ [ t 2 ∗ ] . Then the compound instruction is valid with type [ ] → [ t 2 ∗ ] . S ; C ⊢ label n { instr 0 ∗ } instr ∗ : [ ] → [ t 2 ∗ ] S ; C ⊢ instr 0 ∗ : [ t 1 n ] → x ∗ [ t 2 ∗ ] S ; C , labels [ t 1 n ] ⊢ instr ∗ : [ ] → x ′ ∗ [ t 2 ∗ ] frame n { F } instr ∗ Under the valid return type [ t n ] , the thread F ; instr ∗ must be valid with result type [ t n ] . Then the compound instruction is valid with type [ ] → [ t n ] . S ; C ⊢ frame n { F } instr ∗ : [ ] → [ t n ] { } ⊢ [ t n ] : ok S ; [ t n ] ⊢ F ; instr ∗ : [ t n ] handler n { catch ∗ } instr ∗ For every catch clause catch i in catch ∗ , catch i must be valid . The instruction sequence instr ∗ must be valid with some type [ t 1 ∗ ] → [ t 2 ∗ ] . Then the compound instruction is valid with type [ t 1 ∗ ] → [ t 2 ∗ ] . S ; C ⊢ handler n { catch ∗ } instr ∗ : [ ] → [ t ∗ ] ( C ⊢ catch : ok ) ∗ S ; C ⊢ instr ∗ : [ ] → [ t ∗ ] Store Extension Programs can mutate the store and its contained instances. Any such modification must respect certain invariants, such as not removing allocated instances or changing immutable definitions. While these invariants are inherent to the execution semantics of WebAssembly instructions and modules , host functions do not automatically adhere to them. Consequently, the required invariants must be stated as explicit constraints on the invocation of host functions. Soundness only holds when the embedder ensures these constraints. The necessary constraints are codified by the notion of store extension : a store state S ′ extends state S , written S ⪯ S ′ , when the following rules hold. Note Extension does not imply that the new store is valid, which is defined separately above . Store S The length of S. tags must not shrink. The length of S. globals must not shrink. The length of S. mems must not shrink. The length of S. tables must not shrink. The length of S. funcs must not shrink. The length of S. datas must not shrink. The length of S. elems must not shrink. The length of S. structs must not shrink. The length of S. arrays must not shrink. The length of S. exns must not shrink. For each tag instance taginst i in the original S. tags , the new tag instance must be an extension of the old. For each global instance globalinst i in the original S. globals , the new global instance must be an extension of the old. For each memory instance meminst i in the original S. mems , the new memory instance must be an extension of the old. For each table instance tableinst i in the original S. tables , the new table instance must be an extension of the old. For each function instance funcinst i in the original S. funcs , the new function instance must be an extension of the old. For each data instance datainst i in the original S. datas , the new data instance must be an extension of the old. For each element instance eleminst i in the original S. elems , the new element instance must be an extension of the old. For each structure instance structinst i in the original S. structs , the new structure instance must be an extension of the old. For each array instance arrayinst i in the original S. arrays , the new array instance must be an extension of the old. For each exception instance exninst i in the original S. exns , the new exception instance must be an extension of the old. ⊢ S 1 ⪯ S 2 S 1 . tags = taginst 1 ∗ S 1 . globals = globalinst 1 ∗ S 1 . mems = meminst 1 ∗ S 1 . tables = tableinst 1 ∗ S 1 . funcs = funcinst 1 ∗ S 1 . datas = datainst 1 ∗ S 1 . elems = eleminst 1 ∗ S 1 . structs = structinst 1 ∗ S 1 . arrays = arrayinst 1 ∗ S 1 . exns = exninst 1 ∗ S 2 . tags = taginst 1 ′ ∗ taginst 2 ∗ S 2 . globals = globalinst 1 ′ ∗ globalinst 2 ∗ S 2 . mems = meminst 1 ′ ∗ meminst 2 ∗ S 2 . tables = tableinst 1 ′ ∗ tableinst 2 ∗ S 2 . funcs = funcinst 1 ′ ∗ funcinst 2 ∗ S 2 . datas = datainst 1 ′ ∗ datainst 2 ∗ S 2 . elems = eleminst 1 ′ ∗ eleminst 2 ∗ S 2 . structs = structinst 1 ′ ∗ structinst 2 ∗ S 2 . arrays = arrayinst 1 ′ ∗ arrayinst 2 ∗ S 2 . exns = exninst 1 ′ ∗ exninst 2 ∗ ( ⊢ taginst 1 ⪯ taginst 1 ′ ) ∗ ( ⊢ globalinst 1 ⪯ globalinst 1 ′ ) ∗ ( ⊢ meminst 1 ⪯ meminst 1 ′ ) ∗ ( ⊢ tableinst 1 ⪯ tableinst 1 ′ ) ∗ ( ⊢ funcinst 1 ⪯ funcinst 1 ′ ) ∗ ( ⊢ datainst 1 ⪯ datainst 1 ′ ) ∗ ( ⊢ eleminst 1 ⪯ eleminst 1 ′ ) ∗ ( ⊢ structinst 1 ⪯ structinst 1 ′ ) ∗ ( ⊢ arrayinst 1 ⪯ arrayinst 1 ′ ) ∗ ( ⊢ exninst 1 ⪯ exninst 1 ′ ) ∗ Tag Instance taginst A tag instance must remain unchanged. ⊢ taginst ⪯ taginst Global Instance globalinst The global type globalinst . type must remain unchanged. Let mut t be the structure of globalinst . type . If mut is empty, then the value globalinst . value must remain unchanged. ⊢ { type ( mut t ) , value val 1 } ⪯ { type ( mut t ) , value val 2 } mut = mut ∨ val 1 = val 2 Memory Instance meminst The memory type meminst . type must remain unchanged. The length of meminst . bytes must not shrink. ⊢ { type mt , bytes b 1 n 1 } ⪯ { type mt , bytes b 2 n 2 } n 1 ≤ n 2 Table Instance tableinst The table type tableinst . type must remain unchanged. The length of tableinst . refs must not shrink. ⊢ { type tt , refs ( fa 1 ? ) n 1 } ⪯ { type tt , refs ( fa 2 ? ) n 2 } n 1 ≤ n 2 Function Instance funcinst A function instance must remain unchanged. ⊢ funcinst ⪯ funcinst Data Instance datainst The list datainst . bytes must: either remain unchanged, or shrink to length 0 . ⊢ { bytes b ∗ } ⪯ { bytes b ∗ } ⊢ { bytes b ∗ } ⪯ { bytes ϵ } Element Instance eleminst The reference type eleminst . type must remain unchanged. The list eleminst . refs must: either remain unchanged, or shrink to length 0 . ⊢ { type t , refs a ∗ } ⪯ { type t , refs a ∗ } ⊢ { type t , refs a ∗ } ⪯ { type t , refs ϵ } Structure Instance structinst The defined type structinst . type must remain unchanged. Assert: due to store well-formedness , the expansion of structinst . type is a structure type . Let struct fieldtype ∗ be the expansion of structinst . type . The length of the list structinst . fields must remain unchanged. Assert: due to store well-formedness , the length of structinst . fields is the same as the length of fieldtype ∗ . For each field value fieldval i in structinst . fields and corresponding field type fieldtype i in fieldtype ∗ : Let mut i st i be the structure of fieldtype i . If mut i is empty, then the field value fieldval i must remain unchanged. ⊢ { type ( mut st ) ∗ , fields fieldval 1 ∗ } ⪯ { type ( mut st ) ∗ , fields fieldval 2 ∗ } ( mut = mut ∨ fieldval 1 = fieldval 2 ) ∗ Array Instance arrayinst The defined type arrayinst . type must remain unchanged. Assert: due to store well-formedness , the expansion of arrayinst . type is an array type . Let array fieldtype be the expansion of arrayinst . type . The length of the list arrayinst . fields must remain unchanged. Let mut st be the structure of fieldtype . If mut is empty, then the sequence of field values arrayinst . fields must remain unchanged. ⊢ { type ( mut st ) , fields fieldval 1 ∗ } ⪯ { type ( mut st ) , fields fieldval 2 ∗ } mut = mut ∨ fieldval 1 ∗ = fieldval 2 ∗ Exception Instance exninst An exception instance must remain unchanged. ⊢ exninst ⪯ exninst Theorems Given the definition of valid configurations , the standard soundness theorems hold. [ 2 ] [ 3 ] Theorem (Preservation). If a configuration S ; T is valid with result type [ t ∗ ] (i.e., ⊢ S ; T : [ t ∗ ] ), and steps to S ′ ; T ′ (i.e., S ; T ↪ S ′ ; T ′ ), then S ′ ; T ′ is a valid configuration with the same result type (i.e., ⊢ S ′ ; T ′ : [ t ∗ ] ). Furthermore, S ′ is an extension of S (i.e., ⊢ S ⪯ S ′ ). 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 S ; T is valid (i.e., ⊢ S ; T : [ t ∗ ] for some result type [ t ∗ ] ), then either it is terminal, or it can step to some configuration S ′ ; T ′ (i.e., S ; T ↪ S ′ ; T ′ ). From Preservation and Progress the soundness of the WebAssembly type system follows directly. Corollary (Soundness). If a configuration S ; T is valid (i.e., ⊢ S ; T : [ t ∗ ] for some result type [ t ∗ ] ), then it either diverges or takes a finite number of steps to reach a terminal configuration S ′ ; T ′ (i.e., S ; T ↪ ∗ S ′ ; T ′ ) that is valid with the same result type (i.e., ⊢ S ′ ; T ′ : [ t ∗ ] ) and where S ′ is an extension of S (i.e., ⊢ S ⪯ S ′ ). 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. [ 1 ] 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 WebAssembly . Proceedings of the 38th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017). ACM 2017. [ 2 ] 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 Specification . Proceedings of the 7th ACM SIGPLAN Conference on Certified Programs and Proofs (CPP 2018). ACM 2018. [ 3 ] 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.0 . Proceedings of the 24th International Symposium on Formal Methods (FM 2021). Springer 2021. A.7 Type System Properties Principal Types The type system of WebAssembly features both subtyping and simple forms of polymorphism for instruction types . That has the effect that every instruction or instruction sequence can be classified with multiple different instruction types. However, the typing rules still allow deriving principal types for instruction sequences. That is, every valid instruction sequence has one particular type scheme, possibly containing some unconstrained place holder type variables , that is a subtype of all its valid instruction types, after substituting its type variables with suitable specific types. Moreover, when deriving an instruction type in a “forward” manner, i.e., the input of the instruction sequence is already fixed to specific types, then it has a principal output type expressible without type variables, up to a possibly polymorphic stack bottom representable with one single variable. In other words, “forward” principal types are effectively closed . 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. Technically, the syntax of heap , value , and result types can be enriched with type variables as follows: null heaptype reftype valtype resulttype ::= ::= ::= ::= ::= null ? ∣ α null … ∣ α heaptype ref null heaptype … ∣ α valtype ∣ α numvectype [ α valtype ∗ ? valtype ∗ ] 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., C ⊢ instr ∗ : instrtype ), then it is also valid with a possibly open instruction type instrtype m i n (i.e., C ⊢ instr ∗ : instrtype m i n ), such that for every closed type instrtype ′ with which instr ∗ is valid (i.e., for all C ⊢ instr ∗ : instrtype ′ ), there exists a substitution σ , such that σ ( instrtype m i n ) is a subtype of instrtype ′ (i.e., C ⊢ σ ( instrtype m i n ) ≤ instrtype ′ ). Furthermore, instrtype m i n is unique up to the choice of type variables. Theorem (Closed Principal Forward Types). If closed input type [ t 1 ∗ ] is given and the instruction sequence instr ∗ is valid with instruction type [ t 1 ∗ ] → x ∗ [ t 2 ∗ ] (i.e., C ⊢ instr ∗ : [ t 1 ∗ ] → x ∗ [ t 2 ∗ ] ), then it is also valid with instruction type [ t 1 ∗ ] → x ∗ [ α valtype ∗ t ∗ ] (i.e., C ⊢ instr ∗ : [ t 1 ∗ ] → x ∗ [ α valtype ∗ t ∗ ] ), where all t ∗ are closed, such that for every closed result type [ t 2 ′ ∗ ] with which instr ∗ is valid (i.e., for all C ⊢ instr ∗ : [ t 1 ∗ ] → x ∗ [ t 2 ′ ∗ ] ), there exists a substitution σ , such that [ t 2 ′ ∗ ] = [ σ ( α valtype ∗ ) t ∗ ] . Type Lattice The Principal Types property depends on the existence of a greatest lower bound for any pair of types. Theorem (Greatest Lower Bounds for Value Types). For any two value types t 1 and t 2 that are valid (i.e., C ⊢ t 1 : ok and C ⊢ t 2 : ok ), there exists a valid value type t that is a subtype of both t 1 and t 2 (i.e., C ⊢ t : ok and C ⊢ t ≤ t 1 and C ⊢ t ≤ t 2 ), such that every valid value type t ′ that also is a subtype of both t 1 and t 2 (i.e., for all C ⊢ t ′ : ok and C ⊢ t ′ ≤ t 1 and C ⊢ t ′ ≤ t 2 ), is a subtype of t (i.e., C ⊢ t ′ ≤ t ). Note The greatest lower bound of two types may be bot . Theorem (Conditional Least Upper Bounds for Value Types). Any two value types t 1 and t 2 that are valid (i.e., C ⊢ t 1 : ok and C ⊢ t 2 : ok ) either have no common supertype, or there exists a valid value type t that is a supertype of both t 1 and t 2 (i.e., C ⊢ t : ok and C ⊢ t 1 ≤ t and C ⊢ t 2 ≤ t ), such that every valid value type t ′ that also is a supertype of both t 1 and t 2 (i.e., for all C ⊢ t ′ : ok and C ⊢ t 1 ≤ t ′ and C ⊢ t 2 ≤ t ′ ), is a supertype of t (i.e., C ⊢ t ≤ t ′ ). 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. 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 [ t 1 ∗ ] → x 1 ∗ [ t ∗ ] and [ t ∗ ] → x 2 ∗ [ t 2 ∗ ] , respectively (i.e., C ⊢ instr 1 ∗ : [ t 1 ∗ ] → x 1 ∗ [ t ∗ ] and C ⊢ instr 1 ∗ : [ t ∗ ] → x 2 ∗ [ t 2 ∗ ] ), then the concatenated instruction sequence ( instr 1 ∗ instr 2 ∗ ) is valid with type [ t 1 ∗ ] → x 1 ∗ x 2 ∗ [ t 2 ∗ ] (i.e., C ⊢ instr 1 ∗ instr 2 ∗ : [ t 1 ∗ ] → x 1 ∗ x 2 ∗ [ t 2 ∗ ] ). Note More generally, instead of a shared type [ t ∗ ] , 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 [ t 1 ∗ ] → x ∗ [ t 2 ∗ ] (i.e., C ⊢ instr ∗ : [ t 1 ∗ ] → x ∗ [ t 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 [ t 1 ∗ ] → x 1 ∗ [ t ∗ ] and [ t ∗ ] → x 2 ∗ [ t 2 ∗ ] , respectively (i.e., C ⊢ instr 1 ∗ : [ t 1 ∗ ] → x 1 ∗ [ t ∗ ] and C ⊢ instr 1 ∗ : [ t ∗ ] → x 2 ∗ [ t 2 ∗ ] ), where x ∗ = x 1 ∗ x 2 ∗ . Note This property holds because validation is required even for unreachable code. Without that, instr 2 ∗ might not be valid in isolation. A.4 Validation Algorithm The specification of WebAssembly validation is purely declarative . It describes the constraints that must be met by a module or instruction sequence to be valid. This section sketches the skeleton of a sound and complete algorithm for effectively validating code, i.e., sequences of instructions . (Other aspects of validation are straightforward to implement.) In fact, the algorithm is expressed over the flat sequence of opcodes as occurring in the binary format , and performs only a single pass over it. Consequently, it can be integrated directly into a decoder. The algorithm is expressed in typed pseudo code whose semantics is intended to be self-explanatory. Data Structures Types Value types are representable as sets of enumerations: type num_type = I32 | I64 | F32 | F64 type vec_type = V128 type heap_type = Any | Eq | I31 | Struct | Array | None | Func | Nofunc | Exn | Noexn | Extern | Noextern | Bot | Def( def : def_type) type ref_type = Ref( heap : heap_type, null : bool) type val_type = num_type | vec_type | ref_type | Bot func is_num ( t : val_type) : bool = return t = I32 || t = I64 || t = F32 || t = F64 || t = Bot func is_vec ( t : val_type) : bool = return t = V128 || t = Bot func is_ref ( t : val_type) : bool = return not (is_num t || is_vec t) || t = Bot Similarly, defined types def_type can be represented: type pack_type = I8 | I16 type field_type = Field( val : val_type | pack_type, mut : bool) type struct_type = Struct( fields : list(field_type)) type array_type = Array( fields : field_type) type func_type = Func( params : list(val_type), results : list(val_type)) type comp_type = struct_type | array_type | func_type type sub_type = Sub( super : list(def_type), body : comp_type, final : bool) type rec_type = Rec( types : list(sub_type)) type def_type = Def( rec : rec_type, proj : int32) func unpack_field ( t : field_type) : val_type = if (it = I8 || t = I16) return I32 return t func expand_def ( t : def_type) : comp_type = return t.rec.types[t.proj].body 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: func validate_val_type ( t : val_type) func validate_ref_type ( t : ref_type) func matches_val ( t1 : val_type, t2 : val_type) : bool func matches_ref ( t1 : val_type, t2 : val_type) : bool func is_func ( t : comp_type) : bool func is_struct ( t : comp_type) : bool func is_array ( t : comp_type) : bool Finally, the following function computes the least precise supertype of a given heap type (its corresponding top type): func top_heap_type ( t : heap_type) : heap_type = switch (t) case (Any | Eq | I31 | Struct | Array | None) return Any case (Func | Nofunc) return Func case (Extern | Noextern) return Extern case (Def(dt)) switch (dt.rec.types[dt.proj].body) case (Struct(_) | Array(_)) return Any case (Func(_)) return Func case (Bot) raise CannotOccurInSource Context Validation requires a context for checking uses of indices . For the purpose of presenting the algorithm, it is maintained in a set of global variables: var return_type : list(val_type) var types : array(def_type) var locals : array(val_type) var locals_init : array(bool) var globals : array(global_type) var funcs : array(func_type) var tables : array(table_type) var mems : array(mem_type) 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. type val_stack = stack(val_type) type init_stack = stack(u32) type ctrl_stack = stack(ctrl_frame) type ctrl_frame = { opcode : opcode start_types : list(val_type) end_types : list(val_type) val_height : nat init_height : nat unreachable : bool } 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: var vals : val_stack var inits : init_stack var ctrls : ctrl_stack However, these variables are not manipulated directly by the main checking function, but through a set of auxiliary functions: func push_val ( type : val_type) = vals.push(type) func pop_val () : val_type = if (vals.size() = ctrls[0].val_height && ctrls[0].unreachable) return Bot error_if(vals.size() = ctrls[0].val_height) return vals.pop() func pop_val ( expect : val_type) : val_type = let actual = pop_val() error_if(not matches_val(actual, expect)) return actual func pop_num () : num_type | Bot = let actual = pop_val() error_if(not is_num(actual)) return actual func pop_ref () : ref_type = let actual = pop_val() error_if(not is_ref(actual)) if (actual = Bot) return Ref(Bot, false) return actual func push_vals ( types : list(val_type)) = foreach (t in types) push_val(t) func pop_vals ( types : list(val_type)) : list(val_type) = var popped := [] foreach (t in reverse(types)) popped.prepend(pop_val(t)) return popped 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: func get_local ( idx : u32) = error_if(not locals_init[idx]) func set_local ( idx : u32) = if (not locals_init[idx]) inits.push(idx) locals_init[idx] := true func reset_locals ( height : nat) = while (inits.size() > height) locals_init[inits.pop()] := false 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 push_ctrl ( opcode : opcode, in : list(val_type), out : list(val_type)) = let frame = ctrl_frame(opcode, in , out, vals.size(), inits.size(), false) ctrls.push(frame) push_vals( in ) func pop_ctrl () : ctrl_frame = error_if(ctrls.is_empty()) let frame = ctrls[0] pop_vals(frame.end_types) error_if(vals.size() =/= frame.val_height) reset_locals(frame.init_height) ctrls.pop() return frame func label_types ( frame : ctrl_frame) : list(val_types) = return (if (frame.opcode = loop) frame.start_types else frame.end_types) func unreachable () = vals.resize(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 ( i 32 . const ) i 64 . add ) . However, a polymorphic stack cannot underflow, but instead generates Bot types as needed. Validation of Opcode Sequences The following function shows the validation of a number of representative instructions that manipulate the stack. Other instructions are checked in a similar manner. func validate (opcode) = switch (opcode) case (i32.add) 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(rt.heap, false)) case (ref.test rt) validate_ref_type(rt) pop_val(Ref(top_heap_type(rt), true)) push_val(I32) case (local.get x) get_local(x) push_val(locals[x]) case (local.set x) pop_val(locals[x]) set_local(x) case (unreachable) unreachable() case (block t1*->t2*) pop_vals([t1*]) push_ctrl(block, [t1*], [t2*]) case (loop t1*->t2*) pop_vals([t1*]) push_ctrl(loop, [t1*], [t2*]) case (if t1*->t2*) pop_val(I32) pop_vals([t1*]) push_ctrl(if, [t1*], [t2*]) case (end) let frame = pop_ctrl() push_vals(frame.end_types) case (else) let frame = pop_ctrl() error_if(frame.opcode =/= if ) push_ctrl(else, frame.start_types, frame.end_types) case (br n) error_if(ctrls.size() < n) pop_vals(label_types(ctrls[n])) unreachable() case (br_if n) error_if(ctrls.size() < 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(ctrls.size() < m) let arity = label_types(ctrls[m]).size() foreach (n in n*) error_if(ctrls.size() < n) error_if(label_types(ctrls[n]).size() =/= arity) push_vals(pop_vals(label_types(ctrls[n]))) pop_vals(label_types(ctrls[m])) unreachable() case (br_on_null n) error_if(ctrls.size() < n) let rt = pop_ref() pop_vals(label_types(ctrls[n])) push_vals(label_types(ctrls[n])) push_val(Ref(rt.heap, false)) case (br_on_cast n rt1 rt2) validate_ref_type(rt1) validate_ref_type(rt2) pop_val(rt1) push_val(rt2) pop_vals(label_types(ctrls[n])) push_vals(label_types(ctrls[n])) pop_val(rt2) push_val(diff_ref_type(rt2, rt1)) 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(t.params) pop_val(Ref(Def(types[x]))) push_vals(t.results) case (return_call_ref x) let t = expand_def(types[x]) error_if(not is_func(t)) pop_vals(t.params) pop_val(Ref(Def(types[x]))) error_if(t.results.len() =/= return_types.len()) push_vals(t.results) pop_vals(return_types) unreachable() case (struct.new x) let t = expand_def(types[x]) error_if(not is_struct(t)) for (ti in reverse(t.fields)) pop_val(unpack_field(ti)) push_val(Ref(Def(types[x]))) case (struct.set x n) let t = expand_def(types[x]) error_if(not is_struct(t) || n >= t.fields.len()) pop_val(Ref(Def(types[x]))) pop_val(unpack_field(st.fields[n])) case (throw x) pop_vals(tags[x]. type .params) unreachable() case (try_table t1*->t2* handler*) pop_vals([t1*]) foreach (handler in handler*) error_if(ctrls.size() < handler.label) push_ctrl(catch, [], label_types(ctrls[handler.label])) switch (handler.clause) case (catch x) push_vals(tags[x]. type .params) case (catch_ref x) push_vals(tags[x]. type .params) push_val(Exnref) case (catch_all) skip case (catch_all_ref) push_val(Exnref) pop_ctrl() push_ctrl(try_table, [t1*], [t2*]) 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. Custom Sections and Annotations This appendix defines dedicated custom sections for WebAssembly’s binary format and annotations for the text format. Such sections or annotations do not contribute to, or otherwise affect, the WebAssembly semantics, and may be ignored by an implementation. However, they provide useful meta data that implementations can make use of to improve user experience or take compilation hints. Name Section The name section is a custom section whose name string is itself ‘ name ’ . The name section should appear only once in a module, and only after the data section . The purpose of this section is to attach printable names to definitions in a module, which e.g. can be used by a debugger or when parts of the module are to be rendered in text form . Note All names are represented in [UNICODE] 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 u 32 size of the contents, in bytes, the actual contents , whose structure is dependent on the subsection id. namesec namedata namesubsection N ( B ) ::= ::= ::= section 0 ( namedata ) n : name modulenamesubsec ? funcnamesubsec ? localnamesubsec ? typenamesubsec ? fieldnamesubsec ? tagnamesubsec ? N : byte size : u 32 B ( if n = ‘ name ’ ) ( if size = ∣∣ B ∣∣ ) The following subsection ids are used: 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 nameassoc ::= ::= list ( 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 indirectnameassoc ::= ::= list ( 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 ::= namesubsection 0 ( name ) Function Names The function name subsection has the id 1. It consists of a name map assigning function names to function indices . funcnamesubsec ::= namesubsection 1 ( 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 ::= namesubsection 2 ( indirectnamemap ) Type Names The type name subsection has the id 4. It consists of a name map assigning type names to type indices . typenamesubsec ::= namesubsection 4 ( namemap ) 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 . fieldnamesubsec ::= namesubsection 10 ( indirectnamemap ) Tag Names The tag name subsection has the id 11. It consists of a name map assigning tag names to tag indices . tagnamesubsec ::= namesubsection 11 ( namemap ) Name Annotations Name annotations are the textual analogue to the name section and provide a textual representation for it. Consequently, their id is @name . Analogous to the name section, name annotations are allowed on modules , functions , and locals (including parameters ). They can be placed where the text format allows binding occurrences of respective identifiers . If both an identifier and a name annotation are given, the annotation is expected after the identifier. In that case, the annotation takes precedence over the identifier as a textual representation of the binding’s name. At most one name annotation may be given per binding. All name annotations have the following format: nameannot ::= ‘ ( @name ’ string ‘ ) ’ 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 . modulenameannot ::= nameannot 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. funcnameannot ::= nameannot 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. paramnameannot ::= nameannot 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. localnameannot ::= nameannot 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 . typenameannot ::= nameannot 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. fieldnameannot ::= nameannot 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 . tagnameannot ::= nameannot Custom Annotations Custom annotations are a generic textual representation for any custom section . Their id is @custom . By generating custom annotations, tools converting between binary format and text format can maintain and round-trip the content of custom sections even when they do not recognize them. Custom annotations must be placed inside a module definition. They must occur anywhere after the ‘ module ’ keyword, or if present, after the following module identifier . They must not be nested into other constructs. customannot customplace sec ::= ::= ∣ ∣ ∣ ::= ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ∣ ‘ ( @custom ’ string customplace ? datastring ‘ ) ’ ‘ ( ’ ‘ before ’ ‘ first ’ ‘ ) ’ ‘ ( ’ ‘ before ’ sec ‘ ) ’ ‘ ( ’ ‘ after ’ sec ‘ ) ’ ‘ ( ’ ‘ after ’ ‘ last ’ ‘ ) ’ ‘ type ’ ‘ import ’ ‘ func ’ ‘ table ’ ‘ memory ’ ‘ global ’ ‘ export ’ ‘ start ’ ‘ elem ’ ‘ code ’ ‘ data ’ ‘ datacount ’ 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 For example, the following module, (module (@custom "A" "aaa") (type $t (func)) (@custom "B" (after func) "bbb") (@custom "C" (before func) "ccc") (@custom "D" (after last) "ddd") (table 10 funcref) (func (type $t)) (@custom "E" (after import) "eee") (@custom "F" (before type) "fff") (@custom "G" (after data) "ggg") (@custom "H" (after code) "hhh") (@custom "I" (after func) "iii") (@custom "J" (before func) "jjj") (@custom "K" (before first) "kkk") ) will result in the following section ordering: custom section "K" custom section "F" type section custom section "E" custom section "C" custom section "J" function section custom section "B" custom section "I" table section code section custom section "H" custom section "G" custom section "A" custom section "D" A.8 Change History Since the original release 1.0 of the WebAssembly specification, a number of proposals for extensions have been integrated. The following sections provide an overview of what has changed. All present and future versions of WebAssembly are intended to be backwards-compatible with all previous versions. Concretely: All syntactically well-formed (in binary or text format) and valid modules remain well-formed and valid with an equivalent module type (or a subtype). 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. All non- trapping executions of a valid program retain their behaviour with an equivalent set of possible results (or a non-empty subset). Note This allows previously malformed or invalid programs to become executable. It also allows program executions that previously trapped to execute successfully, although the 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 0x FF to represent an instruction or instruction prefix. Release 2.0 Sign Extension Instructions Added new numeric instructions for performing sign extension within integer representations. [ 1 ] New numeric instructions : i nn . extend N _s Non-trapping Float-to-Int Conversions Added new conversion instructions that avoid trapping when converting a floating-point number to an integer. [ 2 ] New numeric instructions : i nn . trunc _sat_f mm _ sx Multiple Values Generalized the result type of blocks and functions to allow for multiple values; in addition, introduced the ability to have block parameters. [ 3 ] 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. [ 4 ] New reference value types : funcref externref New reference instructions : ref . null ref . func ref . is_null Extended parametric instruction : select with optional type immediate New declarative form of element segment Table Instructions Added instructions to directly access and modify tables. [ 4 ] Table types allow any reference type as element type New table instructions : table . get table . set table . size table . grow Multiple Tables Added the ability to use multiple tables per module. [ 4 ] Modules may define multiple tables import multiple tables export multiple tables Table instructions take a table index immediate: table . get table . set table . size table . grow call_indirect Element segments take a table index Bulk Memory and Table Instructions Added instructions that modify ranges of memory or table entries. [ 4 ] [ 5 ] New memory instructions : memory . fill memory . init memory . copy data . drop New table instructions : table . fill table . init table . copy elem . drop New passive form of data segment New passive form of element segment New data count section in binary format Active data and element segments boundaries are no longer checked at compile time but may trap instead Vector Instructions Added vector type and instructions that manipulate multiple numeric values in parallel (also known as SIMD , single instruction multiple data) [ 6 ] New value type : v 128 New memory instructions : v128. load v128. load N x M _ sx v128. load N _zero v128. load N _splat v128. load N _lane v128. store v128. store N _lane New constant vector instruction : v128. const New unary vector instructions : v128.not i N x M .abs i N x M .neg i8x16.popcnt f N x M .abs f N x M .neg f N x M .sqrt f N x M .ceil f N x M .floor f N x M .trunc f N x M .nearest New binary vector instructions : v128.and v128.andnot v128.or v128.xor i N x M .add i N x M .sub i N x M .mul i N x M .add_sat_ sx i N x M .sub_sat_ sx i N x M .min_ sx i N x M .max_ sx i N x M .shl i N x M .shr_ sx f N x M .add f N x M .sub f N x M .mul f N x M .div i16x8.extadd_pairwise_i8x16_ sx i32x4.extadd_pairwise_i16x8_ sx i N x M .extmul_ half _i N ′ x M ′ _ sx i16x8.q15mulr_sat_s i32x4.dot_i16x8_s i8x16.avgr_u i16x8.avgr_u f N x M .min f N x M .max f N x M .pmin f N x M .pmax New ternary vector instruction : v128.bitselect New test vector instructions : v128.any_true i N x M .all_true New relational vector instructions : i N x M .eq i N x M .ne i N x M .lt_ sx i N x M .gt_ sx i N x M .le_ sx i N x M .ge_ sx f N x M .eq f N x M .ne f N x M .lt f N x M .gt f N x M .le f N x M .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 N x M .extract_lane_ sx ? i N x M .replace_lane f N x M .extract_lane f N x M .replace_lane New lane splitting/combining vector instructions : i N x M .extend_ half _i N ′ x M ′ _ sx i8x16.narrow_i16x8_ sx i16x8.narrow_i32x4_ sx New byte reordering vector instructions : i8x16.shuffle i8x16.swizzle New injection/projection vector instructions : i N x M .splat f N x M .splat i N x M .bitmask [ 1 ] https://github.com/WebAssembly/spec/tree/main/proposals/sign-extension-ops/ [ 2 ] https://github.com/WebAssembly/spec/tree/main/proposals/nontrapping-float-to-int-conversion/ [ 3 ] https://github.com/WebAssembly/spec/tree/main/proposals/multi-value/ [ 4 ] ( 1 , 2 , 3 , 4 ) https://github.com/WebAssembly/spec/tree/main/proposals/reference-types/ [ 5 ] https://github.com/WebAssembly/spec/tree/main/proposals/bulk-memory-operations/ [ 6 ] https://github.com/WebAssembly/spec/tree/main/proposals/simd/ Release 3.0 Extended Constant Expressions Allowed basic numeric computations in constant expressions. [ 7 ] Extended set of constant instructions with: i nn .add i nn .sub i nn .mul global . get for any previously declared immutable global Note The garbage collection extension added further constant instructions. Tail Calls Added instructions to perform tail calls. [ 8 ] New control instructions : return_call return_call_indirect Exception Handling Added tag definitions, imports, and exports, and instructions to throw and catch exceptions. [ 9 ] 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. [ 10 ] Modules may define multiple memories import multiple memories export multiple memories Memory instructions take a memory index immediate: memory . size memory . grow memory . fill memory . copy memory . init t .load t .store t .load N _ sx t .store N v128.load N x M _ sx v128.load N _zero v128.load N _splat v128.load N _lane v128.store N _lane Data segments take a memory index 64-bit Address Space Added the ability to declare an i 64 address type for tables and memories . [ 11 ] Address types denote a subset of the integral number types Table types include an address type Memory types include an address type Operand types of table and memory instructions now depend on the subject’s declared address type: table . get table . set table . size table . grow table . fill table . copy table . init memory . size memory . grow memory . fill memory . copy memory . init t .load t .store t .load N _ sx t .store N v128.load N x M _ sx v128.load N _zero v128.load N _splat v128.load N _lane v128.store N _lane Typeful References Added more precise types for references. [ 12 ] New generalised form of reference types : ( ref null ? heaptype ) New class of heap types : func extern 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 : ref . func 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. [ 13 ] New forms of heap types : any eq i 31 struct array none nofunc noextern New reference type short-hands: anyref eqref i 31 ref structref arrayref nullref nullfuncref nullexternref New forms of type definitions: structure array types sub types recursive types Enriched subtyping based on explicitly declared sub types and the new heap types New generic reference instructions : ref . eq ref . test ref . cast br_on_cast br_on_cast_fail New reference instructions for unboxed scalars : ref . i 31 i 31 . get _ sx New reference instructions for structure types : struct . new struct . new_default struct . get _ sx ? struct . set New reference instructions for array types : array . new array . new_default array . new_fixed array . new_data array . new_elem array . get _ sx ? array . set array . len array . fill array . copy 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 . i 31 struct . new struct . new_default array . new array . new_default array . new_fixed any . convert_extern extern . convert_any Relaxed Vector Instructions Added new relaxed vector instructions, whose behaviour is non-deterministic and implementation-dependent. [ 14 ] New binary vector instruction : f N x M .relaxed_min f N x M .relaxed_max i16x8.relaxed_q15mulr_s i16x8.relaxed_dot_i8x16_i7x16_s New ternary vector instruction : f N x M .relaxed_madd f N x M .relaxed_nmadd i N x M .relaxed_laneselect i32x4.relaxed_dot_i8x16_i7x16_add_s New conversion vector instructions : i32x4.relaxed_trunc_f32x4_ sx i32x4.relaxed_trunc_f64x2_ sx _zero New byte reordering vector instruction : i8x16.relaxed_swizzle 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. [ 15 ] 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 [ 7 ] https://github.com/WebAssembly/spec/tree/main/proposals/extended-const/ [ 8 ] https://github.com/WebAssembly/spec/tree/main/proposals/tail-call/ [ 9 ] https://github.com/WebAssembly/spec/tree/main/proposals/exception-handling/ [ 10 ] https://github.com/WebAssembly/spec/tree/main/proposals/multi-memory/ [ 11 ] https://github.com/WebAssembly/spec/tree/main/proposals/memory64/ [ 12 ] https://github.com/WebAssembly/spec/tree/main/proposals/function-references/ [ 13 ] https://github.com/WebAssembly/spec/tree/main/proposals/gc/ [ 14 ] https://github.com/WebAssembly/spec/tree/main/proposals/relaxed-simd/ [ 15 ] https://github.com/WebAssembly/spec/tree/main/proposals/annotations/ A.9 Index of Types Category Constructor Binary Opcode Type index x (positive number as s 32 or u 32 ) Number type i 32 0x 7F (-1 as s 7 ) Number type i 64 0x 7E (-2 as s 7 ) Number type f 32 0x 7D (-3 as s 7 ) Number type f 64 0x 7C (-4 as s 7 ) Vector type v 128 0x 7B (-5 as s 7 ) (reserved) 0x 7A .. 0x 79 Packed type i 8 0x 78 (-8 as s 7 ) Packed type i 16 0x 77 (-9 as s 7 ) (reserved) 0x 78 .. 0x 75 Heap type noexn 0x 74 (-12 as s 7 ) Heap type nofunc 0x 73 (-13 as s 7 ) Heap type noextern 0x 72 (-14 as s 7 ) Heap type none 0x 71 (-15 as s 7 ) Heap type func 0x 70 (-16 as s 7 ) Heap type extern 0x 6F (-17 as s 7 ) Heap type any 0x 6E (-18 as s 7 ) Heap type eq 0x 6D (-19 as s 7 ) Heap type i 31 0x 6C (-20 as s 7 ) Heap type struct 0x 6B (-21 as s 7 ) Heap type array 0x 6A (-22 as s 7 ) Heap type exn 0x 69 (-23 as s 7 ) (reserved) 0x 68 .. 0x 65 Reference type ref 0x 64 (-28 as s 7 ) Reference type ref null 0x 63 (-29 as s 7 ) (reserved) 0x 62 .. 0x 61 Composite type func [ valtype ∗ ] → [ valtype ∗ ] 0x 60 (-32 as s 7 ) Composite type struct fieldtype ∗ 0x 5F (-33 as s 7 ) Composite type array fieldtype 0x 5E (-34 as s 7 ) (reserved) 0x 5D .. 0x 51 Sub type sub typeidx ∗ comptype 0x 50 (-48 as s 7 ) Sub type sub final typeidx ∗ comptype 0x 4F (-49 as s 7 ) Recursive type rec subtype ∗ 0x 4E (-50 as s 7 ) (reserved) 0x 4D .. 0x 41 Result type [ ϵ ] 0x 40 (-64 as s 7 ) Tag type typeuse (none) Global type mut valtype (none) Memory type addrtype limits (none) Table type addrtype limits reftype (none) A.10 Index of Instructions Instruction Binary Opcode Type Validation Execution unreachable 0x 00 [ t 1 ∗ ] → [ t 2 ∗ ] validation execution nop 0x 01 [ ] → [ ] validation execution block bt 0x 02 [ t 1 ∗ ] → [ t 2 ∗ ] validation execution loop bt 0x 03 [ t 1 ∗ ] → [ t 2 ∗ ] validation execution if bt 0x 04 [ t 1 ∗ i 32 ] → [ t 2 ∗ ] validation execution else 0x 05 (reserved) 0x 06 (reserved) 0x 07 throw x 0x 08 [ t 1 ∗ t x ∗ ] → [ t 2 ∗ ] validation execution (reserved) 0x 09 throw_ref 0x 0A [ t 1 ∗ exnref ] → [ t 2 ∗ ] validation execution end 0x 0B br l 0x 0C [ t 1 ∗ t ∗ ] → [ t 2 ∗ ] validation execution br_if l 0x 0D [ t ∗ i 32 ] → [ t ∗ ] validation execution br_table l ∗ l 0x 0E [ t 1 ∗ t ∗ i 32 ] → [ t 2 ∗ ] validation execution return 0x 0F [ t 1 ∗ t ∗ ] → [ t 2 ∗ ] validation execution call x 0x 10 [ t 1 ∗ ] → [ t 2 ∗ ] validation execution call_indirect x y 0x 11 [ t 1 ∗ i 32 ] → [ t 2 ∗ ] validation execution return_call x 0x 12 [ t 1 ∗ ] → [ t 2 ∗ ] validation execution return_call_indirect x y 0x 13 [ t 1 ∗ i 32 ] → [ t 2 ∗ ] validation execution call_ref x 0x 14 [ t 1 ∗ ( ref null x )] → [ t 2 ∗ ] validation execution return_call_ref x 0x 15 [ t 1 ∗ ( ref null x )] → [ t 2 ∗ ] validation execution (reserved) 0x 16 (reserved) 0x 17 (reserved) 0x 18 (reserved) 0x 19 drop 0x 1A [ t ] → [ ] validation execution select 0x 1B [ t t i 32 ] → [ t ] validation execution select t 0x 1C [ t t i 32 ] → [ t ] validation execution (reserved) 0x 1D (reserved) 0x 1E try_table bt 0x 1F [ t 1 ∗ ] → [ t 2 ∗ ] validation execution local . get x 0x 20 [ ] → [ t ] validation execution local . set x 0x 21 [ t ] → [ ] validation execution local . tee x 0x 22 [ t ] → [ t ] validation execution global . get x 0x 23 [ ] → [ t ] validation execution global . set x 0x 24 [ t ] → [ ] validation execution table . get x 0x 25 [ at ] → [ t ] validation execution table . set x 0x 26 [ at t ] → [ ] validation execution (reserved) 0x 27 i 32 . load x memarg 0x 28 [ at ] → [ i 32 ] validation execution i 64 . load x memarg 0x 29 [ at ] → [ i 64 ] validation execution f 32 . load x memarg 0x 2A [ at ] → [ f 32 ] validation execution f 64 . load x memarg 0x 2B [ at ] → [ f 64 ] validation execution i 32 . load 8_s x memarg 0x 2C [ at ] → [ i 32 ] validation execution i 32 . load 8_u x memarg 0x 2D [ at ] → [ i 32 ] validation execution i 32 . load 16_s x memarg 0x 2E [ at ] → [ i 32 ] validation execution i 32 . load 16_u x memarg 0x 2F [ at ] → [ i 32 ] validation execution i 64 . load 8_s x memarg 0x 30 [ at ] → [ i 64 ] validation execution i 64 . load 8_u x memarg 0x 31 [ at ] → [ i 64 ] validation execution i 64 . load 16_s x memarg 0x 32 [ at ] → [ i 64 ] validation execution i 64 . load 16_u x memarg 0x 33 [ at ] → [ i 64 ] validation execution i 64 . load 32_s x memarg 0x 34 [ at ] → [ i 64 ] validation execution i 64 . load 32_u x memarg 0x 35 [ at ] → [ i 64 ] validation execution i 32 . store x memarg 0x 36 [ at i 32 ] → [ ] validation execution i 64 . store x memarg 0x 37 [ at i 64 ] → [ ] validation execution f 32 . store x memarg 0x 38 [ at f 32 ] → [ ] validation execution f 64 . store x memarg 0x 39 [ at f 64 ] → [ ] validation execution i 32 . store 8 x memarg 0x 3A [ at i 32 ] → [ ] validation execution i 32 . store 16 x memarg 0x 3B [ at i 32 ] → [ ] validation execution i 64 . store 8 x memarg 0x 3C [ at i 64 ] → [ ] validation execution i 64 . store 16 x memarg 0x 3D [ at i 64 ] → [ ] validation execution i 64 . store 32 x memarg 0x 3E [ at i 64 ] → [ ] validation execution memory . size x 0x 3F [ ] → [ at ] validation execution memory . grow x 0x 40 [ at ] → [ at ] validation execution i 32 . const i 32 0x 41 [ ] → [ i 32 ] validation execution i 64 . const i 64 0x 42 [ ] → [ i 64 ] validation execution f 32 . const f 32 0x 43 [ ] → [ f 32 ] validation execution f 64 . const f 64 0x 44 [ ] → [ f 64 ] validation execution i 32 . eqz 0x 45 [ i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . eq 0x 46 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . ne 0x 47 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . lt _s 0x 48 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . lt _u 0x 49 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . gt _s 0x 4A [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . gt _u 0x 4B [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . le _s 0x 4C [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . le _u 0x 4D [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . ge _s 0x 4E [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . ge _u 0x 4F [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 64 . eqz 0x 50 [ i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . eq 0x 51 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . ne 0x 52 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . lt _s 0x 53 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . lt _u 0x 54 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . gt _s 0x 55 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . gt _u 0x 56 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . le _s 0x 57 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . le _u 0x 58 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . ge _s 0x 59 [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) i 64 . ge _u 0x 5A [ i 64 i 64 ] → [ i 32 ] validation execution ( operator ) f 32 . eq 0x 5B [ f 32 f 32 ] → [ i 32 ] validation execution ( operator ) f 32 . ne 0x 5C [ f 32 f 32 ] → [ i 32 ] validation execution ( operator ) f 32 . lt 0x 5D [ f 32 f 32 ] → [ i 32 ] validation execution ( operator ) f 32 . gt 0x 5E [ f 32 f 32 ] → [ i 32 ] validation execution ( operator ) f 32 . le 0x 5F [ f 32 f 32 ] → [ i 32 ] validation execution ( operator ) f 32 . ge 0x 60 [ f 32 f 32 ] → [ i 32 ] validation execution ( operator ) f 64 . eq 0x 61 [ f 64 f 64 ] → [ i 32 ] validation execution ( operator ) f 64 . ne 0x 62 [ f 64 f 64 ] → [ i 32 ] validation execution ( operator ) f 64 . lt 0x 63 [ f 64 f 64 ] → [ i 32 ] validation execution ( operator ) f 64 . gt 0x 64 [ f 64 f 64 ] → [ i 32 ] validation execution ( operator ) f 64 . le 0x 65 [ f 64 f 64 ] → [ i 32 ] validation execution ( operator ) f 64 . ge 0x 66 [ f 64 f 64 ] → [ i 32 ] validation execution ( operator ) i 32 . clz 0x 67 [ i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . ctz 0x 68 [ i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . popcnt 0x 69 [ i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . add 0x 6A [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . sub 0x 6B [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . mul 0x 6C [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . div _s 0x 6D [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . div _u 0x 6E [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . rem _s 0x 6F [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . rem _u 0x 70 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . and 0x 71 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . or 0x 72 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . xor 0x 73 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . shl 0x 74 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . shr _s 0x 75 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . shr _u 0x 76 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . rotl 0x 77 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . rotr 0x 78 [ i 32 i 32 ] → [ i 32 ] validation execution ( operator ) i 64 . clz 0x 79 [ i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . ctz 0x 7A [ i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . popcnt 0x 7B [ i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . add 0x 7C [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . sub 0x 7D [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . mul 0x 7E [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . div _s 0x 7F [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . div _u 0x 80 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . rem _s 0x 81 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . rem _u 0x 82 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . and 0x 83 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . or 0x 84 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . xor 0x 85 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . shl 0x 86 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . shr _s 0x 87 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . shr _u 0x 88 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . rotl 0x 89 [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . rotr 0x 8A [ i 64 i 64 ] → [ i 64 ] validation execution ( operator ) f 32 . abs 0x 8B [ f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . neg 0x 8C [ f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . ceil 0x 8D [ f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . floor 0x 8E [ f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . trunc 0x 8F [ f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . nearest 0x 90 [ f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . sqrt 0x 91 [ f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . add 0x 92 [ f 32 f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . sub 0x 93 [ f 32 f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . mul 0x 94 [ f 32 f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . div 0x 95 [ f 32 f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . min 0x 96 [ f 32 f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . max 0x 97 [ f 32 f 32 ] → [ f 32 ] validation execution ( operator ) f 32 . copysign 0x 98 [ f 32 f 32 ] → [ f 32 ] validation execution ( operator ) f 64 . abs 0x 99 [ f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . neg 0x 9A [ f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . ceil 0x 9B [ f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . floor 0x 9C [ f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . trunc 0x 9D [ f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . nearest 0x 9E [ f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . sqrt 0x 9F [ f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . add 0x A0 [ f 64 f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . sub 0x A1 [ f 64 f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . mul 0x A2 [ f 64 f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . div 0x A3 [ f 64 f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . min 0x A4 [ f 64 f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . max 0x A5 [ f 64 f 64 ] → [ f 64 ] validation execution ( operator ) f 64 . copysign 0x A6 [ f 64 f 64 ] → [ f 64 ] validation execution ( operator ) i 32 . wrap _ i 64 0x A7 [ i 64 ] → [ i 32 ] validation execution ( operator ) i 32 . trunc _ f 32 _s 0x A8 [ f 32 ] → [ i 32 ] validation execution ( operator ) i 32 . trunc _ f 32 _u 0x A9 [ f 32 ] → [ i 32 ] validation execution ( operator ) i 32 . trunc _ f 64 _s 0x AA [ f 64 ] → [ i 32 ] validation execution ( operator ) i 32 . trunc _ f 64 _u 0x AB [ f 64 ] → [ i 32 ] validation execution ( operator ) i 64 . extend _ i 32 _s 0x AC [ i 32 ] → [ i 64 ] validation execution ( operator ) i 64 . extend _ i 32 _u 0x AD [ i 32 ] → [ i 64 ] validation execution ( operator ) i 64 . trunc _ f 32 _s 0x AE [ f 32 ] → [ i 64 ] validation execution ( operator ) i 64 . trunc _ f 32 _u 0x AF [ f 32 ] → [ i 64 ] validation execution ( operator ) i 64 . trunc _ f 64 _s 0x B0 [ f 64 ] → [ i 64 ] validation execution ( operator ) i 64 . trunc _ f 64 _u 0x B1 [ f 64 ] → [ i 64 ] validation execution ( operator ) f 32 . convert _ i 32 _s 0x B2 [ i 32 ] → [ f 32 ] validation execution ( operator ) f 32 . convert _ i 32 _u 0x B3 [ i 32 ] → [ f 32 ] validation execution ( operator ) f 32 . convert _ i 64 _s 0x B4 [ i 64 ] → [ f 32 ] validation execution ( operator ) f 32 . convert _ i 64 _u 0x B5 [ i 64 ] → [ f 32 ] validation execution ( operator ) f 32 . demote _ f 64 0x B6 [ f 64 ] → [ f 32 ] validation execution ( operator ) f 64 . convert _ i 32 _s 0x B7 [ i 32 ] → [ f 64 ] validation execution ( operator ) f 64 . convert _ i 32 _u 0x B8 [ i 32 ] → [ f 64 ] validation execution ( operator ) f 64 . convert _ i 64 _s 0x B9 [ i 64 ] → [ f 64 ] validation execution ( operator ) f 64 . convert _ i 64 _u 0x BA [ i 64 ] → [ f 64 ] validation execution ( operator ) f 64 . promote _ f 32 0x BB [ f 32 ] → [ f 64 ] validation execution ( operator ) i 32 . reinterpret _ f 32 0x BC [ f 32 ] → [ i 32 ] validation execution ( operator ) i 64 . reinterpret _ f 64 0x BD [ f 64 ] → [ i 64 ] validation execution ( operator ) f 32 . reinterpret _ i 32 0x BE [ i 32 ] → [ f 32 ] validation execution ( operator ) f 64 . reinterpret _ i 64 0x BF [ i 64 ] → [ f 64 ] validation execution ( operator ) i 32 . extend 8_s 0x C0 [ i 32 ] → [ i 32 ] validation execution ( operator ) i 32 . extend 16_s 0x C1 [ i 32 ] → [ i 32 ] validation execution ( operator ) i 64 . extend 8_s 0x C2 [ i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . extend 16_s 0x C3 [ i 64 ] → [ i 64 ] validation execution ( operator ) i 64 . extend 32_s 0x C4 [ i 64 ] → [ i 64 ] validation execution ( operator ) (reserved) 0x C5 (reserved) 0x C6 (reserved) 0x C7 (reserved) 0x C8 (reserved) 0x C9 (reserved) 0x CA (reserved) 0x CB (reserved) 0x CC (reserved) 0x CD (reserved) 0x CE (reserved) 0x CF ref . null ht 0x D0 [ ] → [( ref null ht )] validation execution ref . is_null 0x D1 [( ref null ht )] → [ i 32 ] validation execution ref . func x 0x D2 [ ] → [ ref ht ] validation execution ref . eq 0x D3 [ eqref eqref ] → [ i 32 ] validation execution ref . as_non_null 0x D4 [( ref null ht )] → [( ref ht )] validation execution br_on_null l 0x D5 [ t ∗ ( ref null ht )] → [ t ∗ ( ref ht )] validation execution br_on_non_null l 0x D6 [ t ∗ ( ref null ht )] → [ t ∗ ] validation execution (reserved) 0x D7 (reserved) 0x D8 (reserved) 0x D9 (reserved) 0x DA (reserved) 0x DB (reserved) 0x DC (reserved) 0x DD (reserved) 0x DE (reserved) 0x DF (reserved) 0x E0 (reserved) 0x E1 (reserved) 0x E2 (reserved) 0x E3 (reserved) 0x E4 (reserved) 0x E5 (reserved) 0x E6 (reserved) 0x E7 (reserved) 0x E8 (reserved) 0x E9 (reserved) 0x EA (reserved) 0x EB (reserved) 0x EC (reserved) 0x ED (reserved) 0x EE (reserved) 0x EF (reserved) 0x F0 (reserved) 0x F1 (reserved) 0x F2 (reserved) 0x F3 (reserved) 0x F4 (reserved) 0x F5 (reserved) 0x F6 (reserved) 0x F7 (reserved) 0x F8 (reserved) 0x F9 (reserved) 0x FA struct . new x 0x FB 0x 00 [ t ∗ ] → [( ref x )] validation execution struct . new_default x 0x FB 0x 01 [ ] → [( ref x )] validation execution struct . get x y 0x FB 0x 02 [( ref null x )] → [ t ] validation execution struct . get_s x y 0x FB 0x 03 [( ref null x )] → [ i 32 ] validation execution struct . get_u x y 0x FB 0x 04 [( ref null x )] → [ i 32 ] validation execution struct . set x y 0x FB 0x 05 [( ref null x ) t ] → [ ] validation execution array . new x 0x FB 0x 06 [ t i 32 ] → [( ref x )] validation execution array . new_default x 0x FB 0x 07 [ i 32 ] → [( ref x )] validation execution array . new_fixed x n 0x FB 0x 08 [ t n ] → [( ref x )] validation execution array . new_data x y 0x FB 0x 09 [ i 32 i 32 ] → [( ref x )] validation execution array . new_elem x y 0x FB 0x 0A [ i 32 i 32 ] → [( ref x )] validation execution array . get x 0x FB 0x 0B [( ref null x ) i 32 ] → [ t ] validation execution array . get_s x 0x FB 0x 0C [( ref null x ) i 32 ] → [ i 32 ] validation execution array . get_u x 0x FB 0x 0D [( ref null x ) i 32 ] → [ i 32 ] validation execution array . set x 0x FB 0x 0E [( ref null x ) i 32 t ] → [ ] validation execution array . len 0x FB 0x 0F [( ref null array )] → [ i 32 ] validation execution array . fill x 0x FB 0x 10 [( ref null x ) i 32 t i 32 ] → [ ] validation execution array . copy x y 0x FB 0x 11 [( ref null x ) i 32 ( ref null y ) i 32 i 32 ] → [ ] validation execution array . init_data x y 0x FB 0x 12 [( ref null x ) i 32 i 32 i 32 ] → [ ] validation execution array . init_elem x y 0x FB 0x 13 [( ref null x ) i 32 i 32 i 32 ] → [ ] validation execution ref . test ( ref t ) 0x FB 0x 14 [( ref t ′ )] → [ i 32 ] validation execution ref . test ( ref null t ) 0x FB 0x 15 [( ref null t ′ )] → [ i 32 ] validation execution ref . cast ( ref t ) 0x FB 0x 16 [( ref t ′ )] → [( ref t )] validation execution ref . cast ( ref null t ) 0x FB 0x 17 [( ref null t ′ )] → [( ref null t )] validation execution br_on_cast t 1 t 2 0x FB 0x 18 [ t 1 ] → [ t 1 ∖ t 2 ] validation execution br_on_cast_fail t 1 t 2 0x FB 0x 19 [ t 1 ] → [ t 2 ] validation execution any . convert_extern 0x FB 0x 1A [( ref null extern )] → [( ref null any )] validation execution extern . convert_any 0x FB 0x 1B [( ref null any )] → [( ref null extern )] validation execution ref . i 31 0x FB 0x 1C [ i 32 ] → [( ref i 31 )] validation execution i 31 . get_s 0x FB 0x 1D [ i 31 ref ] → [ i 32 ] validation execution i 31 . get_u 0x FB 0x 1E [ i 31 ref ] → [ i 32 ] validation execution (reserved) 0x FB 0x 1F … i 32 . trunc _sat_ f 32 _s 0x FC 0x 00 [ f 32 ] → [ i 32 ] validation execution ( operator ) i 32 . trunc _sat_ f 32 _u 0x FC 0x 01 [ f 32 ] → [ i 32 ] validation execution ( operator ) i 32 . trunc _sat_ f 64 _s 0x FC 0x 02 [ f 64 ] → [ i 32 ] validation execution ( operator ) i 32 . trunc _sat_ f 64 _u 0x FC 0x 03 [ f 64 ] → [ i 32 ] validation execution ( operator ) i 64 . trunc _sat_ f 32 _s 0x FC 0x 04 [ f 32 ] → [ i 64 ] validation execution ( operator ) i 64 . trunc _sat_ f 32 _u 0x FC 0x 05 [ f 32 ] → [ i 64 ] validation execution ( operator ) i 64 . trunc _sat_ f 64 _s 0x FC 0x 06 [ f 64 ] → [ i 64 ] validation execution ( operator ) i 64 . trunc _sat_ f 64 _u 0x FC 0x 07 [ f 64 ] → [ i 64 ] validation execution ( operator ) memory . init x y 0x FC 0x 08 [ at i 32 i 32 ] → [ ] validation execution data . drop x 0x FC 0x 09 [ ] → [ ] validation execution memory . copy x y 0x FC 0x 0A [ at 1 at 2 at ] → [ ] validation execution memory . fill y 0x FC 0x 0B [ at i 32 at ] → [ ] validation execution table . init x y 0x FC 0x 0C [ at i 32 i 32 ] → [ ] validation execution elem . drop x 0x FC 0x 0D [ ] → [ ] validation execution table . copy x y 0x FC 0x 0E [ at 1 at 2 at ] → [ ] validation execution table . grow x 0x FC 0x 0F [ t at ] → [ at ] validation execution table . size x 0x FC 0x 10 [ ] → [ at ] validation execution table . fill x 0x FC 0x 11 [ at t at ] → [ ] validation execution (reserved) 0x FC 0x 12 … v 128 . load x memarg 0x FD 0x 00 [ at ] → [ v 128 ] validation execution v 128 . load 8x8_s x memarg 0x FD 0x 01 [ at ] → [ v 128 ] validation execution v 128 . load 8x8_u x memarg 0x FD 0x 02 [ at ] → [ v 128 ] validation execution v 128 . load 16x4_s x memarg 0x FD 0x 03 [ at ] → [ v 128 ] validation execution v 128 . load 16x4_u x memarg 0x FD 0x 04 [ at ] → [ v 128 ] validation execution v 128 . load 32x2_s x memarg 0x FD 0x 05 [ at ] → [ v 128 ] validation execution v 128 . load 32x2_u x memarg 0x FD 0x 06 [ at ] → [ v 128 ] validation execution v 128 . load 8_splat x memarg 0x FD 0x 07 [ at ] → [ v 128 ] validation execution v 128 . load 16_splat x memarg 0x FD 0x 08 [ at ] → [ v 128 ] validation execution v 128 . load 32_splat x memarg 0x FD 0x 09 [ at ] → [ v 128 ] validation execution v 128 . load 64_splat x memarg 0x FD 0x 0A [ at ] → [ v 128 ] validation execution v 128 . store x memarg 0x FD 0x 0B [ at v 128 ] → [ ] validation execution v 128 . const i 128 0x FD 0x 0C [ ] → [ v 128 ] validation execution i 8 x 16 . shuffle laneidx 16 0x FD 0x 0D [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . swizzle 0x FD 0x 0E [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . splat 0x FD 0x 0F [ i 32 ] → [ v 128 ] validation execution i 16 x 8 . splat 0x FD 0x 10 [ i 32 ] → [ v 128 ] validation execution i 32 x 4 . splat 0x FD 0x 11 [ i 32 ] → [ v 128 ] validation execution i 64 x 2 . splat 0x FD 0x 12 [ i 64 ] → [ v 128 ] validation execution f 32 x 4 . splat 0x FD 0x 13 [ f 32 ] → [ v 128 ] validation execution f 64 x 2 . splat 0x FD 0x 14 [ f 64 ] → [ v 128 ] validation execution i 8 x 16 . extract_lane _s laneidx 0x FD 0x 15 [ v 128 ] → [ i 32 ] validation execution i 8 x 16 . extract_lane _u laneidx 0x FD 0x 16 [ v 128 ] → [ i 32 ] validation execution i 8 x 16 . replace_lane laneidx 0x FD 0x 17 [ v 128 i 32 ] → [ v 128 ] validation execution i 16 x 8 . extract_lane _s laneidx 0x FD 0x 18 [ v 128 ] → [ i 32 ] validation execution i 16 x 8 . extract_lane _u laneidx 0x FD 0x 19 [ v 128 ] → [ i 32 ] validation execution i 16 x 8 . replace_lane laneidx 0x FD 0x 1A [ v 128 i 32 ] → [ v 128 ] validation execution i 32 x 4 . extract_lane laneidx 0x FD 0x 1B [ v 128 ] → [ i 32 ] validation execution i 32 x 4 . replace_lane laneidx 0x FD 0x 1C [ v 128 i 32 ] → [ v 128 ] validation execution i 64 x 2 . extract_lane laneidx 0x FD 0x 1D [ v 128 ] → [ i 64 ] validation execution i 64 x 2 . replace_lane laneidx 0x FD 0x 1E [ v 128 i 64 ] → [ v 128 ] validation execution f 32 x 4 . extract_lane laneidx 0x FD 0x 1F [ v 128 ] → [ f 32 ] validation execution f 32 x 4 . replace_lane laneidx 0x FD 0x 20 [ v 128 f 32 ] → [ v 128 ] validation execution f 64 x 2 . extract_lane laneidx 0x FD 0x 21 [ v 128 ] → [ f 64 ] validation execution f 64 x 2 . replace_lane laneidx 0x FD 0x 22 [ v 128 f 64 ] → [ v 128 ] validation execution i 8 x 16 . eq 0x FD 0x 23 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . ne 0x FD 0x 24 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . lt _s 0x FD 0x 25 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . lt _u 0x FD 0x 26 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . gt _s 0x FD 0x 27 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . gt _u 0x FD 0x 28 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . le _s 0x FD 0x 29 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . le _u 0x FD 0x 2A [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . ge _s 0x FD 0x 2B [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . ge _u 0x FD 0x 2C [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . eq 0x FD 0x 2D [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . ne 0x FD 0x 2E [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . lt _s 0x FD 0x 2F [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . lt _u 0x FD 0x 30 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . gt _s 0x FD 0x 31 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . gt _u 0x FD 0x 32 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . le _s 0x FD 0x 33 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . le _u 0x FD 0x 34 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . ge _s 0x FD 0x 35 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . ge _u 0x FD 0x 36 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . eq 0x FD 0x 37 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . ne 0x FD 0x 38 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . lt _s 0x FD 0x 39 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . lt _u 0x FD 0x 3A [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . gt _s 0x FD 0x 3B [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . gt _u 0x FD 0x 3C [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . le _s 0x FD 0x 3D [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . le _u 0x FD 0x 3E [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . ge _s 0x FD 0x 3F [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . ge _u 0x FD 0x 40 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . eq 0x FD 0x 41 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . ne 0x FD 0x 42 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . lt 0x FD 0x 43 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . gt 0x FD 0x 44 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . le 0x FD 0x 45 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . ge 0x FD 0x 46 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . eq 0x FD 0x 47 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . ne 0x FD 0x 48 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . lt 0x FD 0x 49 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . gt 0x FD 0x 4A [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . le 0x FD 0x 4B [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . ge 0x FD 0x 4C [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) v 128 . not 0x FD 0x 4D [ v 128 ] → [ v 128 ] validation execution ( operator ) v 128 . and 0x FD 0x 4E [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) v 128 . andnot 0x FD 0x 4F [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) v 128 . or 0x FD 0x 50 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) v 128 . xor 0x FD 0x 51 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) v 128 . bitselect 0x FD 0x 52 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) v 128 . any_true 0x FD 0x 53 [ v 128 ] → [ i 32 ] validation execution v 128 . load 8_lane memarg laneidx 0x FD 0x 54 [ at v 128 ] → [ v 128 ] validation execution v 128 . load 16_lane memarg laneidx 0x FD 0x 55 [ at v 128 ] → [ v 128 ] validation execution v 128 . load 32_lane memarg laneidx 0x FD 0x 56 [ at v 128 ] → [ v 128 ] validation execution v 128 . load 64_lane memarg laneidx 0x FD 0x 57 [ at v 128 ] → [ v 128 ] validation execution v 128 . store 8_lane memarg laneidx 0x FD 0x 58 [ at v 128 ] → [ ] validation execution v 128 . store 16_lane memarg laneidx 0x FD 0x 59 [ at v 128 ] → [ ] validation execution v 128 . store 32_lane memarg laneidx 0x FD 0x 5A [ at v 128 ] → [ ] validation execution v 128 . store 64_lane memarg laneidx 0x FD 0x 5B [ at v 128 ] → [ ] validation execution v 128 . load 32_zero memarg 0x FD 0x 5C [ at ] → [ v 128 ] validation execution v 128 . load 64_zero memarg 0x FD 0x 5D [ at ] → [ v 128 ] validation execution f 32 x 4 . demote _f64x2_zero 0x FD 0x 5E [ v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . promote _ low _f32x4 0x FD 0x 5F [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . abs 0x FD 0x 60 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . neg 0x FD 0x 61 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . popcnt 0x FD 0x 62 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . all_true 0x FD 0x 63 [ v 128 ] → [ i 32 ] validation execution i 8 x 16 . bitmask 0x FD 0x 64 [ v 128 ] → [ i 32 ] validation execution ( operator ) i 8 x 16 . narrow _i16x8_s 0x FD 0x 65 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . narrow _i16x8_u 0x FD 0x 66 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . ceil 0x FD 0x 67 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . floor 0x FD 0x 68 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . trunc 0x FD 0x 69 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . nearest 0x FD 0x 6A [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . shl 0x FD 0x 6B [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . shr _s 0x FD 0x 6C [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . shr _u 0x FD 0x 6D [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . add 0x FD 0x 6E [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . add_sat _s 0x FD 0x 6F [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . add_sat _u 0x FD 0x 70 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . sub 0x FD 0x 71 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . sub_sat _s 0x FD 0x 72 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . sub_sat _u 0x FD 0x 73 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . ceil 0x FD 0x 74 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . floor 0x FD 0x 75 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . min _s 0x FD 0x 76 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . min _u 0x FD 0x 77 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . max _s 0x FD 0x 78 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . max _u 0x FD 0x 79 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . trunc 0x FD 0x 7A [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . avgr _u 0x FD 0x 7B [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . extadd_pairwise _i8x16_s 0x FD 0x 7C [ v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . extadd_pairwise _i8x16_u 0x FD 0x 7D [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . extadd_pairwise _i16x8_s 0x FD 0x 7E [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . extadd_pairwise _i16x8_u 0x FD 0x 7F [ v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . abs 0x FD 0x 80 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . neg 0x FD 0x 81 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . q 15 mulr_sat _s 0x FD 0x 82 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . all_true 0x FD 0x 83 0x 01 [ v 128 ] → [ i 32 ] validation execution i 16 x 8 . bitmask 0x FD 0x 84 0x 01 [ v 128 ] → [ i 32 ] validation execution ( operator ) i 16 x 8 . narrow _i32x4_s 0x FD 0x 85 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . narrow _i32x4_u 0x FD 0x 86 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . extend _ low _i8x16_s 0x FD 0x 87 0x 01 [ v 128 ] → [ v 128 ] validation execution i 16 x 8 . extend _ high _i8x16_s 0x FD 0x 88 0x 01 [ v 128 ] → [ v 128 ] validation execution i 16 x 8 . extend _ low _i8x16_u 0x FD 0x 89 0x 01 [ v 128 ] → [ v 128 ] validation execution i 16 x 8 . extend _ high _i8x16_u 0x FD 0x 8A 0x 01 [ v 128 ] → [ v 128 ] validation execution i 16 x 8 . shl 0x FD 0x 8B 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . shr _s 0x FD 0x 8C 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . shr _u 0x FD 0x 8D 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . add 0x FD 0x 8E 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . add _sat_s 0x FD 0x 8F 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . add _sat_u 0x FD 0x 90 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . sub 0x FD 0x 91 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . sub _sat_s 0x FD 0x 92 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . sub _sat_u 0x FD 0x 93 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . nearest 0x FD 0x 94 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . mul 0x FD 0x 95 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . min _s 0x FD 0x 96 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . min _u 0x FD 0x 97 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . max _s 0x FD 0x 98 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . max _u 0x FD 0x 99 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x 9A 0x 01 i 16 x 8 . avgr _u 0x FD 0x 9B 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . extmul _ low _i8x16_s 0x FD 0x 9C 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . extmul _ high _i8x16_s 0x FD 0x 9D 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . extmul _ low _i8x16_u 0x FD 0x 9E 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . extmul _ high _i8x16_u 0x FD 0x 9F 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . abs 0x FD 0x A0 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . neg 0x FD 0x A1 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x A2 0x 01 i 32 x 4 . all_true 0x FD 0x A3 0x 01 [ v 128 ] → [ i 32 ] validation execution i 32 x 4 . bitmask 0x FD 0x A4 0x 01 [ v 128 ] → [ i 32 ] validation execution ( operator ) (reserved) 0x FD 0x A5 0x 01 (reserved) 0x FD 0x A6 0x 01 i 32 x 4 . extend _ low _i16x8_s 0x FD 0x A7 0x 01 [ v 128 ] → [ v 128 ] validation execution i 32 x 4 . extend _ high _i16x8_s 0x FD 0x A8 0x 01 [ v 128 ] → [ v 128 ] validation execution i 32 x 4 . extend _ low _i16x8_u 0x FD 0x A9 0x 01 [ v 128 ] → [ v 128 ] validation execution i 32 x 4 . extend _ high _i16x8_u 0x FD 0x AA 0x 01 [ v 128 ] → [ v 128 ] validation execution i 32 x 4 . shl 0x FD 0x AB 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . shr _s 0x FD 0x AC 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . shr _u 0x FD 0x AD 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . add 0x FD 0x AE 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x AF 0x 01 (reserved) 0x FD 0x B0 0x 01 i 32 x 4 . sub 0x FD 0x B1 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x B2 0x 01 (reserved) 0x FD 0x B3 0x 01 (reserved) 0x FD 0x B4 0x 01 i 32 x 4 . mul 0x FD 0x B5 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . min _s 0x FD 0x B6 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . min _u 0x FD 0x B7 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . max _s 0x FD 0x B8 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . max _u 0x FD 0x B9 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . dot _i16x8_s 0x FD 0x BA 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . extmul _ low _i16x8_s 0x FD 0x BC 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . extmul _ high _i16x8_s 0x FD 0x BD 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . extmul _ low _i16x8_u 0x FD 0x BE 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . extmul _ high _i16x8_u 0x FD 0x BF 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . abs 0x FD 0x C0 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . neg 0x FD 0x C1 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x C2 0x 01 i 64 x 2 . all_true 0x FD 0x C3 0x 01 [ v 128 ] → [ i 32 ] validation execution i 64 x 2 . bitmask 0x FD 0x C4 0x 01 [ v 128 ] → [ i 32 ] validation execution ( operator ) (reserved) 0x FD 0x C5 0x 01 (reserved) 0x FD 0x C6 0x 01 i 64 x 2 . extend _ low _i32x4_s 0x FD 0x C7 0x 01 [ v 128 ] → [ v 128 ] validation execution i 64 x 2 . extend _ high _i32x4_s 0x FD 0x C8 0x 01 [ v 128 ] → [ v 128 ] validation execution i 64 x 2 . extend _ low _i32x4_u 0x FD 0x C9 0x 01 [ v 128 ] → [ v 128 ] validation execution i 64 x 2 . extend _ high _i32x4_u 0x FD 0x CA 0x 01 [ v 128 ] → [ v 128 ] validation execution i 64 x 2 . shl 0x FD 0x CB 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . shr _s 0x FD 0x CC 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . shr _u 0x FD 0x CD 0x 01 [ v 128 i 32 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . add 0x FD 0x CE 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x CF 0x 01 (reserved) 0x FD 0x D0 0x 01 i 64 x 2 . sub 0x FD 0x D1 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x D2 0x 01 (reserved) 0x FD 0x D3 0x 01 (reserved) 0x FD 0x D4 0x 01 i 64 x 2 . mul 0x FD 0x D5 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . eq 0x FD 0x D6 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . ne 0x FD 0x D7 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . lt _s 0x FD 0x D8 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . gt _s 0x FD 0x D9 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . le _s 0x FD 0x DA 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . ge _s 0x FD 0x DB 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . extmul _ low _i32x4_s 0x FD 0x DC 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . extmul _ high _i32x4_s 0x FD 0x DD 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . extmul _ low _i32x4_u 0x FD 0x DE 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . extmul _ high _i32x4_u 0x FD 0x DF 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . abs 0x FD 0x E0 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . neg 0x FD 0x E1 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x E2 0x 01 f 32 x 4 . sqrt 0x FD 0x E3 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . add 0x FD 0x E4 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . sub 0x FD 0x E5 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . mul 0x FD 0x E6 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . div 0x FD 0x E7 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . min 0x FD 0x E8 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . max 0x FD 0x E9 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . pmin 0x FD 0x EA 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . pmax 0x FD 0x EB 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . abs 0x FD 0x EC 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . neg 0x FD 0x ED 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x EE 0x 01 f 64 x 2 . sqrt 0x FD 0x EF 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . add 0x FD 0x F0 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . sub 0x FD 0x F1 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . mul 0x FD 0x F2 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . div 0x FD 0x F3 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . min 0x FD 0x F4 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . max 0x FD 0x F5 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . pmin 0x FD 0x F6 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . pmax 0x FD 0x F7 0x 01 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . trunc_sat _f32x4_s 0x FD 0x F8 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . trunc_sat _f32x4_u 0x FD 0x F9 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . convert _i32x4_s 0x FD 0x FA 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . convert _i32x4_u 0x FD 0x FB 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . trunc_sat _f64x2_s_zero 0x FD 0x FC 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . trunc_sat _f64x2_u_zero 0x FD 0x FD 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . convert _ low _i32x4_s 0x FD 0x FE 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . convert _ low _i32x4_u 0x FD 0x FF 0x 01 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . relaxed_swizzle 0x FD 0x 80 0x 02 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . relaxed_trunc _f32x4_s 0x FD 0x 81 0x 02 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . relaxed_trunc _f32x4_u 0x FD 0x 82 0x 02 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . relaxed_trunc _f64x2_s 0x FD 0x 83 0x 02 [ v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . relaxed_trunc _f64x2_u 0x FD 0x 84 0x 02 [ v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . relaxed_madd 0x FD 0x 85 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . relaxed_nmadd 0x FD 0x 86 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . relaxed_madd 0x FD 0x 87 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . relaxed_nmadd 0x FD 0x 88 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 8 x 16 . relaxed_laneselect 0x FD 0x 89 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . relaxed_laneselect 0x FD 0x 8A 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . relaxed_laneselect 0x FD 0x 8B 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 64 x 2 . relaxed_laneselect 0x FD 0x 8C 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . relaxed_min 0x FD 0x 8D 0x 02 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 32 x 4 . relaxed_max 0x FD 0x 8E 0x 02 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . relaxed_min 0x FD 0x 8F 0x 02 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) f 64 x 2 . relaxed_max 0x FD 0x 90 0x 02 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . relaxed_q15mulr _s 0x FD 0x 91 0x 02 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 16 x 8 . relaxed_dot _i8x16_i7x16_s 0x FD 0x 92 0x 02 [ v 128 v 128 ] → [ v 128 ] validation execution ( operator ) i 32 x 4 . relaxed_dot _i8x16_i7x16_add_s 0x FD 0x 93 0x 02 [ v 128 v 128 v 128 ] → [ v 128 ] validation execution ( operator ) (reserved) 0x FD 0x 94 0x 02 … (reserved) 0x FE (reserved) 0x FF 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. A.11 Index of Semantic Rules Well-formedness of Types Construct Judgement Numeric type C ⊢ numtype : ok Vector type C ⊢ vectype : ok Heap type C ⊢ heaptype : ok Reference type C ⊢ reftype : ok Value type C ⊢ valtype : ok Packed type C ⊢ packtype : ok Storage type C ⊢ storagetype : ok Field type C ⊢ fieldtype : ok Result type C ⊢ resulttype : ok Instruction type C ⊢ instrtype : ok Composite type C ⊢ comptype : ok Sub type C ⊢ subtype : ok Recursive type C ⊢ rectype : ok Defined type C ⊢ deftype : ok Block type C ⊢ blocktype : instrtype Tag type C ⊢ tagtype : ok Global type C ⊢ globaltype : ok Memory type C ⊢ memtype : ok Table type C ⊢ tabletype : ok External type C ⊢ externtype : ok Type definitions C ⊢ type ∗ : ok Typing of Static Constructs Construct Judgement Instruction S ; C ⊢ instr : instrtype Instruction sequence S ; C ⊢ instr ∗ : instrtype Catch clause C ⊢ catch : ok Expression C ⊢ expr : resulttype Limits C ⊢ limits : k Tag C ⊢ tag : tagtype Global C ⊢ global : globaltype Memory C ⊢ mem : memtype Table C ⊢ table : tabletype Function C ⊢ func : deftype Local C ⊢ local : localtype Element segment C ⊢ elem : reftype Element mode C ⊢ elemmode : reftype Data segment C ⊢ data : ok Data mode C ⊢ datamode : ok Start function C ⊢ start : ok Import C ⊢ import : externtype Export C ⊢ export : externtype Module ⊢ module : externtype ∗ → externtype ∗ Typing of Runtime Constructs Construct Judgement Value S ⊢ val : valtype Result S ⊢ result : resulttype Packed value S ⊢ packval : packtype Field value S ⊢ fieldval : storagetype External address S ⊢ externaddr : externtype Tag instance S ⊢ taginst : tagtype Global instance S ⊢ globalinst : globaltype Memory instance S ⊢ meminst : memtype Table instance S ⊢ tableinst : tabletype Function instance S ⊢ funcinst : deftype Data instance S ⊢ datainst : ok Element instance S ⊢ eleminst : t Structure instance S ⊢ structinst : ok Array instance S ⊢ arrayinst : ok Export instance S ⊢ exportinst : ok Module instance S ⊢ moduleinst : C Store ⊢ store : ok Configuration ⊢ config : [ t ∗ ] Thread S ; resulttype ? ⊢ thread : resulttype Frame S ⊢ frame : C Constantness Construct Judgement Constant expression C ⊢ expr const Constant instruction C ⊢ instr const Matching Construct Judgement Number type C ⊢ numtype 1 ≤ numtype 2 Vector type C ⊢ vectype 1 ≤ vectype 2 Heap type C ⊢ heaptype 1 ≤ heaptype 2 Reference type C ⊢ reftype 1 ≤ reftype 2 Value type C ⊢ valtype 1 ≤ valtype 2 Packed type C ⊢ packtype 1 ≤ packtype 2 Storage type C ⊢ storagetype 1 ≤ storagetype 2 Field type C ⊢ fieldtype 1 ≤ fieldtype 2 Result type C ⊢ resulttype 1 ≤ resulttype 2 Instruction type C ⊢ instrtype 1 ≤ instrtype 2 Composite type C ⊢ comptype 1 ≤ comptype 2 Defined type C ⊢ deftype 1 ≤ deftype 2 Limits C ⊢ limits 1 ≤ limits 2 Tag type C ⊢ tagtype 1 ≤ tagtype 2 Global type C ⊢ globaltype 1 ≤ globaltype 2 Memory type C ⊢ memtype 1 ≤ memtype 2 Table type C ⊢ tabletype 1 ≤ tabletype 2 External type C ⊢ externtype 1 ≤ externtype 2 Store Extension 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 Execution Construct Judgement Instruction S ; F ; instr ∗ ↪ S ′ ; F ′ ; instr ′ ∗ Expression S ; F ; expr ↪ S ′ ; F ′ ; expr ′ Conformance Document conventions Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification. All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119] Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example" , like this: This is an example of an informative example. Informative notes begin with the word “Note” and are set apart from the normative text with class="note" , like this: Note, this is an informative note. Conformant Algorithms Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm. Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize. References Normative References [IEEE-754-2019] IEEE Standard for Floating-Point Arithmetic . 22 July 2019. URL: https://ieeexplore.ieee.org/document/8766229 [RFC2119] S. Bradner. Key words for use in RFCs to Indicate Requirement Levels . March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119 [UNICODE] The Unicode Standard . URL: https://www.unicode.org/versions/latest/