Skip to content

Latest commit

 

History

History
1647 lines (1095 loc) · 41.2 KB

instructions.rst

File metadata and controls

1647 lines (1095 loc) · 41.2 KB
.. index:: instruction, function type, context, value, operand stack, ! polymorphism, ! bottom type

Instructions

:ref:`Instructions <syntax-instr>` are classified by stack types [t_1^\ast] \to [t_2^\ast] that describe how instructions manipulate the :ref:`operand stack <stack>`.

\begin{array}{llll}
\production{stack type} & \stacktype &::=&
  [\opdtype^\ast] \to [\opdtype^\ast] \\
\production{operand type} & \opdtype &::=&
  \valtype ~|~ \bot \\
\end{array}

The types describe the required input stack with operand types t_1^\ast that an instruction pops off and the provided output stack with result values of types t_2^\ast that it pushes back. Stack types are akin to :ref:`function types <syntax-functype>`, except that they allow individual operands to be classified as \bot (bottom), indicating that the type is unconstrained. As an auxiliary notion, an operand type t_1 matches another operand type t_2, if t_1 is either \bot or equal to t_2. This is extended to stack types in a point-wise manner.

\frac{
}{
  \vdash t \leq t
}
\qquad
\frac{
}{
  \vdash \bot \leq t
}
\frac{
  (\vdash t \leq t')^\ast
}{
  \vdash [t^\ast] \leq [{t'}^\ast]
}

Note

For example, the instruction \I32.\ADD has type [\I32~\I32] \to [\I32], consuming two |I32| values and producing one.

Typing extends to :ref:`instruction sequences <valid-instr-seq>` \instr^\ast. Such a sequence has a :ref:`function type <syntax-functype>` [t_1^\ast] \to [t_2^\ast] if the accumulative effect of executing the instructions is consuming values of types t_1^\ast off the operand stack and pushing new values of types t_2^\ast.

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 :ref:`value type <syntax-valtype>` t of one or several individual operands is unconstrained. That is the case for all :ref:`parametric instructions <valid-instr-parametric>` like |DROP| and |SELECT|.
  • stack-polymorphic: the entire (or most of the) :ref:`function type <syntax-functype>` [t_1^\ast] \to [t_2^\ast] of the instruction is unconstrained. That is the case for all :ref:`control instructions <valid-instr-control>` that perform an unconditional control transfer, such as |UNREACHABLE|, |BR|, |BRTABLE|, and |RETURN|.

In both cases, the unconstrained types or type sequences can be chosen arbitrarily, as long as they meet the constraints imposed for the surrounding parts of the program.

Note

For example, the |SELECT| instruction is valid with type [t~t~\I32] \to [t], for any possible :ref:`number type <syntax-numtype>` t. Consequently, both instruction sequences

(\I32.\CONST~1)~~(\I32.\CONST~2)~~(\I32.\CONST~3)~~\SELECT{}

and

(\F64.\CONST~1.0)~~(\F64.\CONST~2.0)~~(\I32.\CONST~3)~~\SELECT{}

are valid, with t in the typing of |SELECT| being instantiated to |I32| or |F64|, respectively.

The |UNREACHABLE| instruction is valid with type [t_1^\ast] \to [t_2^\ast] for any possible sequences of value types t_1^\ast and t_2^\ast. Consequently,

\UNREACHABLE~~\I32.\ADD

is valid by assuming type [] \to [\I32~\I32] for the |UNREACHABLE| instruction. In contrast,

\UNREACHABLE~~(\I64.\CONST~0)~~\I32.\ADD

is invalid, because there is no possible type to pick for the |UNREACHABLE| instruction that would make the sequence well-typed.

The :ref:`Appendix <algo-valid>` describes a type checking :ref:`algorithm <algo-valid>` that efficiently implements validation of instruction sequences as prescribed by the rules given here.

.. index:: numeric instruction
   pair: validation; instruction
   single: abstract syntax; instruction

Numeric Instructions

t\K{.}\CONST~c

  • The instruction is valid with type [] \to [t].
\frac{
}{
  C \vdashinstr t\K{.}\CONST~c : [] \to [t]
}

t\K{.}\unop

  • The instruction is valid with type [t] \to [t].
\frac{
}{
  C \vdashinstr t\K{.}\unop : [t] \to [t]
}

t\K{.}\binop

  • The instruction is valid with type [t~t] \to [t].
\frac{
}{
  C \vdashinstr t\K{.}\binop : [t~t] \to [t]
}

t\K{.}\testop

  • The instruction is valid with type [t] \to [\I32].
\frac{
}{
  C \vdashinstr t\K{.}\testop : [t] \to [\I32]
}

t\K{.}\relop

  • The instruction is valid with type [t~t] \to [\I32].
\frac{
}{
  C \vdashinstr t\K{.}\relop : [t~t] \to [\I32]
}

t_2\K{.}\cvtop\K{\_}t_1\K{\_}\sx^?

  • The instruction is valid with type [t_1] \to [t_2].
\frac{
}{
  C \vdashinstr t_2\K{.}\cvtop\K{\_}t_1\K{\_}\sx^? : [t_1] \to [t_2]
}
.. index:: reference instructions, reference type
   pair: validation; instruction
   single: abstract syntax; instruction

Reference Instructions

\REFNULL~t

  • The instruction is valid with type [] \to [t].
\frac{
}{
  C \vdashinstr \REFNULL~t : [] \to [t]
}

Note

In future versions of WebAssembly, there may be reference types for which no null reference is allowed.

\REFISNULL

  • The instruction is valid with type [t] \to [\I32], for any :ref:`reference type <syntax-reftype>` t.
\frac{
  t = \reftype
}{
  C \vdashinstr \REFISNULL : [t] \to [\I32]
}

\REFFUNC~x

  • The function C.\CFUNCS[x] must be defined in the context.
  • The :ref:`function index <syntax-funcidx>` x must be contained in C.\CREFS.
  • The instruction is valid with type [] \to [\FUNCREF].
\frac{
  C.\CFUNCS[x] = \functype
  \qquad
  x \in C.\CREFS
}{
  C \vdashinstr \REFFUNC~x : [] \to [\FUNCREF]
}
.. index:: vector instruction
   pair: validation; instruction
   single: abstract syntax; instruction

Vector Instructions

Vector instructions can have a prefix to describe the :ref:`shape <syntax-vec-shape>` of the operand. Packed numeric types, |I8| and |I16|, are not :ref:`value type <syntax-valtype>`, we define an auxiliary function to map such packed types into value types:

\begin{array}{lll@{\qquad}l}
\unpacked(\K{i8x16}) &=& \I32 \\
\unpacked(\K{i16x8}) &=& \I32 \\
\unpacked(t\K{x}N) &=& t
\end{array}

We also define an auxiliary function to get number of packed numeric types in a |V128|, dimension:

\begin{array}{lll@{\qquad}l}
\dim(t\K{x}N) &=& N
\end{array}

\V128\K{.}\VCONST~c

  • The instruction is valid with type [] \to [\V128].
\frac{
}{
  C \vdashinstr \V128\K{.}\VCONST~c : [] \to [\V128]
}

\V128\K{.}\vvunop

  • The instruction is valid with type [\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \V128\K{.}\vvunop : [\V128] \to [\V128]
}

\V128\K{.}\vvbinop

  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \V128\K{.}\vvbinop : [\V128~\V128] \to [\V128]
}

\V128\K{.}\vvternop

  • The instruction is valid with type [\V128~\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \V128\K{.}\vvternop : [\V128~\V128~\V128] \to [\V128]
}

\V128\K{.}\vvtestop

  • The instruction is valid with type [\V128] \to [\I32].
\frac{
}{
  C \vdashinstr \V128\K{.}\vvtestop : [\V128] \to [\I32]
}

\K{i8x16.}\SWIZZLE

  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \K{i8x16.}\SWIZZLE : [\V128~\V128] \to [\V128]
}

\K{i8x16.}\SHUFFLE~\laneidx^{16}

  • For all \laneidx_i, in \laneidx^{16}, \laneidx_i must be smaller than 32.
  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
  (\laneidx < 32)^{16}
}{
  C \vdashinstr \K{i8x16.}\SHUFFLE~\laneidx^{16} : [\V128~\V128] \to [\V128]
}

