diff --git a/document/core/appendix/algorithm.rst b/document/core/appendix/algorithm.rst index feede049e1..488eca36e1 100644 --- a/document/core/appendix/algorithm.rst +++ b/document/core/appendix/algorithm.rst @@ -33,7 +33,8 @@ the latter surrounding :ref:`structured control instructions `, or :code:`Unknown` when the type is not known. -For each entered block, the control stack records a *control frame* with the type of the associated :ref:`label ` (used to type-check branches), the result type of the block (used to check its result), the height of the operand stack at the start of the block (used to check that operands do not underflow the current block), and a flag recording whether the remainder of the block is unreachable (used to handle :ref:`stack-polymorphic ` typing after branches). - -.. note:: - In the presentation of this algorithm, multiple values are supported for the :ref:`result types ` classifying blocks and labels. - With the current version of WebAssembly, the :code:`list` could be simplified to an optional value. +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), and a flag recording whether the remainder of the block is unreachable (used to handle :ref:`stack-polymorphic ` typing after branches). For the purpose of presenting the algorithm, the operand and control stacks are simply maintained as global variables: @@ -98,17 +95,21 @@ The control stack is likewise manipulated through auxiliary functions: .. code-block:: pseudo - func push_ctrl(label : list(val_type), out : list(val_type)) = -  let frame = ctrl_frame(label, out, opds.size(), false) + func push_ctrl(opcode : opcode, in : list(val_type), out : list(val_type)) = +  let frame = ctrl_frame(opcode, in, out, opds.size(), false)   ctrls.push(frame) + push_opds(in) - func pop_ctrl() : list(val_type) = + func pop_ctrl() : ctrl_frame =  error_if(ctrls.is_empty())  let frame = ctrls[0]   pop_opds(frame.end_types)   error_if(opds.size() =/= frame.height) ctrls.pop() -   return frame.end_types +   return frame + + func label_types(frame : ctrl_frame) : list(val_types) = + return (if frame.opcode == loop then frame.start_types else frame.end_types) func unreachable() =   opds.resize(ctrls[0].height) @@ -121,6 +122,8 @@ 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. +The type of the :ref:`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 operand stack, in order to allow for the :ref:`stack-polymorphism ` logic in :code:`pop_opd` to take effect. @@ -163,41 +166,45 @@ Other instructions are checked in a similar manner.    case (unreachable)       unreachable() - case (block t*) - push_ctrl([t*], [t*]) + case (block t1*->t2*) + pop_opds([t1*]) + push_ctrl(block, [t1*], [t2*]) - case (loop t*) - push_ctrl([], [t*]) + case (loop t1*->t2*) + pop_opds([t1*]) + push_ctrl(loop, [t1*], [t2*]) - case (if t*) + case (if t1*->t2*) pop_opd(I32) - push_ctrl([t*], [t*]) + pop_opds([t1*]) + push_ctrl(if, [t1*], [t2*]) case (end) - let results = pop_ctrl() - push_opds(results) + let frame = pop_ctrl() + push_opds(frame.end_types) case (else) - let results = pop_ctrl() - push_ctrl(results, results) + 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_opds(ctrls[n].label_types) +       pop_opds(label_types(ctrls[n]))       unreachable() case (br_if n)      error_if(ctrls.size() < n) pop_opd(I32) -       pop_opds(ctrls[n].label_types) -       push_opds(ctrls[n].label_types) +       pop_opds(label_types(ctrls[n])) +       push_opds(label_types(ctrls[n]))    case (br_table n* m)       error_if(ctrls.size() < m)       foreach (n in n*) -         error_if(ctrls.size() < n || ctrls[n].label_types =/= ctrls[m].label_types) +         error_if(ctrls.size() < n || label_types(ctrls[n]) =/= label_types(ctrls[m])) pop_opd(I32) -       pop_opds(ctrls[m].label_types) +       pop_opds(label_types(ctrls[m]))       unreachable() .. note:: diff --git a/document/core/appendix/implementation.rst b/document/core/appendix/implementation.rst index df2885ccc8..d5e53920fa 100644 --- a/document/core/appendix/implementation.rst +++ b/document/core/appendix/implementation.rst @@ -43,6 +43,8 @@ An implementation may impose restrictions on the following dimensions of a modul * the number of :ref:`exports ` from a :ref:`module ` * the number of parameters in a :ref:`function type ` * the number of results in a :ref:`function type ` +* the number of parameters in a :ref:`block type ` +* the number of results in a :ref:`block type ` * the number of :ref:`locals ` in a :ref:`function ` * the size of a :ref:`function ` body * the size of a :ref:`structured control instruction ` diff --git a/document/core/appendix/index-instructions.rst b/document/core/appendix/index-instructions.rst index a3fc18e632..bd4ba04d80 100644 --- a/document/core/appendix/index-instructions.rst +++ b/document/core/appendix/index-instructions.rst @@ -4,14 +4,14 @@ Index of Instructions --------------------- -========================================= ========================= ========================================== ======================================== =============================================================== -Instruction Binary Opcode Type Validation Execution -========================================= ========================= ========================================== ======================================== =============================================================== -:math:`\UNREACHABLE` :math:`\hex{00}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` -:math:`\NOP` :math:`\hex{01}` :math:`[] \to []` :ref:`validation ` :ref:`execution ` -:math:`\BLOCK~[t^?]` :math:`\hex{02}` :math:`[] \to [t^\ast]` :ref:`validation ` :ref:`execution ` -:math:`\LOOP~[t^?]` :math:`\hex{03}` :math:`[] \to [t^\ast]` :ref:`validation ` :ref:`execution ` -:math:`\IF~[t^?]` :math:`\hex{04}` :math:`[\I32] \to [t^\ast]` :ref:`validation ` :ref:`execution ` +========================================= ========================= ============================================= ======================================== =============================================================== +Instruction Binary Opcode Type Validation Execution +========================================= ========================= ============================================= ======================================== =============================================================== +:math:`\UNREACHABLE` :math:`\hex{00}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\NOP` :math:`\hex{01}` :math:`[] \to []` :ref:`validation ` :ref:`execution ` +:math:`\BLOCK~\X{bt}` :math:`\hex{02}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\LOOP~\X{bt}` :math:`\hex{03}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\IF~\X{bt}` :math:`\hex{04}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` :math:`\ELSE` :math:`\hex{05}` (reserved) :math:`\hex{06}` (reserved) :math:`\hex{07}` @@ -19,12 +19,12 @@ Instruction Binary Opcode Type (reserved) :math:`\hex{09}` (reserved) :math:`\hex{0A}` :math:`\END` :math:`\hex{0B}` -:math:`\BR~l` :math:`\hex{0C}` :math:`[t_1^\ast~t^?] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` -:math:`\BRIF~l` :math:`\hex{0D}` :math:`[t^?~\I32] \to [t^?]` :ref:`validation ` :ref:`execution ` -:math:`\BRTABLE~l^\ast~l` :math:`\hex{0E}` :math:`[t_1^\ast~t^?~\I32] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` -:math:`\RETURN` :math:`\hex{0F}` :math:`[t_1^\ast~t^?] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` -:math:`\CALL~x` :math:`\hex{10}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` -:math:`\CALLINDIRECT~x` :math:`\hex{11}` :math:`[t_1^\ast~\I32] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\BR~l` :math:`\hex{0C}` :math:`[t_1^\ast~t^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\BRIF~l` :math:`\hex{0D}` :math:`[t^\ast~\I32] \to [t^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\BRTABLE~l^\ast~l` :math:`\hex{0E}` :math:`[t_1^\ast~t^\ast~\I32] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\RETURN` :math:`\hex{0F}` :math:`[t_1^\ast~t^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\CALL~x` :math:`\hex{10}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\CALLINDIRECT~x` :math:`\hex{11}` :math:`[t_1^\ast~\I32] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` (reserved) :math:`\hex{12}` (reserved) :math:`\hex{13}` (reserved) :math:`\hex{14}` @@ -33,183 +33,183 @@ Instruction Binary Opcode Type (reserved) :math:`\hex{17}` (reserved) :math:`\hex{18}` (reserved) :math:`\hex{19}` -:math:`\DROP` :math:`\hex{1A}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` -:math:`\SELECT` :math:`\hex{1B}` :math:`[t~t~\I32] \to [t]` :ref:`validation ` :ref:`execution ` +:math:`\DROP` :math:`\hex{1A}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` +:math:`\SELECT` :math:`\hex{1B}` :math:`[t~t~\I32] \to [t]` :ref:`validation ` :ref:`execution ` (reserved) :math:`\hex{1C}` (reserved) :math:`\hex{1D}` (reserved) :math:`\hex{1E}` (reserved) :math:`\hex{1F}` -:math:`\LOCALGET~x` :math:`\hex{20}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` -:math:`\LOCALSET~x` :math:`\hex{21}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` -:math:`\LOCALTEE~x` :math:`\hex{22}` :math:`[t] \to [t]` :ref:`validation ` :ref:`execution ` -:math:`\GLOBALGET~x` :math:`\hex{23}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` -:math:`\GLOBALSET~x` :math:`\hex{24}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` +:math:`\LOCALGET~x` :math:`\hex{20}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` +:math:`\LOCALSET~x` :math:`\hex{21}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` +:math:`\LOCALTEE~x` :math:`\hex{22}` :math:`[t] \to [t]` :ref:`validation ` :ref:`execution ` +:math:`\GLOBALGET~x` :math:`\hex{23}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` +:math:`\GLOBALSET~x` :math:`\hex{24}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` (reserved) :math:`\hex{25}` (reserved) :math:`\hex{26}` (reserved) :math:`\hex{27}` -:math:`\I32.\LOAD~\memarg` :math:`\hex{28}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\LOAD~\memarg` :math:`\hex{29}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\F32.\LOAD~\memarg` :math:`\hex{2A}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution ` -:math:`\F64.\LOAD~\memarg` :math:`\hex{2B}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution ` -:math:`\I32.\LOAD\K{8\_s}~\memarg` :math:`\hex{2C}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\I32.\LOAD\K{8\_u}~\memarg` :math:`\hex{2D}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\I32.\LOAD\K{16\_s}~\memarg` :math:`\hex{2E}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\I32.\LOAD\K{16\_u}~\memarg` :math:`\hex{2F}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\LOAD\K{8\_s}~\memarg` :math:`\hex{30}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\LOAD\K{8\_u}~\memarg` :math:`\hex{31}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\LOAD\K{16\_s}~\memarg` :math:`\hex{32}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\LOAD\K{16\_u}~\memarg` :math:`\hex{33}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\LOAD\K{32\_s}~\memarg` :math:`\hex{34}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\LOAD\K{32\_u}~\memarg` :math:`\hex{35}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\I32.\STORE~\memarg` :math:`\hex{36}` :math:`[\I32~\I32] \to []` :ref:`validation ` :ref:`execution ` -:math:`\I64.\STORE~\memarg` :math:`\hex{37}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` -:math:`\F32.\STORE~\memarg` :math:`\hex{38}` :math:`[\I32~\F32] \to []` :ref:`validation ` :ref:`execution ` -:math:`\F64.\STORE~\memarg` :math:`\hex{39}` :math:`[\I32~\F64] \to []` :ref:`validation ` :ref:`execution ` -:math:`\I32.\STORE\K{8}~\memarg` :math:`\hex{3A}` :math:`[\I32~\I32] \to []` :ref:`validation ` :ref:`execution ` -:math:`\I32.\STORE\K{16}~\memarg` :math:`\hex{3B}` :math:`[\I32~\I32] \to []` :ref:`validation ` :ref:`execution ` -:math:`\I64.\STORE\K{8}~\memarg` :math:`\hex{3C}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` -:math:`\I64.\STORE\K{16}~\memarg` :math:`\hex{3D}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` -:math:`\I64.\STORE\K{32}~\memarg` :math:`\hex{3E}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` -:math:`\MEMORYSIZE` :math:`\hex{3F}` :math:`[] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\MEMORYGROW` :math:`\hex{40}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\I32.\CONST~\i32` :math:`\hex{41}` :math:`[] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\I64.\CONST~\i64` :math:`\hex{42}` :math:`[] \to [\I64]` :ref:`validation ` :ref:`execution ` -:math:`\F32.\CONST~\f32` :math:`\hex{43}` :math:`[] \to [\F32]` :ref:`validation ` :ref:`execution ` -:math:`\F64.\CONST~\f64` :math:`\hex{44}` :math:`[] \to [\F64]` :ref:`validation ` :ref:`execution ` -:math:`\I32.\EQZ` :math:`\hex{45}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\EQ` :math:`\hex{46}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\NE` :math:`\hex{47}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\LT\K{\_s}` :math:`\hex{48}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\LT\K{\_u}` :math:`\hex{49}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\GT\K{\_s}` :math:`\hex{4A}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\GT\K{\_u}` :math:`\hex{4B}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\LE\K{\_s}` :math:`\hex{4C}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\LE\K{\_u}` :math:`\hex{4D}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\GE\K{\_s}` :math:`\hex{4E}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\GE\K{\_u}` :math:`\hex{4F}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EQZ` :math:`\hex{50}` :math:`[\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EQ` :math:`\hex{51}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\NE` :math:`\hex{52}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\LT\K{\_s}` :math:`\hex{53}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\LT\K{\_u}` :math:`\hex{54}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\GT\K{\_s}` :math:`\hex{55}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\GT\K{\_u}` :math:`\hex{56}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\LE\K{\_s}` :math:`\hex{57}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\LE\K{\_u}` :math:`\hex{58}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\GE\K{\_s}` :math:`\hex{59}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\GE\K{\_u}` :math:`\hex{5A}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\EQ` :math:`\hex{5B}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\NE` :math:`\hex{5C}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\LT` :math:`\hex{5D}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\GT` :math:`\hex{5E}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\LE` :math:`\hex{5F}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\GE` :math:`\hex{60}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\EQ` :math:`\hex{61}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\NE` :math:`\hex{62}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\LT` :math:`\hex{63}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\GT` :math:`\hex{64}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\LE` :math:`\hex{65}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\GE` :math:`\hex{66}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\CLZ` :math:`\hex{67}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\CTZ` :math:`\hex{68}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\POPCNT` :math:`\hex{69}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\ADD` :math:`\hex{6A}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\SUB` :math:`\hex{6B}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\MUL` :math:`\hex{6C}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\DIV\K{\_s}` :math:`\hex{6D}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\DIV\K{\_u}` :math:`\hex{6E}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\REM\K{\_s}` :math:`\hex{6F}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\REM\K{\_u}` :math:`\hex{70}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\AND` :math:`\hex{71}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\OR` :math:`\hex{72}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\XOR` :math:`\hex{73}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\SHL` :math:`\hex{74}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\SHR\K{\_s}` :math:`\hex{75}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\SHR\K{\_u}` :math:`\hex{76}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\ROTL` :math:`\hex{77}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\ROTR` :math:`\hex{78}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\CLZ` :math:`\hex{79}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\CTZ` :math:`\hex{7A}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\POPCNT` :math:`\hex{7B}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\ADD` :math:`\hex{7C}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\SUB` :math:`\hex{7D}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\MUL` :math:`\hex{7E}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\DIV\K{\_s}` :math:`\hex{7F}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\DIV\K{\_u}` :math:`\hex{80}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\REM\K{\_s}` :math:`\hex{81}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\REM\K{\_u}` :math:`\hex{82}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\AND` :math:`\hex{83}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\OR` :math:`\hex{84}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\XOR` :math:`\hex{85}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\SHL` :math:`\hex{86}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\SHR\K{\_s}` :math:`\hex{87}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\SHR\K{\_u}` :math:`\hex{88}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\ROTL` :math:`\hex{89}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\ROTR` :math:`\hex{8A}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\ABS` :math:`\hex{8B}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\NEG` :math:`\hex{8C}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CEIL` :math:`\hex{8D}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\FLOOR` :math:`\hex{8E}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\TRUNC` :math:`\hex{8F}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\NEAREST` :math:`\hex{90}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\SQRT` :math:`\hex{91}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\ADD` :math:`\hex{92}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\SUB` :math:`\hex{93}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\MUL` :math:`\hex{94}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\DIV` :math:`\hex{95}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\FMIN` :math:`\hex{96}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\FMAX` :math:`\hex{97}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\COPYSIGN` :math:`\hex{98}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\ABS` :math:`\hex{99}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\NEG` :math:`\hex{9A}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CEIL` :math:`\hex{9B}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\FLOOR` :math:`\hex{9C}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\TRUNC` :math:`\hex{9D}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\NEAREST` :math:`\hex{9E}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\SQRT` :math:`\hex{9F}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\ADD` :math:`\hex{A0}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\SUB` :math:`\hex{A1}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\MUL` :math:`\hex{A2}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\DIV` :math:`\hex{A3}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\FMIN` :math:`\hex{A4}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\FMAX` :math:`\hex{A5}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\COPYSIGN` :math:`\hex{A6}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\WRAP\K{\_}\I64` :math:`\hex{A7}` :math:`[\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_}\F32\K{\_s}` :math:`\hex{A8}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_}\F32\K{\_u}` :math:`\hex{A9}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_}\F64\K{\_s}` :math:`\hex{AA}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_}\F64\K{\_u}` :math:`\hex{AB}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EXTEND\K{\_}\I32\K{\_s}` :math:`\hex{AC}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EXTEND\K{\_}\I32\K{\_u}` :math:`\hex{AD}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_}\F32\K{\_s}` :math:`\hex{AE}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_}\F32\K{\_u}` :math:`\hex{AF}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_}\F64\K{\_s}` :math:`\hex{B0}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_}\F64\K{\_u}` :math:`\hex{B1}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_}\I32\K{\_s}` :math:`\hex{B2}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_}\I32\K{\_u}` :math:`\hex{B3}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_}\I64\K{\_s}` :math:`\hex{B4}` :math:`[\I64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_}\I64\K{\_u}` :math:`\hex{B5}` :math:`[\I64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\DEMOTE\K{\_}\F64` :math:`\hex{B6}` :math:`[\F64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CONVERT\K{\_}\I32\K{\_s}` :math:`\hex{B7}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CONVERT\K{\_}\I32\K{\_u}` :math:`\hex{B8}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CONVERT\K{\_}\I64\K{\_s}` :math:`\hex{B9}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CONVERT\K{\_}\I64\K{\_u}` :math:`\hex{BA}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\PROMOTE\K{\_}\F32` :math:`\hex{BB}` :math:`[\F32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\REINTERPRET\K{\_}\F32` :math:`\hex{BC}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\REINTERPRET\K{\_}\F64` :math:`\hex{BD}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\REINTERPRET\K{\_}\I32` :math:`\hex{BE}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\REINTERPRET\K{\_}\I64` :math:`\hex{BF}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\EXTEND\K{8\_s}` :math:`\hex{C0}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\EXTEND\K{16\_s}` :math:`\hex{C1}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EXTEND\K{8\_s}` :math:`\hex{C2}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EXTEND\K{16\_s}` :math:`\hex{C3}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EXTEND\K{32\_s}` :math:`\hex{C4}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_sat\_}\F32\K{\_s}` :math:`\hex{FC}~\hex{00}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_sat\_}\F32\K{\_u}` :math:`\hex{FC}~\hex{01}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_sat\_}\F64\K{\_s}` :math:`\hex{FC}~\hex{02}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_sat\_}\F64\K{\_u}` :math:`\hex{FC}~\hex{03}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_sat\_}\F32\K{\_s}` :math:`\hex{FC}~\hex{04}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_sat\_}\F32\K{\_u}` :math:`\hex{FC}~\hex{05}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_sat}\_\F64\K{\_s}` :math:`\hex{FC}~\hex{06}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_sat\_}\F64\K{\_u}` :math:`\hex{FC}~\hex{07}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -========================================= ========================= ========================================== ======================================== =============================================================== +:math:`\I32.\LOAD~\memarg` :math:`\hex{28}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\LOAD~\memarg` :math:`\hex{29}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\F32.\LOAD~\memarg` :math:`\hex{2A}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution ` +:math:`\F64.\LOAD~\memarg` :math:`\hex{2B}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution ` +:math:`\I32.\LOAD\K{8\_s}~\memarg` :math:`\hex{2C}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\I32.\LOAD\K{8\_u}~\memarg` :math:`\hex{2D}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\I32.\LOAD\K{16\_s}~\memarg` :math:`\hex{2E}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\I32.\LOAD\K{16\_u}~\memarg` :math:`\hex{2F}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\LOAD\K{8\_s}~\memarg` :math:`\hex{30}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\LOAD\K{8\_u}~\memarg` :math:`\hex{31}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\LOAD\K{16\_s}~\memarg` :math:`\hex{32}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\LOAD\K{16\_u}~\memarg` :math:`\hex{33}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\LOAD\K{32\_s}~\memarg` :math:`\hex{34}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\LOAD\K{32\_u}~\memarg` :math:`\hex{35}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\I32.\STORE~\memarg` :math:`\hex{36}` :math:`[\I32~\I32] \to []` :ref:`validation ` :ref:`execution ` +:math:`\I64.\STORE~\memarg` :math:`\hex{37}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` +:math:`\F32.\STORE~\memarg` :math:`\hex{38}` :math:`[\I32~\F32] \to []` :ref:`validation ` :ref:`execution ` +:math:`\F64.\STORE~\memarg` :math:`\hex{39}` :math:`[\I32~\F64] \to []` :ref:`validation ` :ref:`execution ` +:math:`\I32.\STORE\K{8}~\memarg` :math:`\hex{3A}` :math:`[\I32~\I32] \to []` :ref:`validation ` :ref:`execution ` +:math:`\I32.\STORE\K{16}~\memarg` :math:`\hex{3B}` :math:`[\I32~\I32] \to []` :ref:`validation ` :ref:`execution ` +:math:`\I64.\STORE\K{8}~\memarg` :math:`\hex{3C}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` +:math:`\I64.\STORE\K{16}~\memarg` :math:`\hex{3D}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` +:math:`\I64.\STORE\K{32}~\memarg` :math:`\hex{3E}` :math:`[\I32~\I64] \to []` :ref:`validation ` :ref:`execution ` +:math:`\MEMORYSIZE` :math:`\hex{3F}` :math:`[] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\MEMORYGROW` :math:`\hex{40}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\I32.\CONST~\i32` :math:`\hex{41}` :math:`[] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\I64.\CONST~\i64` :math:`\hex{42}` :math:`[] \to [\I64]` :ref:`validation ` :ref:`execution ` +:math:`\F32.\CONST~\f32` :math:`\hex{43}` :math:`[] \to [\F32]` :ref:`validation ` :ref:`execution ` +:math:`\F64.\CONST~\f64` :math:`\hex{44}` :math:`[] \to [\F64]` :ref:`validation ` :ref:`execution ` +:math:`\I32.\EQZ` :math:`\hex{45}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\EQ` :math:`\hex{46}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\NE` :math:`\hex{47}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\LT\K{\_s}` :math:`\hex{48}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\LT\K{\_u}` :math:`\hex{49}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\GT\K{\_s}` :math:`\hex{4A}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\GT\K{\_u}` :math:`\hex{4B}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\LE\K{\_s}` :math:`\hex{4C}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\LE\K{\_u}` :math:`\hex{4D}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\GE\K{\_s}` :math:`\hex{4E}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\GE\K{\_u}` :math:`\hex{4F}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EQZ` :math:`\hex{50}` :math:`[\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EQ` :math:`\hex{51}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\NE` :math:`\hex{52}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\LT\K{\_s}` :math:`\hex{53}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\LT\K{\_u}` :math:`\hex{54}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\GT\K{\_s}` :math:`\hex{55}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\GT\K{\_u}` :math:`\hex{56}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\LE\K{\_s}` :math:`\hex{57}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\LE\K{\_u}` :math:`\hex{58}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\GE\K{\_s}` :math:`\hex{59}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\GE\K{\_u}` :math:`\hex{5A}` :math:`[\I64~\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\EQ` :math:`\hex{5B}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\NE` :math:`\hex{5C}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\LT` :math:`\hex{5D}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\GT` :math:`\hex{5E}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\LE` :math:`\hex{5F}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\GE` :math:`\hex{60}` :math:`[\F32~\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\EQ` :math:`\hex{61}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\NE` :math:`\hex{62}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\LT` :math:`\hex{63}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\GT` :math:`\hex{64}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\LE` :math:`\hex{65}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\GE` :math:`\hex{66}` :math:`[\F64~\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\CLZ` :math:`\hex{67}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\CTZ` :math:`\hex{68}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\POPCNT` :math:`\hex{69}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\ADD` :math:`\hex{6A}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\SUB` :math:`\hex{6B}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\MUL` :math:`\hex{6C}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\DIV\K{\_s}` :math:`\hex{6D}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\DIV\K{\_u}` :math:`\hex{6E}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\REM\K{\_s}` :math:`\hex{6F}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\REM\K{\_u}` :math:`\hex{70}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\AND` :math:`\hex{71}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\OR` :math:`\hex{72}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\XOR` :math:`\hex{73}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\SHL` :math:`\hex{74}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\SHR\K{\_s}` :math:`\hex{75}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\SHR\K{\_u}` :math:`\hex{76}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\ROTL` :math:`\hex{77}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\ROTR` :math:`\hex{78}` :math:`[\I32~\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\CLZ` :math:`\hex{79}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\CTZ` :math:`\hex{7A}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\POPCNT` :math:`\hex{7B}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\ADD` :math:`\hex{7C}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\SUB` :math:`\hex{7D}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\MUL` :math:`\hex{7E}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\DIV\K{\_s}` :math:`\hex{7F}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\DIV\K{\_u}` :math:`\hex{80}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\REM\K{\_s}` :math:`\hex{81}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\REM\K{\_u}` :math:`\hex{82}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\AND` :math:`\hex{83}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\OR` :math:`\hex{84}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\XOR` :math:`\hex{85}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\SHL` :math:`\hex{86}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\SHR\K{\_s}` :math:`\hex{87}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\SHR\K{\_u}` :math:`\hex{88}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\ROTL` :math:`\hex{89}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\ROTR` :math:`\hex{8A}` :math:`[\I64~\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\ABS` :math:`\hex{8B}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\NEG` :math:`\hex{8C}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CEIL` :math:`\hex{8D}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\FLOOR` :math:`\hex{8E}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\TRUNC` :math:`\hex{8F}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\NEAREST` :math:`\hex{90}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\SQRT` :math:`\hex{91}` :math:`[\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\ADD` :math:`\hex{92}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\SUB` :math:`\hex{93}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\MUL` :math:`\hex{94}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\DIV` :math:`\hex{95}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\FMIN` :math:`\hex{96}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\FMAX` :math:`\hex{97}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\COPYSIGN` :math:`\hex{98}` :math:`[\F32~\F32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\ABS` :math:`\hex{99}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\NEG` :math:`\hex{9A}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CEIL` :math:`\hex{9B}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\FLOOR` :math:`\hex{9C}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\TRUNC` :math:`\hex{9D}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\NEAREST` :math:`\hex{9E}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\SQRT` :math:`\hex{9F}` :math:`[\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\ADD` :math:`\hex{A0}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\SUB` :math:`\hex{A1}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\MUL` :math:`\hex{A2}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\DIV` :math:`\hex{A3}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\FMIN` :math:`\hex{A4}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\FMAX` :math:`\hex{A5}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\COPYSIGN` :math:`\hex{A6}` :math:`[\F64~\F64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\WRAP\K{\_}\I64` :math:`\hex{A7}` :math:`[\I64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_}\F32\K{\_s}` :math:`\hex{A8}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_}\F32\K{\_u}` :math:`\hex{A9}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_}\F64\K{\_s}` :math:`\hex{AA}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_}\F64\K{\_u}` :math:`\hex{AB}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EXTEND\K{\_}\I32\K{\_s}` :math:`\hex{AC}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EXTEND\K{\_}\I32\K{\_u}` :math:`\hex{AD}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_}\F32\K{\_s}` :math:`\hex{AE}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_}\F32\K{\_u}` :math:`\hex{AF}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_}\F64\K{\_s}` :math:`\hex{B0}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_}\F64\K{\_u}` :math:`\hex{B1}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_}\I32\K{\_s}` :math:`\hex{B2}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_}\I32\K{\_u}` :math:`\hex{B3}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_}\I64\K{\_s}` :math:`\hex{B4}` :math:`[\I64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_}\I64\K{\_u}` :math:`\hex{B5}` :math:`[\I64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\DEMOTE\K{\_}\F64` :math:`\hex{B6}` :math:`[\F64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CONVERT\K{\_}\I32\K{\_s}` :math:`\hex{B7}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CONVERT\K{\_}\I32\K{\_u}` :math:`\hex{B8}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CONVERT\K{\_}\I64\K{\_s}` :math:`\hex{B9}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CONVERT\K{\_}\I64\K{\_u}` :math:`\hex{BA}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\PROMOTE\K{\_}\F32` :math:`\hex{BB}` :math:`[\F32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\REINTERPRET\K{\_}\F32` :math:`\hex{BC}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\REINTERPRET\K{\_}\F64` :math:`\hex{BD}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\REINTERPRET\K{\_}\I32` :math:`\hex{BE}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\REINTERPRET\K{\_}\I64` :math:`\hex{BF}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\EXTEND\K{8\_s}` :math:`\hex{C0}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\EXTEND\K{16\_s}` :math:`\hex{C1}` :math:`[\I32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EXTEND\K{8\_s}` :math:`\hex{C2}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EXTEND\K{16\_s}` :math:`\hex{C3}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EXTEND\K{32\_s}` :math:`\hex{C4}` :math:`[\I64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat\_}\F32\K{\_s}` :math:`\hex{FC}~\hex{00}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat\_}\F32\K{\_u}` :math:`\hex{FC}~\hex{01}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat\_}\F64\K{\_s}` :math:`\hex{FC}~\hex{02}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat\_}\F64\K{\_u}` :math:`\hex{FC}~\hex{03}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat\_}\F32\K{\_s}` :math:`\hex{FC}~\hex{04}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat\_}\F32\K{\_u}` :math:`\hex{FC}~\hex{05}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat}\_\F64\K{\_s}` :math:`\hex{FC}~\hex{06}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat\_}\F64\K{\_u}` :math:`\hex{FC}~\hex{07}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +========================================= ========================= ============================================= ======================================== =============================================================== diff --git a/document/core/appendix/index-rules.rst b/document/core/appendix/index-rules.rst index 4d610e09ea..eb35c60713 100644 --- a/document/core/appendix/index-rules.rst +++ b/document/core/appendix/index-rules.rst @@ -15,6 +15,7 @@ Construct Judgement =============================================== =============================================================================== :ref:`Limits ` :math:`\vdashlimits \limits : k` :ref:`Function type ` :math:`\vdashfunctype \functype \ok` +:ref:`Block type ` :math:`\vdashblocktype \blocktype \ok` :ref:`Table type ` :math:`\vdashtabletype \tabletype \ok` :ref:`Memory type ` :math:`\vdashmemtype \memtype \ok` :ref:`Global type ` :math:`\vdashglobaltype \globaltype \ok` diff --git a/document/core/appendix/properties.rst b/document/core/appendix/properties.rst index 7f263d4343..9ed38ba633 100644 --- a/document/core/appendix/properties.rst +++ b/document/core/appendix/properties.rst @@ -376,17 +376,17 @@ Finally, :ref:`frames ` are classified with *frame contexts*, whic * The :ref:`store ` :math:`S` must be :ref:`valid `. * Under no allowed return type, - the :ref:`thread ` :math:`T` must be :ref:`valid ` with some :ref:`result type ` :math:`[t^?]`. + the :ref:`thread ` :math:`T` must be :ref:`valid ` with some :ref:`result type ` :math:`[t^\ast]`. -* Then the configuration is valid with the :ref:`result type ` :math:`[t^?]`. +* Then the configuration is valid with the :ref:`result type ` :math:`[t^\ast]`. .. math:: \frac{ \vdashstore S \ok \qquad - S; \epsilon \vdashthread T : [t^?] + S; \epsilon \vdashthread T : [t^\ast] }{ - \vdashconfig S; T : [t^?] + \vdashconfig S; T : [t^\ast] } @@ -403,17 +403,17 @@ Finally, :ref:`frames ` are classified with *frame contexts*, whic * Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with |CRETURN| set to :math:`\resulttype^?`. * Under context :math:`C'`, - the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with some type :math:`[] \to [t^?]`. + the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with some type :math:`[] \to [t^\ast]`. -* Then the thread is valid with the :ref:`result type ` :math:`[t^?]`. +* Then the thread is valid with the :ref:`result type ` :math:`[t^\ast]`. .. math:: \frac{ S \vdashframe F : C \qquad - S; C,\CRETURN~\resulttype^? \vdashinstrseq \instr^\ast : [] \to [t^?] + S; C,\CRETURN~\resulttype^? \vdashinstrseq \instr^\ast : [] \to [t^\ast] }{ - S; \resulttype^? \vdashthread F; \instr^\ast : [t^?] + S; \resulttype^? \vdashthread F; \instr^\ast : [t^\ast] } @@ -538,22 +538,22 @@ To that end, all previous typing judgements :math:`C \vdash \X{prop}` are genera :math:`\LABEL_n\{\instr_0^\ast\}~\instr^\ast~\END` .................................................. -* The instruction sequence :math:`\instr_0^\ast` must be :ref:`valid ` with some type :math:`[t_1^n] \to [t_2^?]`. +* The instruction sequence :math:`\instr_0^\ast` must be :ref:`valid ` with some type :math:`[t_1^n] \to [t_2^*]`. * Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_1^n]` prepended to the |CLABELS| vector. * Under context :math:`C'`, - the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t_2^?]`. + the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t_2^*]`. -* Then the compound instruction is valid with type :math:`[] \to [t_2^?]`. +* Then the compound instruction is valid with type :math:`[] \to [t_2^*]`. .. math:: \frac{ - S; C \vdashinstrseq \instr_0^\ast : [t_1^n] \to [t_2^?] + S; C \vdashinstrseq \instr_0^\ast : [t_1^n] \to [t_2^*] \qquad - S; C,\CLABELS\,[t_1^n] \vdashinstrseq \instr^\ast : [] \to [t_2^?] + S; C,\CLABELS\,[t_1^n] \vdashinstrseq \instr^\ast : [] \to [t_2^*] }{ - S; C \vdashadmininstr \LABEL_n\{\instr_0^\ast\}~\instr^\ast~\END : [] \to [t_2^?] + S; C \vdashadmininstr \LABEL_n\{\instr_0^\ast\}~\instr^\ast~\END : [] \to [t_2^*] } diff --git a/document/core/binary/instructions.rst b/document/core/binary/instructions.rst index 198420c062..a0ae94b4d9 100644 --- a/document/core/binary/instructions.rst +++ b/document/core/binary/instructions.rst @@ -13,8 +13,9 @@ The only exception are :ref:`structured control instructions ` have varying encodings. For structured instructions, the instruction sequences forming nested blocks are terminated with explicit opcodes for |END| and |ELSE|. +:ref:`Block types ` are encoded in special compressed form, by either the byte :math:`\hex{40}` indicating the empty type, as a single :ref:`value type `, or as a :ref:`type index ` encoded as a positive :ref:`signed integer `. + +.. _binary-blocktype: .. _binary-nop: .. _binary-unreachable: .. _binary-block: @@ -35,19 +39,23 @@ Control Instructions .. _binary-call_indirect: .. math:: - \begin{array}{llclll} + \begin{array}{llcllll} + \production{block type} & \Bblocktype &::=& + \hex{40} &\Rightarrow& \epsilon \\ &&|& + t{:}\Bvaltype &\Rightarrow& t \\ &&|& + x{:}\Bs33 &\Rightarrow& x & (\iff x \geq 0) \\ \production{instruction} & \Binstr &::=& \hex{00} &\Rightarrow& \UNREACHABLE \\ &&|& \hex{01} &\Rightarrow& \NOP \\ &&|& - \hex{02}~~\X{rt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{0B} - &\Rightarrow& \BLOCK~\X{rt}~\X{in}^\ast~\END \\ &&|& - \hex{03}~~\X{rt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{0B} - &\Rightarrow& \LOOP~\X{rt}~\X{in}^\ast~\END \\ &&|& - \hex{04}~~\X{rt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{0B} - &\Rightarrow& \IF~\X{rt}~\X{in}^\ast~\ELSE~\epsilon~\END \\ &&|& - \hex{04}~~\X{rt}{:}\Bblocktype~~(\X{in}_1{:}\Binstr)^\ast~~ + \hex{02}~~\X{bt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{0B} + &\Rightarrow& \BLOCK~\X{bt}~\X{in}^\ast~\END \\ &&|& + \hex{03}~~\X{bt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{0B} + &\Rightarrow& \LOOP~\X{bt}~\X{in}^\ast~\END \\ &&|& + \hex{04}~~\X{bt}{:}\Bblocktype~~(\X{in}{:}\Binstr)^\ast~~\hex{0B} + &\Rightarrow& \IF~\X{bt}~\X{in}^\ast~\ELSE~\epsilon~\END \\ &&|& + \hex{04}~~\X{bt}{:}\Bblocktype~~(\X{in}_1{:}\Binstr)^\ast~~ \hex{05}~~(\X{in}_2{:}\Binstr)^\ast~~\hex{0B} - &\Rightarrow& \IF~\X{rt}~\X{in}_1^\ast~\ELSE~\X{in}_2^\ast~\END \\ &&|& + &\Rightarrow& \IF~\X{bt}~\X{in}_1^\ast~\ELSE~\X{in}_2^\ast~\END \\ &&|& \hex{0C}~~l{:}\Blabelidx &\Rightarrow& \BR~l \\ &&|& \hex{0D}~~l{:}\Blabelidx &\Rightarrow& \BRIF~l \\ &&|& \hex{0E}~~l^\ast{:}\Bvec(\Blabelidx)~~l_N{:}\Blabelidx @@ -60,10 +68,13 @@ Control Instructions .. note:: The |ELSE| opcode :math:`\hex{05}` in the encoding of an |IF| instruction can be omitted if the following instruction sequence is empty. -.. note:: + Unlike any :ref:`other occurrence `, the :ref:`type index ` in a :ref:`block type ` is encoded as a positive :ref:`signed integer `, so that its |SignedLEB128| bit pattern cannot collide with the encoding of :ref:`value types ` or the special code :math:`\hex{40}`, 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. + In future versions of WebAssembly, the zero byte occurring in the encoding of the |CALLINDIRECT| instruction may be used to index additional tables. + .. index:: value type, polymorphism pair: binary format; instruction .. _binary-instr-parametric: diff --git a/document/core/binary/types.rst b/document/core/binary/types.rst index c114211b30..05e1c02438 100644 --- a/document/core/binary/types.rst +++ b/document/core/binary/types.rst @@ -24,30 +24,25 @@ Value Types \end{array} .. note:: - In future versions of WebAssembly, value types may include types denoted by :ref:`type indices `. + Value types can occur in contexts where :ref:`type indices ` are also allowed, such as in the case of :ref:`block types `. Thus, the binary format for types corresponds to the |SignedLEB128|_ :ref:`encoding ` of small negative :math:`\sN` values, so that they can coexist with (positive) type indices in the future. .. index:: result type, value type pair: binary format; result type -.. _binary-blocktype: .. _binary-resulttype: Result Types ~~~~~~~~~~~~ -The only :ref:`result types ` occurring in the binary format are the types of blocks. These are encoded in special compressed form, by either the byte :math:`\hex{40}` indicating the empty type or as a single :ref:`value type `. +:ref:`Result types ` are encoded by the respective :ref:`vectors ` of :ref:`value types ``. .. math:: \begin{array}{llclll@{\qquad\qquad}l} - \production{result type} & \Bblocktype &::=& - \hex{40} &\Rightarrow& [] \\ &&|& - t{:}\Bvaltype &\Rightarrow& [t] \\ + \production{result type} & \Bresulttype &::=& + t^\ast{:\,}\Bvec(\Bvaltype) &\Rightarrow& [t^\ast] \\ \end{array} -.. note:: - In future versions of WebAssembly, this scheme may be extended to support multiple results or more general block types. - .. index:: function type, value type, result type pair: binary format; function type @@ -61,8 +56,8 @@ Function Types .. math:: \begin{array}{llclll@{\qquad\qquad}l} \production{function type} & \Bfunctype &::=& - \hex{60}~~t_1^\ast{:\,}\Bvec(\Bvaltype)~~t_2^\ast{:\,}\Bvec(\Bvaltype) - &\Rightarrow& [t_1^\ast] \to [t_2^\ast] \\ + \hex{60}~~\X{rt}_1{:\,}\Bresulttype~~\X{rt}_2{:\,}\Bresulttype + &\Rightarrow& \X{rt}_1 \to \X{rt}_2 \\ \end{array} diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 6a79fe4fe3..9dc6727f43 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -665,70 +665,92 @@ Control Instructions .. _exec-block: -:math:`\BLOCK~[t^?]~\instr^\ast~\END` -..................................... +:math:`\BLOCK~\blocktype~\instr^\ast~\END` +.......................................... + +1. Assert: due to :ref:`validation `, :math:`\expand_F(\blocktype)` is defined. + +2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`\expand_F(\blocktype)`. + +3. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the block. -1. Let :math:`n` be the arity :math:`|t^?|` of the :ref:`result type ` :math:`t^?`. +4. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. -2. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the block. +5. Pop the values :math:`\val^m` from the stack. -3. :ref:`Enter ` the block :math:`\instr^\ast` with label :math:`L`. +6. :ref:`Enter ` the block :math:`\val^m~\instr^\ast` with label :math:`L`. .. math:: ~\\[-1ex] \begin{array}{lcl@{\qquad}l} - \BLOCK~[t^n]~\instr^\ast~\END &\stepto& - \LABEL_n\{\epsilon\}~\instr^\ast~\END + F; \val^m~\BLOCK~\X{bt}~\instr^\ast~\END &\stepto& + F; \LABEL_n\{\epsilon\}~\val^m~\instr^\ast~\END + & (\iff \expand_F(\X{bt}) = [t_1^m] \to [t_2^n]) \end{array} .. _exec-loop: -:math:`\LOOP~[t^?]~\instr^\ast~\END` -.................................... +:math:`\LOOP~\blocktype~\instr^\ast~\END` +......................................... + +1. Assert: due to :ref:`validation `, :math:`\expand_F(\blocktype)` is defined. + +2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`\expand_F(\blocktype)`. + +3. Let :math:`L` be the label whose arity is :math:`m` and whose continuation is the start of the loop. + +4. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. -1. Let :math:`L` be the label whose arity is :math:`0` and whose continuation is the start of the loop. +5. Pop the values :math:`\val^m` from the stack. -2. :ref:`Enter ` the block :math:`\instr^\ast` with label :math:`L`. +6. :ref:`Enter ` the block :math:`\val^m~\instr^\ast` with label :math:`L`. .. math:: ~\\[-1ex] \begin{array}{lcl@{\qquad}l} - \LOOP~[t^?]~\instr^\ast~\END &\stepto& - \LABEL_0\{\LOOP~[t^?]~\instr^\ast~\END\}~\instr^\ast~\END + F; \val^m~\LOOP~\X{bt}~\instr^\ast~\END &\stepto& + F; \LABEL_m\{\LOOP~\X{bt}~\instr^\ast~\END\}~\val^m~\instr^\ast~\END + & (\iff \expand_F(\X{bt}) = [t_1^m] \to [t_2^n]) \end{array} .. _exec-if: -:math:`\IF~[t^?]~\instr_1^\ast~\ELSE~\instr_2^\ast~\END` -........................................................ +:math:`\IF~\blocktype~\instr_1^\ast~\ELSE~\instr_2^\ast~\END` +............................................................. -1. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +1. Assert: due to :ref:`validation `, :math:`\expand_F(\blocktype)` is defined. -2. Pop the value :math:`\I32.\CONST~c` from the stack. +2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`\expand_F(\blocktype)`. + +3. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the |IF| instruction. + +4. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. -3. Let :math:`n` be the arity :math:`|t^?|` of the :ref:`result type ` :math:`t^?`. +5. Pop the value :math:`\I32.\CONST~c` from the stack. -4. Let :math:`L` be the label whose arity is :math:`n` and whose continuation is the end of the |IF| instruction. +6. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. -5. If :math:`c` is non-zero, then: +7. Pop the values :math:`\val^m` from the stack. - a. :ref:`Enter ` the block :math:`\instr_1^\ast` with label :math:`L`. +8. If :math:`c` is non-zero, then: -6. Else: + a. :ref:`Enter ` the block :math:`\val^m~\instr_1^\ast` with label :math:`L`. - a. :ref:`Enter ` the block :math:`\instr_2^\ast` with label :math:`L`. +9. Else: + + a. :ref:`Enter ` the block :math:`\val^m~\instr_2^\ast` with label :math:`L`. .. math:: ~\\[-1ex] \begin{array}{lcl@{\qquad}l} - (\I32.\CONST~c)~\IF~[t^n]~\instr_1^\ast~\ELSE~\instr_2^\ast~\END &\stepto& - \LABEL_n\{\epsilon\}~\instr_1^\ast~\END - & (\iff c \neq 0) \\ - (\I32.\CONST~c)~\IF~[t^n]~\instr_1^\ast~\ELSE~\instr_2^\ast~\END &\stepto& - \LABEL_n\{\epsilon\}~\instr_2^\ast~\END - & (\iff c = 0) \\ + F; \val^m~(\I32.\CONST~c)~\IF~\X{bt}~\instr_1^\ast~\ELSE~\instr_2^\ast~\END &\stepto& + F; \LABEL_n\{\epsilon\}~\val^m~\instr_1^\ast~\END + & (\iff c \neq 0 \wedge \expand_F(\X{bt}) = [t_1^m] \to [t_2^n]) \\ + F; \val^m~(\I32.\CONST~c)~\IF~\X{bt}~\instr_1^\ast~\ELSE~\instr_2^\ast~\END &\stepto& + F; \LABEL_n\{\epsilon\}~\val^m~\instr_2^\ast~\END + & (\iff c = 0 \wedge \expand_F(\X{bt}) = [t_1^m] \to [t_2^n]) \\ \end{array} @@ -1019,35 +1041,34 @@ Invocation of :ref:`function address ` :math:`a` 3. Let :math:`[t_1^n] \to [t_2^m]` be the :ref:`function type ` :math:`f.\FITYPE`. -4. Assert: due to :ref:`validation `, :math:`m \leq 1`. +4. Let :math:`t^\ast` be the list of :ref:`value types ` :math:`f.\FICODE.\FLOCALS`. -5. Let :math:`t^\ast` be the list of :ref:`value types ` :math:`f.\FICODE.\FLOCALS`. +5. Let :math:`\instr^\ast~\END` be the :ref:`expression ` :math:`f.\FICODE.\FBODY`. -6. Let :math:`\instr^\ast~\END` be the :ref:`expression ` :math:`f.\FICODE.\FBODY`. +6. Assert: due to :ref:`validation `, :math:`n` values are on the top of the stack. -7. Assert: due to :ref:`validation `, :math:`n` values are on the top of the stack. +7. Pop the values :math:`\val^n` from the stack. -8. Pop the values :math:`\val^n` from the stack. +8. Let :math:`\val_0^\ast` be the list of zero values of types :math:`t^\ast`. -9. Let :math:`\val_0^\ast` be the list of zero values of types :math:`t^\ast`. +9. Let :math:`F` be the :ref:`frame ` :math:`\{ \AMODULE~f.\FIMODULE, \ALOCALS~\val^n~\val_0^\ast \}`. -10. Let :math:`F` be the :ref:`frame ` :math:`\{ \AMODULE~f.\FIMODULE, \ALOCALS~\val^n~\val_0^\ast \}`. +10. Push the activation of :math:`F` with arity :math:`m` to the stack. -11. Push the activation of :math:`F` with arity :math:`m` to the stack. +11. Let :math:`L` be the :ref:`label ` whose arity is :math:`m` and whose continuation is the end of the function. -12. :ref:`Execute ` the instruction :math:`\BLOCK~[t_2^m]~\instr^\ast~\END`. +12. :ref:`Enter ` the instruction sequence :math:`\instr^\ast` with label :math:`L`. .. math:: ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; \val^n~(\INVOKE~a) &\stepto& S; \FRAME_m\{F\}~\BLOCK~[t_2^m]~\instr^\ast~\END~\END + S; \val^n~(\INVOKE~a) &\stepto& S; \FRAME_m\{F\}~\LABEL_m\{\}~\instr^\ast~\END~\END \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} (\iff & S.\SFUNCS[a] = f \\ \wedge & f.\FITYPE = [t_1^n] \to [t_2^m] \\ - \wedge & m \leq 1 \\ \wedge & f.\FICODE = \{ \FTYPE~x, \FLOCALS~t^k, \FBODY~\instr^\ast~\END \} \\ \wedge & F = \{ \AMODULE~f.\FIMODULE, ~\ALOCALS~\val^n~(t.\CONST~0)^k \}) \end{array} \\ diff --git a/document/core/exec/runtime.rst b/document/core/exec/runtime.rst index fd3004c8a3..a736feb37d 100644 --- a/document/core/exec/runtime.rst +++ b/document/core/exec/runtime.rst @@ -329,7 +329,6 @@ It filters out entries of a specific kind in an order-preserving fashion: * :math:`\evglobals(\externval^\ast) = [\globaladdr ~|~ (\EVGLOBAL~\globaladdr) \in \externval^\ast]` - .. index:: ! stack, ! frame, ! label, instruction, store, activation, function, call, local, module instance pair: abstract syntax; frame pair: abstract syntax; label @@ -381,7 +380,7 @@ Intuitively, :math:`\instr^\ast` is the *continuation* to execute when the branc For example, a loop label has the form .. math:: - \LABEL_n\{\LOOP~[t^?]~\dots~\END\} + \LABEL_n\{\LOOP~\dots~\END\} 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 @@ -416,9 +415,13 @@ Conventions * The meta variable :math:`F` ranges over frames where clear from context. -.. note:: - In the current version of WebAssembly, the arities of labels and frames cannot be larger than :math:`1`. - This may be generalized in future versions. +* The following auxiliary definition takes a :ref:`block type ` and looks up the :ref:`function type ` that it denotes in the current frame: + +.. math:: + \begin{array}{lll} + \expand_F(\typeidx) &=& F.\AMODULE.\MITYPES[\typeidx] \\ + \expand_F([\valtype^?]) &=& [] \to [\valtype^?] \\ + \end{array} .. index:: ! administrative instructions, function, function instance, function address, label, frame, instruction, trap, call, memory, memory instance, table, table instance, element, data, segment diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index 6ac02dbf66..d707beb48c 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -9,11 +9,6 @@ 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. -.. note:: - In the current version of WebAssembly, - at most one result value can be pushed by a single instruction. - This restriction may be lifted in future versions. - In addition to dynamic operands from the stack, some instructions also have static *immediate* arguments, typically :ref:`indices ` or type annotations, which are part of the instruction itself. @@ -274,8 +269,11 @@ Both instructions operate in units of :ref:`page size `. This restriction may be lifted in future versions. -.. index:: ! control instruction, ! structured control, ! label, ! block, ! branch, ! unwinding, result type, label index, function index, type index, vector, trap, function, table, function type +.. index:: ! control instruction, ! structured control, ! label, ! block, ! block type, ! branch, ! unwinding, result type, label index, function index, type index, vector, trap, function, table, function type, value type, type index pair: abstract syntax; instruction + pair: abstract syntax; block type + pair: block; type +.. _syntax-blocktype: .. _syntax-nop: .. _syntax-unreachable: .. _syntax-block: @@ -297,13 +295,15 @@ Instructions in this group affect the flow of control. .. math:: \begin{array}{llcl} + \production{block type} & \blocktype &::=& + \typeidx ~|~ \valtype^? \\ \production{instruction} & \instr &::=& \dots \\&&|& \NOP \\&&|& \UNREACHABLE \\&&|& - \BLOCK~\resulttype~\instr^\ast~\END \\&&|& - \LOOP~\resulttype~\instr^\ast~\END \\&&|& - \IF~\resulttype~\instr^\ast~\ELSE~\instr^\ast~\END \\&&|& + \BLOCK~\blocktype~\instr^\ast~\END \\&&|& + \LOOP~\blocktype~\instr^\ast~\END \\&&|& + \IF~\blocktype~\instr^\ast~\ELSE~\instr^\ast~\END \\&&|& \BR~\labelidx \\&&|& \BRIF~\labelidx \\&&|& \BRTABLE~\vec(\labelidx)~\labelidx \\&&|& @@ -319,7 +319,9 @@ The |UNREACHABLE| instruction causes an unconditional :ref:`trap `. The |BLOCK|, |LOOP| and |IF| instructions are *structured* instructions. They bracket nested sequences of instructions, called *blocks*, terminated with, or separated by, |END| or |ELSE| pseudo-instructions. As the grammar prescribes, they must be well-nested. -A structured instruction can produce a value as described by the annotated :ref:`result type `. + +A structured instruction can consume *input* and produce *output* on the operand stack according to its annotated *block type*. +It is given either as a :ref:`type index ` that refers to a suitable :ref:`function type `, or as an optional :ref:`value type ` inline, which is a shorthand for the function type :math:`[] \to [\valtype^?]`. Each structured control instruction introduces an implicit *label*. Labels are targets for branch instructions that reference them with :ref:`label indices `. @@ -345,7 +347,9 @@ Branch instructions come in several flavors: and |BRTABLE| performs an indirect branch through an operand indexing into the label vector that is an immediate to the instruction, or to a default target if the operand is out of bounds. 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, forward branches that target a control instruction with a non-empty result type consume matching operands first and push them back on the operand stack after unwinding, as a result for the terminated structured instruction. +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 :ref:`function `, consuming the necessary arguments from the stack and returning the result values of the call. The |CALLINDIRECT| instruction calls a function indirectly through an operand indexing into a :ref:`table `. diff --git a/document/core/syntax/types.rst b/document/core/syntax/types.rst index be81063622..2310a6ef1f 100644 --- a/document/core/syntax/types.rst +++ b/document/core/syntax/types.rst @@ -40,7 +40,7 @@ Conventions That is, :math:`|\I32| = |\F32| = 32` and :math:`|\I64| = |\F64| = 64`. -.. index:: ! result type, value type, instruction, execution, block +.. index:: ! result type, value type, instruction, execution, function pair: abstract syntax; result type pair: result; type .. _syntax-resulttype: @@ -48,21 +48,17 @@ Conventions Result Types ~~~~~~~~~~~~ -*Result types* classify the result of :ref:`executing ` :ref:`instructions ` or :ref:`blocks `, +*Result types* classify the result of :ref:`executing ` :ref:`instructions ` or :ref:`functions `, which is a sequence of values written with brackets. .. math:: \begin{array}{llll} \production{result type} & \resulttype &::=& - [\valtype^?] \\ + [\vec(\valtype)] \\ \end{array} -.. note:: - In the current version of WebAssembly, at most one value is allowed as a result. - However, this may be generalized to sequences of values in future versions. - -.. index:: ! function type, value type, vector, function, parameter, result +.. index:: ! function type, value type, vector, function, parameter, result, result type pair: abstract syntax; function type pair: function; type .. _syntax-functype: @@ -71,19 +67,15 @@ Function Types ~~~~~~~~~~~~~~ *Function types* classify the signature of :ref:`functions `, -mapping a vector of parameters to a vector of results, written as follows. +mapping a vector of parameters to a vector of results. +They are also used to classify the inputs and outputs of :ref:`instructions `. .. math:: \begin{array}{llll} \production{function type} & \functype &::=& - [\vec(\valtype)] \to [\vec(\valtype)] \\ + \resulttype \to \resulttype \\ \end{array} -.. note:: - In the current version of WebAssembly, - the length of the result type vector of a :ref:`valid ` function type may be at most :math:`1`. - This restriction may be removed in future versions. - .. index:: ! limits, memory type, table type pair: abstract syntax; limits diff --git a/document/core/text/instructions.rst b/document/core/text/instructions.rst index 5fba8289da..a3add4ae07 100644 --- a/document/core/text/instructions.rst +++ b/document/core/text/instructions.rst @@ -52,6 +52,7 @@ The following grammar handles the corresponding update to the :ref:`identifier c Control Instructions ~~~~~~~~~~~~~~~~~~~~ +.. _text-blocktype: .. _text-block: .. _text-loop: .. _text-if: @@ -60,21 +61,35 @@ Control Instructions :ref:`Structured control instructions ` can bind an optional symbolic :ref:`label identifier `. The same label identifier may optionally be repeated after the corresponding :math:`\T{end}` and :math:`\T{else}` pseudo instructions, to indicate the matching delimiters. +Their :ref:`block type ` is given as a :ref:`type use `, analogous to the type of :ref:`functions `. +However, the special case of a type use that is syntactically empty or consists of only a single :ref:`result ` is not regarded as an :ref:`abbreviation ` for an inline :ref:`function type `, but is parsed directly into an optional :ref:`value type `. + .. math:: \begin{array}{llclll} + \production{block type} & \Tblocktype_I & + \begin{array}[t]{@{}c@{}} ::= \\ | \\ \end{array} + & + \begin{array}[t]{@{}lcll@{}} + (t{:}\Tresult)^? &\Rightarrow& t^? \\ + x,I'{:}\Ttypeuse_I &\Rightarrow& x & (\iff I' = \{\}) \\ + \end{array} \\ \production{block instruction} & \Tblockinstr_I &::=& - \text{block}~~I'{:}\Tlabel_I~~\X{rt}{:}\Tresulttype~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? - \\ &&&\qquad \Rightarrow\quad \BLOCK~\X{rt}~\X{in}^\ast~\END + \text{block}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? + \\ &&&\qquad \Rightarrow\quad \BLOCK~\X{bt}~\X{in}^\ast~\END \qquad\quad~~ (\iff \Tid^? = \epsilon \vee \Tid^? = \Tlabel) \\ &&|& - \text{loop}~~I'{:}\Tlabel_I~~\X{rt}{:}\Tresulttype~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? - \\ &&&\qquad \Rightarrow\quad \LOOP~\X{rt}~\X{in}^\ast~\END + \text{loop}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? + \\ &&&\qquad \Rightarrow\quad \LOOP~\X{bt}~\X{in}^\ast~\END \qquad\qquad (\iff \Tid^? = \epsilon \vee \Tid^? = \Tlabel) \\ &&|& - \text{if}~~I'{:}\Tlabel_I~~\X{rt}{:}\Tresulttype~~(\X{in}_1{:}\Tinstr_{I'})^\ast~~ + \text{if}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype~~(\X{in}_1{:}\Tinstr_{I'})^\ast~~ \text{else}~~\Tid_1^?~~(\X{in}_2{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid_2^? - \\ &&&\qquad \Rightarrow\quad \IF~\X{rt}~\X{in}_1^\ast~\ELSE~\X{in}_2^\ast~\END + \\ &&&\qquad \Rightarrow\quad \IF~\X{bt}~\X{in}_1^\ast~\ELSE~\X{in}_2^\ast~\END \qquad (\iff \Tid_1^? = \epsilon \vee \Tid_1^? = \Tlabel, \Tid_2^? = \epsilon \vee \Tid_2^? = \Tlabel) \\ \end{array} +.. note:: + The side condition stating that the :ref:`identifier context ` :math:`I'` must be empty in the rule for |Ttypeuse| block types enforces that no identifier can be bound in any |Tparam| declaration for a block type. + + .. _text-nop: .. _text-unreachable: .. _text-br: @@ -113,9 +128,9 @@ The :math:`\text{else}` keyword of an :math:`\text{if}` instruction can be omitt .. math:: \begin{array}{llclll} \production{block instruction} & - \text{if}~~\Tlabel~~\Tresulttype~~\Tinstr^\ast~~\text{end} + \text{if}~~\Tlabel~~\Tblocktype~~\Tinstr^\ast~~\text{end} &\equiv& - \text{if}~~\Tlabel~~\Tresulttype~~\Tinstr^\ast~~\text{else}~~\text{end} + \text{if}~~\Tlabel~~\Tblocktype~~\Tinstr^\ast~~\text{else}~~\text{end} \end{array} @@ -453,14 +468,14 @@ Such a folded instruction can appear anywhere a regular instruction can. \production{instruction} & \text{(}~\Tplaininstr~~\Tfoldedinstr^\ast~\text{)} &\equiv\quad \Tfoldedinstr^\ast~~\Tplaininstr \\ & - \text{(}~\text{block}~~\Tlabel~~\Tresulttype~~\Tinstr^\ast~\text{)} - &\equiv\quad \text{block}~~\Tlabel~~\Tresulttype~~\Tinstr^\ast~~\text{end} \\ & - \text{(}~\text{loop}~~\Tlabel~~\Tresulttype~~\Tinstr^\ast~\text{)} - &\equiv\quad \text{loop}~~\Tlabel~~\Tresulttype~~\Tinstr^\ast~~\text{end} \\ & - \text{(}~\text{if}~~\Tlabel~~\Tresulttype~~\Tfoldedinstr^\ast + \text{(}~\text{block}~~\Tlabel~~\Tblocktype~~\Tinstr^\ast~\text{)} + &\equiv\quad \text{block}~~\Tlabel~~\Tblocktype~~\Tinstr^\ast~~\text{end} \\ & + \text{(}~\text{loop}~~\Tlabel~~\Tblocktype~~\Tinstr^\ast~\text{)} + &\equiv\quad \text{loop}~~\Tlabel~~\Tblocktype~~\Tinstr^\ast~~\text{end} \\ & + \text{(}~\text{if}~~\Tlabel~~\Tblocktype~~\Tfoldedinstr^\ast &\hspace{-3ex} \text{(}~\text{then}~~\Tinstr_1^\ast~\text{)}~~\text{(}~\text{else}~~\Tinstr_2^\ast~\text{)}^?~~\text{)} \quad\equiv \\ &\qquad - \Tfoldedinstr^\ast~~\text{if}~~\Tlabel~~\Tresulttype &\hspace{-1ex} \Tinstr_1^\ast~~\text{else}~~(\Tinstr_2^\ast)^?~\text{end} \\ + \Tfoldedinstr^\ast~~\text{if}~~\Tlabel~~\Tblocktype &\hspace{-1ex} \Tinstr_1^\ast~~\text{else}~~(\Tinstr_2^\ast)^?~\text{end} \\ \end{array} .. note:: diff --git a/document/core/text/types.rst b/document/core/text/types.rst index 3943faa3a5..01714835cc 100644 --- a/document/core/text/types.rst +++ b/document/core/text/types.rst @@ -22,23 +22,6 @@ Value Types \end{array} -.. index:: result type, value type - pair: text format; result type -.. _text-resulttype: - -Result Types -~~~~~~~~~~~~ - -.. math:: - \begin{array}{llclll@{\qquad\qquad}l} - \production{result type} & \Tresulttype &::=& - (t{:}\Tresult)^? &\Rightarrow& [t^?] \\ - \end{array} - -.. note:: - In future versions of WebAssembly, this scheme may be extended to support multiple results or more general result types. - - .. index:: function type, value type, result type pair: text format; function type .. _text-param: @@ -61,6 +44,7 @@ Function Types &\Rightarrow& t \\ \end{array} + Abbreviations ............. diff --git a/document/core/util/macros.def b/document/core/util/macros.def index fc086709ef..2c3b687174 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -196,6 +196,7 @@ .. |valtype| mathdef:: \xref{syntax/types}{syntax-valtype}{\X{valtype}} .. |resulttype| mathdef:: \xref{syntax/types}{syntax-resulttype}{\X{resulttype}} .. |functype| mathdef:: \xref{syntax/types}{syntax-functype}{\X{functype}} + .. |globaltype| mathdef:: \xref{syntax/types}{syntax-globaltype}{\X{globaltype}} .. |tabletype| mathdef:: \xref{syntax/types}{syntax-tabletype}{\X{tabletype}} .. |elemtype| mathdef:: \xref{syntax/types}{syntax-elemtype}{\X{elemtype}} @@ -397,6 +398,8 @@ .. |sx| mathdef:: \xref{syntax/instructions}{syntax-sx}{\X{sx}} .. |memarg| mathdef:: \xref{syntax/instructions}{syntax-memarg}{\X{memarg}} +.. |blocktype| mathdef:: \xref{syntax/instructions}{syntax-blocktype}{\X{blocktype}} + .. |instr| mathdef:: \xref{syntax/instructions}{syntax-instr}{\X{instr}} .. |expr| mathdef:: \xref{syntax/instructions}{syntax-expr}{\X{expr}} @@ -429,6 +432,7 @@ .. |BsN| mathdef:: \xref{binary/values}{binary-int}{\BsX{N}} .. |Bs7| mathdef:: \xref{binary/values}{binary-int}{\BsX{\B{7}}} .. |Bs32| mathdef:: \xref{binary/values}{binary-int}{\BsX{\B{32}}} +.. |Bs33| mathdef:: \xref{binary/values}{binary-int}{\BsX{\B{33}}} .. |Bs64| mathdef:: \xref{binary/values}{binary-int}{\BsX{\B{64}}} .. |BiN| mathdef:: \xref{binary/values}{binary-int}{\BiX{N}} @@ -451,7 +455,6 @@ .. |Bvaltype| mathdef:: \xref{binary/types}{binary-valtype}{\B{valtype}} .. |Bresulttype| mathdef:: \xref{binary/types}{binary-resulttype}{\B{resulttype}} -.. |Bblocktype| mathdef:: \xref{binary/types}{binary-blocktype}{\B{blocktype}} .. |Bfunctype| mathdef:: \xref{binary/types}{binary-functype}{\B{functype}} .. |Bglobaltype| mathdef:: \xref{binary/types}{binary-globaltype}{\B{globaltype}} .. |Btabletype| mathdef:: \xref{binary/types}{binary-tabletype}{\B{tabletype}} @@ -514,6 +517,7 @@ .. Instructions, non-terminals .. |Bmemarg| mathdef:: \xref{binary/instructions}{binary-memarg}{\B{memarg}} +.. |Bblocktype| mathdef:: \xref{binary/instructions}{binary-blocktype}{\B{blocktype}} .. |Binstr| mathdef:: \xref{binary/instructions}{binary-instr}{\B{instr}} .. |Bexpr| mathdef:: \xref{binary/instructions}{binary-expr}{\B{expr}} @@ -606,14 +610,14 @@ .. Types, non-terminals .. |Tvaltype| mathdef:: \xref{text/types}{text-valtype}{\T{valtype}} -.. |Tresulttype| mathdef:: \xref{text/types}{text-resulttype}{\T{resulttype}} -.. |Tblocktype| mathdef:: \xref{text/types}{text-blocktype}{\T{blocktype}} .. |Tfunctype| mathdef:: \xref{text/types}{text-functype}{\T{functype}} + .. |Tglobaltype| mathdef:: \xref{text/types}{text-globaltype}{\T{globaltype}} .. |Ttabletype| mathdef:: \xref{text/types}{text-tabletype}{\T{tabletype}} .. |Telemtype| mathdef:: \xref{text/types}{text-elemtype}{\T{elemtype}} .. |Tmemtype| mathdef:: \xref{text/types}{text-memtype}{\T{memtype}} .. |Tlimits| mathdef:: \xref{text/types}{text-limits}{\T{limits}} + .. |Tparam| mathdef:: \xref{text/types}{text-functype}{\T{param}} .. |Tresult| mathdef:: \xref{text/types}{text-functype}{\T{result}} @@ -667,6 +671,8 @@ .. |Talign| mathdef:: \xref{text/instructions}{text-memarg}{\T{align}} .. |Toffset| mathdef:: \xref{text/instructions}{text-memarg}{\T{offset}} +.. |Tblocktype| mathdef:: \xref{text/instructions}{text-blocktype}{\T{blocktype}} + .. |Tlabel| mathdef:: \xref{text/instructions}{text-label}{\T{label}} .. |Tinstr| mathdef:: \xref{text/instructions}{text-instr}{\T{instr}} .. |Tplaininstr| mathdef:: \xref{text/instructions}{text-plaininstr}{\T{plaininstr}} @@ -723,6 +729,7 @@ .. Judgments .. |vdashlimits| mathdef:: \xref{valid/types}{valid-limits}{\vdash} +.. |vdashblocktype| mathdef:: \xref{valid/types}{valid-blocktype}{\vdash} .. |vdashfunctype| mathdef:: \xref{valid/types}{valid-functype}{\vdash} .. |vdashtabletype| mathdef:: \xref{valid/types}{valid-tabletype}{\vdash} .. |vdashmemtype| mathdef:: \xref{valid/types}{valid-memtype}{\vdash} @@ -864,6 +871,11 @@ .. |frame| mathdef:: \xref{exec/runtime}{syntax-frame}{\X{frame}} +.. Stack, meta functions + +.. |expand| mathdef:: \xref{exec/runtime}{syntax-frame}{\F{expand}} + + .. Administrative Instructions, terminals .. |TRAP| mathdef:: \xref{exec/runtime}{syntax-trap}{\K{trap}} diff --git a/document/core/valid/conventions.rst b/document/core/valid/conventions.rst index e316ff6876..fb4c3e7539 100644 --- a/document/core/valid/conventions.rst +++ b/document/core/valid/conventions.rst @@ -168,15 +168,17 @@ and there is one respective rule for each relevant construct :math:`A` of the ab .. math:: \frac{ - C,\LABEL\,[t^?] \vdash \instr^\ast : [] \to [t^?] + C \vdash \blocktype : [t_1^\ast] \to [t_2^\ast] + \qquad + C,\LABEL\,[t_2^\ast] \vdash \instr^\ast : [t_1^\ast] \to [t_2^\ast] }{ - C \vdash \BLOCK~[t^?]~\instr^\ast~\END : [] \to [t^?] + C \vdash \BLOCK~\blocktype~\instr^\ast~\END : [t_1^\ast] \to [t_2^\ast] } A |BLOCK| instruction is only valid when the instruction sequence in its body is. - Moreover, the result type must match the block's annotation :math:`[t^?]`. + Moreover, the result type must match the block's annotation :math:`\blocktype`. If so, then the |BLOCK| instruction has the same type as the body. - Inside the body an additional label of the same type is available, + Inside the body an additional label of the corresponding result type is available, which is expressed by extending the context :math:`C` with the additional label information for the premise. diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index 3d4aecf0a3..91c52f21c0 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -423,7 +423,7 @@ Memory Instructions } -.. index:: control instructions, structured control, label, block, branch, result type, label index, function index, type index, vector, polymorphism, context +.. index:: control instructions, structured control, label, block, branch, block type, result type, label index, function index, type index, vector, polymorphism, context pair: validation; instruction single: abstract syntax; instruction .. _valid-label: @@ -465,85 +465,88 @@ Control Instructions .. _valid-block: -:math:`\BLOCK~[t^?]~\instr^\ast~\END` -..................................... +:math:`\BLOCK~\blocktype~\instr^\ast~\END` +.......................................... + +* The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t^?]` prepended to the |CLABELS| vector. +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. * Under context :math:`C'`, - the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t^?]`. + the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. -* Then the compound instruction is valid with type :math:`[] \to [t^?]`. +* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. .. math:: \frac{ - C,\CLABELS\,[t^?] \vdashinstrseq \instr^\ast : [] \to [t^?] + 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~[t^?]~\instr^\ast~\END : [] \to [t^?] + C \vdashinstr \BLOCK~\blocktype~\instr^\ast~\END : [t_1^\ast] \to [t_2^\ast] } .. note:: - The :ref:`notation ` :math:`C,\CLABELS\,[t^?]` inserts the new label type at index :math:`0`, shifting all others. - - The fact that the nested instruction sequence :math:`\instr^\ast` must have type :math:`[] \to [t^?]` implies that it cannot access operands that have been pushed on the stack before the block was entered. - This may be generalized in future versions of WebAssembly. + The :ref:`notation ` :math:`C,\CLABELS\,[t^\ast]` inserts the new label type at index :math:`0`, shifting all others. .. _valid-loop: -:math:`\LOOP~[t^?]~\instr^\ast~\END` -.................................... +:math:`\LOOP~\blocktype~\instr^\ast~\END` +......................................... + +* The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the empty :ref:`result type ` :math:`[]` prepended to the |CLABELS| vector. +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_1^\ast]` prepended to the |CLABELS| vector. * Under context :math:`C'`, - the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t^?]`. + the instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. -* Then the compound instruction is valid with type :math:`[] \to [t^?]`. +* Then the compound instruction is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. .. math:: \frac{ - C,\CLABELS\,[] \vdashinstrseq \instr^\ast : [] \to [t^?] + 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~[t^?]~\instr^\ast~\END : [] \to [t^?] + C \vdashinstr \LOOP~\blocktype~\instr^\ast~\END : [t_1^\ast] \to [t_2^\ast] } .. note:: - The :ref:`notation ` :math:`C,\CLABELS\,[]` inserts the new label type at index :math:`0`, shifting all others. - - The fact that the nested instruction sequence :math:`\instr^\ast` must have type :math:`[] \to [t^?]` implies that it cannot access operands that have been pushed on the stack before the loop was entered. - This may be generalized in future versions of WebAssembly. + The :ref:`notation ` :math:`C,\CLABELS\,[t^\ast]` inserts the new label type at index :math:`0`, shifting all others. .. _valid-if: -:math:`\IF~[t^?]~\instr_1^\ast~\ELSE~\instr_2^\ast~\END` -........................................................ +:math:`\IF~\blocktype~\instr_1^\ast~\ELSE~\instr_2^\ast~\END` +............................................................. + +* The :ref:`block type ` must be :ref:`valid ` as some :ref:`function type ` :math:`[t_1^\ast] \to [t_2^\ast]`. -* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t^?]` prepended to the |CLABELS| vector. +* Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with the :ref:`result type ` :math:`[t_2^\ast]` prepended to the |CLABELS| vector. * Under context :math:`C'`, - the instruction sequence :math:`\instr_1^\ast` must be :ref:`valid ` with type :math:`[] \to [t^?]`. + the instruction sequence :math:`\instr_1^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. * Under context :math:`C'`, - the instruction sequence :math:`\instr_2^\ast` must be :ref:`valid ` with type :math:`[] \to [t^?]`. + the instruction sequence :math:`\instr_2^\ast` must be :ref:`valid ` with type :math:`[t_1^\ast] \to [t_2^\ast]`. -* Then the compound instruction is valid with type :math:`[\I32] \to [t^?]`. +* Then the compound instruction is valid with type :math:`[t_1^\ast~\I32] \to [t_2^\ast]`. .. math:: \frac{ - C,\CLABELS\,[t^?] \vdashinstrseq \instr_1^\ast : [] \to [t^?] + C \vdashblocktype \blocktype : [t_1^\ast] \to [t_2^\ast] \qquad - C,\CLABELS\,[t^?] \vdashinstrseq \instr_2^\ast : [] \to [t^?] + 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~[t^?]~\instr_1^\ast~\ELSE~\instr_2^\ast~\END : [\I32] \to [t^?] + C \vdashinstr \IF~\blocktype~\instr_1^\ast~\ELSE~\instr_2^\ast~\END : [t_1^\ast~\I32] \to [t_2^\ast] } .. note:: - The :ref:`notation ` :math:`C,\CLABELS\,[t^?]` inserts the new label type at index :math:`0`, shifting all others. - - The fact that the nested instruction sequence :math:`\instr^\ast` must have type :math:`[] \to [t^?]` implies that it cannot access operands that have been pushed on the stack before the conditional was entered. - This may be generalized in future versions of WebAssembly. + The :ref:`notation ` :math:`C,\CLABELS\,[t^\ast]` inserts the new label type at index :math:`0`, shifting all others. .. _valid-br: @@ -553,15 +556,15 @@ Control Instructions * The label :math:`C.\CLABELS[l]` must be defined in the context. -* Let :math:`[t^?]` be the :ref:`result type ` :math:`C.\CLABELS[l]`. +* Let :math:`[t^\ast]` be the :ref:`result type ` :math:`C.\CLABELS[l]`. -* Then the instruction is valid with type :math:`[t_1^\ast~t^?] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. +* Then the instruction is valid with type :math:`[t_1^\ast~t^\ast] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. .. math:: \frac{ - C.\CLABELS[l] = [t^?] + C.\CLABELS[l] = [t^\ast] }{ - C \vdashinstr \BR~l : [t_1^\ast~t^?] \to [t_2^\ast] + C \vdashinstr \BR~l : [t_1^\ast~t^\ast] \to [t_2^\ast] } .. note:: @@ -577,15 +580,15 @@ Control Instructions * The label :math:`C.\CLABELS[l]` must be defined in the context. -* Let :math:`[t^?]` be the :ref:`result type ` :math:`C.\CLABELS[l]`. +* Let :math:`[t^\ast]` be the :ref:`result type ` :math:`C.\CLABELS[l]`. -* Then the instruction is valid with type :math:`[t^?~\I32] \to [t^?]`. +* Then the instruction is valid with type :math:`[t^\ast~\I32] \to [t^\ast]`. .. math:: \frac{ - C.\CLABELS[l] = [t^?] + C.\CLABELS[l] = [t^\ast] }{ - C \vdashinstr \BRIF~l : [t^?~\I32] \to [t^?] + C \vdashinstr \BRIF~l : [t^\ast~\I32] \to [t^\ast] } .. note:: @@ -599,23 +602,23 @@ Control Instructions * The label :math:`C.\CLABELS[l_N]` must be defined in the context. -* Let :math:`[t^?]` be the :ref:`result type ` :math:`C.\CLABELS[l_N]`. +* Let :math:`[t^\ast]` be the :ref:`result type ` :math:`C.\CLABELS[l_N]`. * For all :math:`l_i` in :math:`l^\ast`, the label :math:`C.\CLABELS[l_i]` must be defined in the context. * For all :math:`l_i` in :math:`l^\ast`, - :math:`C.\CLABELS[l_i]` must be :math:`[t^?]`. + :math:`C.\CLABELS[l_i]` must be :math:`[t^\ast]`. -* Then the instruction is valid with type :math:`[t_1^\ast~t^?~\I32] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. +* Then the instruction is valid with type :math:`[t_1^\ast~t^\ast~\I32] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. .. math:: \frac{ - (C.\CLABELS[l] = [t^?])^\ast + (C.\CLABELS[l] = [t^\ast])^\ast \qquad - C.\CLABELS[l_N] = [t^?] + C.\CLABELS[l_N] = [t^\ast] }{ - C \vdashinstr \BRTABLE~l^\ast~l_N : [t_1^\ast~t^?~\I32] \to [t_2^\ast] + C \vdashinstr \BRTABLE~l^\ast~l_N : [t_1^\ast~t^\ast~\I32] \to [t_2^\ast] } .. note:: @@ -631,15 +634,15 @@ Control Instructions * The return type :math:`C.\CRETURN` must not be absent in the context. -* Let :math:`[t^?]` be the :ref:`result type ` of :math:`C.\CRETURN`. +* Let :math:`[t^\ast]` be the :ref:`result type ` of :math:`C.\CRETURN`. -* Then the instruction is valid with type :math:`[t_1^\ast~t^?] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. +* Then the instruction is valid with type :math:`[t_1^\ast~t^\ast] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. .. math:: \frac{ - C.\CRETURN = [t^?] + C.\CRETURN = [t^\ast] }{ - C \vdashinstr \RETURN : [t_1^\ast~t^?] \to [t_2^\ast] + C \vdashinstr \RETURN : [t_1^\ast~t^\ast] \to [t_2^\ast] } .. note:: @@ -740,7 +743,7 @@ Non-empty Instruction Sequence: :math:`\instr^\ast~\instr_N` } -.. index:: expression +.. index:: expression,result type pair: validation; expression single: abstract syntax; expression single: expression; constant @@ -749,22 +752,22 @@ Non-empty Instruction Sequence: :math:`\instr^\ast~\instr_N` Expressions ~~~~~~~~~~~ -Expressions :math:`\expr` are classified by :ref:`result types ` of the form :math:`[t^?]`. +Expressions :math:`\expr` are classified by :ref:`result types ` of the form :math:`[t^\ast]`. :math:`\instr^\ast~\END` ........................ -* The instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t^?]`, - for some optional :ref:`value type ` :math:`t^?`. +* The instruction sequence :math:`\instr^\ast` must be :ref:`valid ` with type :math:`[] \to [t^\ast]`, + for some :ref:`result type ` :math:`[t^\ast]`. -* Then the expression is valid with :ref:`result type ` :math:`[t^?]`. +* Then the expression is valid with :ref:`result type ` :math:`[t^\ast]`. .. math:: \frac{ - C \vdashinstrseq \instr^\ast : [] \to [t^?] + C \vdashinstrseq \instr^\ast : [] \to [t^\ast] }{ - C \vdashexpr \instr^\ast~\END : [t^?] + C \vdashexpr \instr^\ast~\END : [t^\ast] } diff --git a/document/core/valid/modules.rst b/document/core/valid/modules.rst index a7c93c418f..d08c70f8b3 100644 --- a/document/core/valid/modules.rst +++ b/document/core/valid/modules.rst @@ -14,7 +14,7 @@ Furthermore, most definitions are themselves classified with a suitable type. Functions ~~~~~~~~~ -Functions :math:`\func` are classified by :ref:`function types ` of the form :math:`[t_1^\ast] \to [t_2^?]`. +Functions :math:`\func` are classified by :ref:`function types ` of the form :math:`[t_1^\ast] \to [t_2^\ast]`. :math:`\{ \FTYPE~x, \FLOCALS~t^\ast, \FBODY~\expr \}` @@ -22,34 +22,31 @@ Functions :math:`\func` are classified by :ref:`function types * The type :math:`C.\CTYPES[x]` must be defined in the context. -* Let :math:`[t_1^\ast] \to [t_2^?]` be the :ref:`function type ` :math:`C.\CTYPES[x]`. +* Let :math:`[t_1^\ast] \to [t_2^\ast]` be the :ref:`function type ` :math:`C.\CTYPES[x]`. * Let :math:`C'` be the same :ref:`context ` as :math:`C`, but with: * |CLOCALS| set to the sequence of :ref:`value types ` :math:`t_1^\ast~t^\ast`, concatenating parameters and locals, - * |CLABELS| set to the singular sequence containing only :ref:`result type ` :math:`[t_2^?]`. + * |CLABELS| set to the singular sequence containing only :ref:`result type ` :math:`[t_2^\ast]`. - * |CRETURN| set to the :ref:`result type ` :math:`[t_2^?]`. + * |CRETURN| set to the :ref:`result type ` :math:`[t_2^\ast]`. * Under the context :math:`C'`, - the expression :math:`\expr` must be valid with type :math:`t_2^?`. + the expression :math:`\expr` must be valid with type :math:`[t_2^\ast]`. -* Then the function definition is valid with type :math:`[t_1^\ast] \to [t_2^?]`. +* Then the function definition is valid with type :math:`[t_1^\ast] \to [t_2^\ast]`. .. math:: \frac{ - C.\CTYPES[x] = [t_1^\ast] \to [t_2^?] + C.\CTYPES[x] = [t_1^\ast] \to [t_2^\ast] \qquad - C,\CLOCALS\,t_1^\ast~t^\ast,\CLABELS~[t_2^?],\CRETURN~[t_2^?] \vdashexpr \expr : [t_2^?] + C,\CLOCALS\,t_1^\ast~t^\ast,\CLABELS~[t_2^\ast],\CRETURN~[t_2^\ast] \vdashexpr \expr : [t_2^\ast] }{ - C \vdashfunc \{ \FTYPE~x, \FLOCALS~t^\ast, \FBODY~\expr \} : [t_1^\ast] \to [t_2^?] + C \vdashfunc \{ \FTYPE~x, \FLOCALS~t^\ast, \FBODY~\expr \} : [t_1^\ast] \to [t_2^\ast] } -.. note:: - The restriction on the length of the result types :math:`t_2^\ast` may be lifted in future versions of WebAssembly. - .. index:: table, table type pair: validation; table diff --git a/document/core/valid/types.rst b/document/core/valid/types.rst index a7b928313e..45395f6a53 100644 --- a/document/core/valid/types.rst +++ b/document/core/valid/types.rst @@ -2,7 +2,8 @@ Types ----- Most :ref:`types ` are universally valid. -However, restrictions apply to :ref:`function types ` as well as the :ref:`limits ` of :ref:`table types ` and :ref:`memory types `, which must be checked during validation. +However, restrictions apply to :ref:`limits `, which must be checked during validation. +Moreover, :ref:`block types ` are converted to plain :ref:`function types ` for ease of processing. .. index:: limits @@ -40,6 +41,43 @@ Limits } +.. index:: block type + pair: validation; block type + single: abstract syntax; block type +.. _valid-blocktype: + +Block Types +~~~~~~~~~~~ + +:ref:`Block types ` may be expressed in one of two forms, both of which are converted to plain :ref:`function types ` by the following rules. + +:math:`\typeidx` +................ + +* The type :math:`C.\CTYPES[\typeidx]` must be defined in the context. + +* Then the block type is valid as :ref:`function type ` :math:`C.\CTYPES[\typeidx]`. + +.. math:: + \frac{ + C.\CTYPES[\typeidx] = \functype + }{ + C \vdashblocktype \typeidx : \functype + } + + +:math:`[\valtype^?]` +.................... + +* The block type is valid as :ref:`function type ` :math:`[] \to [\valtype^?]`. + +.. math:: + \frac{ + }{ + C \vdashblocktype [\valtype^?] : [] \to [\valtype^?] + } + + .. index:: function type pair: validation; function type single: abstract syntax; function type @@ -48,24 +86,19 @@ Limits Function Types ~~~~~~~~~~~~~~ -:ref:`Function types ` may not specify more than one result. +:ref:`Function types ` are always valid. :math:`[t_1^n] \to [t_2^m]` ........................... -* The arity :math:`m` must not be larger than :math:`1`. - -* Then the function type is valid. +* The function type is valid. .. math:: \frac{ }{ - \vdashfunctype [t_1^\ast] \to [t_2^?] \ok + \vdashfunctype [t_1^\ast] \to [t_2^\ast] \ok } -.. note:: - The restriction to at most one result may be removed in future versions of WebAssembly. - .. index:: table type, element type, limits pair: validation; table type diff --git a/document/index.html b/document/index.html index f95c9a1d40..37eed9762e 100644 --- a/document/index.html +++ b/document/index.html @@ -33,8 +33,8 @@