\shape\K{.}\SPLAT

  • Let t be \unpacked(\shape).
  • The instruction is valid with type [t] \to [\V128].
\frac{
}{
  C \vdashinstr \shape\K{.}\SPLAT : [\unpacked(\shape)] \to [\V128]
}

\shape\K{.}\EXTRACTLANE\K{\_}\sx^?~\laneidx

  • The lane index \laneidx must be smaller than \dim(\shape).
  • The instruction is valid with type [\V128] \to [\unpacked(\shape)].
\frac{
  \laneidx < \dim(\shape)
}{
  C \vdashinstr t\K{x}N\K{.}\EXTRACTLANE\K{\_}\sx^?~\laneidx : [\V128] \to [\unpacked(\shape)]
}

\shape\K{.}\REPLACELANE~\laneidx

  • The lane index \laneidx must be smaller than \dim(\shape).
  • Let t be \unpacked(\shape).
  • The instruction is valid with type [\V128~t] \to [\V128].
\frac{
  \laneidx < \dim(\shape)
}{
  C \vdashinstr \shape\K{.}\REPLACELANE~\laneidx : [\V128~\unpacked(\shape)] \to [\V128]
}

\shape\K{.}\vunop

  • The instruction is valid with type [\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \shape\K{.}\vunop : [\V128] \to [\V128]
}

\shape\K{.}\vbinop

  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \shape\K{.}\vbinop : [\V128~\V128] \to [\V128]
}

\shape\K{.}\vrelop

  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \shape\K{.}\vrelop : [\V128~\V128] \to [\V128]
}

\ishape\K{.}\vishiftop

  • The instruction is valid with type [\V128~\I32] \to [\V128].
\frac{
}{
  C \vdashinstr \ishape\K{.}\vishiftop : [\V128~\I32] \to [\V128]
}

\shape\K{.}\vtestop

  • The instruction is valid with type [\V128] \to [\I32].
\frac{
}{
  C \vdashinstr \shape\K{.}\vtestop : [\V128] \to [\I32]
}

\shape\K{.}\vcvtop\K{\_}\half^?\K{\_}\shape\K{\_}\sx^?\K{\_zero}^?

  • The instruction is valid with type [\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \shape\K{.}\vcvtop\K{\_}\half^?\K{\_}\shape\K{\_}\sx^?\K{\_zero}^? : [\V128] \to [\V128]
}

\ishape_1\K{.}\NARROW\K{\_}\ishape_2\K{\_}\sx

  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \ishape_1\K{.}\NARROW\K{\_}\ishape_2\K{\_}\sx : [\V128~\V128] \to [\V128]
}

\ishape\K{.}\BITMASK

  • The instruction is valid with type [\V128] \to [\I32].
\frac{
}{
  C \vdashinstr \ishape\K{.}\BITMASK : [\V128] \to [\I32]
}

\ishape_1\K{.}\DOT\K{\_}\ishape_2\K{\_s}

  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \ishape_1\K{.}\DOT\K{\_}\ishape_2\K{\_s} : [\V128~\V128] \to [\V128]
}

\ishape_1\K{.}\EXTMUL\K{\_}\half\K{\_}\ishape_2\K{\_}\sx

  • The instruction is valid with type [\V128~\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \ishape_1\K{.}\EXTMUL\K{\_}\half\K{\_}\ishape_2\K{\_}\sx : [\V128~\V128] \to [\V128]
}

\ishape_1\K{.}\EXTADDPAIRWISE\K{\_}\ishape_2\K{\_}\sx

  • The instruction is valid with type [\V128] \to [\V128].
\frac{
}{
  C \vdashinstr \ishape_1\K{.}\EXTADDPAIRWISE\K{\_}\ishape_2\K{\_}\sx : [\V128] \to [\V128]
}
.. index:: parametric instructions, value type, polymorphism
   pair: validation; instruction
   single: abstract syntax; instruction

Parametric Instructions

\DROP

  • The instruction is valid with type [t] \to [], for any :ref:`value type <syntax-valtype>` t.
\frac{
}{
  C \vdashinstr \DROP : [t] \to []
}

Note

Both |DROP| and |SELECT| without annotation are :ref:`value-polymorphic <polymorphism>` instructions.

\SELECT~(t^\ast)^?

  • If t^\ast is present, then:
    • The length of t^\ast must be 1.
    • Then the instruction is valid with type [t^\ast~t^\ast~\I32] \to [t^\ast].
  • Else:
    • The instruction is valid with type [t~t~\I32] \to [t], for any :ref:`operand type <syntax-opdtype>` t that :ref:`matches <match-opdtype>` some :ref:`number type <syntax-numtype>` or :ref:`vector type <syntax-vectype>`.
\frac{
}{
  C \vdashinstr \SELECT~t : [t~t~\I32] \to [t]
}
\qquad
\frac{
  \vdash t \leq \numtype
}{
  C \vdashinstr \SELECT : [t~t~\I32] \to [t]
}
\qquad
\frac{
  \vdash t \leq \vectype
}{
  C \vdashinstr \SELECT : [t~t~\I32] \to [t]
}

Note

In future versions of WebAssembly, |SELECT| may allow more than one value per choice.

.. index:: variable instructions, local index, global index, context
   pair: validation; instruction
   single: abstract syntax; instruction

Variable Instructions

\LOCALGET~x

  • The local C.\CLOCALS[x] must be defined in the context.
  • Let t be the :ref:`value type <syntax-valtype>` C.\CLOCALS[x].
  • Then the instruction is valid with type [] \to [t].
\frac{
  C.\CLOCALS[x] = t
}{
  C \vdashinstr \LOCALGET~x : [] \to [t]
}

\LOCALSET~x

  • The local C.\CLOCALS[x] must be defined in the context.
  • Let t be the :ref:`value type <syntax-valtype>` C.\CLOCALS[x].
  • Then the instruction is valid with type [t] \to [].
\frac{
  C.\CLOCALS[x] = t
}{
  C \vdashinstr \LOCALSET~x : [t] \to []
}