Core specification

instantiation, and execution.

diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 9a33ed557f..d01b0ec984 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -86,6 +86,9 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT text: NumberToRawBytes; url: sec-numbertorawbytes text: Built-in Function Objects; url: sec-built-in-function-objects text: NativeError Object Structure; url: sec-nativeerror-object-structure + text: CreateArrayFromList; url: sec-createarrayfromlist + text: GetMethod; url: sec-getmethod + text: IterableToList; url: sec-iterabletolist type: abstract-op text: CreateMethodProperty; url: sec-createmethodproperty urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: dfn @@ -977,8 +980,13 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ 1. Let (|store|, |ret|) be the result of [=func_invoke=](|store|, |funcaddr|, |argsSeq|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |ret| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. - 1. If |ret| is empty, return undefined. - 1. Otherwise, return [=ToJSValue=](|v|), where |v| is the singular element of |ret|. + 1. Let |outArity| be the [=list/size=] of |ret|. + 1. If |outArity| is 0, return undefined. + 1. Otherwise, if |outArity| is 1, return [=ToJSValue=](|v|), where |v| is the singular element of |ret|. + 1. Otherwise, + 1. Let |values| be a new, empty [=list=]. + 1. For each |r| in |ret|, [=append=] [=ToJSValue=](|r|) to |values|. + 1. Return [=CreateArrayFromList=](|values|). Note: [=call an Exported Function|Calling an Exported Function=] executes in the \[[Realm]] of the callee Exported Function, as per the definition of [=built-in function objects=]. @@ -989,14 +997,23 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not To run a host function from the JavaScript object |func|, type |functype|, and [=list=] of [=WebAssembly values=] |arguments|, perform the following steps: 1. Let [|parameters|] → [|results|] be |functype|. - 1. Assert: |results|'s [=list/size=] is at most one. 1. If either |parameters| or |results| contains [=𝗂𝟨𝟦=], throw a {{TypeError}}. + 1. Let |arguments| be a [=list=] of the arguments of the invocation of this function. 1. Let |jsArguments| be an empty [=list=]. 1. [=list/iterate|For each=] |arg| of |arguments|, 1. [=list/Append=] ! [=ToJSValue=](|arg|) to |jsArguments|. 1. Let |ret| be ? [=Call=](|func|, undefined, |jsArguments|). 1. If |results| [=list/is empty=], return « ». - 1. Otherwise, return « ? [=ToWebAssemblyValue=](|ret|, |results|[0]) ». + 1. Otherwise, if |results|'s [=list/size=] is 1, return « ? [=ToWebAssemblyValue=](|ret|, |results|[0]) ». + 1. Otherwise, + 1. Let |method| be ? [=GetMethod=](|ret|, [=@@iterator=]). + 1. If |method| is undefined, [=throw=] a {{TypeError}}. + 1. Let |values| be ? [=IterableToList=](|ret|, |method|). + 1. Let |wasmValues| be a new, empty [=list=]. + 1. If |values| and |results| do not have the same length, throw a {{TypeError}}. + 1. For each |value| and |resultType| in |values| and |results|, paired linearly, + 1. [=list/Append=] [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmValues|. + 1. Return |wasmValues|.
@@ -1128,8 +1145,8 @@ In practice, an implementation may run out of resources for valid modules below
  • The maximum number of memories, including declared or imported memories, is 1.
  • The initial or maximum number of pages for any memory, declared or imported, is at most 65536.
  • -
  • The maximum number of parameters to any function is 1000.
  • -
  • The maximum number of return values for any function is 1.
  • +
  • The maximum number of parameters to any function or block is 1000.
  • +
  • The maximum number of return values for any function or block is 1000.
  • The maximum size of a function body, including locals declarations, is 7654321 bytes.
  • The maximum number of locals declared in a function, including implicitly declared as parameters, is 50000.
  • diff --git a/interpreter/Makefile b/interpreter/Makefile index b726ced610..2948ddb5cb 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -126,7 +126,7 @@ debugtest/%: $(UNOPT) run/%: $(OPT) ./$(OPT) $(@:run/%=../test/core/%.wast) -debug/%: $(UNOPT) +debug/%: $(UNOPT) ./$(UNOPT) $(@:debug/%=../test/core/%.wast) diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index 962edea4ac..4c869ead73 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -98,6 +98,7 @@ let vu1 s = Int64.to_int (vuN 1 s) let vu32 s = Int64.to_int32 (vuN 32 s) let vs7 s = Int64.to_int (vsN 7 s) let vs32 s = Int64.to_int32 (vsN 32 s) +let vs33 s = I32_convert.wrap_i64 (vsN 33 s) let vs64 s = vsN 64 s let f32 s = F32.of_bits (u32 s) let f64 s = F64.of_bits (u64 s) @@ -144,16 +145,12 @@ let elem_type s = | -0x10 -> FuncRefType | _ -> error s (pos s - 1) "malformed element type" -let stack_type s = - match peek s with - | Some 0x40 -> skip 1 s; [] - | _ -> [value_type s] - +let stack_type s = vec value_type s let func_type s = match vs7 s with | -0x20 -> - let ins = vec value_type s in - let out = vec value_type s in + let ins = stack_type s in + let out = stack_type s in FuncType (ins, out) | _ -> error s (pos s - 1) "malformed function type" @@ -200,6 +197,12 @@ let memop s = let offset = vu32 s in Int32.to_int align, offset +let block_type s = + match peek s with + | Some 0x40 -> skip 1 s; ValBlockType None + | Some b when b land 0xc0 = 0x40 -> ValBlockType (Some (value_type s)) + | _ -> VarBlockType (at vs33 s) + let math_prefix s = let pos = pos s in match op s with @@ -220,26 +223,26 @@ let rec instr s = | 0x01 -> nop | 0x02 -> - let ts = stack_type s in + let bt = block_type s in let es' = instr_block s in end_ s; - block ts es' + block bt es' | 0x03 -> - let ts = stack_type s in + let bt = block_type s in let es' = instr_block s in end_ s; - loop ts es' + loop bt es' | 0x04 -> - let ts = stack_type s in + let bt = block_type s in let es1 = instr_block s in if peek s = Some 0x05 then begin expect 0x05 s "ELSE or END opcode expected"; let es2 = instr_block s in end_ s; - if_ ts es1 es2 + if_ bt es1 es2 end else begin end_ s; - if_ ts es1 [] + if_ bt es1 [] end | 0x05 -> error s pos "misplaced ELSE opcode" diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index 4baac46d73..3b98c804e0 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -60,6 +60,7 @@ let encode m = let vu32 i = vu64 Int64.(logand (of_int32 i) 0xffffffffL) let vs7 i = vs64 (Int64.of_int i) let vs32 i = vs64 (Int64.of_int32 i) + let vs33 i = vs64 (I64_convert.extend_i32_s i) let f32 x = u32 (F32.to_bits x) let f64 x = u64 (F64.to_bits x) @@ -99,15 +100,9 @@ let encode m = let elem_type = function | FuncRefType -> vs7 (-0x10) - let stack_type = function - | [] -> vs7 (-0x40) - | [t] -> value_type t - | _ -> - Code.error Source.no_region - "cannot encode stack type with arity > 1 (yet)" - + let stack_type = vec value_type let func_type = function - | FuncType (ins, out) -> vs7 (-0x20); vec value_type ins; vec value_type out + | FuncType (ins, out) -> vs7 (-0x20); stack_type ins; stack_type out let limits vu {min; max} = bool (max <> None); vu min; opt vu max @@ -138,15 +133,20 @@ let encode m = let var x = vu32 x.it + let block_type = function + | VarBlockType x -> vs33 x.it + | ValBlockType None -> vs7 (-0x40) + | ValBlockType (Some t) -> value_type t + let rec instr e = match e.it with | Unreachable -> op 0x00 | Nop -> op 0x01 - | Block (ts, es) -> op 0x02; stack_type ts; list instr es; end_ () - | Loop (ts, es) -> op 0x03; stack_type ts; list instr es; end_ () - | If (ts, es1, es2) -> - op 0x04; stack_type ts; list instr es1; + | Block (bt, es) -> op 0x02; block_type bt; list instr es; end_ () + | Loop (bt, es) -> op 0x03; block_type bt; list instr es; end_ () + | If (bt, es1, es2) -> + op 0x04; block_type bt; list instr es1; if es2 <> [] then op 0x05; list instr es2; end_ () diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index d3252f350c..6687dec930 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -54,8 +54,8 @@ and admin_instr' = | Trapping of string | Returning of value stack | Breaking of int32 * value stack - | Label of int * instr list * code - | Frame of int * frame * code + | Label of int32 * instr list * code + | Frame of int32 * frame * code type config = { @@ -93,11 +93,21 @@ let func_elem inst x i at = | FuncElem f -> f | _ -> Crash.error at ("type mismatch for element " ^ Int32.to_string i) +let func_type_of = function + | Func.AstFunc (t, inst, f) -> t + | Func.HostFunc (t, _) -> t + +let block_type inst bt = + match bt with + | VarBlockType x -> type_ inst x + | ValBlockType None -> FuncType ([], []) + | ValBlockType (Some t) -> FuncType ([], [t]) + let take n (vs : 'a stack) at = - try Lib.List.take n vs with Failure _ -> Crash.error at "stack underflow" + try Lib.List32.take n vs with Failure _ -> Crash.error at "stack underflow" let drop n (vs : 'a stack) at = - try Lib.List.drop n vs with Failure _ -> Crash.error at "stack underflow" + try Lib.List32.drop n vs with Failure _ -> Crash.error at "stack underflow" (* Evaluation *) @@ -124,17 +134,24 @@ let rec step (c : config) : config = | Nop, vs -> vs, [] - | Block (ts, es'), vs -> - vs, [Label (List.length ts, [], ([], List.map plain es')) @@ e.at] + | Block (bt, es'), vs -> + let FuncType (ts1, ts2) = block_type frame.inst bt in + let n1 = Lib.List32.length ts1 in + let n2 = Lib.List32.length ts2 in + let args, vs' = take n1 vs e.at, drop n1 vs e.at in + vs', [Label (n2, [], (args, List.map plain es')) @@ e.at] - | Loop (ts, es'), vs -> - vs, [Label (0, [e' @@ e.at], ([], List.map plain es')) @@ e.at] + | Loop (bt, es'), vs -> + let FuncType (ts1, ts2) = block_type frame.inst bt in + let n1 = Lib.List32.length ts1 in + let args, vs' = take n1 vs e.at, drop n1 vs e.at in + vs', [Label (n1, [e' @@ e.at], (args, List.map plain es')) @@ e.at] - | If (ts, es1, es2), I32 0l :: vs' -> - vs', [Plain (Block (ts, es2)) @@ e.at] + | If (bt, es1, es2), I32 0l :: vs' -> + vs', [Plain (Block (bt, es2)) @@ e.at] - | If (ts, es1, es2), I32 i :: vs' -> - vs', [Plain (Block (ts, es1)) @@ e.at] + | If (bt, es1, es2), I32 i :: vs' -> + vs', [Plain (Block (bt, es1)) @@ e.at] | Br x, vs -> [], [Breaking (x.it, vs) @@ e.at] @@ -301,15 +318,15 @@ let rec step (c : config) : config = Exhaustion.error e.at "call stack exhausted" | Invoke func, vs -> - let FuncType (ins, out) = Func.type_of func in - let n = List.length ins in - let args, vs' = take n vs e.at, drop n vs e.at in + let FuncType (ins, out) = func_type_of func in + let n1, n2 = Lib.List32.length ins, Lib.List32.length out in + let args, vs' = take n1 vs e.at, drop n1 vs e.at in (match func with | Func.AstFunc (t, inst', f) -> let locals' = List.rev args @ List.map default_value f.it.locals in - let code' = [], [Plain (Block (out, f.it.body)) @@ f.at] in let frame' = {inst = !inst'; locals = List.map ref locals'} in - vs', [Frame (List.length out, frame', code') @@ e.at] + let instr' = [Label (n2, [], ([], List.map plain f.it.body)) @@ f.at] in + vs', [Frame (n2, frame', ([], instr')) @@ e.at] | Func.HostFunc (t, f) -> try List.rev (f (List.rev args)) @ vs', [] diff --git a/interpreter/script/js.ml b/interpreter/script/js.ml index f4ec97c310..9dc7a9ece1 100644 --- a/interpreter/script/js.ml +++ b/interpreter/script/js.ml @@ -120,22 +120,32 @@ function assert_exhaustion(action) { throw new Error("Wasm resource exhaustion expected"); } -function assert_return(action, expected) { +function assert_return(action, ...expected) { let actual = action(); - switch (expected) { - case "nan:canonical": - case "nan:arithmetic": - case "nan:any": - // Note that JS can't reliably distinguish different NaN values, - // so there's no good way to test that it's a canonical NaN. - if (!Number.isNaN(actual)) { - throw new Error("Wasm return value NaN expected, got " + actual); - }; - return; - default: - if (!Object.is(actual, expected)) { - throw new Error("Wasm return value " + expected + " expected, got " + actual); - }; + if (actual === undefined) { + actual = []; + } else if (!Array.isArray(actual)) { + actual = [actual]; + } + if (actual.length !== expected.length) { + throw new Error(expected.length + " value(s) expected, got " + actual.length); + } + for (let i = 0; i < actual.length; ++i) { + switch (expected[i]) { + case "nan:canonical": + case "nan:arithmetic": + case "nan:any": + // Note that JS can't reliably distinguish different NaN values, + // so there's no good way to test that it's a canonical NaN. + if (!Number.isNaN(actual[i])) { + throw new Error("Wasm return value NaN expected, got " + actual[i]); + }; + return; + default: + if (!Object.is(actual[i], expected[i])) { + throw new Error("Wasm return value " + expected[i] + " expected, got " + actual[i]); + }; + } } } |} @@ -256,7 +266,7 @@ let wrap module_name item_name wrap_action wrap_assertion at = let edesc = FuncExport item @@ at in let exports = [{name = Utf8.decode "run"; edesc} @@ at] in let body = - [ Block ([], action @ assertion @ [Return @@ at]) @@ at; + [ Block (ValBlockType None, action @ assertion @ [Return @@ at]) @@ at; Unreachable @@ at ] in let funcs = [{ftype = 0l @@ at; locals; body} @@ at] in diff --git a/interpreter/syntax/ast.ml b/interpreter/syntax/ast.ml index 91c374d793..12369afbaa 100644 --- a/interpreter/syntax/ast.ml +++ b/interpreter/syntax/ast.ml @@ -68,15 +68,17 @@ type var = int32 Source.phrase type literal = Values.value Source.phrase type name = int list +type block_type = VarBlockType of var | ValBlockType of value_type option + type instr = instr' Source.phrase and instr' = | Unreachable (* trap unconditionally *) | Nop (* do nothing *) | Drop (* forget a value *) | Select (* branchless conditional *) - | Block of stack_type * instr list (* execute in sequence *) - | Loop of stack_type * instr list (* loop header *) - | If of stack_type * instr list * instr list (* conditional *) + | Block of block_type * instr list (* execute in sequence *) + | Loop of block_type * instr list (* loop header *) + | If of block_type * instr list * instr list (* conditional *) | Br of var (* break to n-th surrounding label *) | BrIf of var (* conditional break *) | BrTable of var list * var (* indexed break *) diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index d0ecd02351..57c87d1681 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -13,12 +13,12 @@ let unreachable = Unreachable let nop = Nop let drop = Drop let select = Select -let block ts es = Block (ts, es) -let loop ts es = Loop (ts, es) +let block bt es = Block (bt, es) +let loop bt es = Loop (bt, es) +let if_ bt es1 es2 = If (bt, es1, es2) let br x = Br x let br_if x = BrIf x let br_table xs x = BrTable (xs, x) -let if_ ts es1 es2 = If (ts, es1, es2) let return = Return let call x = Call x diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index ce179c5113..77cf930d35 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -61,8 +61,6 @@ let elem_type t = string_of_elem_type t let decls kind ts = tab kind (atom value_type) ts -let stack_type ts = decls "result" ts - let func_type (FuncType (ins, out)) = Node ("func", decls "param" ins @ decls "result" out) @@ -224,6 +222,10 @@ let var x = nat32 x.it let value v = string_of_value v.it let constop v = value_type (type_of v.it) ^ ".const" +let block_type = function + | VarBlockType x -> [Node ("type " ^ var x, [])] + | ValBlockType ts -> decls "result" (list_of_opt ts) + let rec instr e = let head, inner = match e.it with @@ -231,10 +233,10 @@ let rec instr e = | Nop -> "nop", [] | Drop -> "drop", [] | Select -> "select", [] - | Block (ts, es) -> "block", stack_type ts @ list instr es - | Loop (ts, es) -> "loop", stack_type ts @ list instr es - | If (ts, es1, es2) -> - "if", stack_type ts @ + | Block (bt, es) -> "block", block_type bt @ list instr es + | Loop (bt, es) -> "loop", block_type bt @ list instr es + | If (bt, es1, es2) -> + "if", block_type bt @ [Node ("then", list instr es1); Node ("else", list instr es2)] | Br x -> "br " ^ var x, [] | BrIf x -> "br_if " ^ var x, [] diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 54a6cca292..031608431e 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -397,22 +397,43 @@ call_instr_results_instr : block_instr : | BLOCK labeling_opt block END labeling_end_opt - { fun c -> let c' = $2 c $5 in let ts, es = $3 c' in block ts es } + { fun c -> let c' = $2 c $5 in let bt, es = $3 c' in block bt es } | LOOP labeling_opt block END labeling_end_opt - { fun c -> let c' = $2 c $5 in let ts, es = $3 c' in loop ts es } + { fun c -> let c' = $2 c $5 in let bt, es = $3 c' in loop bt es } | IF labeling_opt block END labeling_end_opt - { fun c -> let c' = $2 c $5 in let ts, es = $3 c' in if_ ts es [] } + { fun c -> let c' = $2 c $5 in let bt, es = $3 c' in if_ bt es [] } | IF labeling_opt block ELSE labeling_end_opt instr_list END labeling_end_opt { fun c -> let c' = $2 c ($5 @ $8) in let ts, es1 = $3 c' in if_ ts es1 ($6 c') } -block_type : - | LPAR RESULT VALUE_TYPE RPAR { [$3] } - block : - | block_type instr_list - { fun c -> $1, $2 c } - | instr_list { fun c -> [], $1 c } + | type_use block_param_body + { let at1 = ati 1 in + fun c -> + VarBlockType (inline_type_explicit c ($1 c type_) (fst $2) at1), + snd $2 c } + | block_param_body /* Sugar */ + { let at = at () in + fun c -> + let bt = + match fst $1 with + | FuncType ([], []) -> ValBlockType None + | FuncType ([], [t]) -> ValBlockType (Some t) + | ft -> VarBlockType (inline_type c ft at) + in bt, snd $1 c } + +block_param_body : + | block_result_body { $1 } + | LPAR PARAM value_type_list RPAR block_param_body + { let FuncType (ins, out) = fst $5 in + FuncType ($3 @ ins, out), snd $5 } + +block_result_body : + | instr_list { FuncType ([], []), $1 } + | LPAR RESULT value_type_list RPAR block_result_body + { let FuncType (ins, out) = fst $5 in + FuncType (ins, $3 @ out), snd $5 } + expr : /* Sugar */ | LPAR expr1 RPAR @@ -423,12 +444,12 @@ expr1 : /* Sugar */ | CALL_INDIRECT call_expr_type { fun c -> let x, es = $2 c in es, call_indirect x } | BLOCK labeling_opt block - { fun c -> let c' = $2 c [] in let ts, es = $3 c' in [], block ts es } + { fun c -> let c' = $2 c [] in let bt, es = $3 c' in [], block bt es } | LOOP labeling_opt block - { fun c -> let c' = $2 c [] in let ts, es = $3 c' in [], loop ts es } + { fun c -> let c' = $2 c [] in let bt, es = $3 c' in [], loop bt es } | IF labeling_opt if_block { fun c -> let c' = $2 c [] in - let ts, (es, es1, es2) = $3 c c' in es, if_ ts es1 es2 } + let bt, (es, es1, es2) = $3 c c' in es, if_ bt es1 es2 } call_expr_type : | type_use call_expr_params @@ -456,8 +477,32 @@ call_expr_results : if_block : - | block_type if_block { fun c c' -> let ts, ess = $2 c c' in $1 @ ts, ess } - | if_ { fun c c' -> [], $1 c c' } + | type_use if_block_param_body + { let at = at () in + fun c c' -> + VarBlockType (inline_type_explicit c ($1 c type_) (fst $2) at), + snd $2 c c' } + | if_block_param_body /* Sugar */ + { let at = at () in + fun c c' -> + let bt = + match fst $1 with + | FuncType ([], []) -> ValBlockType None + | FuncType ([], [t]) -> ValBlockType (Some t) + | ft -> VarBlockType (inline_type c ft at) + in bt, snd $1 c c' } + +if_block_param_body : + | if_block_result_body { $1 } + | LPAR PARAM value_type_list RPAR if_block_param_body + { let FuncType (ins, out) = fst $5 in + FuncType ($3 @ ins, out), snd $5 } + +if_block_result_body : + | if_ { FuncType ([], []), $1 } + | LPAR RESULT value_type_list RPAR if_block_result_body + { let FuncType (ins, out) = fst $5 in + FuncType (ins, $3 @ out), snd $5 } if_ : | expr if_ @@ -491,24 +536,24 @@ func : func_fields : | type_use func_fields_body { fun c x at -> - let t = inline_type_explicit c ($1 c type_) (fst $2) at in - [{(snd $2 (enter_func c)) with ftype = t} @@ at], [], [] } + let y = inline_type_explicit c ($1 c type_) (fst $2) at in + [{(snd $2 (enter_func c)) with ftype = y} @@ at], [], [] } | func_fields_body /* Sugar */ { fun c x at -> - let t = inline_type c (fst $1) at in - [{(snd $1 (enter_func c)) with ftype = t} @@ at], [], [] } + let y = inline_type c (fst $1) at in + [{(snd $1 (enter_func c)) with ftype = y} @@ at], [], [] } | inline_import type_use func_fields_import /* Sugar */ { fun c x at -> - let t = inline_type_explicit c ($2 c type_) $3 at in + let y = inline_type_explicit c ($2 c type_) $3 at in [], [{ module_name = fst $1; item_name = snd $1; - idesc = FuncImport t @@ at } @@ at ], [] } + idesc = FuncImport y @@ at } @@ at ], [] } | inline_import func_fields_import /* Sugar */ { fun c x at -> - let t = inline_type c $2 at in + let y = inline_type c $2 at in [], [{ module_name = fst $1; item_name = snd $1; - idesc = FuncImport t @@ at } @@ at ], [] } + idesc = FuncImport y @@ at } @@ at ], [] } | inline_export func_fields /* Sugar */ { fun c x at -> let fns, ims, exs = $2 c x at in fns, ims, $1 (FuncExport x) c :: exs } diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index e3bbef8686..584331b2c7 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -160,9 +160,6 @@ let check_memop (c : context) (memop : 'a memop) get_sz at = require (1 lsl memop.align <= size) at "alignment must not be larger than natural" -let check_arity n at = - require (n <= 1) at "invalid result arity, larger than 1 is not (yet) allowed" - (* * Conventions: @@ -184,6 +181,12 @@ let check_arity n at = * declarative typing rules. *) +let check_block_type (c : context) (bt : block_type) : func_type = + match bt with + | VarBlockType x -> type_ c x + | ValBlockType None -> FuncType ([], []) + | ValBlockType (Some t) -> FuncType ([], [t]) + let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type = match e.it with | Unreachable -> @@ -199,21 +202,21 @@ let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type = let t = peek 1 s in [t; t; Some I32Type] -~> [t] - | Block (ts, es) -> - check_arity (List.length ts) e.at; - check_block {c with labels = ts :: c.labels} es ts e.at; - [] --> ts + | Block (bt, es) -> + let FuncType (ts1, ts2) as ft = check_block_type c bt in + check_block {c with labels = ts2 :: c.labels} es ft e.at; + ts1 --> ts2 - | Loop (ts, es) -> - check_arity (List.length ts) e.at; - check_block {c with labels = [] :: c.labels} es ts e.at; - [] --> ts + | Loop (bt, es) -> + let FuncType (ts1, ts2) as ft = check_block_type c bt in + check_block {c with labels = ts1 :: c.labels} es ft e.at; + ts1 --> ts2 - | If (ts, es1, es2) -> - check_arity (List.length ts) e.at; - check_block {c with labels = ts :: c.labels} es1 ts e.at; - check_block {c with labels = ts :: c.labels} es2 ts e.at; - [I32Type] --> ts + | If (bt, es1, es2) -> + let FuncType (ts1, ts2) as ft = check_block_type c bt in + check_block {c with labels = ts2 :: c.labels} es1 ft e.at; + check_block {c with labels = ts2 :: c.labels} es2 ft e.at; + (ts1 @ [I32Type]) --> ts2 | Br x -> label c x -->... [] @@ -238,8 +241,6 @@ let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type = let FuncType (ins, out) = type_ c x in (ins @ [I32Type]) --> out - - | LocalGet x -> [] --> [local c x] @@ -299,22 +300,24 @@ let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type = let t1, t2 = type_cvtop e.at cvtop in [t1] --> [t2] -and check_seq (c : context) (es : instr list) : infer_stack_type = +and check_seq (c : context) (s : infer_stack_type) (es : instr list) + : infer_stack_type = match es with | [] -> - stack [] + s | _ -> let es', e = Lib.List.split_last es in - let s = check_seq c es' in - let {ins; outs} = check_instr c e s in - push outs (pop ins s e.at) - -and check_block (c : context) (es : instr list) (ts : stack_type) at = - let s = check_seq c es in - let s' = pop (stack ts) s at in + let s' = check_seq c s es' in + let {ins; outs} = check_instr c e s' in + push outs (pop ins s' e.at) + +and check_block (c : context) (es : instr list) (ft : func_type) at = + let FuncType (ts1, ts2) = ft in + let s = check_seq c (stack ts1) es in + let s' = pop (stack ts2) s at in require (snd s' = []) at - ("type mismatch: operator requires " ^ string_of_stack_type ts ^ + ("type mismatch: block requires " ^ string_of_stack_type ts2 ^ " but stack has " ^ string_of_infer_types (snd s)) @@ -335,8 +338,7 @@ let check_value_type (t : value_type) at = let check_func_type (ft : func_type) at = let FuncType (ins, out) = ft in List.iter (fun t -> check_value_type t at) ins; - List.iter (fun t -> check_value_type t at) out; - check_arity (List.length out) at + List.iter (fun t -> check_value_type t at) out let check_table_type (tt : table_type) at = let TableType (lim, _) = tt in @@ -352,6 +354,10 @@ let check_global_type (gt : global_type) at = check_value_type t at +let check_type (t : type_) = + check_func_type t.it t.at + + (* Functions & Constants *) (* @@ -366,14 +372,11 @@ let check_global_type (gt : global_type) at = * x : variable *) -let check_type (t : type_) = - check_func_type t.it t.at - let check_func (c : context) (f : func) = let {ftype; locals; body} = f.it in let FuncType (ins, out) = type_ c ftype in let c' = {c with locals = ins @ locals; results = out; labels = [out]} in - check_block c' body out f.at + check_block c' body (FuncType ([], out)) f.at let is_const (c : context) (e : instr) = @@ -385,7 +388,7 @@ let is_const (c : context) (e : instr) = let check_const (c : context) (const : const) (t : value_type) = require (List.for_all (is_const c) const.it) const.at "constant expression required"; - check_block c const.it [t] const.at + check_block c const.it (FuncType ([], [t])) const.at (* Tables, Memories, & Globals *) diff --git a/proposals/multi-value/Overview.md b/proposals/multi-value/Overview.md new file mode 100644 index 0000000000..a6aeb61b69 --- /dev/null +++ b/proposals/multi-value/Overview.md @@ -0,0 +1,196 @@ +# Multi-value Extension + +## Introduction + +### Background + +* Currently, functions and instructions consume multiple operands but can produce at most one result + - functions: `value* -> value?` + - instructions: `value* -> value?` + - blocks: `[] -> value?` + +* In a stack machine, these asymmetries are artifical restrictions + - were imposed to simplify the initial WebAssembly release (multiple results deferred to post-MVP) + - can easily be lifted by generalising to value* -> value* + +* Generalised semantics is well-understood + - https://github.com/WebAssembly/spec/tree/master/papers/pldi2017.pdf + +* Semi-complete implementation of multiple results in V8 + + +### Motivation + +* Multiple return values for functions: + - enable unboxing of tuples or structs returned by value + - efficient compilation of multiple return values + +* Multiple results for instructions: + - enable instructions producing several results (divmod, arithmetics with carry) + +* Inputs to blocks: + - loop labels can have arguments + - can represent phis on backward edges + - enable future `pick` operator to cross block boundary + - macro definability of instructons with inputs + * `i32.select3` = `dup if ... else ... end` + + +## Examples + +### Functions with multiple return Values + +A simple swap function. +```wasm +(func $swap (param i32 i32) (result i32 i32) + (get_local 1) (get_local 0) +) +``` + +An addition function returning an additional carry bit. +```wasm +(func $add64_u_with_carry (param $i i64) (param $j i64) (param $c i32) (result i64 i32) + (local $k i64) + (set_local $k + (i64.add (i64.add (get_local $i) (get_local $j)) (i64.extend_u/i32 (get_local $c))) + ) + (return (get_local $k) (i64.lt_u (get_local $k) (get_local $i))) +) +``` + +### Instructions with multiple results + +* `iNN.divrem` : \[iNN iNN\] -> \[iNN iNN\] +* `iNN.add_carry` : \[iNN iNN i32\] -> \[iNN i32\] +* `iNN.sub_carry` : \[iNN iNN i32\] -> \[iNN i32\] +* etc. + + +### Blocks with inputs + +Conditionally manipulating a stack operand without using a local. +```wasm +(func $add64_u_saturated (param i64 i64) (result i64) + ($i64.add_u_carry (get_local 0) (get_local 1) (i32.const 0)) + (if (param i64) (result i64) + (then (drop) (i64.const 0xffff_ffff_ffff_ffff)) + ) +) +``` + +An iterative factorial function whose loop doesn't use locals, but uses arguments like phis. +```wasm +(func $fac (param i64) (result i64) + (i64.const 1) (get_local 0) + (loop $l (param i64 i64) (result i64) + (pick 1) (pick 1) (i64.mul) + (pick 1) (i64.const 1) (i64.sub) + (pick 0) (i64.const 0) (i64.gt_u) + (br_if $l) + (pick 1) (return) + ) +) +``` + +Macro definition of an instruction expanding into an `if`. +``` +i64.select3 = + dup if (param i64 i64 i64 i32) (result i64) … select ... else … end +``` + +Macro expansion of `if` itself. +``` +if (param t*) (result u*) A else B end = + block (param t* i32) (result u*) + block (param t* i32) (result t*) (br_if 0) B (br 1) end A + end +``` + + +## Spec Changes + +### Structure + +The structure of the language is mostly unaffected. The only changes are to the type syntax: + +* *resulttype* is generalised from \[*valtype*?\] to \[*valtype*\*\] +* block types (in `block`, `loop`, `if` instructions) are generalised from *resulttype* to *functype* + + +### Validation + +Arity restrictions are removed: + +* no arity check is imposed for valid *functype* +* all occurrences of superscript "?" are replaced with superscript "\*" (e.g. blocks, calls, return) + +Validation for block instructions is generalised: + +* The type of `block`, `loop`, and `if` is the *functype* \[t1\*\] -> \[t2\*\] given as the block type +* The type of the label of `block` and `if` is \[t2\*\] +* The type of the label of `loop` is \[t1\*\] + + +### Execution + +Nothing much needs to be done for multiple results: + +* replace all occurrences of superscript "?" with superscript "\*". + +The only non-mechanical change involves entering blocks with operands: + +* The operand values are popped of the stack, and pushed right back after the label. +* See paper for formulation of formal reduction rules + + +### Binary Format + +The binary requires a change to allow function types as block types. That requires extending the current ad-hoc encoding to allow references to function types. + +* `blocktype` is extended to the following format: + ``` + blocktype ::= 0x40 => [] -> [] + | t:valtype => [] -> [t] + | ft:typeidx => ft + ``` + +### Text Format + +The text format is mostly unaffected, except that the syntax for block types is generalised: + +* `resulttype` is replaced with `blocktype`, whose syntax is + ``` + blocktype ::= vec(param) vec(result) + ``` + +* `block`, `loop`, and `if` instructions contain a `blocktype` instead of `resulttype`. + +* The existing abbreviations for functions apply to block types. + + +### Soundness Proof + +The typing of administrative instructions need to be generalised, see the paper. + + +## Possible Alternatives and Extensions + +### More Flexible Block and Function Types + +* Instead of inline function types, could use references to the type section + - more bureaucracy in the semantics, but otherwise no problem + +* Could also allow both + - inline function types are slightly more compact for one-off uses + +* Could even unify the encoding of block types with function types everywhere + - allow inline types even for functions, for the same benefits + + +## Open Questions + +* Destructuring or reshuffling multiple values requires locals, is that enough? + - could add `pick` instruction (generalised `dup`) + - could add `let` instruction (if you squint, a generalised `swap`) + - different use cases? + diff --git a/test/core/binary.wast b/test/core/binary.wast index e91d876437..de625bf28b 100644 --- a/test/core/binary.wast +++ b/test/core/binary.wast @@ -636,7 +636,7 @@ "\0a\04\01" ;; code section "\02\00\0b" ;; function body ) - "malformed value type" + "unexpected end" ) ;; 1 elem segment declared, 2 given @@ -777,7 +777,7 @@ "\02" ;; break depth for default "\0b\0b\0b" ;; end ) - "malformed value type" + "unexpected end" ) ;; Start section diff --git a/test/core/block.wast b/test/core/block.wast index 625dd98fbf..44915b991b 100644 --- a/test/core/block.wast +++ b/test/core/block.wast @@ -18,7 +18,16 @@ (func (export "multi") (result i32) (block (call $dummy) (call $dummy) (call $dummy) (call $dummy)) - (block (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8)) + (block (result i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 7) (call $dummy) + ) + (drop) + (block (result i32 i64 i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 8) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i64.const 7) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i32.const 9) (call $dummy) + ) + (drop) (drop) ) (func (export "nested") (result i32) @@ -198,6 +207,28 @@ (block (result f32) (call $dummy) (f32.const 3)) ) ) + (func (export "as-binary-operands") (result i32) + (i32.mul + (block (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + ) + ) + (func (export "as-compare-operands") (result i32) + (f32.gt + (block (result f32 f32) + (call $dummy) (f32.const 3) (call $dummy) (f32.const 3) + ) + ) + ) + (func (export "as-mixed-operands") (result i32) + (block (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) (func (export "break-bare") (result i32) (block (br 0) (unreachable)) @@ -209,6 +240,12 @@ (func (export "break-value") (result i32) (block (result i32) (br 0 (i32.const 18)) (i32.const 19)) ) + (func (export "break-multi-value") (result i32 i32 i64) + (block (result i32 i32 i64) + (br 0 (i32.const 18) (i32.const -18) (i64.const 18)) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + ) (func (export "break-repeated") (result i32) (block (result i32) (br 0 (i32.const 18)) @@ -235,6 +272,49 @@ (local.get 0) ) + (func (export "param") (result i32) + (i32.const 1) + (block (param i32) (result i32) + (i32.const 2) + (i32.add) + ) + ) + (func (export "params") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32) + (i32.add) + ) + ) + (func (export "params-id") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32 i32)) + (i32.add) + ) + (func (export "param-break") (result i32) + (i32.const 1) + (block (param i32) (result i32) + (i32.const 2) + (i32.add) + (br 0) + ) + ) + (func (export "params-break") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32) + (i32.add) + (br 0) + ) + ) + (func (export "params-id-break") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32 i32) (br 0)) + (i32.add) + ) + (func (export "effects") (result i32) (local i32) (block @@ -247,6 +327,27 @@ ) (i32.eq (local.get 0) (i32.const -14)) ) + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (block (type $block-sig-1)) + (block (type $block-sig-2) (i32.const 0)) + (block (type $block-sig-3) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (block (type $block-sig-4)) + (drop) (drop) (drop) + (block (type $block-sig-2) (result i32) (i32.const 0)) + (block (type $block-sig-3) (param i32) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (block (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + ) + (drop) (drop) (drop) + ) ) (assert_return (invoke "empty")) @@ -294,14 +395,112 @@ (assert_return (invoke "as-binary-operand") (i32.const 12)) (assert_return (invoke "as-test-operand") (i32.const 0)) (assert_return (invoke "as-compare-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operands") (i32.const 12)) +(assert_return (invoke "as-compare-operands") (i32.const 0)) +(assert_return (invoke "as-mixed-operands") (i32.const 27)) (assert_return (invoke "break-bare") (i32.const 19)) (assert_return (invoke "break-value") (i32.const 18)) +(assert_return (invoke "break-multi-value") + (i32.const 18) (i32.const -18) (i64.const 18) +) (assert_return (invoke "break-repeated") (i32.const 18)) (assert_return (invoke "break-inner") (i32.const 0xf)) +(assert_return (invoke "param") (i32.const 3)) +(assert_return (invoke "params") (i32.const 3)) +(assert_return (invoke "params-id") (i32.const 3)) +(assert_return (invoke "param-break") (i32.const 3)) +(assert_return (invoke "params-break") (i32.const 3)) +(assert_return (invoke "params-id-break") (i32.const 3)) + (assert_return (invoke "effects") (i32.const 1)) +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (result i32) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (param i32) (type $sig) (result i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (param i32) (result i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (result i32) (type $sig) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (result i32) (param i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (block (result i32) (param i32)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (i32.const 0) (block (param $x i32) (drop)))") + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (block (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (block (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (param i32) (drop)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (param i32) (result i32)) (unreachable))" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (block (type $sig) (i32.const 0))) + ) + "type mismatch" +) + (assert_invalid (module (func $type-empty-i32 (result i32) (block))) "type mismatch" @@ -343,7 +542,12 @@ )) "type mismatch" ) - +(assert_invalid + (module (func $type-value-nums-vs-void + (block (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-empty-vs-i32 (result i32) (block (result i32)) @@ -368,6 +572,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-value-empty-vs-nums (result i32 i32) + (block (result i32 i32)) + )) + "type mismatch" +) (assert_invalid (module @@ -421,7 +631,12 @@ )) "type mismatch" ) - +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (block (result i32 i32) (nop)) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-i32-vs-i64 (result i32) (block (result i32) (i64.const 0)) @@ -494,6 +709,24 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-value-num-vs-nums (result i32 i32) + (block (result i32 i32) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-partial-vs-nums (result i32 i32) + (i32.const 1) (block (result i32 i32) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result i32) + (block (result i32) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-unreached-select-i32-i64 (result i32) @@ -592,6 +825,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-last-void-vs-nums (result i32 i32) + (block (result i32 i32) (br 0)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-empty-vs-i32 (result i32) @@ -617,6 +856,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-empty-vs-nums (result i32 i32) + (block (result i32 i32) (br 0) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-void-vs-i32 (result i32) @@ -715,6 +960,18 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-num-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (i32.const 0)) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-partial-vs-nums (result i32 i32) + (i32.const 1) (block (result i32 i32) (br 0 (i32.const 0)) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-first-void-vs-i32 (result i32) @@ -740,6 +997,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-first-void-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (nop)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-first-i32-vs-i64 (result i32) @@ -813,6 +1076,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-first-num-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (i32.const 0)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-nested-i32-vs-void @@ -838,6 +1107,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-nested-nums-vs-void + (block (result i32 i32) (block (result i32 i32) (br 1 (i32.const 1) (i32.const 2))) (br 0)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-nested-empty-vs-i32 (result i32) @@ -863,6 +1138,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-nested-empty-vs-nums (result i32 i32) + (block (result i32 i32) (block (br 1)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-nested-void-vs-i32 (result i32) @@ -888,6 +1169,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-nested-void-vs-nums (result i32 i32) + (block (result i32 i32) (block (result i32 i32) (br 1 (nop))) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-nested-i32-vs-i64 (result i32) @@ -985,6 +1272,14 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-nested-num-vs-nums (result i32 i32) + (block (result i32 i32) + (block (result i32 i32) (br 1 (i32.const 0))) (br 0 (i32.const 1) (i32.const 2)) + ) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-operand-empty-vs-i32 (result i32) @@ -1010,6 +1305,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-operand-empty-vs-nums (result i32) + (i32.add (block (br 0))) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-operand-void-vs-i32 (result i32) @@ -1035,6 +1336,12 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-operand-void-vs-nums (result i32) + (i32.add (block (br 0 (nop)))) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-operand-i32-vs-i64 (result i32) @@ -1108,6 +1415,70 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-operand-num-vs-nums (result i32) + (i32.add (block (br 0 (i64.const 9) (i32.const 10)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-param-void-vs-num + (block (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (param i32 f64) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (block (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (block (param f32 i32) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (block (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (block (param i32 f64) (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (block (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (block (param f32 i32) (drop) (drop))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) block (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (block (param $x i32)))") + "unexpected token" +) (assert_malformed diff --git a/test/core/br.wast b/test/core/br.wast index 98a3a34bdc..35f707c922 100644 --- a/test/core/br.wast +++ b/test/core/br.wast @@ -8,6 +8,10 @@ (func (export "type-i64") (block (drop (i64.ctz (br 0))))) (func (export "type-f32") (block (drop (f32.neg (br 0))))) (func (export "type-f64") (block (drop (f64.neg (br 0))))) + (func (export "type-i32-i32") (block (drop (i32.add (br 0))))) + (func (export "type-i64-i64") (block (drop (i64.add (br 0))))) + (func (export "type-f32-f32") (block (drop (f32.add (br 0))))) + (func (export "type-f64-f64") (block (drop (f64.add (br 0))))) (func (export "type-i32-value") (result i32) (block (result i32) (i32.ctz (br 0 (i32.const 1)))) @@ -21,6 +25,11 @@ (func (export "type-f64-value") (result f64) (block (result f64) (f64.neg (br 0 (f64.const 4)))) ) + (func (export "type-f64-f64-value") (result f64 f64) + (block (result f64 f64) + (f64.add (br 0 (f64.const 4) (f64.const 5))) (f64.const 6) + ) + ) (func (export "as-block-first") (block (br 0) (call $dummy)) @@ -84,6 +93,10 @@ (func (export "as-return-value") (result i64) (block (result i64) (return (br 0 (i64.const 7)))) ) + (func (export "as-return-values") (result i32 i64) + (i32.const 2) + (block (result i64) (return (br 0 (i32.const 1) (i64.const 7)))) + ) (func (export "as-if-cond") (result i32) (block (result i32) @@ -125,6 +138,9 @@ (select (i32.const 0) (i32.const 1) (br 0 (i32.const 7))) ) ) + (func (export "as-select-all") (result i32) + (block (result i32) (select (br 0 (i32.const 8)))) + ) (func $f (param i32 i32 i32) (result i32) (i32.const -1)) (func (export "as-call-first") (result i32) @@ -142,6 +158,9 @@ (call $f (i32.const 1) (i32.const 2) (br 0 (i32.const 14))) ) ) + (func (export "as-call-all") (result i32) + (block (result i32) (call $f (br 0 (i32.const 15)))) + ) (type $sig (func (param i32 i32 i32) (result i32))) (table funcref (elem $f)) @@ -177,6 +196,9 @@ ) ) ) + (func (export "as-call_indirect-all") (result i32) + (block (result i32) (call_indirect (type $sig) (br 0 (i32.const 24)))) + ) (func (export "as-local.set-value") (result i32) (local f32) (block (result i32) (local.set 0 (br 0 (i32.const 17))) (i32.const -1)) @@ -207,6 +229,11 @@ (i64.store (i32.const 2) (br 0 (i32.const 31))) (i32.const -1) ) ) + (func (export "as-store-both") (result i32) + (block (result i32) + (i64.store (br 0 (i32.const 32))) (i32.const -1) + ) + ) (func (export "as-storeN-address") (result i32) (block (result i32) @@ -218,6 +245,11 @@ (i64.store16 (i32.const 2) (br 0 (i32.const 33))) (i32.const -1) ) ) + (func (export "as-storeN-both") (result i32) + (block (result i32) + (i64.store16 (br 0 (i32.const 34))) (i32.const -1) + ) + ) (func (export "as-unary-operand") (result f32) (block (result f32) (f32.neg (br 0 (f32.const 3.4)))) @@ -229,6 +261,9 @@ (func (export "as-binary-right") (result i64) (block (result i64) (i64.sub (i64.const 10) (br 0 (i64.const 45)))) ) + (func (export "as-binary-both") (result i32) + (block (result i32) (i32.add (br 0 (i32.const 46)))) + ) (func (export "as-test-operand") (result i32) (block (result i32) (i32.eqz (br 0 (i32.const 44)))) @@ -240,6 +275,9 @@ (func (export "as-compare-right") (result i32) (block (result i32) (f32.ne (f32.const 10) (br 0 (i32.const 42)))) ) + (func (export "as-compare-both") (result i32) + (block (result i32) (f64.le (br 0 (i32.const 44)))) + ) (func (export "as-convert-operand") (result i32) (block (result i32) (i32.wrap_i64 (br 0 (i32.const 41)))) @@ -335,11 +373,16 @@ (assert_return (invoke "type-i64")) (assert_return (invoke "type-f32")) (assert_return (invoke "type-f64")) +(assert_return (invoke "type-i32-i32")) +(assert_return (invoke "type-i64-i64")) +(assert_return (invoke "type-f32-f32")) +(assert_return (invoke "type-f64-f64")) (assert_return (invoke "type-i32-value") (i32.const 1)) (assert_return (invoke "type-i64-value") (i64.const 2)) (assert_return (invoke "type-f32-value") (f32.const 3)) (assert_return (invoke "type-f64-value") (f64.const 4)) +(assert_return (invoke "type-f64-f64-value") (f64.const 4) (f64.const 5)) (assert_return (invoke "as-block-first")) (assert_return (invoke "as-block-mid")) @@ -361,6 +404,7 @@ (assert_return (invoke "as-br_table-value-index") (i32.const 11)) (assert_return (invoke "as-return-value") (i64.const 7)) +(assert_return (invoke "as-return-values") (i32.const 2) (i64.const 7)) (assert_return (invoke "as-if-cond") (i32.const 2)) (assert_return (invoke "as-if-then" (i32.const 1) (i32.const 6)) (i32.const 3)) @@ -373,15 +417,18 @@ (assert_return (invoke "as-select-second" (i32.const 0) (i32.const 6)) (i32.const 6)) (assert_return (invoke "as-select-second" (i32.const 1) (i32.const 6)) (i32.const 6)) (assert_return (invoke "as-select-cond") (i32.const 7)) +(assert_return (invoke "as-select-all") (i32.const 8)) (assert_return (invoke "as-call-first") (i32.const 12)) (assert_return (invoke "as-call-mid") (i32.const 13)) (assert_return (invoke "as-call-last") (i32.const 14)) +(assert_return (invoke "as-call-all") (i32.const 15)) (assert_return (invoke "as-call_indirect-func") (i32.const 20)) (assert_return (invoke "as-call_indirect-first") (i32.const 21)) (assert_return (invoke "as-call_indirect-mid") (i32.const 22)) (assert_return (invoke "as-call_indirect-last") (i32.const 23)) +(assert_return (invoke "as-call_indirect-all") (i32.const 24)) (assert_return (invoke "as-local.set-value") (i32.const 17)) (assert_return (invoke "as-local.tee-value") (i32.const 1)) @@ -392,18 +439,22 @@ (assert_return (invoke "as-store-address") (i32.const 30)) (assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-store-both") (i32.const 32)) (assert_return (invoke "as-storeN-address") (i32.const 32)) (assert_return (invoke "as-storeN-value") (i32.const 33)) +(assert_return (invoke "as-storeN-both") (i32.const 34)) (assert_return (invoke "as-unary-operand") (f32.const 3.4)) (assert_return (invoke "as-binary-left") (i32.const 3)) (assert_return (invoke "as-binary-right") (i64.const 45)) +(assert_return (invoke "as-binary-both") (i32.const 46)) (assert_return (invoke "as-test-operand") (i32.const 44)) (assert_return (invoke "as-compare-left") (i32.const 43)) (assert_return (invoke "as-compare-right") (i32.const 42)) +(assert_return (invoke "as-compare-both") (i32.const 44)) (assert_return (invoke "as-convert-operand") (i32.const 41)) diff --git a/test/core/break-drop.wast b/test/core/break-drop.wast deleted file mode 100644 index e10df66715..0000000000 --- a/test/core/break-drop.wast +++ /dev/null @@ -1,9 +0,0 @@ -(module - (func (export "br") (block (br 0))) - (func (export "br_if") (block (br_if 0 (i32.const 1)))) - (func (export "br_table") (block (br_table 0 (i32.const 0)))) -) - -(assert_return (invoke "br")) -(assert_return (invoke "br_if")) -(assert_return (invoke "br_table")) diff --git a/test/core/call.wast b/test/core/call.wast index 4d0f1a7c25..e4f854f7a5 100644 --- a/test/core/call.wast +++ b/test/core/call.wast @@ -6,11 +6,25 @@ (func $const-i64 (result i64) (i64.const 0x164)) (func $const-f32 (result f32) (f32.const 0xf32)) (func $const-f64 (result f64) (f64.const 0xf64)) + (func $const-i32-i64 (result i32 i64) (i32.const 0x132) (i64.const 0x164)) (func $id-i32 (param i32) (result i32) (local.get 0)) (func $id-i64 (param i64) (result i64) (local.get 0)) (func $id-f32 (param f32) (result f32) (local.get 0)) (func $id-f64 (param f64) (result f64) (local.get 0)) + (func $id-i32-f64 (param i32 f64) (result i32 f64) + (local.get 0) (local.get 1) + ) + + (func $swap-i32-i32 (param i32 i32) (result i32 i32) + (local.get 1) (local.get 0) + ) + (func $swap-f32-f64 (param f32 f64) (result f64 f32) + (local.get 1) (local.get 0) + ) + (func $swap-f64-i32 (param f64 i32) (result i32 f64) + (local.get 1) (local.get 0) + ) (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) @@ -23,6 +37,7 @@ (func (export "type-i64") (result i64) (call $const-i64)) (func (export "type-f32") (result f32) (call $const-f32)) (func (export "type-f64") (result f64) (call $const-f64)) + (func (export "type-i32-i64") (result i32 i64) (call $const-i32-i64)) (func (export "type-first-i32") (result i32) (call $id-i32 (i32.const 32))) (func (export "type-first-i64") (result i64) (call $id-i64 (i64.const 64))) @@ -42,6 +57,36 @@ (call $i64-f64 (i64.const 64) (f64.const 64.1)) ) + (func (export "type-all-i32-f64") (result i32 f64) + (call $id-i32-f64 (i32.const 32) (f64.const 1.64)) + ) + (func (export "type-all-i32-i32") (result i32 i32) + (call $swap-i32-i32 (i32.const 1) (i32.const 2)) + ) + (func (export "type-all-f32-f64") (result f64 f32) + (call $swap-f32-f64 (f32.const 1) (f64.const 2)) + ) + (func (export "type-all-f64-i32") (result i32 f64) + (call $swap-f64-i32 (f64.const 1) (i32.const 2)) + ) + + ;; Composition + + (func (export "as-binary-all-operands") (result i32) + (i32.add (call $swap-i32-i32 (i32.const 3) (i32.const 4))) + ) + + (func (export "as-mixed-operands") (result i32) + (call $swap-i32-i32 (i32.const 3) (i32.const 4)) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "as-call-all-operands") (result i32 i32) + (call $swap-i32-i32 (call $swap-i32-i32 (i32.const 3) (i32.const 4))) + ) + ;; Recursion (func $fac (export "fac") (param i64) (result i64) @@ -241,6 +286,7 @@ (assert_return (invoke "type-i64") (i64.const 0x164)) (assert_return (invoke "type-f32") (f32.const 0xf32)) (assert_return (invoke "type-f64") (f64.const 0xf64)) +(assert_return (invoke "type-i32-i64") (i32.const 0x132) (i64.const 0x164)) (assert_return (invoke "type-first-i32") (i32.const 32)) (assert_return (invoke "type-first-i64") (i64.const 64)) @@ -252,6 +298,15 @@ (assert_return (invoke "type-second-f32") (f32.const 32)) (assert_return (invoke "type-second-f64") (f64.const 64.1)) +(assert_return (invoke "type-all-i32-f64") (i32.const 32) (f64.const 1.64)) +(assert_return (invoke "type-all-i32-i32") (i32.const 2) (i32.const 1)) +(assert_return (invoke "type-all-f32-f64") (f64.const 2) (f32.const 1)) +(assert_return (invoke "type-all-f64-i32") (i32.const 2) (f64.const 1)) + +(assert_return (invoke "as-binary-all-operands") (i32.const 7)) +(assert_return (invoke "as-mixed-operands") (i32.const 32)) +(assert_return (invoke "as-call-all-operands") (i32.const 3) (i32.const 4)) + (assert_return (invoke "fac" (i64.const 0)) (i64.const 1)) (assert_return (invoke "fac" (i64.const 1)) (i64.const 1)) (assert_return (invoke "fac" (i64.const 5)) (i64.const 120)) diff --git a/test/core/call_indirect.wast b/test/core/call_indirect.wast index 87d1df75a5..224e3bef09 100644 --- a/test/core/call_indirect.wast +++ b/test/core/call_indirect.wast @@ -7,10 +7,13 @@ (type $out-i64 (func (result i64))) (type $out-f32 (func (result f32))) (type $out-f64 (func (result f64))) + (type $out-f64-i32 (func (result f64 i32))) (type $over-i32 (func (param i32) (result i32))) (type $over-i64 (func (param i64) (result i64))) (type $over-f32 (func (param f32) (result f32))) (type $over-f64 (func (param f64) (result f64))) + (type $over-i32-f64 (func (param i32 f64) (result i32 f64))) + (type $swap-i32-i64 (func (param i32 i64) (result i64 i32))) (type $f32-i32 (func (param f32 i32) (result i32))) (type $i32-i64 (func (param i32 i64) (result i64))) (type $f64-f32 (func (param f64 f32) (result f32))) @@ -24,11 +27,14 @@ (func $const-i64 (type $out-i64) (i64.const 0x164)) (func $const-f32 (type $out-f32) (f32.const 0xf32)) (func $const-f64 (type $out-f64) (f64.const 0xf64)) + (func $const-f64-i32 (type $out-f64-i32) (f64.const 0xf64) (i32.const 32)) (func $id-i32 (type $over-i32) (local.get 0)) (func $id-i64 (type $over-i64) (local.get 0)) (func $id-f32 (type $over-f32) (local.get 0)) (func $id-f64 (type $over-f64) (local.get 0)) + (func $id-i32-f64 (type $over-i32-f64) (local.get 0) (local.get 1)) + (func $swap-i32-i64 (type $swap-i32-i64) (local.get 1) (local.get 0)) (func $i32-i64 (type $i32-i64) (local.get 1)) (func $i64-f64 (type $i64-f64) (local.get 1)) @@ -42,15 +48,16 @@ (table funcref (elem - $const-i32 $const-i64 $const-f32 $const-f64 - $id-i32 $id-i64 $id-f32 $id-f64 - $f32-i32 $i32-i64 $f64-f32 $i64-f64 - $fac-i64 $fib-i64 $even $odd - $runaway $mutual-runaway1 $mutual-runaway2 - $over-i32-duplicate $over-i64-duplicate - $over-f32-duplicate $over-f64-duplicate - $fac-i32 $fac-f32 $fac-f64 - $fib-i32 $fib-f32 $fib-f64 + $const-i32 $const-i64 $const-f32 $const-f64 ;; 0..3 + $id-i32 $id-i64 $id-f32 $id-f64 ;; 4..7 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 ;; 9..11 + $fac-i64 $fib-i64 $even $odd ;; 12..15 + $runaway $mutual-runaway1 $mutual-runaway2 ;; 16..18 + $over-i32-duplicate $over-i64-duplicate ;; 19..20 + $over-f32-duplicate $over-f64-duplicate ;; 21..22 + $fac-i32 $fac-f32 $fac-f64 ;; 23..25 + $fib-i32 $fib-f32 $fib-f64 ;; 26..28 + $const-f64-i32 $id-i32-f64 $swap-i32-i64 ;; 29..31 ) ) @@ -96,6 +103,9 @@ (func (export "type-f64") (result f64) (call_indirect (type $out-f64) (i32.const 3)) ) + (func (export "type-f64-i32") (result f64 i32) + (call_indirect (type $out-f64-i32) (i32.const 29)) + ) (func (export "type-index") (result i64) (call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) @@ -127,6 +137,20 @@ (call_indirect (type $i64-f64) (i64.const 64) (f64.const 64.1) (i32.const 11)) ) + (func (export "type-all-f64-i32") (result f64 i32) + (call_indirect (type $out-f64-i32) (i32.const 29)) + ) + (func (export "type-all-i32-f64") (result i32 f64) + (call_indirect (type $over-i32-f64) + (i32.const 1) (f64.const 2) (i32.const 30) + ) + ) + (func (export "type-all-i32-i64") (result i64 i32) + (call_indirect (type $swap-i32-i64) + (i32.const 1) (i64.const 2) (i32.const 31) + ) + ) + ;; Dispatch (func (export "dispatch") (param i32 i64) (result i64) @@ -448,6 +472,7 @@ (assert_return (invoke "type-i64") (i64.const 0x164)) (assert_return (invoke "type-f32") (f32.const 0xf32)) (assert_return (invoke "type-f64") (f64.const 0xf64)) +(assert_return (invoke "type-f64-i32") (f64.const 0xf64) (i32.const 32)) (assert_return (invoke "type-index") (i64.const 100)) @@ -461,6 +486,10 @@ (assert_return (invoke "type-second-f32") (f32.const 32)) (assert_return (invoke "type-second-f64") (f64.const 64.1)) +(assert_return (invoke "type-all-f64-i32") (f64.const 0xf64) (i32.const 32)) +(assert_return (invoke "type-all-i32-f64") (i32.const 1) (f64.const 2)) +(assert_return (invoke "type-all-i32-i64") (i64.const 2) (i32.const 1)) + (assert_return (invoke "dispatch" (i32.const 5) (i64.const 2)) (i64.const 2)) (assert_return (invoke "dispatch" (i32.const 5) (i64.const 5)) (i64.const 5)) (assert_return (invoke "dispatch" (i32.const 12) (i64.const 5)) (i64.const 120)) @@ -468,7 +497,7 @@ (assert_return (invoke "dispatch" (i32.const 20) (i64.const 2)) (i64.const 2)) (assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch") (assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch") -(assert_trap (invoke "dispatch" (i32.const 29) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const 32) (i64.const 2)) "undefined element") (assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element") (assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element") diff --git a/test/core/fac.wast b/test/core/fac.wast index ef10991a82..0e61c1f9ee 100644 --- a/test/core/fac.wast +++ b/test/core/fac.wast @@ -79,6 +79,24 @@ ) (local.get 1) ) + + ;; Iterative factorial without locals. + (func $pick0 (param i64) (result i64 i64) + (local.get 0) (local.get 0) + ) + (func $pick1 (param i64 i64) (result i64 i64 i64) + (local.get 0) (local.get 1) (local.get 0) + ) + (func (export "fac-ssa") (param i64) (result i64) + (i64.const 1) (local.get 0) + (loop $l (param i64 i64) (result i64) + (call $pick1) (call $pick1) (i64.mul) + (call $pick1) (i64.const 1) (i64.sub) + (call $pick0) (i64.const 0) (i64.gt_u) + (br_if $l) + (drop) (return) + ) + ) ) (assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) @@ -86,4 +104,6 @@ (assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) (assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) (assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-ssa" (i64.const 25)) (i64.const 7034535277573963776)) + (assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") diff --git a/test/core/func.wast b/test/core/func.wast index 263440a271..af81d982c7 100644 --- a/test/core/func.wast +++ b/test/core/func.wast @@ -28,7 +28,12 @@ (func (param i32) (param f64)) (func (param i32 f32) (param $x i64) (param) (param i32 f64)) + (func (result)) + (func (result) (result)) (func (result i32) (unreachable)) + (func (result i32 f64 f32) (unreachable)) + (func (result i32) (result f64) (unreachable)) + (func (result i32 f32) (result i64) (result) (result i32 f64) (unreachable)) (type $sig-1 (func)) (type $sig-2 (func (result i32))) @@ -50,7 +55,7 @@ (func $complex (param i32 f32) (param $x i64) (param) (param i32) - (result) (result i32) (result) + (result) (result i32) (result) (result i64 i32) (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) (unreachable) (unreachable) ) @@ -104,7 +109,7 @@ (local.get 4) ) - ;; Typing of result + ;; Typing of results (func (export "empty")) (func (export "value-void") (call $dummy)) @@ -112,28 +117,53 @@ (func (export "value-i64") (result i64) (i64.const 7777)) (func (export "value-f32") (result f32) (f32.const 77.7)) (func (export "value-f64") (result f64) (f64.const 77.77)) + (func (export "value-i32-f64") (result i32 f64) (i32.const 77) (f64.const 7)) + (func (export "value-i32-i32-i32") (result i32 i32 i32) + (i32.const 1) (i32.const 2) (i32.const 3) + ) (func (export "value-block-void") (block (call $dummy) (call $dummy))) (func (export "value-block-i32") (result i32) (block (result i32) (call $dummy) (i32.const 77)) ) + (func (export "value-block-i32-i64") (result i32 i64) + (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2)) + ) (func (export "return-empty") (return)) (func (export "return-i32") (result i32) (return (i32.const 78))) (func (export "return-i64") (result i64) (return (i64.const 7878))) (func (export "return-f32") (result f32) (return (f32.const 78.7))) (func (export "return-f64") (result f64) (return (f64.const 78.78))) + (func (export "return-i32-f64") (result i32 f64) + (return (i32.const 78) (f64.const 78.78)) + ) + (func (export "return-i32-i32-i32") (result i32 i32 i32) + (return (i32.const 1) (i32.const 2) (i32.const 3)) + ) (func (export "return-block-i32") (result i32) (return (block (result i32) (call $dummy) (i32.const 77))) ) + (func (export "return-block-i32-i64") (result i32 i64) + (return (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) (func (export "break-empty") (br 0)) (func (export "break-i32") (result i32) (br 0 (i32.const 79))) (func (export "break-i64") (result i64) (br 0 (i64.const 7979))) (func (export "break-f32") (result f32) (br 0 (f32.const 79.9))) (func (export "break-f64") (result f64) (br 0 (f64.const 79.79))) + (func (export "break-i32-f64") (result i32 f64) + (br 0 (i32.const 79) (f64.const 79.79)) + ) + (func (export "break-i32-i32-i32") (result i32 i32 i32) + (br 0 (i32.const 1) (i32.const 2) (i32.const 3)) + ) (func (export "break-block-i32") (result i32) (br 0 (block (result i32) (call $dummy) (i32.const 77))) ) + (func (export "break-block-i32-i64") (result i32 i64) + (br 0 (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) (func (export "break-br_if-empty") (param i32) (br_if 0 (local.get 0)) @@ -141,6 +171,10 @@ (func (export "break-br_if-num") (param i32) (result i32) (drop (br_if 0 (i32.const 50) (local.get 0))) (i32.const 51) ) + (func (export "break-br_if-num-num") (param i32) (result i32 i64) + (drop (drop (br_if 0 (i32.const 50) (i64.const 51) (local.get 0)))) + (i32.const 51) (i64.const 52) + ) (func (export "break-br_table-empty") (param i32) (br_table 0 0 0 (local.get 0)) @@ -148,6 +182,10 @@ (func (export "break-br_table-num") (param i32) (result i32) (br_table 0 0 (i32.const 50) (local.get 0)) (i32.const 51) ) + (func (export "break-br_table-num-num") (param i32) (result i32 i64) + (br_table 0 0 (i32.const 50) (i64.const 51) (local.get 0)) + (i32.const 51) (i64.const 52) + ) (func (export "break-br_table-nested-empty") (param i32) (block (br_table 0 1 0 (local.get 0))) ) @@ -159,6 +197,38 @@ (i32.const 2) ) ) + (func (export "break-br_table-nested-num-num") (param i32) (result i32 i32) + (i32.add + (block (result i32 i32) + (br_table 0 1 0 (i32.const 50) (i32.const 51) (local.get 0)) + (i32.const 51) (i32.const -3) + ) + ) + (i32.const 52) + ) + + ;; Large signatures + + (func (export "large-sig") + (param i32 i64 f32 f32 i32 f64 f32 i32 i32 i32 f32 f64 f64 f64 i32 i32 f32) + (result f64 f32 i32 i32 i32 i64 f32 i32 i32 f32 f64 f64 i32 f32 i32 f64) + (local.get 5) + (local.get 2) + (local.get 0) + (local.get 8) + (local.get 7) + (local.get 1) + (local.get 3) + (local.get 9) + (local.get 4) + (local.get 6) + (local.get 13) + (local.get 11) + (local.get 15) + (local.get 16) + (local.get 14) + (local.get 12) + ) ;; Default initialization of locals @@ -231,27 +301,48 @@ (assert_return (invoke "value-i64") (i64.const 7777)) (assert_return (invoke "value-f32") (f32.const 77.7)) (assert_return (invoke "value-f64") (f64.const 77.77)) +(assert_return (invoke "value-i32-f64") (i32.const 77) (f64.const 7)) +(assert_return (invoke "value-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) (assert_return (invoke "value-block-void")) (assert_return (invoke "value-block-i32") (i32.const 77)) +(assert_return (invoke "value-block-i32-i64") (i32.const 1) (i64.const 2)) (assert_return (invoke "return-empty")) (assert_return (invoke "return-i32") (i32.const 78)) (assert_return (invoke "return-i64") (i64.const 7878)) (assert_return (invoke "return-f32") (f32.const 78.7)) (assert_return (invoke "return-f64") (f64.const 78.78)) +(assert_return (invoke "return-i32-f64") (i32.const 78) (f64.const 78.78)) +(assert_return (invoke "return-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) (assert_return (invoke "return-block-i32") (i32.const 77)) +(assert_return (invoke "return-block-i32-i64") (i32.const 1) (i64.const 2)) (assert_return (invoke "break-empty")) (assert_return (invoke "break-i32") (i32.const 79)) (assert_return (invoke "break-i64") (i64.const 7979)) (assert_return (invoke "break-f32") (f32.const 79.9)) (assert_return (invoke "break-f64") (f64.const 79.79)) +(assert_return (invoke "break-i32-f64") (i32.const 79) (f64.const 79.79)) +(assert_return (invoke "break-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) (assert_return (invoke "break-block-i32") (i32.const 77)) +(assert_return (invoke "break-block-i32-i64") (i32.const 1) (i64.const 2)) (assert_return (invoke "break-br_if-empty" (i32.const 0))) (assert_return (invoke "break-br_if-empty" (i32.const 2))) (assert_return (invoke "break-br_if-num" (i32.const 0)) (i32.const 51)) (assert_return (invoke "break-br_if-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_if-num-num" (i32.const 0)) + (i32.const 51) (i64.const 52) +) +(assert_return (invoke "break-br_if-num-num" (i32.const 1)) + (i32.const 50) (i64.const 51) +) (assert_return (invoke "break-br_table-empty" (i32.const 0))) (assert_return (invoke "break-br_table-empty" (i32.const 1))) @@ -261,6 +352,18 @@ (assert_return (invoke "break-br_table-num" (i32.const 1)) (i32.const 50)) (assert_return (invoke "break-br_table-num" (i32.const 10)) (i32.const 50)) (assert_return (invoke "break-br_table-num" (i32.const -100)) (i32.const 50)) +(assert_return (invoke "break-br_table-num-num" (i32.const 0)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 1)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 10)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const -100)) + (i32.const 50) (i64.const 51) +) (assert_return (invoke "break-br_table-nested-empty" (i32.const 0))) (assert_return (invoke "break-br_table-nested-empty" (i32.const 1))) (assert_return (invoke "break-br_table-nested-empty" (i32.const 3))) @@ -277,6 +380,36 @@ (assert_return (invoke "break-br_table-nested-num" (i32.const -3)) (i32.const 52) ) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 0)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 1)) + (i32.const 50) (i32.const 51) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 2)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const -3)) + (i32.const 101) (i32.const 52) +) + +(assert_return + (invoke "large-sig" + (i32.const 0) (i64.const 1) (f32.const 2) (f32.const 3) + (i32.const 4) (f64.const 5) (f32.const 6) (i32.const 7) + (i32.const 8) (i32.const 9) (f32.const 10) (f64.const 11) + (f64.const 12) (f64.const 13) (i32.const 14) (i32.const 15) + (f32.const 16) + ) + (f64.const 5) (f32.const 2) (i32.const 0) (i32.const 8) + (i32.const 7) (i64.const 1) (f32.const 3) (i32.const 9) + (i32.const 4) (f32.const 6) (f64.const 13) (f64.const 11) + (i32.const 15) (f32.const 16) (i32.const 14) (f64.const 12) +) (assert_return (invoke "init-local-i32") (i32.const 0)) (assert_return (invoke "init-local-i64") (i64.const 0)) @@ -489,19 +622,6 @@ ;; Invalid typing of result -(assert_invalid - (module (func $type-multiple-result (result i32 i32) (unreachable))) - "invalid result arity" -) -(assert_invalid - (module - (type (func (result i32 i32))) - (func $type-multiple-result (type 0) (unreachable)) - ) - "invalid result arity" -) - - (assert_invalid (module (func $type-empty-i32 (result i32))) "type mismatch" @@ -518,6 +638,10 @@ (module (func $type-empty-f64 (result f64))) "type mismatch" ) +(assert_invalid + (module (func $type-empty-f64-i32 (result f64 i32))) + "type mismatch" +) (assert_invalid (module (func $type-value-void-vs-num (result i32) @@ -525,18 +649,42 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (nop) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-num-vs-void (i32.const 0) )) "type mismatch" ) +(assert_invalid + (module (func $type-value-nums-vs-void + (i32.const 0) (i64.const 0) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-num-vs-num (result i32) (f32.const 0) )) "type mismatch" ) +(assert_invalid + (module (func $type-value-num-vs-nums (result f32 f32) + (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result f32) + (f32.const 0) (f32.const 0) + )) + "type mismatch" +) (assert_invalid (module (func $type-return-last-empty-vs-num (result i32) @@ -544,18 +692,36 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-return-last-empty-vs-nums (result i32 i32) + (return) + )) + "type mismatch" +) (assert_invalid (module (func $type-return-last-void-vs-num (result i32) (return (nop)) )) "type mismatch" ) +(assert_invalid + (module (func $type-return-last-void-vs-nums (result i32 i64) + (return (nop)) + )) + "type mismatch" +) (assert_invalid (module (func $type-return-last-num-vs-num (result i32) (return (i64.const 0)) )) "type mismatch" ) +(assert_invalid + (module (func $type-return-last-num-vs-nums (result i64 i64) + (return (i64.const 0)) + )) + "type mismatch" +) (assert_invalid (module (func $type-return-empty-vs-num (result i32) @@ -563,24 +729,54 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-return-empty-vs-nums (result i32 i32) + (return) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-partial-vs-nums (result i32 i32) + (i32.const 1) (return) (i32.const 2) + )) + "type mismatch" +) (assert_invalid (module (func $type-return-void-vs-num (result i32) (return (nop)) (i32.const 1) )) "type mismatch" ) +(assert_invalid + (module (func $type-return-void-vs-nums (result i32 i32) + (return (nop)) (i32.const 1) + )) + "type mismatch" +) (assert_invalid (module (func $type-return-num-vs-num (result i32) (return (i64.const 1)) (i32.const 1) )) "type mismatch" ) +(assert_invalid + (module (func $type-return-num-vs-nums (result i32 i32) + (return (i64.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) (assert_invalid (module (func $type-return-first-num-vs-num (result i32) (return (i64.const 1)) (return (i32.const 1)) )) "type mismatch" ) +(assert_invalid + (module (func $type-return-first-num-vs-nums (result i32 i32) + (return (i32.const 1)) (return (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-last-void-vs-num (result i32) @@ -588,24 +784,48 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-last-void-vs-nums (result i32 i32) + (br 0) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-last-num-vs-num (result i32) (br 0 (f32.const 0)) )) "type mismatch" ) +(assert_invalid + (module (func $type-break-last-num-vs-nums (result i32 i32) + (br 0 (i32.const 0)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-void-vs-num (result i32) (br 0) (i32.const 1) )) "type mismatch" ) +(assert_invalid + (module (func $type-break-void-vs-nums (result i32 i32) + (br 0) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-num-vs-num (result i32) (br 0 (i64.const 1)) (i32.const 1) )) "type mismatch" ) +(assert_invalid + (module (func $type-break-num-vs-nums (result i32 i32) + (br 0 (i32.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-first-num-vs-num (result i32) (br 0 (i64.const 1)) (br 0 (i32.const 1)) @@ -619,18 +839,36 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-break-nested-empty-vs-nums (result i32 i32) + (block (br 1)) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-nested-void-vs-num (result i32) (block (br 1 (nop))) (br 0 (i32.const 1)) )) "type mismatch" ) +(assert_invalid + (module (func $type-break-nested-void-vs-nums (result i32 i32) + (block (br 1 (nop))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-break-nested-num-vs-num (result i32) (block (br 1 (i64.const 1))) (br 0 (i32.const 1)) )) "type mismatch" ) +(assert_invalid + (module (func $type-break-nested-num-vs-nums (result i32 i32) + (block (result i32) (br 1 (i32.const 1))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) ;; Syntax errors diff --git a/test/core/if.wast b/test/core/if.wast index ae7f7b3859..1cbb617a7f 100644 --- a/test/core/if.wast +++ b/test/core/if.wast @@ -19,13 +19,26 @@ (if (result i32) (local.get 0) (then (i32.const 7)) (else (i32.const 8))) ) - (func (export "multi") (param i32) (result i32) + (func (export "multi") (param i32) (result i32 i32) (if (local.get 0) (then (call $dummy) (call $dummy) (call $dummy))) (if (local.get 0) (then) (else (call $dummy) (call $dummy) (call $dummy))) (if (result i32) (local.get 0) - (then (call $dummy) (call $dummy) (i32.const 8)) - (else (call $dummy) (call $dummy) (i32.const 9)) + (then (call $dummy) (call $dummy) (i32.const 8) (call $dummy)) + (else (call $dummy) (call $dummy) (i32.const 9) (call $dummy)) ) + (if (result i32 i64 i32) (local.get 0) + (then + (call $dummy) (call $dummy) (i32.const 1) (call $dummy) + (call $dummy) (call $dummy) (i64.const 2) (call $dummy) + (call $dummy) (call $dummy) (i32.const 3) (call $dummy) + ) + (else + (call $dummy) (call $dummy) (i32.const -1) (call $dummy) + (call $dummy) (call $dummy) (i64.const -2) (call $dummy) + (call $dummy) (call $dummy) (i32.const -3) (call $dummy) + ) + ) + (drop) (drop) ) (func (export "nested") (param i32 i32) (result i32) @@ -337,6 +350,31 @@ ) ) ) + (func (export "as-binary-operands") (param i32) (result i32) + (i32.mul + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const 3) (call $dummy) (i32.const -4)) + ) + ) + ) + (func (export "as-compare-operands") (param i32) (result i32) + (f32.gt + (if (result f32 f32) (local.get 0) + (then (call $dummy) (f32.const 3) (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -2) (call $dummy) (f32.const -3)) + ) + ) + ) + (func (export "as-mixed-operands") (param i32) (result i32) + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -3) (call $dummy) (i32.const -4)) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) (func (export "break-bare") (result i32) (if (i32.const 1) (then (br 0) (unreachable))) @@ -357,6 +395,61 @@ (else (br 0 (i32.const 21)) (i32.const 20)) ) ) + (func (export "break-multi-value") (param i32) (result i32 i32 i64) + (if (result i32 i32 i64) (local.get 0) + (then + (br 0 (i32.const 18) (i32.const -18) (i64.const 18)) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + (else + (br 0 (i32.const -18) (i32.const 18) (i64.const -18)) + (i32.const -19) (i32.const 19) (i64.const -19) + ) + ) + ) + + (func (export "param") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add)) + (else (i32.const -2) (i32.add)) + ) + ) + (func (export "params") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add)) + (else (i32.sub)) + ) + ) + (func (export "params-id") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then)) + (i32.add) + ) + (func (export "param-break") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add) (br 0)) + (else (i32.const -2) (i32.add) (br 0)) + ) + ) + (func (export "params-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add) (br 0)) + (else (i32.sub) (br 0)) + ) + ) + (func (export "params-id-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then (br 0))) + (i32.add) + ) (func (export "effects") (param i32) (result i32) (local i32) @@ -379,6 +472,58 @@ ) (local.get 1) ) + + ;; Examples + + (func $add64_u_with_carry (export "add64_u_with_carry") + (param $i i64) (param $j i64) (param $c i32) (result i64 i32) + (local $k i64) + (local.set $k + (i64.add + (i64.add (local.get $i) (local.get $j)) + (i64.extend_i32_u (local.get $c)) + ) + ) + (return (local.get $k) (i64.lt_u (local.get $k) (local.get $i))) + ) + + (func $add64_u_saturated (export "add64_u_saturated") + (param i64 i64) (result i64) + (call $add64_u_with_carry (local.get 0) (local.get 1) (i32.const 0)) + (if (param i64) (result i64) + (then (drop) (i64.const -1)) + ) + ) + + ;; Block signature syntax + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (if (type $block-sig-1) (i32.const 1) (then)) + (if (type $block-sig-2) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (i32.const 1) (then (drop)) (else (drop))) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) (i32.const 1) (then)) + (drop) (drop) (drop) + (if (type $block-sig-2) (result i32) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (param i32) (i32.const 1) + (then (drop)) (else (drop)) + ) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + (i32.const 1) (then) + ) + (drop) (drop) (drop) + ) ) (assert_return (invoke "empty" (i32.const 0))) @@ -391,10 +536,10 @@ (assert_return (invoke "singular" (i32.const 10)) (i32.const 7)) (assert_return (invoke "singular" (i32.const -10)) (i32.const 7)) -(assert_return (invoke "multi" (i32.const 0)) (i32.const 9)) -(assert_return (invoke "multi" (i32.const 1)) (i32.const 8)) -(assert_return (invoke "multi" (i32.const 13)) (i32.const 8)) -(assert_return (invoke "multi" (i32.const -5)) (i32.const 8)) +(assert_return (invoke "multi" (i32.const 0)) (i32.const 9) (i32.const -1)) +(assert_return (invoke "multi" (i32.const 1)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const 13)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const -5)) (i32.const 8) (i32.const 1)) (assert_return (invoke "nested" (i32.const 0) (i32.const 0)) (i32.const 11)) (assert_return (invoke "nested" (i32.const 1) (i32.const 0)) (i32.const 10)) @@ -488,13 +633,203 @@ (assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 0)) (i32.const 1)) (assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-binary-operands" (i32.const 0)) (i32.const -12)) +(assert_return (invoke "as-binary-operands" (i32.const 1)) (i32.const 12)) + +(assert_return (invoke "as-compare-operands" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operands" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-mixed-operands" (i32.const 0)) (i32.const -3)) +(assert_return (invoke "as-mixed-operands" (i32.const 1)) (i32.const 27)) + (assert_return (invoke "break-bare") (i32.const 19)) (assert_return (invoke "break-value" (i32.const 1)) (i32.const 18)) (assert_return (invoke "break-value" (i32.const 0)) (i32.const 21)) +(assert_return (invoke "break-multi-value" (i32.const 0)) + (i32.const -18) (i32.const 18) (i64.const -18) +) +(assert_return (invoke "break-multi-value" (i32.const 1)) + (i32.const 18) (i32.const -18) (i64.const 18) +) + +(assert_return (invoke "param" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "param-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 1)) (i32.const 3)) (assert_return (invoke "effects" (i32.const 1)) (i32.const -14)) (assert_return (invoke "effects" (i32.const 0)) (i32.const -6)) +(assert_return + (invoke "add64_u_with_carry" (i64.const 0) (i64.const 0) (i32.const 0)) + (i64.const 0) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 100) (i64.const 124) (i32.const 0)) + (i64.const 224) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 0)) + (i64.const -1) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 0)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const -1) (i32.const 0)) + (i64.const -2) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 1)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 1)) + (i64.const 1) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000) (i32.const 0)) + (i64.const 0) (i32.const 1) +) + +(assert_return + (invoke "add64_u_saturated" (i64.const 0) (i64.const 0)) (i64.const 0) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 1230) (i64.const 23)) (i64.const 1253) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 0)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const -1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const -1) +) + +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (type $sig) (result i32) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (type $sig) (result i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (result i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (type $sig) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (param i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (if (result i32) (param i32) (i32.const 1) (then)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(func (i32.const 0) (i32.const 1)" + " (if (param $x i32) (then (drop)) (else (drop)))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (then (drop)) (else (drop)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (result i32) (then)) (unreachable)" + ")" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (i32.const 1) (if (type $sig) (i32.const 0) (then))) + ) + "type mismatch" +) + (assert_invalid (module (func $type-empty-i32 (result i32) (if (i32.const 0) (then)))) "type mismatch" @@ -536,7 +871,7 @@ "type mismatch" ) (assert_invalid - (module (func $type-then-value-num-vs-void + (module (func $type-then-value-num-vs-void-else (if (i32.const 1) (then (i32.const 1)) (else)) )) "type mismatch" @@ -554,6 +889,31 @@ "type mismatch" ) +(assert_invalid + (module (func $type-then-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-value-nums-vs-void-else + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-void + (if (i32.const 1) (then) (else (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else (i32.const 2) (i32.const 1))) + )) + "type mismatch" +) + (assert_invalid (module (func $type-then-value-empty-vs-num (result i32) (if (result i32) (i32.const 1) (then) (else (i32.const 0))) @@ -561,7 +921,7 @@ "type mismatch" ) (assert_invalid - (module (func $type-then-value-empty-vs-num (result i32) + (module (func $type-else-value-empty-vs-num (result i32) (if (result i32) (i32.const 1) (then (i32.const 0)) (else)) )) "type mismatch" @@ -572,12 +932,38 @@ )) "type mismatch" ) + +(assert_invalid + (module (func $type-then-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else (i32.const 0) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 1)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else)) + )) + "type mismatch" +) + (assert_invalid (module (func $type-no-else-vs-num (result i32) (if (result i32) (i32.const 1) (then (i32.const 1))) )) "type mismatch" ) +(assert_invalid + (module (func $type-no-else-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) (assert_invalid (module (func $type-then-value-void-vs-num (result i32) @@ -586,7 +972,7 @@ "type mismatch" ) (assert_invalid - (module (func $type-then-value-void-vs-num (result i32) + (module (func $type-else-value-void-vs-num (result i32) (if (result i32) (i32.const 1) (then (i32.const 0)) (else (nop))) )) "type mismatch" @@ -598,6 +984,25 @@ "type mismatch" ) +(assert_invalid + (module (func $type-then-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (i32.const 0) (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 0)) (else (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (nop))) + )) + "type mismatch" +) + (assert_invalid (module (func $type-then-value-num-vs-num (result i32) (if (result i32) (i32.const 1) (then (i64.const 1)) (else (i32.const 1))) @@ -605,7 +1010,7 @@ "type mismatch" ) (assert_invalid - (module (func $type-then-value-num-vs-num (result i32) + (module (func $type-else-value-num-vs-num (result i32) (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i64.const 1))) )) "type mismatch" @@ -616,12 +1021,79 @@ )) "type mismatch" ) + +(assert_invalid + (module (func $type-then-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) + (assert_invalid (module (func $type-both-different-value-num-vs-num (result i32) (if (result i32) (i32.const 1) (then (i64.const 1)) (else (f64.const 1))) )) "type mismatch" ) +(assert_invalid + (module (func $type-both-different-value-nums-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) (assert_invalid (module (func $type-then-value-unreached-select (result i32) @@ -666,6 +1138,19 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-then-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (br 0)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (br 0))) + )) + "type mismatch" +) + (assert_invalid (module (func $type-then-break-empty-vs-num (result i32) (if (result i32) (i32.const 1) @@ -684,6 +1169,25 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-then-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) + (assert_invalid (module (func $type-then-break-void-vs-num (result i32) (if (result i32) (i32.const 1) @@ -702,6 +1206,24 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-then-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (nop)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (nop)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) (assert_invalid (module (func $type-then-break-num-vs-num (result i32) @@ -721,6 +1243,44 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-then-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1)) + ) + )) + "type mismatch" +) (assert_invalid (module @@ -890,6 +1450,63 @@ "type mismatch" ) +(assert_invalid + (module (func $type-param-void-vs-num + (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (if (param i32 f64) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (if (param i32 f64) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) if (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (if (param $x i32) (then)))") + "unexpected token" +) (assert_malformed (module quote "(func i32.const 0 if end $l)") diff --git a/test/core/loop.wast b/test/core/loop.wast index 9141d00ed0..4557869b39 100644 --- a/test/core/loop.wast +++ b/test/core/loop.wast @@ -17,7 +17,14 @@ (func (export "multi") (result i32) (loop (call $dummy) (call $dummy) (call $dummy) (call $dummy)) - (loop (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8)) + (loop (result i32) (call $dummy) (call $dummy) (i32.const 8) (call $dummy)) + (drop) + (loop (result i32 i64 i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 8) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i64.const 7) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i32.const 9) (call $dummy) + ) + (drop) (drop) ) (func (export "nested") (result i32) @@ -188,6 +195,28 @@ (loop (result f32) (call $dummy) (f32.const 3)) ) ) + (func (export "as-binary-operands") (result i32) + (i32.mul + (loop (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + ) + ) + (func (export "as-compare-operands") (result i32) + (f32.gt + (loop (result f32 f32) + (call $dummy) (f32.const 3) (call $dummy) (f32.const 3) + ) + ) + ) + (func (export "as-mixed-operands") (result i32) + (loop (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) (func (export "break-bare") (result i32) (block (loop (br 1) (br 0) (unreachable))) @@ -198,7 +227,22 @@ ) (func (export "break-value") (result i32) (block (result i32) - (loop (result i32) (br 1 (i32.const 18)) (br 0) (i32.const 19)) + (i32.const 0) + (loop (param i32) + (block (br 2 (i32.const 18))) + (br 0 (i32.const 20)) + ) + (i32.const 19) + ) + ) + (func (export "break-multi-value") (result i32 i32 i64) + (block (result i32 i32 i64) + (i32.const 0) (i32.const 0) (i64.const 0) + (loop (param i32 i32 i64) + (block (br 2 (i32.const 18) (i32.const -18) (i64.const 18))) + (br 0 (i32.const 20) (i32.const -20) (i64.const 20)) + ) + (i32.const 19) (i32.const -19) (i64.const 19) ) ) (func (export "break-repeated") (result i32) @@ -234,6 +278,66 @@ (local.get 0) ) + (func (export "param") (result i32) + (i32.const 1) + (loop (param i32) (result i32) + (i32.const 2) + (i32.add) + ) + ) + (func (export "params") (result i32) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32) + (i32.add) + ) + ) + (func (export "params-id") (result i32) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32 i32)) + (i32.add) + ) + (func (export "param-break") (result i32) + (local $x i32) + (i32.const 1) + (loop (param i32) (result i32) + (i32.const 4) + (i32.add) + (local.tee $x) + (local.get $x) + (i32.const 10) + (i32.lt_u) + (br_if 0) + ) + ) + (func (export "params-break") (result i32) + (local $x i32) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32) + (i32.add) + (local.tee $x) + (i32.const 3) + (local.get $x) + (i32.const 10) + (i32.lt_u) + (br_if 0) + (drop) + ) + ) + (func (export "params-id-break") (result i32) + (local $x i32) + (local.set $x (i32.const 0)) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32 i32) + (local.set $x (i32.add (local.get $x) (i32.const 1))) + (br_if 0 (i32.lt_u (local.get $x) (i32.const 10))) + ) + (i32.add) + ) + (func $fx (export "effects") (result i32) (local i32) (block @@ -300,6 +404,27 @@ ) (local.get 3) ) + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (loop (type $block-sig-1)) + (loop (type $block-sig-2) (i32.const 0)) + (loop (type $block-sig-3) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (loop (type $block-sig-4)) + (drop) (drop) (drop) + (loop (type $block-sig-2) (result i32) (i32.const 0)) + (loop (type $block-sig-3) (param i32) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (loop (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + ) + (drop) (drop) (drop) + ) ) (assert_return (invoke "empty")) @@ -343,12 +468,25 @@ (assert_return (invoke "as-binary-operand") (i32.const 12)) (assert_return (invoke "as-test-operand") (i32.const 0)) (assert_return (invoke "as-compare-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operands") (i32.const 12)) +(assert_return (invoke "as-compare-operands") (i32.const 0)) +(assert_return (invoke "as-mixed-operands") (i32.const 27)) (assert_return (invoke "break-bare") (i32.const 19)) (assert_return (invoke "break-value") (i32.const 18)) +(assert_return (invoke "break-multi-value") + (i32.const 18) (i32.const -18) (i64.const 18) +) (assert_return (invoke "break-repeated") (i32.const 18)) (assert_return (invoke "break-inner") (i32.const 0x1f)) +(assert_return (invoke "param") (i32.const 3)) +(assert_return (invoke "params") (i32.const 3)) +(assert_return (invoke "params-id") (i32.const 3)) +(assert_return (invoke "param-break") (i32.const 13)) +(assert_return (invoke "params-break") (i32.const 12)) +(assert_return (invoke "params-id-break") (i32.const 3)) + (assert_return (invoke "effects") (i32.const 1)) (assert_return (invoke "while" (i64.const 0)) (i64.const 1)) @@ -382,6 +520,91 @@ (assert_return (invoke "nesting" (f32.const 7) (f32.const 100)) (f32.const 4381.54785156)) (assert_return (invoke "nesting" (f32.const 7) (f32.const 101)) (f32.const 2601)) +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (type $sig) (result i32) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (param i32) (type $sig) (result i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (param i32) (result i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (result i32) (type $sig) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (result i32) (param i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (loop (result i32) (param i32)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (i32.const 0) (loop (param $x i32) (drop)))") + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (loop (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (loop (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (type $sig) (param i32) (drop)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (loop (type $sig) (param i32) (result i32)) (unreachable))" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (loop (type $sig) (i32.const 0))) + ) + "type mismatch" +) + (assert_invalid (module (func $type-empty-i32 (result i32) (loop))) "type mismatch" @@ -405,24 +628,60 @@ )) "type mismatch" ) +(assert_invalid + (module (func $type-value-nums-vs-void + (loop (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-empty-vs-num (result i32) (loop (result i32)) )) "type mismatch" ) +(assert_invalid + (module (func $type-value-empty-vs-nums (result i32 i32) + (loop (result i32 i32)) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-void-vs-num (result i32) (loop (result i32) (nop)) )) "type mismatch" ) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (loop (result i32 i32) (nop)) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-num-vs-num (result i32) (loop (result i32) (f32.const 0)) )) "type mismatch" ) +(assert_invalid + (module (func $type-value-num-vs-nums (result i32 i32) + (loop (result i32 i32) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-partial-vs-nums (result i32 i32) + (i32.const 1) (loop (result i32 i32) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result i32) + (loop (result i32) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) (assert_invalid (module (func $type-value-unreached-select (result i32) (loop (result i64) (select (unreachable) (unreachable) (unreachable))) @@ -458,6 +717,63 @@ "type mismatch" ) +(assert_invalid + (module (func $type-param-void-vs-num + (loop (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (loop (param i32 f64) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (loop (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (loop (param f32 i32) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (loop (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (loop (param i32 f64) (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (loop (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (loop (param f32 i32) (drop) (drop))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) loop (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (loop (param $x i32)))") + "unexpected token" +) (assert_malformed (module quote "(func loop end $l)") diff --git a/test/core/type.wast b/test/core/type.wast index 5ceeeb2697..b94063e6a0 100644 --- a/test/core/type.wast +++ b/test/core/type.wast @@ -11,33 +11,33 @@ (type (func (param $x i32) (result i32))) (type (func (param f32 f64))) - ;; (type (func (result i64 f32))) - ;; (type (func (param i32 i64) (result f32 f64))) + (type (func (result i64 f32))) + (type (func (param i32 i64) (result f32 f64))) (type (func (param f32) (param f64))) (type (func (param $x f32) (param f64))) (type (func (param f32) (param $y f64))) (type (func (param $x f32) (param $y f64))) - ;; (type (func (result i64) (result f32))) - ;; (type (func (param i32) (param i64) (result f32) (result f64))) - ;; (type (func (param $x i32) (param $y i64) (result f32) (result f64))) + (type (func (result i64) (result f32))) + (type (func (param i32) (param i64) (result f32) (result f64))) + (type (func (param $x i32) (param $y i64) (result f32) (result f64))) (type (func (param f32 f64) (param $x i32) (param f64 i32 i32))) - ;; (type (func (result i64 i64 f32) (result f32 i32))) - ;; (type - ;; (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)) - ;; ) + (type (func (result i64 i64 f32) (result f32 i32))) + (type + (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)) + ) (type (func (param) (param $x f32) (param) (param) (param f64 i32) (param))) - ;; (type - ;; (func (result) (result) (result i64 i64) (result) (result f32) (result)) - ;; ) - ;; (type - ;; (func - ;; (param i32 i32) (param i64 i32) (param) (param $x i32) (param) - ;; (result) (result f32 f64) (result f64 i32) (result) - ;; ) - ;; ) + (type + (func (result) (result) (result i64 i64) (result) (result f32) (result)) + ) + (type + (func + (param i32 i32) (param i64 i32) (param) (param $x i32) (param) + (result) (result f32 f64) (result f64 i32) (result) + ) + ) ) (assert_malformed @@ -48,12 +48,3 @@ (module quote "(type (func (result $x i32)))") "unexpected token" ) - -(assert_invalid - (module (type (func (result i32 i32)))) - "invalid result arity" -) -(assert_invalid - (module (type (func (result i32) (result i32)))) - "invalid result arity" -)