\LOCALTEE~x

  • The local C.\CLOCALS[x] must be defined in the context.
  • Let t be the :ref:`value type <syntax-valtype>` C.\CLOCALS[x].
  • Then the instruction is valid with type [t] \to [t].
\frac{
  C.\CLOCALS[x] = t
}{
  C \vdashinstr \LOCALTEE~x : [t] \to [t]
}

\GLOBALGET~x

  • The global C.\CGLOBALS[x] must be defined in the context.
  • Let \mut~t be the :ref:`global type <syntax-globaltype>` C.\CGLOBALS[x].
  • Then the instruction is valid with type [] \to [t].
\frac{
  C.\CGLOBALS[x] = \mut~t
}{
  C \vdashinstr \GLOBALGET~x : [] \to [t]
}

\GLOBALSET~x

  • The global C.\CGLOBALS[x] must be defined in the context.
  • Let \mut~t be the :ref:`global type <syntax-globaltype>` C.\CGLOBALS[x].
  • The mutability \mut must be |MVAR|.
  • Then the instruction is valid with type [t] \to [].
\frac{
  C.\CGLOBALS[x] = \MVAR~t
}{
  C \vdashinstr \GLOBALSET~x : [t] \to []
}
.. index:: table instruction, table index, context
   pair: validation; instruction
   single: abstract syntax; instruction

Table Instructions

\TABLEGET~x

  • The table C.\CTABLES[x] must be defined in the context.
  • Let \limits~t be the :ref:`table type <syntax-tabletype>` C.\CTABLES[x].
  • Then the instruction is valid with type [\I32] \to [t].
\frac{
  C.\CTABLES[x] = \limits~t
}{
  C \vdashinstr \TABLEGET~x : [\I32] \to [t]
}

\TABLESET~x

  • The table C.\CTABLES[x] must be defined in the context.
  • Let \limits~t be the :ref:`table type <syntax-tabletype>` C.\CTABLES[x].
  • Then the instruction is valid with type [\I32~t] \to [].
\frac{
  C.\CTABLES[x] = t
}{
  C \vdashinstr \TABLESET~x : [\I32~t] \to []
}

\TABLESIZE~x

  • The table C.\CTABLES[x] must be defined in the context.
  • Then the instruction is valid with type [] \to [\I32].
\frac{
  C.\CTABLES[x] = \tabletype
}{
  C \vdashinstr \TABLESIZE~x : [] \to [\I32]
}

\TABLEGROW~x

  • The table C.\CTABLES[x] must be defined in the context.
  • Let \limits~t be the :ref:`table type <syntax-tabletype>` C.\CTABLES[x].
  • Then the instruction is valid with type [t~\I32] \to [\I32].
\frac{
  C.\CTABLES[x] = \limits~t
}{
  C \vdashinstr \TABLEGROW~x : [t~\I32] \to [\I32]
}

\TABLEFILL~x

  • The table C.\CTABLES[x] must be defined in the context.
  • Let \limits~t be the :ref:`table type <syntax-tabletype>` C.\CTABLES[x].
  • Then the instruction is valid with type [\I32~t~\I32] \to [].
\frac{
  C.\CTABLES[x] = \limits~t
}{
  C \vdashinstr \TABLEFILL~x : [\I32~t~\I32] \to []
}

\TABLECOPY~x~y

  • The table C.\CTABLES[x] must be defined in the context.
  • Let \limits_1~t_1 be the :ref:`table type <syntax-tabletype>` C.\CTABLES[x].
  • The table C.\CTABLES[y] must be defined in the context.
  • Let \limits_2~t_2 be the :ref:`table type <syntax-tabletype>` C.\CTABLES[y].
  • The :ref:`reference type <syntax-reftype>` t_1 must be the same as t_2.
  • Then the instruction is valid with type [\I32~\I32~\I32] \to [].
\frac{
  C.\CTABLES[x] = \limits_1~t
  \qquad
  C.\CTABLES[x] = \limits_2~t
}{
  C \vdashinstr \TABLECOPY~x~y : [\I32~\I32~\I32] \to []
}

\TABLEINIT~x~y

  • The table C.\CTABLES[x] must be defined in the context.
  • Let \limits~t_1 be the :ref:`table type <syntax-tabletype>` C.\CTABLES[x].
  • The element segment C.\CELEMS[y] must be defined in the context.
  • Let t_2 be the :ref:`reference type <syntax-reftype>` C.\CELEMS[y].
  • The :ref:`reference type <syntax-reftype>` t_1 must be the same as t_2.
  • Then the instruction is valid with type [\I32~\I32~\I32] \to [].
\frac{
  C.\CTABLES[x] = \limits_1~t
  \qquad
  C.\CELEMS[y] = t
}{
  C \vdashinstr \TABLEINIT~x~y : [\I32~\I32~\I32] \to []
}

\ELEMDROP~x

  • The element segment C.\CELEMS[x] must be defined in the context.
  • Then the instruction is valid with type [] \to [].
\frac{
  C.\CELEMS[x] = t
}{
  C \vdashinstr \ELEMDROP~x : [] \to []
}
.. index:: memory instruction, memory index, context
   pair: validation; instruction
   single: abstract syntax; instruction

Memory Instructions

t\K{.}\LOAD~\memarg

  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than the :ref:`bit width <syntax-numtype>` of t divided by 8.
  • Then the instruction is valid with type [\I32] \to [t].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} \leq |t|/8
}{
  C \vdashinstr t\K{.load}~\memarg : [\I32] \to [t]
}

t\K{.}\LOAD{N}\K{\_}\sx~\memarg

  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than N/8.
  • Then the instruction is valid with type [\I32] \to [t].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} \leq N/8
}{
  C \vdashinstr t\K{.load}N\K{\_}\sx~\memarg : [\I32] \to [t]
}

t\K{.}\STORE~\memarg

  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than the :ref:`bit width <syntax-numtype>` of t divided by 8.
  • Then the instruction is valid with type [\I32~t] \to [].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} \leq |t|/8
}{
  C \vdashinstr t\K{.store}~\memarg : [\I32~t] \to []
}

t\K{.}\STORE{N}~\memarg

  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than N/8.
  • Then the instruction is valid with type [\I32~t] \to [].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} \leq N/8
}{
  C \vdashinstr t\K{.store}N~\memarg : [\I32~t] \to []
}

\K{v128.}\LOAD{N}\K{x}M\_\sx~\memarg

  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than N/8 \cdot M.
  • Then the instruction is valid with type [\I32] \to [\V128].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} \leq N/8 \cdot M
}{
  C \vdashinstr \K{v128.}\K{.}\LOAD{N}\K{x}M\_\sx~\memarg : [\I32] \to [\V128]
}

\K{v128.}\LOAD{N}\K{\_splat}~\memarg

  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than N/8.
  • Then the instruction is valid with type [\I32] \to [\V128].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} \leq N/8
}{
  C \vdashinstr \K{v128.}\LOAD{N}\K{\_splat}~\memarg : [\I32] \to [\V128]
}

\K{v128.}\LOAD{N}\K{\_zero}~\memarg

  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than N/8.
  • Then the instruction is valid with type [\I32] \to [\V128].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} \leq N/8
}{
  C \vdashinstr \K{v128.}\LOAD{N}\K{\_zero}~\memarg : [\I32] \to [\V128]
}

\K{v128.}\LOAD{N}\K{\_lane}~\memarg~\laneidx

  • The lane index \laneidx must be smaller than 128/N.
  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than N/8.
  • Then the instruction is valid with type [\I32~\V128] \to [\V128].
\frac{
  \laneidx < 128/N
  \qquad
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} < N/8
}{
  C \vdashinstr \K{v128.}\LOAD{N}\K{\_lane}~\memarg~\laneidx : [\I32~\V128] \to [\V128]
}

\K{v128.}\STORE{N}\K{\_lane}~\memarg~\laneidx

  • The lane index \laneidx must be smaller than 128/N.
  • The memory C.\CMEMS[0] must be defined in the context.
  • The alignment 2^{\memarg.\ALIGN} must not be larger than N/8.
  • Then the instruction is valid with type [\I32~\V128] \to [\V128].
\frac{
  \laneidx < 128/N
  \qquad
  C.\CMEMS[0] = \memtype
  \qquad
  2^{\memarg.\ALIGN} < N/8
}{
  C \vdashinstr \K{v128.}\STORE{N}\K{\_lane}~\memarg~\laneidx : [\I32~\V128] \to []
}

\MEMORYSIZE

  • The memory C.\CMEMS[0] must be defined in the context.
  • Then the instruction is valid with type [] \to [\I32].
\frac{
  C.\CMEMS[0] = \memtype
}{
  C \vdashinstr \MEMORYSIZE : [] \to [\I32]
}

\MEMORYGROW

  • The memory C.\CMEMS[0] must be defined in the context.
  • Then the instruction is valid with type [\I32] \to [\I32].
\frac{
  C.\CMEMS[0] = \memtype
}{
  C \vdashinstr \MEMORYGROW : [\I32] \to [\I32]
}

\MEMORYFILL

  • The memory C.\CMEMS[0] must be defined in the context.
  • Then the instruction is valid with type [\I32~\I32~\I32] \to [].
\frac{
  C.\CMEMS[0] = \memtype
}{
  C \vdashinstr \MEMORYFILL : [\I32~\I32~\I32] \to []
}

\MEMORYCOPY

  • The memory C.\CMEMS[0] must be defined in the context.
  • Then the instruction is valid with type [\I32~\I32~\I32] \to [].
\frac{
  C.\CMEMS[0] = \memtype
}{
  C \vdashinstr \MEMORYCOPY : [\I32~\I32~\I32] \to []
}

\MEMORYINIT~x

  • The memory C.\CMEMS[0] must be defined in the context.
  • The data segment C.\CDATAS[x] must be defined in the context.
  • Then the instruction is valid with type [\I32~\I32~\I32] \to [].
\frac{
  C.\CMEMS[0] = \memtype
  \qquad
  C.\CDATAS[x] = {\ok}
}{
  C \vdashinstr \MEMORYINIT~x : [\I32~\I32~\I32] \to []
}

\DATADROP~x

  • The data segment C.\CDATAS[x] must be defined in the context.
  • Then the instruction is valid with type [] \to [].
\frac{
  C.\CDATAS[x] = {\ok}
}{
  C \vdashinstr \DATADROP~x : [] \to []
}
.. index:: control instructions, structured control, label, block, branch, block type, label index, function index, type index, vector, polymorphism, context
   pair: validation; instruction
   single: abstract syntax; instruction

Control Instructions

\NOP

  • The instruction is valid with type [] \to [].
\frac{
}{
  C \vdashinstr \NOP : [] \to []
}

\UNREACHABLE

  • The instruction is valid with type [t_1^\ast] \to [t_2^\ast], for any sequences of :ref:`value types <syntax-valtype>` t_1^\ast and t_2^\ast.
\frac{
}{
  C \vdashinstr \UNREACHABLE : [t_1^\ast] \to [t_2^\ast]
}

Note

The |UNREACHABLE| instruction is :ref:`stack-polymorphic <polymorphism>`.

\BLOCK~\blocktype~\instr^\ast~\END

  • The :ref:`block type <syntax-blocktype>` must be :ref:`valid <valid-blocktype>` as some :ref:`function type <syntax-functype>` [t_1^\ast] \to [t_2^\ast].
  • Let C' be the same :ref:`context <context>` as C, but with the :ref:`result type <syntax-resulttype>` [t_2^\ast] prepended to the |CLABELS| vector.
  • Under context C', the instruction sequence \instr^\ast must be :ref:`valid <valid-instr-seq>` with type [t_1^\ast] \to [t_2^\ast].
  • Then the compound instruction is valid with type [t_1^\ast] \to [t_2^\ast].
\frac{
  C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast]
  \qquad
  C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_2^\ast]
}{
  C \vdashinstr \BLOCK~\blocktype~\instr^\ast~\END : [t_1^\ast] \to [t_2^\ast]
}

Note

The :ref:`notation <notation-extend>` C,\CLABELS\,[t^\ast] inserts the new label type at index 0, shifting all others.

\LOOP~\blocktype~\instr^\ast~\END

  • The :ref:`block type <syntax-blocktype>` must be :ref:`valid <valid-blocktype>` as some :ref:`function type <syntax-functype>` [t_1^\ast] \to [t_2^\ast].
  • Let C' be the same :ref:`context <context>` as C, but with the :ref:`result type <syntax-resulttype>` [t_1^\ast] prepended to the |CLABELS| vector.
  • Under context C', the instruction sequence \instr^\ast must be :ref:`valid <valid-instr-seq>` with type [t_1^\ast] \to [t_2^\ast].
  • Then the compound instruction is valid with type [t_1^\ast] \to [t_2^\ast].
\frac{
  C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast]
  \qquad
  C,\CLABELS\,[t_1^\ast] \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_2^\ast]
}{
  C \vdashinstr \LOOP~\blocktype~\instr^\ast~\END : [t_1^\ast] \to [t_2^\ast]
}

Note

The :ref:`notation <notation-extend>` C,\CLABELS\,[t^\ast] inserts the new label type at index 0, shifting all others.

\IF~\blocktype~\instr_1^\ast~\ELSE~\instr_2^\ast~\END

  • The :ref:`block type <syntax-blocktype>` must be :ref:`valid <valid-blocktype>` as some :ref:`function type <syntax-functype>` [t_1^\ast] \to [t_2^\ast].
  • Let C' be the same :ref:`context <context>` as C, but with the :ref:`result type <syntax-resulttype>` [t_2^\ast] prepended to the |CLABELS| vector.
  • Under context C', the instruction sequence \instr_1^\ast must be :ref:`valid <valid-instr-seq>` with type [t_1^\ast] \to [t_2^\ast].
  • Under context C', the instruction sequence \instr_2^\ast must be :ref:`valid <valid-instr-seq>` with type [t_1^\ast] \to [t_2^\ast].
  • Then the compound instruction is valid with type [t_1^\ast~\I32] \to [t_2^\ast].
\frac{
  C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast]
  \qquad
  C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr_1^\ast : [t_1^\ast] \to [t_2^\ast]
  \qquad
  C,\CLABELS\,[t_2^\ast] \vdashinstrseq \instr_2^\ast : [t_1^\ast] \to [t_2^\ast]
}{
  C \vdashinstr \IF~\blocktype~\instr_1^\ast~\ELSE~\instr_2^\ast~\END : [t_1^\ast~\I32] \to [t_2^\ast]
}

Note

The :ref:`notation <notation-extend>` C,\CLABELS\,[t^\ast] inserts the new label type at index 0, shifting all others.

\BR~l

  • The label C.\CLABELS[l] must be defined in the context.
  • Let [t^\ast] be the :ref:`result type <syntax-resulttype>` C.\CLABELS[l].
  • Then the instruction is valid with type [t_1^\ast~t^\ast] \to [t_2^\ast], for any sequences of :ref:`value types <syntax-valtype>` t_1^\ast and t_2^\ast.
\frac{
  C.\CLABELS[l] = [t^\ast]
}{
  C \vdashinstr \BR~l : [t_1^\ast~t^\ast] \to [t_2^\ast]
}

Note

The :ref:`label index <syntax-labelidx>` space in the :ref:`context <context>` C contains the most recent label first, so that C.\CLABELS[l] performs a relative lookup as expected.

The |BR| instruction is :ref:`stack-polymorphic <polymorphism>`.

\BRIF~l

  • The label C.\CLABELS[l] must be defined in the context.
  • Let [t^\ast] be the :ref:`result type <syntax-resulttype>` C.\CLABELS[l].
  • Then the instruction is valid with type [t^\ast~\I32] \to [t^\ast].
\frac{
  C.\CLABELS[l] = [t^\ast]
}{
  C \vdashinstr \BRIF~l : [t^\ast~\I32] \to [t^\ast]
}

Note

The :ref:`label index <syntax-labelidx>` space in the :ref:`context <context>` C contains the most recent label first, so that C.\CLABELS[l] performs a relative lookup as expected.

\BRTABLE~l^\ast~l_N

  • The label C.\CLABELS[l_N] must be defined in the context.
  • For all l_i in l^\ast, the label C.\CLABELS[l_i] must be defined in the context.
  • There must be a :ref:`result type <syntax-resulttype>` [t^\ast], such that:
    • For each :ref:`operand type <syntax-opdtype>` t_j in t^\ast and corresponding type t'_{Nj} in C.\CLABELS[l_N], t_j :ref:`matches <match-opdtype>` t'_{Nj}.
    • For all l_i in l^\ast, and for each :ref:`operand type <syntax-opdtype>` t_j in t^\ast and corresponding type t'_{ij} in C.\CLABELS[l_i], t_j :ref:`matches <match-opdtype>` t'_{ij}.
  • Then the instruction is valid with type [t_1^\ast~t^\ast~\I32] \to [t_2^\ast], for any sequences of :ref:`value types <syntax-valtype>` t_1^\ast and t_2^\ast.
\frac{
  (\vdash [t^\ast] \leq C.\CLABELS[l])^\ast
  \qquad
  \vdash [t^\ast] \leq C.\CLABELS[l_N]
}{
  C \vdashinstr \BRTABLE~l^\ast~l_N : [t_1^\ast~t^\ast~\I32] \to [t_2^\ast]
}

Note

The :ref:`label index <syntax-labelidx>` space in the :ref:`context <context>` C contains the most recent label first, so that C.\CLABELS[l_i] performs a relative lookup as expected.

The |BRTABLE| instruction is :ref:`stack-polymorphic <polymorphism>`.

\RETURN

  • The return type C.\CRETURN must not be absent in the context.
  • Let [t^\ast] be the :ref:`result type <syntax-resulttype>` of C.\CRETURN.
  • Then the instruction is valid with type [t_1^\ast~t^\ast] \to [t_2^\ast], for any sequences of :ref:`value types <syntax-valtype>` t_1^\ast and t_2^\ast.
\frac{
  C.\CRETURN = [t^\ast]
}{
  C \vdashinstr \RETURN : [t_1^\ast~t^\ast] \to [t_2^\ast]
}

Note

The |RETURN| instruction is :ref:`stack-polymorphic <polymorphism>`.

C.\CRETURN is absent (set to \epsilon) when validating an :ref:`expression <valid-expr>` that is not a function body. This differs from it being set to the empty result type ([\epsilon]), which is the case for functions not returning anything.

\CALL~x

  • The function C.\CFUNCS[x] must be defined in the context.
  • Then the instruction is valid with type C.\CFUNCS[x].
\frac{
  C.\CFUNCS[x] = [t_1^\ast] \to [t_2^\ast]
}{
  C \vdashinstr \CALL~x : [t_1^\ast] \to [t_2^\ast]
}

\CALLINDIRECT~x~y

  • The table C.\CTABLES[x] must be defined in the context.
  • Let \limits~t be the :ref:`table type <syntax-tabletype>` C.\CTABLES[x].
  • The :ref:`reference type <syntax-reftype>` t must be |FUNCREF|.
  • The type C.\CTYPES[y] must be defined in the context.
  • Let [t_1^\ast] \to [t_2^\ast] be the :ref:`function type <syntax-functype>` C.\CTYPES[y].
  • Then the instruction is valid with type [t_1^\ast~\I32] \to [t_2^\ast].
\frac{
  C.\CTABLES[x] = \limits~\FUNCREF
  \qquad
  C.\CTYPES[y] = [t_1^\ast] \to [t_2^\ast]
}{
  C \vdashinstr \CALLINDIRECT~x~y : [t_1^\ast~\I32] \to [t_2^\ast]
}
.. index:: instruction, instruction sequence

Instruction Sequences

Typing of instruction sequences is defined recursively.

Empty Instruction Sequence: \epsilon

  • The empty instruction sequence is valid with type [t^\ast] \to [t^\ast], for any sequence of :ref:`operand types <syntax-opdtype>` t^\ast.
\frac{
}{
  C \vdashinstrseq \epsilon : [t^\ast] \to [t^\ast]
}

Non-empty Instruction Sequence: \instr^\ast~\instr_N

  • The instruction sequence \instr^\ast must be valid with type [t_1^\ast] \to [t_2^\ast], for some sequences of :ref:`value types <syntax-valtype>` t_1^\ast and t_2^\ast.
  • The instruction \instr_N must be valid with type [t^\ast] \to [t_3^\ast], for some sequences of :ref:`value types <syntax-valtype>` t^\ast and t_3^\ast.
  • There must be a sequence of :ref:`value types <syntax-valtype>` t_0^\ast, such that t_2^\ast = t_0^\ast~{t'}^\ast where the type sequence {t'}^\ast is as long as t^\ast.
  • For each :ref:`operand type <syntax-opdtype>` t'_i in {t'}^\ast and corresponding type t_i in t^\ast, t'_i :ref:`matches <match-opdtype>` t_i.
  • Then the combined instruction sequence is valid with type [t_1^\ast] \to [t_0^\ast~t_3^\ast].
\frac{
  C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_0^\ast~{t'}^\ast]
  \qquad
  \vdash [{t'}^\ast] \leq [t^\ast]
  \qquad
  C \vdashinstr \instr_N : [t^\ast] \to [t_3^\ast]
}{
  C \vdashinstrseq \instr^\ast~\instr_N : [t_1^\ast] \to [t_0^\ast~t_3^\ast]
}
.. index:: expression, result type
   pair: validation; expression
   single: abstract syntax; expression
   single: expression; constant

Expressions

Expressions \expr are classified by :ref:`result types <syntax-resulttype>` of the form [t^\ast].

\instr^\ast~\END

  • The instruction sequence \instr^\ast must be :ref:`valid <valid-instr-seq>` with some :ref:`stack type <syntax-stacktype>` [] \to [{t'}^\ast].
  • For each :ref:`operand type <syntax-opdtype>` t'_i in {t'}^\ast and corresponding :ref:`value type <syntax-valtype>` type t_i in t^\ast, t'_i :ref:`matches <match-opdtype>` t_i.
  • Then the expression is valid with :ref:`result type <syntax-resulttype>` [t^\ast].
\frac{
  C \vdashinstrseq \instr^\ast : [] \to [{t'}^\ast]
  \qquad
  \vdash [{t'}^\ast] \leq [t^\ast]
}{
  C \vdashexpr \instr^\ast~\END : [t^\ast]
}
.. index:: ! constant

Constant Expressions

  • In a constant expression \instr^\ast~\END all instructions in \instr^\ast must be constant.
  • A constant instruction \instr must be:
    • either of the form t.\CONST~c,
    • or of the form \REFNULL,
    • or of the form \REFFUNC~x,
    • or of the form \GLOBALGET~x, in which case C.\CGLOBALS[x] must be a :ref:`global type <syntax-globaltype>` of the form \CONST~t.
\frac{
  (C \vdashinstrconst \instr \const)^\ast
}{
  C \vdashexprconst \instr^\ast~\END \const
}
\frac{
}{
  C \vdashinstrconst t.\CONST~c \const
}
\qquad
\frac{
}{
  C \vdashinstrconst \REFNULL \const
}
\qquad
\frac{
}{
  C \vdashinstrconst \REFFUNC~x \const
}
\frac{
  C.\CGLOBALS[x] = \CONST~t
}{
  C \vdashinstrconst \GLOBALGET~x \const
}

Note

Currently, constant expressions occurring as initializers of :ref:`globals <syntax-global>` are further constrained in that contained |GLOBALGET| instructions are only allowed to refer to imported globals. This is enforced in the :ref:`validation rule for modules <valid-module>` by constraining the context C accordingly.

The definition of constant expression may be extended in future versions of WebAssembly.