From 1e25971b922a49f96eedcb430af749adf5fbb466 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 14 Jul 2017 13:55:14 +0200 Subject: [PATCH 01/18] Overview --- README.md | 28 ++++++-- proposals/tail-call/Overview.md | 118 ++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 proposals/tail-call/Overview.md diff --git a/README.md b/README.md index 26d58a3a5c..eaeab76816 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,28 @@ -[![Build Status](https://travis-ci.org/WebAssembly/spec.svg?branch=master)](https://travis-ci.org/WebAssembly/spec) +[![Build Status](https://travis-ci.org/WebAssembly/tail-call.svg?branch=master)](https://travis-ci.org/WebAssembly/tail-call) + +# Tail Call Proposal for WebAssembly + +This repository is a clone of [github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/). +It is meant for discussion, prototype specification and implementation of a proposal to add tail call support to WebAssembly. + +See the [overview](proposals/tail-call/Overview.md) for a summary of the proposal. + +Original `README` from upstream repository follows... # spec -This repository holds the sources for the WebAssembly draft specification -(to seed a future -[WebAssembly Working Group](https://lists.w3.org/Archives/Public/public-new-work/2017Jun/0005.html)), -a reference implementation, and the official testsuite. +This repository holds a prototypical reference implementation for WebAssembly, +which is currently serving as the official specification. Eventually, we expect +to produce a specification either written in human-readable prose or in a formal +specification language. + +It also holds the WebAssembly testsuite, which tests numerous aspects of +conformance to the spec. + +View the work-in-progress spec at [webassembly.github.io/spec](https://webassembly.github.io/spec/). -A formatted version of the spec is available here: -[webassembly.github.io/spec](https://webassembly.github.io/spec/), +At this time, the contents of this repository are under development and known +to be "incomplet and inkorrect". Participation is welcome. Discussions about new features, significant semantic changes, or any specification change likely to generate substantial discussion diff --git a/proposals/tail-call/Overview.md b/proposals/tail-call/Overview.md new file mode 100644 index 0000000000..91c381fdca --- /dev/null +++ b/proposals/tail-call/Overview.md @@ -0,0 +1,118 @@ +# Tail Call Extension + +## Introduction + +### Motivation + +* Currently, the Wasm design explicitly forbids tail call optimisations + +* Want support to enable + - the correct and efficient implementations of languages that require tail call elimination + - the compilation of control constructs that can be implemented with it (e.g., forms of coroutines, continuations) + - compilation and optimization techniques that require it (e.g., dynamic recompilation, tracing, CPS) + - other sorts of computation being expressed as Wasm functions, e.g., FSMs + + +### Semantics + +Conceptually, tail-calling a function unwinds the current call frame before performing the actual call. +This can be applied to any form of call, that is: + +* Caller and callee can differ +* Caller and callee type can differ +* Callee may be dynamic (e.g., `call_indirect`) + + +### Design Space + +* Tail calls should be explicit instructions (current instructions explicitly disallow TCE) + +* Two possible schemes: + 1. introduce tail version of every call instruction + 2. introduce single prefix instruction that can be applied to every call instruction + +Note: WebAssembly will likely get more call instructions in the future, e.g., `call_ptr`. + + +## Examples + +A simple boring example of a tail-recursive factorial funciton. +``` +(func $fac (param $x i64) (result i64) + (return_call $fac-aux (get_local $x) (i64.const 1)) +) + +(func $fac-aux (param $x i64) (param $r i64) (result i64) + (if (i64.eqz (get_local $x)) + (then (return (get_local $r))) + (else + (return_call $fac-aux + (i64.sub (get_local $x) (i64.const 1)) + (i64.mul (get_local $x) (get_local $r)) + ) + ) + ) +) + +``` + + +## Spec Changes + +For now, we assume that separate instructions are introduced. +It is not difficult to adapt the rules to an alternative design with instruction prefixes. + +### Structure + +Add two instructions (for now): + +* `return_call x`, the tail-call version of `call` +* `return_call_indirect x`, the tail-call version of `call_indirect` + + +### Validation + +Validation of the new instructions is simply a combination of the typing rules for `return` and those for basic calls (and thus is stack-polymorphic). + +* If `x` refers to a function of type \[t1\*\] -> \[t2\*\], + then the instruction `return_call x` has type \[t3\* t1\*\] -> \[t4\* t2\*\], + for any t3\* and t4\*. + +* If `x` refers to a function type \[t1\*\] -> \[t2\*\], + then the instruction `return_call_indirect x` has type \[t3\* t1\* i32\] -> \[t4\* t2\*\], + for any t3\* and t4\*. + +Note that caller and callee type do not need to match. + + +### Execution + +Execution semantics of the new instructions would + +1. pop the call operands +2. clear and pop the topmost stack frame in the same way `return` does +3. push back the operands +4. delegate to the semantics of the respective plain call instructions + + +### Binary Format + +Need to assign opcodes for the new instructions. +Fortunately (and not just coincidentally), there is reserved opcode space next to the existing call instructions. + + +### Text Format + +The text format is extended with two new instructions in the obvious manner. + + +## Open Questions + +* What about tail calls to host functions? + - treat as tail-calling a wrapper, or trap? (probably former) + - note: cannot distinguish statically, e.g. with indirect calls + +* Which instruction scheme should be picked? + +* Instruction name bikeshedding + From 7570fe217ea193bf8818c4213492af9575fa1dbe Mon Sep 17 00:00:00 2001 From: rossberg-chromium Date: Mon, 17 Jul 2017 11:44:05 +0200 Subject: [PATCH 02/18] Typing --- proposals/tail-call/Overview.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/proposals/tail-call/Overview.md b/proposals/tail-call/Overview.md index 91c381fdca..bf206640cc 100644 --- a/proposals/tail-call/Overview.md +++ b/proposals/tail-call/Overview.md @@ -23,7 +23,9 @@ This can be applied to any form of call, that is: * Callee may be dynamic (e.g., `call_indirect`) -### Design Space +## Design Space + +### Instructions * Tail calls should be explicit instructions (current instructions explicitly disallow TCE) @@ -34,6 +36,16 @@ This can be applied to any form of call, that is: Note: WebAssembly will likely get more call instructions in the future, e.g., `call_ptr`. +### Typing + +* Two options: + 1. All functions are tail-callable + 2. Tail-callable functions are distinguished by type + +Option 2 allows different calling conventions for non-tail-callable functions, which may be reduce constraints on ABIs. +On the other hand, it creates a bifurcated function space, which might lead to difficulties e.g. when using function tables or other forms of dynamic indirection. + + ## Examples A simple boring example of a tail-recursive factorial funciton. @@ -108,9 +120,11 @@ The text format is extended with two new instructions in the obvious manner. ## Open Questions +* Differentiate tail-callable functions by type? + * What about tail calls to host functions? - - treat as tail-calling a wrapper, or trap? (probably former) - - note: cannot distinguish statically, e.g. with indirect calls + - treat as tail-calling a wrapper, use type distinction, or trap? + - note: cannot distinguish statically without type system support, e.g. with indirect calls * Which instruction scheme should be picked? From 8e82d8c9f534b0d262c756677c0576e527eb840d Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 4 Apr 2018 14:46:56 +0200 Subject: [PATCH 03/18] [spec/interpreter/test] Implement basic tail-call proposal --- document/core/binary/instructions.rst | 8 +- document/core/exec/instructions.rst | 112 ++++++ document/core/exec/runtime.rst | 2 + document/core/syntax/instructions.rst | 10 +- document/core/text/instructions.rst | 5 + document/core/util/macros.def | 3 + document/core/valid/instructions.rst | 63 ++++ interpreter/README.md | 2 + interpreter/binary/decode.ml | 7 +- interpreter/binary/encode.ml | 2 + interpreter/exec/eval.ml | 30 +- interpreter/syntax/ast.ml | 2 + interpreter/syntax/operators.ml | 2 + interpreter/text/arrange.ml | 3 + interpreter/text/lexer.mll | 2 + interpreter/text/parser.mly | 10 +- interpreter/valid/valid.ml | 9 + proposals/tail-call/Overview.md | 60 ++- test/core/return_call.wast | 202 ++++++++++ test/core/return_call_indirect.wast | 511 ++++++++++++++++++++++++++ 20 files changed, 1017 insertions(+), 28 deletions(-) create mode 100644 test/core/return_call.wast create mode 100644 test/core/return_call_indirect.wast diff --git a/document/core/binary/instructions.rst b/document/core/binary/instructions.rst index faf5411de8..e46b00b897 100644 --- a/document/core/binary/instructions.rst +++ b/document/core/binary/instructions.rst @@ -33,6 +33,8 @@ Control Instructions .. _binary-return: .. _binary-call: .. _binary-call_indirect: +.. _binary-return_call: +.. _binary-return_call_indirect: .. math:: \begin{array}{llclll} @@ -54,7 +56,9 @@ Control Instructions &\Rightarrow& \BRTABLE~l^\ast~l_N \\ &&|& \hex{0F} &\Rightarrow& \RETURN \\ &&|& \hex{10}~~x{:}\Bfuncidx &\Rightarrow& \CALL~x \\ &&|& - \hex{11}~~x{:}\Btypeidx~~\hex{00} &\Rightarrow& \CALLINDIRECT~x \\ + \hex{11}~~x{:}\Btypeidx~~\hex{00} &\Rightarrow& \CALLINDIRECT~x \\ &&|& + \hex{12}~~x{:}\Bfuncidx &\Rightarrow& \RETURNCALL~x \\ &&|& + \hex{13}~~x{:}\Btypeidx~~\hex{00} &\Rightarrow& \RETURNCALLINDIRECT~x \\ \end{array} .. note:: @@ -62,7 +66,7 @@ Control Instructions .. note:: In future versions of WebAssembly, the zero byte occurring in the encoding - of the |CALLINDIRECT| instruction may be used to index additional tables. + of the |CALLINDIRECT| and |RETURNCALLINDIRECT| instructions may be used to index additional tables. .. index:: value type, polymorphism pair: binary format; instruction diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 5472b73e5d..7c7616a231 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -934,6 +934,82 @@ Control Instructions \end{array} +.. _exec-return_call: + +:math:`\RETURNCALL~x` +..................... + +1. Let :math:`F` be the :ref:`current ` :ref:`frame `. + +2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MIFUNCS[x]` exists. + +3. Let :math:`a` be the :ref:`function address ` :math:`F.\AMODULE.\MIFUNCS[x]`. + +4. :ref:`Tail-invoke ` the function instance at address :math:`a`. + + +.. math:: + \begin{array}{lcl@{\qquad}l} + (\RETURNCALL~x) &\stepto& (\RETURNINVOKE~a) + & (\iff \CALL~x \stepto \INVOKE~a) + \end{array} + + +.. _exec-return_call_indirect: + +:math:`\RETURNCALLINDIRECT~x` +............................. + +1. Let :math:`F` be the :ref:`current ` :ref:`frame `. + +2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITABLES[0]` exists. + +3. Let :math:`\X{ta}` be the :ref:`table address ` :math:`F.\AMODULE.\MITABLES[0]`. + +4. Assert: due to :ref:`validation `, :math:`S.\STABLES[\X{ta}]` exists. + +5. Let :math:`\X{tab}` be the :ref:`table instance ` :math:`S.\STABLES[\X{ta}]`. + +6. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITYPES[x]` exists. + +7. Let :math:`\X{ft}_{\F{expect}}` be the :ref:`function type ` :math:`F.\AMODULE.\MITYPES[x]`. + +8. Assert: due to :ref:`validation `, a value with :ref:`value type ` |I32| is on the top of the stack. + +9. Pop the value :math:`\I32.\CONST~i` from the stack. + +10. If :math:`i` is not smaller than the length of :math:`\X{tab}.\TIELEM`, then: + + a. Trap. + +11. If :math:`\X{tab}.\TIELEM[i]` is uninitialized, then: + + a. Trap. + +12. Let :math:`a` be the :ref:`function address ` :math:`\X{tab}.\TIELEM[i]`. + +13. Assert: due to :ref:`validation `, :math:`S.\SFUNCS[a]` exists. + +14. Let :math:`\X{f}` be the :ref:`function instance ` :math:`S.\SFUNCS[a]`. + +15. Let :math:`\X{ft}_{\F{actual}}` be the :ref:`function type ` :math:`\X{f}.\FITYPE`. + +16. If :math:`\X{ft}_{\F{actual}}` and :math:`\X{ft}_{\F{expect}}` differ, then: + + a. Trap. + +17. :ref:`Tail-invoke ` the function instance at address :math:`a`. + + +.. math:: + \begin{array}{lcl@{\qquad}l} + (\RETURNCALLINDIRECT~x) &\stepto& (\RETURNINVOKE~a) + & (\iff \CALLINDIRECT~x \stepto \INVOKE~a) \\ + (\RETURNCALLINDIRECT~x) &\stepto& \TRAP + & (\iff \CALLINDIRECT~x \stepto \TRAP) \\ + \end{array} + + .. index:: instruction, instruction sequence, block .. _exec-instr-seq: @@ -1044,6 +1120,42 @@ Invocation of :ref:`function address ` :math:`a` \end{array} +.. _exec-return-invoke: + +Tail-invocation of :ref:`function address ` :math:`a` +...................................................................... + +1. Assert: due to :ref:`validation `, :math:`S.\SFUNCS[a]` exists. + +2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`S.\SFUNCS[a].\FITYPE`. + +3. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. + +4. Pop the results :math:`\val^m` from the stack. + +5. Assert: due to :ref:`validation `, the stack contains at least one :ref:`frame `. + +6. While the top of the stack is not a frame, do: + + a. Pop the top element from the stack. + +7. Assert: the top of the stack is a frame. + +8. Pop the frame from the stack. + +9. Push :math:`\val^m` to the stack. + +10. :ref:`Invoke ` the function instance at address :math:`a`. + +.. math:: + ~\\[-1ex] + \begin{array}{lcl@{\qquad}l} + S; \FRAME_n\{F\}~B^*[\val^m~(\RETURNINVOKE~a)]~\END &\stepto& + \val^m~(\INVOKE~a) + & (\iff S.\SFUNCS[a].\FITYPE = [t_1^m] \to [t_2^n]) + \end{array} + + .. _exec-invoke-exit: Returning from a function diff --git a/document/core/exec/runtime.rst b/document/core/exec/runtime.rst index e798f06f4a..201c50f927 100644 --- a/document/core/exec/runtime.rst +++ b/document/core/exec/runtime.rst @@ -443,6 +443,7 @@ In order to express the reduction of :ref:`traps `, :ref:`calls `, identified by its :ref:`address `. It unifies the handling of different forms of calls. +Analogously, |RETURNINVOKE| represents the imminent tail invocation of a function instance. The |INITELEM| and |INITDATA| instructions perform initialization of :ref:`element ` and :ref:`data ` segments during module :ref:`instantiation `. diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index 060933bf7f..f6a7c8ae97 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -301,7 +301,9 @@ Instructions in this group affect the flow of control. \BRTABLE~\vec(\labelidx)~\labelidx \\&&|& \RETURN \\&&|& \CALL~\funcidx \\&&|& - \CALLINDIRECT~\typeidx \\ + \CALLINDIRECT~\typeidx \\&&|& + \RETURNCALL~\funcidx \\&&|& + \RETURNCALLINDIRECT~\typeidx \\ \end{array} The |NOP| instruction does nothing. @@ -344,9 +346,13 @@ The |CALLINDIRECT| instruction calls a function indirectly through an operand in Since tables may contain function elements of heterogeneous type |ANYFUNC|, the callee is dynamically checked against the :ref:`function type ` indexed by the instruction's immediate, and the call aborted with a :ref:`trap ` if it does not match. +The |RETURNCALL| and |RETURNCALLINDIRECT| instructions are *tail-call* variants of the previous ones. +That is, they first return from the current function before actually performing the respective call. +It is guaranteed that no sequence of nested calls using only these instructions can cause resource exhaustion due to hitting an :ref:`implementation's limit ` on the number of active calls. + .. note:: In the current version of WebAssembly, - |CALLINDIRECT| implicitly operates on :ref:`table ` :ref:`index ` :math:`0`. + |CALLINDIRECT| and |RETURNCALLINDIRECT| implicitly operate on :ref:`table ` :ref:`index ` :math:`0`. This restriction may be lifted in future versions. diff --git a/document/core/text/instructions.rst b/document/core/text/instructions.rst index dadd435b07..26708d2045 100644 --- a/document/core/text/instructions.rst +++ b/document/core/text/instructions.rst @@ -83,6 +83,8 @@ The same label identifier may optionally be repeated after the corresponding :ma .. _text-return: .. _text-call: .. _text-call_indirect: +.. _text-return_call: +.. _text-return_call_indirect: All other control instruction are represented verbatim. @@ -98,6 +100,9 @@ All other control instruction are represented verbatim. \text{return} &\Rightarrow& \RETURN \\ &&|& \text{call}~~x{:}\Tfuncidx_I &\Rightarrow& \CALL~x \\ &&|& \text{call\_indirect}~~x,I'{:}\Ttypeuse_I &\Rightarrow& \CALLINDIRECT~x + & (\iff I' = \{\}) \\ &&|& + \text{return\_call}~~x{:}\Tfuncidx_I &\Rightarrow& \RETURNCALL~x \\ &&|& + \text{return\_call\_indirect}~~x,I'{:}\Ttypeuse_I &\Rightarrow& \RETURNCALLINDIRECT~x & (\iff I' = \{\}) \\ \end{array} diff --git a/document/core/util/macros.def b/document/core/util/macros.def index 2216694b13..26978afc47 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -313,6 +313,8 @@ .. |RETURN| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{return}} .. |CALL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{call}} .. |CALLINDIRECT| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{call\_indirect}} +.. |RETURNCALL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{return\_call}} +.. |RETURNCALLINDIRECT| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{return\_call\_indirect}} .. |DROP| mathdef:: \xref{syntax/instructions}{syntax-instr-parametric}{\K{drop}} .. |SELECT| mathdef:: \xref{syntax/instructions}{syntax-instr-parametric}{\K{select}} @@ -865,6 +867,7 @@ .. |TRAP| mathdef:: \xref{exec/runtime}{syntax-trap}{\K{trap}} .. |INVOKE| mathdef:: \xref{exec/runtime}{syntax-invoke}{\K{invoke}} +.. |RETURNINVOKE| mathdef:: \xref{exec/runtime}{syntax-return_invoke}{\K{return\_invoke}} .. |INITELEM| mathdef:: \xref{exec/runtime}{syntax-init_elem}{\K{init\_elem}} .. |INITDATA| mathdef:: \xref{exec/runtime}{syntax-init_data}{\K{init\_data}} diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index ffc308ff29..dcbf64135d 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -681,6 +681,69 @@ Control Instructions } +.. _valid-return_call: + +:math:`\RETURNCALL~x` +..................... + +* The return type :math:`C.\CRETURN` must not be empty in the context. + +* The function :math:`C.\CFUNCS[x]` must be defined in the context. + +* Let :math:`[t_1^\ast] \to [t_2^?]` be the :ref:`function type ` :math:`C.\CFUNCS[x]`. + +* The :ref:`result type ` must be the same as :math:`C.\CRETURN`. + +* Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. + +.. math:: + \frac{ + C.\CFUNCS[x] = [t_1^\ast] \to [t_2^?] + \qquad + C.\CRETURN = [t_2^?] + }{ + C \vdashinstr \CALL~x : [t_3^\ast~t_1^\ast] \to [t_4^\ast] + } + +.. note:: + The |RETURNCALL| instruction is :ref:`stack-polymorphic `. + + +.. _valid-return_call_indirect: + +:math:`\RETURNCALLINDIRECT~x` +............................. + +* The return type :math:`C.\CRETURN` must not be empty in the context. + +* The table :math:`C.\CTABLES[0]` must be defined in the context. + +* Let :math:`\limits~\elemtype` be the :ref:`table type ` :math:`C.\CTABLES[0]`. + +* The :ref:`element type ` :math:`\elemtype` must be |ANYFUNC|. + +* 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]`. + +* Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. + + +.. math:: + \frac{ + C.\CTABLES[0] = \limits~\ANYFUNC + \qquad + C.\CTYPES[x] = [t_1^\ast] \to [t_2^?] + \qquad + C.\CRETURN = [t_2^?] + }{ + C \vdashinstr \CALLINDIRECT~x : [t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast] + } + +.. note:: + The |RETURNCALLINDIRECT| instruction is :ref:`stack-polymorphic `. + + .. index:: instruction, instruction sequence .. _valid-instr-seq: diff --git a/interpreter/README.md b/interpreter/README.md index 2547f6a7ee..20cee1253e 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -214,6 +214,8 @@ op: br_if br_table + return + return_call + return_call_indirect call call_indirect drop diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index 9f8530ec86..cebea4bd87 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -246,8 +246,13 @@ let rec instr s = let x = at var s in expect 0x00 s "zero flag expected"; call_indirect x + | 0x12 -> return_call (at var s) + | 0x13 -> + let x = at var s in + expect 0x00 s "zero flag expected"; + return_call_indirect x - | 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 as b -> illegal s pos b + | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 as b -> illegal s pos b | 0x1a -> drop | 0x1b -> select diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index adbf418a44..824099e49b 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -157,6 +157,8 @@ let encode m = | Return -> op 0x0f | Call x -> op 0x10; var x | CallIndirect x -> op 0x11; var x; u8 0x00 + | ReturnCall x -> op 0x12; var x + | ReturnCallIndirect x -> op 0x13; var x; u8 0x00 | Drop -> op 0x1a | Select -> op 0x1b diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index a8b69ea7b6..83e6bf416f 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -53,6 +53,7 @@ and admin_instr' = | Invoke of func_inst | Trapping of string | Returning of value stack + | ReturningInvoke of value stack * func_inst | Breaking of int32 * value stack | Label of int * instr list * code | Frame of int * frame * code @@ -164,6 +165,21 @@ let rec step (c : config) : config = else vs, [Invoke func @@ e.at] + | ReturnCall x, vs -> + (match (step {c with code = (vs, [Plain (Call x) @@ e.at])}).code with + | vs', [{it = Invoke a; at}] -> vs', [ReturningInvoke (vs', a) @@ at] + | _ -> assert false + ) + + | ReturnCallIndirect x, vs -> + (match + (step {c with code = (vs, [Plain (CallIndirect x) @@ e.at])}).code + with + | vs', [{it = Invoke a; at}] -> vs', [ReturningInvoke (vs', a) @@ at] + | vs', [{it = Trapping s; at}] -> vs', [Trapping s @@ at] + | _ -> assert false + ) + | Drop, v :: vs' -> vs', [] @@ -256,13 +272,14 @@ let rec step (c : config) : config = ("missing or ill-typed operand on stack (" ^ s1 ^ " : " ^ s2 ^ ")") ) - | Trapping msg, vs -> + | Trapping _, vs -> assert false - | Returning vs', vs -> + | Returning _, vs + | ReturningInvoke _, vs -> Crash.error e.at "undefined frame" - | Breaking (k, vs'), vs -> + | Breaking _, vs -> Crash.error e.at "undefined label" | Label (n, es0, (vs', [])), vs -> @@ -274,6 +291,9 @@ let rec step (c : config) : config = | Label (n, es0, (vs', {it = Returning vs0; at} :: es')), vs -> vs, [Returning vs0 @@ at] + | Label (n, es0, (vs', {it = ReturningInvoke (vs0, f); at} :: es')), vs -> + vs, [ReturningInvoke (vs0, f) @@ at] + | Label (n, es0, (vs', {it = Breaking (0l, vs0); at} :: es')), vs -> take n vs0 e.at @ vs, List.map plain es0 @@ -293,6 +313,10 @@ let rec step (c : config) : config = | Frame (n, frame', (vs', {it = Returning vs0; at} :: es')), vs -> take n vs0 e.at @ vs, [] + | Frame (n, frame', (vs', {it = ReturningInvoke (vs0, f); at} :: es')), vs -> + let FuncType (ins, out) = Func.type_of f in + take (List.length ins) vs0 e.at @ vs, [Invoke f @@ at] + | Frame (n, frame', code'), vs -> let c' = step {frame = frame'; code = code'; budget = c.budget - 1} in vs, [Frame (n, c'.frame, c'.code) @@ e.at] diff --git a/interpreter/syntax/ast.ml b/interpreter/syntax/ast.ml index 4ae9c58753..2ca9184c5d 100644 --- a/interpreter/syntax/ast.ml +++ b/interpreter/syntax/ast.ml @@ -82,6 +82,8 @@ and instr' = | Return (* break from function body *) | Call of var (* call function *) | CallIndirect of var (* call function through table *) + | ReturnCall of var (* tail-call function *) + | ReturnCallIndirect of var (* tail-call function through table *) | GetLocal of var (* read local variable *) | SetLocal of var (* write local variable *) | TeeLocal of var (* write local variable and keep value *) diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index 8f177bd2bc..d4c53efa91 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -24,6 +24,8 @@ let if_ ts es1 es2 = If (ts, es1, es2) let return = Return let call x = Call x let call_indirect x = CallIndirect x +let return_call x = ReturnCall x +let return_call_indirect x = ReturnCallIndirect x let get_local x = GetLocal x let set_local x = SetLocal x diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index 6c9feebe2f..5a28dd60ab 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -237,6 +237,9 @@ let rec instr e = | Return -> "return", [] | Call x -> "call " ^ var x, [] | CallIndirect x -> "call_indirect", [Node ("type " ^ var x, [])] + | ReturnCall x -> "return_call " ^ var x, [] + | ReturnCallIndirect x -> + "return_call_indirect", [Node ("type " ^ var x, [])] | GetLocal x -> "get_local " ^ var x, [] | SetLocal x -> "set_local " ^ var x, [] | TeeLocal x -> "tee_local " ^ var x, [] diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 2a41d8cfe7..58af3176d2 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -192,6 +192,8 @@ rule token = parse | "select" { SELECT } | "call" { CALL } | "call_indirect" { CALL_INDIRECT } + | "return_call" { RETURN_CALL } + | "return_call_indirect" { RETURN_CALL_INDIRECT } | "get_local" { GET_LOCAL } | "set_local" { SET_LOCAL } diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index fc437bc5df..9c5050dc94 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -147,7 +147,7 @@ let inline_type_explicit (c : context) x ft at = %token NAT INT FLOAT STRING VAR VALUE_TYPE ANYFUNC MUT LPAR RPAR %token NOP DROP BLOCK END IF THEN ELSE SELECT LOOP BR BR_IF BR_TABLE -%token CALL CALL_INDIRECT RETURN +%token CALL CALL_INDIRECT RETURN RETURN_CALL RETURN_CALL_INDIRECT %token GET_LOCAL SET_LOCAL TEE_LOCAL GET_GLOBAL SET_GLOBAL %token LOAD STORE OFFSET_EQ_NAT ALIGN_EQ_NAT %token CONST UNARY BINARY TEST COMPARE CONVERT @@ -310,6 +310,7 @@ plain_instr : br_table xs x } | RETURN { fun c -> return } | CALL var { fun c -> call ($2 c func) } + | RETURN_CALL var { fun c -> return_call ($2 c func) } | GET_LOCAL var { fun c -> get_local ($2 c local) } | SET_LOCAL var { fun c -> set_local ($2 c local) } | TEE_LOCAL var { fun c -> tee_local ($2 c local) } @@ -330,6 +331,8 @@ plain_instr : call_instr : | CALL_INDIRECT call_instr_type { let at = at () in fun c -> call_indirect ($2 c) @@ at } + | RETURN_CALL_INDIRECT call_instr_type + { let at = at () in fun c -> return_call_indirect ($2 c) @@ at } call_instr_type : | type_use call_instr_params @@ -358,6 +361,9 @@ call_instr_instr : | CALL_INDIRECT call_instr_type_instr { let at1 = ati 1 in fun c -> let x, es = $2 c in call_indirect x @@ at1, es } + | RETURN_CALL_INDIRECT call_instr_type_instr + { let at1 = ati 1 in + fun c -> let x, es = $2 c in return_call_indirect x @@ at1, es } call_instr_type_instr : | type_use call_instr_params_instr @@ -411,6 +417,8 @@ expr1 : /* Sugar */ | plain_instr expr_list { fun c -> $2 c, $1 c } | CALL_INDIRECT call_expr_type { fun c -> let x, es = $2 c in es, call_indirect x } + | RETURN_CALL_INDIRECT call_expr_type + { fun c -> let x, es = $2 c in es, return_call_indirect x } | BLOCK labeling_opt block { fun c -> let c' = $2 c [] in let ts, es = $3 c' in [], block ts es } | LOOP labeling_opt block diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index 0170e084d5..fb86704bbf 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -228,7 +228,16 @@ 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 + | ReturnCall x -> + let FuncType (ins, out) = func c x in + require (out = c.results) e.at "type mismatch in function result"; + ins -->... [] + | ReturnCallIndirect x -> + ignore (table c (0l @@ e.at)); + let FuncType (ins, out) = type_ c x in + require (out = c.results) e.at "type mismatch in function result"; + (ins @ [I32Type]) -->... [] | GetLocal x -> [] --> [local c x] diff --git a/proposals/tail-call/Overview.md b/proposals/tail-call/Overview.md index bf206640cc..ef4dd9e92e 100644 --- a/proposals/tail-call/Overview.md +++ b/proposals/tail-call/Overview.md @@ -27,23 +27,38 @@ This can be applied to any form of call, that is: ### Instructions -* Tail calls should be explicit instructions (current instructions explicitly disallow TCE) +* Tail calls should be separate, explicit call instructions (current instructions explicitly disallow TCE) * Two possible schemes: 1. introduce tail version of every call instruction 2. introduce single prefix instruction that can be applied to every call instruction -Note: WebAssembly will likely get more call instructions in the future, e.g., `call_ptr`. +* Consideration: WebAssembly will likely get more call instructions in the future, e.g., `call_ref` + + +### Execution + +* Tail calls behave like a combination of `return` followed by a respective call + +* Hence they unwind the operand stack like `return` does + +* Only keeps the necessary call arguments ### Typing -* Two options: - 1. All functions are tail-callable - 2. Tail-callable functions are distinguished by type +* Because tail calls transfer control and unwind the stack they are stack-polymorphic: + +* Open question: distinguish tail-calls in function type? Possibilities: + 1. Distinguish tail-callees by type + 2. Distinguish tail-callers by type + 3. Both + 4. Neither -Option 2 allows different calling conventions for non-tail-callable functions, which may be reduce constraints on ABIs. -On the other hand, it creates a bifurcated function space, which might lead to difficulties e.g. when using function tables or other forms of dynamic indirection. +* Considerations: + - Option 1 (and 3) allows different calling conventions for non-tail-callable functions, which may be reduce constraints on ABIs. + - On the other hand, it creates a bifurcated function space, which can lead to difficulties e.g. when using function tables or other forms of dynamic indirection. + - Benefit of option 2 (and 3) unclear. ## Examples @@ -74,12 +89,15 @@ A simple boring example of a tail-recursive factorial funciton. For now, we assume that separate instructions are introduced. It is not difficult to adapt the rules to an alternative design with instruction prefixes. +The details of possible typing refinements to distinguish tail-callers/callees are to be discussed and not yet included. + + ### Structure Add two instructions (for now): -* `return_call x`, the tail-call version of `call` -* `return_call_indirect x`, the tail-call version of `call_indirect` +* `return_call `, the tail-call version of `call` +* `return_call_indirect `, the tail-call version of `call_indirect` ### Validation @@ -87,14 +105,16 @@ Add two instructions (for now): Validation of the new instructions is simply a combination of the typing rules for `return` and those for basic calls (and thus is stack-polymorphic). * If `x` refers to a function of type \[t1\*\] -> \[t2\*\], - then the instruction `return_call x` has type \[t3\* t1\*\] -> \[t4\* t2\*\], - for any t3\* and t4\*. + then the instruction `return_call x` has type \[t3\* t1\*\] -> \[t4\*\], + for any t3\* and t4\*, + provided that the current function has return type \[t2\*\]. * If `x` refers to a function type \[t1\*\] -> \[t2\*\], - then the instruction `return_call_indirect x` has type \[t3\* t1\* i32\] -> \[t4\* t2\*\], - for any t3\* and t4\*. + then the instruction `return_call_indirect x` has type \[t3\* t1\* i32\] -> \[t4\*\], + for any t3\* and t4\*, + provided that the current function has return type \[t2\*\]. -Note that caller and callee type do not need to match. +Note that caller's and callee's parameter types do not need to match. ### Execution @@ -109,8 +129,10 @@ Execution semantics of the new instructions would ### Binary Format -Need to assign opcodes for the new instructions. -Fortunately (and not just coincidentally), there is reserved opcode space next to the existing call instructions. +Use the reserved opcodes after existing call instructions, i.e.: + +* `return_call` is 0x12 +* `return_call_indirect` is 0x13 ### Text Format @@ -120,13 +142,13 @@ The text format is extended with two new instructions in the obvious manner. ## Open Questions -* Differentiate tail-callable functions by type? +* Which instruction scheme should be picked? + +* Differentiate tail-callers or callees by type? * What about tail calls to host functions? - treat as tail-calling a wrapper, use type distinction, or trap? - note: cannot distinguish statically without type system support, e.g. with indirect calls -* Which instruction scheme should be picked? - * Instruction name bikeshedding diff --git a/test/core/return_call.wast b/test/core/return_call.wast new file mode 100644 index 0000000000..f7bb8c4107 --- /dev/null +++ b/test/core/return_call.wast @@ -0,0 +1,202 @@ +;; Test `call` operator + +(module + ;; Auxiliary definitions + (func $const-i32 (result i32) (i32.const 0x132)) + (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 $id-i32 (param i32) (result i32) (get_local 0)) + (func $id-i64 (param i64) (result i64) (get_local 0)) + (func $id-f32 (param f32) (result f32) (get_local 0)) + (func $id-f64 (param f64) (result f64) (get_local 0)) + + (func $f32-i32 (param f32 i32) (result i32) (get_local 1)) + (func $i32-i64 (param i32 i64) (result i64) (get_local 1)) + (func $f64-f32 (param f64 f32) (result f32) (get_local 1)) + (func $i64-f64 (param i64 f64) (result f64) (get_local 1)) + + ;; Typing + + (func (export "type-i32") (result i32) (call $const-i32)) + (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-first-i32") (result i32) (call $id-i32 (i32.const 32))) + (func (export "type-first-i64") (result i64) (call $id-i64 (i64.const 64))) + (func (export "type-first-f32") (result f32) (call $id-f32 (f32.const 1.32))) + (func (export "type-first-f64") (result f64) (call $id-f64 (f64.const 1.64))) + + (func (export "type-second-i32") (result i32) + (return_call $f32-i32 (f32.const 32.1) (i32.const 32)) + ) + (func (export "type-second-i64") (result i64) + (return_call $i32-i64 (i32.const 32) (i64.const 64)) + ) + (func (export "type-second-f32") (result f32) + (return_call $f64-f32 (f64.const 64) (f32.const 32)) + ) + (func (export "type-second-f64") (result f64) + (return_call $i64-f64 (i64.const 64) (f64.const 64.1)) + ) + + ;; Recursion + + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (get_local 0)) + (then (get_local 1)) + (else + (return_call $fac-acc + (i64.sub (get_local 0) (i64.const 1)) + (i64.mul (get_local 0) (get_local 1)) + ) + ) + ) + ) + + (func $count (export "count") (param i64) (result i64) + (if (result i64) (i64.eqz (get_local 0)) + (then (get_local 0)) + (else (return_call $count (i64.sub (get_local 0) (i64.const 1)))) + ) + ) + + (func $even (export "even") (param i64) (result i32) + (if (result i32) (i64.eqz (get_local 0)) + (then (i32.const 44)) + (else (return_call $odd (i64.sub (get_local 0) (i64.const 1)))) + ) + ) + (func $odd (export "odd") (param i64) (result i32) + (if (result i32) (i64.eqz (get_local 0)) + (then (i32.const 99)) + (else (return_call $even (i64.sub (get_local 0) (i64.const 1)))) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(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-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "count" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1_000_000)) (i64.const 0)) + +(assert_return (invoke "even" (i64.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 1_000_000)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1_000_001)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 1_000_000)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 999_999)) (i32.const 44)) + + +;; Invalid typing + +(assert_invalid + (module + (func $type-void-vs-num (result i32) (return_call 1) (i32.const 0)) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-num-vs-num (result i32) (return_call 1) (i32.const 0)) + (func (result i64) (i64.const 1)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $arity-0-vs-1 (return_call 1)) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-0-vs-2 (return_call 1)) + (func (param f64 i32)) + ) + "type mismatch" +) + +(module + (func $arity-1-vs-0 (i32.const 1) (return_call 1)) + (func) +) + +(module + (func $arity-2-vs-0 (f64.const 2) (i32.const 1) (return_call 1)) + (func) +) + +(assert_invalid + (module + (func $type-first-void-vs-num (return_call 1 (nop) (i32.const 1))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-void-vs-num (return_call 1 (i32.const 1) (nop))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-num-vs-num (return_call 1 (f64.const 1) (i32.const 1))) + (func (param i32 f64)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-num-vs-num (return_call 1 (i32.const 1) (f64.const 1))) + (func (param f64 i32)) + ) + "type mismatch" +) + + +;; Unbound function + +(assert_invalid + (module (func $unbound-func (return_call 1))) + "unknown function" +) +(assert_invalid + (module (func $large-func (return_call 1012321300))) + "unknown function" +) diff --git a/test/core/return_call_indirect.wast b/test/core/return_call_indirect.wast new file mode 100644 index 0000000000..8f098c42ab --- /dev/null +++ b/test/core/return_call_indirect.wast @@ -0,0 +1,511 @@ +;; Test `call_indirect` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (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 $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))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + (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 $id-i32 (type $over-i32) (get_local 0)) + (func $id-i64 (type $over-i64) (get_local 0)) + (func $id-f32 (type $over-f32) (get_local 0)) + (func $id-f64 (type $over-f64) (get_local 0)) + + (func $i32-i64 (type $i32-i64) (get_local 1)) + (func $i64-f64 (type $i64-f64) (get_local 1)) + (func $f32-i32 (type $f32-i32) (get_local 1)) + (func $f64-f32 (type $f64-f32) (get_local 1)) + + (func $over-i32-duplicate (type $over-i32-duplicate) (get_local 0)) + (func $over-i64-duplicate (type $over-i64-duplicate) (get_local 0)) + (func $over-f32-duplicate (type $over-f32-duplicate) (get_local 0)) + (func $over-f64-duplicate (type $over-f64-duplicate) (get_local 0)) + + (table anyfunc + (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 $fac-acc $even $odd + $over-i32-duplicate $over-i64-duplicate + $over-f32-duplicate $over-f64-duplicate + ) + ) + + ;; Syntax + + (func + (return_call_indirect (i32.const 0)) + (return_call_indirect (param i64) (i64.const 0) (i32.const 0)) + (return_call_indirect (param i64) (param) (param f64 i32 i64) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + (return_call_indirect (result) (i32.const 0)) + ) + + (func (result i32) + (return_call_indirect (result i32) (i32.const 0)) + (return_call_indirect (result i32) (result) (i32.const 0)) + (return_call_indirect (param i64) (result i32) (i64.const 0) (i32.const 0)) + (return_call_indirect + (param) (param i64) (param) (param f64 i32 i64) (param) (param) + (result) (result i32) (result) (result) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + ) + + (func (result i64) + (return_call_indirect (type $over-i64) (param i64) (result i64) + (i64.const 0) (i32.const 0) + ) + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (return_call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (return_call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (return_call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (return_call_indirect (type $out-f64) (i32.const 3)) + ) + + (func (export "type-index") (result i64) + (return_call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (return_call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (return_call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (return_call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (return_call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (return_call_indirect (type $f32-i32) + (f32.const 32.1) (i32.const 32) (i32.const 8) + ) + ) + (func (export "type-second-i64") (result i64) + (return_call_indirect (type $i32-i64) + (i32.const 32) (i64.const 64) (i32.const 9) + ) + ) + (func (export "type-second-f32") (result f32) + (return_call_indirect (type $f64-f32) + (f64.const 64) (f32.const 32) (i32.const 10) + ) + ) + (func (export "type-second-f64") (result f64) + (return_call_indirect (type $i64-f64) + (i64.const 64) (f64.const 64.1) (i32.const 11) + ) + ) + + ;; Dispatch + + (func (export "dispatch") (param i32 i64) (result i64) + (return_call_indirect (type $over-i64) (get_local 1) (get_local 0)) + ) + + (func (export "dispatch-structural") (param i32) (result i64) + (return_call_indirect (type $over-i64-duplicate) + (i64.const 9) (get_local 0) + ) + ) + + ;; Recursion + + (func $fac (export "fac") (type $over-i64) + (return_call_indirect (param i64 i64) (result i64) + (get_local 0) (i64.const 1) (i32.const 13) + ) + ) + + (func $fac-acc (param i64 i64) (result i64) + (if (result i64) (i64.eqz (get_local 0)) + (then (get_local 1)) + (else + (return_call_indirect (param i64 i64) (result i64) + (i64.sub (get_local 0) (i64.const 1)) + (i64.mul (get_local 0) (get_local 1)) + (i32.const 13) + ) + ) + ) + ) + + (func $even (export "even") (param i32) (result i32) + (if (result i32) (i32.eqz (get_local 0)) + (then (i32.const 44)) + (else + (return_call_indirect (type $over-i32) + (i32.sub (get_local 0) (i32.const 1)) + (i32.const 15) + ) + ) + ) + ) + (func $odd (export "odd") (param i32) (result i32) + (if (result i32) (i32.eqz (get_local 0)) + (then (i32.const 99)) + (else + (return_call_indirect (type $over-i32) + (i32.sub (get_local 0) (i32.const 1)) + (i32.const 14) + ) + ) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(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-index") (i64.const 100)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.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)) +(assert_return (invoke "dispatch" (i32.const 17) (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 20) (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") + +(assert_return (invoke "dispatch-structural" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural" (i32.const 12)) (i64.const 362880)) +(assert_return (invoke "dispatch-structural" (i32.const 17)) (i64.const 9)) +(assert_trap (invoke "dispatch-structural" (i32.const 11)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural" (i32.const 16)) "indirect call type mismatch") + +(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)) +(assert_return (invoke "fac" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_return (invoke "even" (i32.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 77)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100_000)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 111_111)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 77)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200_002)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 300_003)) (i32.const 44)) + + +;; Invalid syntax + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (param i32) (type $sig) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (param i32) (result i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (result i32) (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (result i32) (param i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(table 0 anyfunc)" + "(func (return_call_indirect (param $x i32) (i32.const 0) (i32.const 0)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 anyfunc)" + "(func" + " (return_call_indirect (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(table 0 anyfunc)" + "(func (result i32)" + " (return_call_indirect (type $sig) (param i32) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) + +;; Invalid typing + +(assert_invalid + (module + (type (func)) + (func $no-table (return_call_indirect (type 0) (i32.const 0))) + ) + "unknown table" +) + +(assert_invalid + (module + (type (func)) + (table 0 anyfunc) + (func $type-void-vs-num (i32.eqz (return_call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (result i64))) + (table 0 anyfunc) + (func $type-num-vs-num (i32.eqz (return_call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 anyfunc) + (func $arity-0-vs-1 (return_call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 anyfunc) + (func $arity-0-vs-2 (return_call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) + +(module + (type (func)) + (table 0 anyfunc) + (func $arity-1-vs-0 (return_call_indirect (type 0) (i32.const 1) (i32.const 0))) +) + +(module + (type (func)) + (table 0 anyfunc) + (func $arity-2-vs-0 + (return_call_indirect (type 0) (f64.const 2) (i32.const 1) (i32.const 0)) + ) +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 anyfunc) + (func $type-func-void-vs-i32 (return_call_indirect (type 0) (i32.const 1) (nop))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32))) + (table 0 anyfunc) + (func $type-func-num-vs-i32 (return_call_indirect (type 0) (i32.const 0) (i64.const 1))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 anyfunc) + (func $type-first-void-vs-num + (return_call_indirect (type 0) (nop) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 anyfunc) + (func $type-second-void-vs-num + (return_call_indirect (type 0) (i32.const 1) (nop) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 f64))) + (table 0 anyfunc) + (func $type-first-num-vs-num + (return_call_indirect (type 0) (f64.const 1) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 anyfunc) + (func $type-second-num-vs-num + (return_call_indirect (type 0) (i32.const 1) (f64.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) + + +;; Unbound type + +(assert_invalid + (module + (table 0 anyfunc) + (func $unbound-type (return_call_indirect (type 1) (i32.const 0))) + ) + "unknown type" +) +(assert_invalid + (module + (table 0 anyfunc) + (func $large-type (return_call_indirect (type 1012321300) (i32.const 0))) + ) + "unknown type" +) + + +;; Unbound function in table + +(assert_invalid + (module (table anyfunc (elem 0 0))) + "unknown function 0" +) From c2dc15009d629f6c8a80c015890398dddcf6f9b2 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Sat, 7 Apr 2018 19:46:52 +0200 Subject: [PATCH 04/18] Comments; instruction index --- document/core/appendix/index-instructions.rst | 352 +++++++++--------- document/core/exec/instructions.rst | 16 +- document/core/valid/instructions.rst | 10 +- test/core/return_call.wast | 18 +- test/core/return_call_indirect.wast | 2 +- 5 files changed, 201 insertions(+), 197 deletions(-) diff --git a/document/core/appendix/index-instructions.rst b/document/core/appendix/index-instructions.rst index f5811e024f..20ce260771 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:`[] \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~[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:`[] \to [t^\ast]` :ref:`validation ` :ref:`execution ` :math:`\ELSE` :math:`\hex{05}` (reserved) :math:`\hex{06}` (reserved) :math:`\hex{07}` @@ -19,185 +19,185 @@ 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 ` -(reserved) :math:`\hex{12}` -(reserved) :math:`\hex{13}` +: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:`\RETURNCALL~x` :math:`\hex{12}` :math:`[t_1^\ast] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` +:math:`\RETURNCALLINDIRECT~x` :math:`\hex{12}` :math:`[t_1^\ast~\I32] \to [t_2^\ast]` :ref:`validation ` :ref:`execution ` (reserved) :math:`\hex{14}` (reserved) :math:`\hex{15}` (reserved) :math:`\hex{16}` (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:`\GETLOCAL~x` :math:`\hex{20}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` -:math:`\SETLOCAL~x` :math:`\hex{21}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` -:math:`\TEELOCAL~x` :math:`\hex{22}` :math:`[t] \to [t]` :ref:`validation ` :ref:`execution ` -:math:`\GETGLOBAL~x` :math:`\hex{23}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` -:math:`\SETGLOBAL~x` :math:`\hex{24}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` +:math:`\GETLOCAL~x` :math:`\hex{20}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` +:math:`\SETLOCAL~x` :math:`\hex{21}` :math:`[t] \to []` :ref:`validation ` :ref:`execution ` +:math:`\TEELOCAL~x` :math:`\hex{22}` :math:`[t] \to [t]` :ref:`validation ` :ref:`execution ` +:math:`\GETGLOBAL~x` :math:`\hex{23}` :math:`[] \to [t]` :ref:`validation ` :ref:`execution ` +:math:`\SETGLOBAL~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:`\CURRENTMEMORY` :math:`\hex{3F}` :math:`[] \to [\I32]` :ref:`validation ` :ref:`execution ` -:math:`\GROWMEMORY` :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{\_s/}\F32` :math:`\hex{A8}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_u/}\F32` :math:`\hex{A9}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_s/}\F64` :math:`\hex{AA}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I32.\TRUNC\K{\_u/}\F64` :math:`\hex{AB}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EXTEND\K{\_s/}\I32` :math:`\hex{AC}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\EXTEND\K{\_u/}\I32` :math:`\hex{AD}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_s/}\F32` :math:`\hex{AE}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_u/}\F32` :math:`\hex{AF}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_s/}\F64` :math:`\hex{B0}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\I64.\TRUNC\K{\_u/}\F64` :math:`\hex{B1}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_s/}\I32` :math:`\hex{B2}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_u/}\I32` :math:`\hex{B3}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_s/}\I64` :math:`\hex{B4}` :math:`[\I64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F32.\CONVERT\K{\_u/}\I64` :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{\_s/}\I32` :math:`\hex{B7}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CONVERT\K{\_u/}\I32` :math:`\hex{B8}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CONVERT\K{\_s/}\I64` :math:`\hex{B9}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` -:math:`\F64.\CONVERT\K{\_u/}\I64` :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.\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:`\CURRENTMEMORY` :math:`\hex{3F}` :math:`[] \to [\I32]` :ref:`validation ` :ref:`execution ` +:math:`\GROWMEMORY` :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{\_s/}\F32` :math:`\hex{A8}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_u/}\F32` :math:`\hex{A9}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_s/}\F64` :math:`\hex{AA}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_u/}\F64` :math:`\hex{AB}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EXTEND\K{\_s/}\I32` :math:`\hex{AC}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\EXTEND\K{\_u/}\I32` :math:`\hex{AD}` :math:`[\I32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_s/}\F32` :math:`\hex{AE}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_u/}\F32` :math:`\hex{AF}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_s/}\F64` :math:`\hex{B0}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_u/}\F64` :math:`\hex{B1}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_s/}\I32` :math:`\hex{B2}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_u/}\I32` :math:`\hex{B3}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_s/}\I64` :math:`\hex{B4}` :math:`[\I64] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F32.\CONVERT\K{\_u/}\I64` :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{\_s/}\I32` :math:`\hex{B7}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CONVERT\K{\_u/}\I32` :math:`\hex{B8}` :math:`[\I32] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CONVERT\K{\_s/}\I64` :math:`\hex{B9}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\F64.\CONVERT\K{\_u/}\I64` :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 ` +=================================== ================ ========================================== =============================================== =============================================================== diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 7c7616a231..b13e80846a 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -844,7 +844,7 @@ Control Instructions .. math:: ~\\[-1ex] \begin{array}{lcl@{\qquad}l} - \FRAME_n\{F\}~\XB^k[\val^n~\RETURN]~\END &\stepto& \val^n + \FRAME_n\{F\}~B^\ast[\val^n~\RETURN]~\END &\stepto& \val^n \end{array} @@ -939,6 +939,8 @@ Control Instructions :math:`\RETURNCALL~x` ..................... +.. todo: find a way to reuse call/call_indirect prose for tail call versions + 1. Let :math:`F` be the :ref:`current ` :ref:`frame `. 2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MIFUNCS[x]` exists. @@ -1127,11 +1129,11 @@ Tail-invocation of :ref:`function address ` :math:`a` 1. Assert: due to :ref:`validation `, :math:`S.\SFUNCS[a]` exists. -2. Let :math:`[t_1^m] \to [t_2^n]` be the :ref:`function type ` :math:`S.\SFUNCS[a].\FITYPE`. +2. Let :math:`[t_1^n] \to [t_2^m]` be the :ref:`function type ` :math:`S.\SFUNCS[a].\FITYPE`. 3. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. -4. Pop the results :math:`\val^m` from the stack. +4. Pop the results :math:`\val^n` from the stack. 5. Assert: due to :ref:`validation `, the stack contains at least one :ref:`frame `. @@ -1143,16 +1145,16 @@ Tail-invocation of :ref:`function address ` :math:`a` 8. Pop the frame from the stack. -9. Push :math:`\val^m` to the stack. +9. Push :math:`\val^n` to the stack. 10. :ref:`Invoke ` the function instance at address :math:`a`. .. math:: ~\\[-1ex] \begin{array}{lcl@{\qquad}l} - S; \FRAME_n\{F\}~B^*[\val^m~(\RETURNINVOKE~a)]~\END &\stepto& - \val^m~(\INVOKE~a) - & (\iff S.\SFUNCS[a].\FITYPE = [t_1^m] \to [t_2^n]) + S; \FRAME_m\{F\}~B^\ast[\val^n~(\RETURNINVOKE~a)]~\END &\stepto& + \val^n~(\INVOKE~a) + & (\iff S.\SFUNCS[a].\FITYPE = [t_1^n] \to [t_2^m]) \end{array} diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index dcbf64135d..0757a37b98 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -686,13 +686,13 @@ Control Instructions :math:`\RETURNCALL~x` ..................... -* The return type :math:`C.\CRETURN` must not be empty in the context. +* The return type :math:`C.\CRETURN` must not be absent in the context. * The function :math:`C.\CFUNCS[x]` must be defined in the context. * Let :math:`[t_1^\ast] \to [t_2^?]` be the :ref:`function type ` :math:`C.\CFUNCS[x]`. -* The :ref:`result type ` must be the same as :math:`C.\CRETURN`. +* The :ref:`result type ` :math:`[t_2^?]` must be the same as :math:`C.\CRETURN`. * Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. @@ -702,7 +702,7 @@ Control Instructions \qquad C.\CRETURN = [t_2^?] }{ - C \vdashinstr \CALL~x : [t_3^\ast~t_1^\ast] \to [t_4^\ast] + C \vdashinstr \RETURNCALL~x : [t_3^\ast~t_1^\ast] \to [t_4^\ast] } .. note:: @@ -726,6 +726,8 @@ Control Instructions * Let :math:`[t_1^\ast] \to [t_2^?]` be the :ref:`function type ` :math:`C.\CTYPES[x]`. +* The :ref:`result type ` :math:`[t_2^?]` must be the same as :math:`C.\CRETURN`. + * Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. @@ -737,7 +739,7 @@ Control Instructions \qquad C.\CRETURN = [t_2^?] }{ - C \vdashinstr \CALLINDIRECT~x : [t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast] + C \vdashinstr \RETURNCALLINDIRECT~x : [t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast] } .. note:: diff --git a/test/core/return_call.wast b/test/core/return_call.wast index f7bb8c4107..df75df403f 100644 --- a/test/core/return_call.wast +++ b/test/core/return_call.wast @@ -1,4 +1,4 @@ -;; Test `call` operator +;; Test `return_call` operator (module ;; Auxiliary definitions @@ -19,15 +19,15 @@ ;; Typing - (func (export "type-i32") (result i32) (call $const-i32)) - (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") (result i32) (return_call $const-i32)) + (func (export "type-i64") (result i64) (return_call $const-i64)) + (func (export "type-f32") (result f32) (return_call $const-f32)) + (func (export "type-f64") (result f64) (return_call $const-f64)) - (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))) - (func (export "type-first-f32") (result f32) (call $id-f32 (f32.const 1.32))) - (func (export "type-first-f64") (result f64) (call $id-f64 (f64.const 1.64))) + (func (export "type-first-i32") (result i32) (return_call $id-i32 (i32.const 32))) + (func (export "type-first-i64") (result i64) (return_call $id-i64 (i64.const 64))) + (func (export "type-first-f32") (result f32) (return_call $id-f32 (f32.const 1.32))) + (func (export "type-first-f64") (result f64) (return_call $id-f64 (f64.const 1.64))) (func (export "type-second-i32") (result i32) (return_call $f32-i32 (f32.const 32.1) (i32.const 32)) diff --git a/test/core/return_call_indirect.wast b/test/core/return_call_indirect.wast index 8f098c42ab..899a899b3b 100644 --- a/test/core/return_call_indirect.wast +++ b/test/core/return_call_indirect.wast @@ -1,4 +1,4 @@ -;; Test `call_indirect` operator +;; Test `return_call_indirect` operator (module ;; Auxiliary definitions From 125201ced9a0f158553d08d8a20b7152f3057367 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Sat, 12 May 2018 08:12:55 +0200 Subject: [PATCH 05/18] Link spec in README --- README.md | 4 +++- document/core/index.rst | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eaeab76816..5a91df62bd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ This repository is a clone of [github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/). It is meant for discussion, prototype specification and implementation of a proposal to add tail call support to WebAssembly. -See the [overview](proposals/tail-call/Overview.md) for a summary of the proposal. +* See the [overview](proposals/tail-call/Overview.md) for a summary of the proposal. + +* See the [modified spec](https://webassembly.github.io/tail-call/core/) for details. Original `README` from upstream repository follows... diff --git a/document/core/index.rst b/document/core/index.rst index f6dca0a227..155b79ef00 100644 --- a/document/core/index.rst +++ b/document/core/index.rst @@ -3,7 +3,7 @@ WebAssembly Specification .. only:: html - Release |release| (Draft, last updated |today|) + Release |release| + tail calls (Draft, last updated |today|) .. toctree:: :maxdepth: 3 From b3380e355cd756eb920bf07fbd6c214788bd4a3c Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 13 Dec 2019 09:40:34 +0100 Subject: [PATCH 06/18] Update Overview --- proposals/tail-call/Overview.md | 66 +++++++++++++++------------------ 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/proposals/tail-call/Overview.md b/proposals/tail-call/Overview.md index ef4dd9e92e..ec4b2521d6 100644 --- a/proposals/tail-call/Overview.md +++ b/proposals/tail-call/Overview.md @@ -27,13 +27,13 @@ This can be applied to any form of call, that is: ### Instructions -* Tail calls should be separate, explicit call instructions (current instructions explicitly disallow TCE) +* Tail calls are performed via separate, explicit call instructions (existing call instructions explicitly disallow TCE) -* Two possible schemes: - 1. introduce tail version of every call instruction - 2. introduce single prefix instruction that can be applied to every call instruction +* The proposal thus introduces a tail version of every call instruction -* Consideration: WebAssembly will likely get more call instructions in the future, e.g., `call_ref` +* An alternative scheme introducing a single instruction prefix applicable to every call instruction was considered but rejected by the CG + - considerations: WebAssembly will likely get a few more call instructions in the future, e.g., `call_ref` + - but instruction prefixes as modifiers re not used anywhere else in Wasm ### Execution @@ -44,21 +44,28 @@ This can be applied to any form of call, that is: * Only keeps the necessary call arguments +* Tail calls to host functions cannot guarantee tail behaviour (outside the scope of the spec) + ### Typing -* Because tail calls transfer control and unwind the stack they are stack-polymorphic: +* Typing rule for tail call instruction is derived by their nature of merging call and return + +* Because tail calls transfer control and unwind the stack they are stack-polymorphic -* Open question: distinguish tail-calls in function type? Possibilities: +* Previously open question: should tail calls induce different function types? Possibilities: 1. Distinguish tail-callees by type 2. Distinguish tail-callers by type 3. Both 4. Neither * Considerations: - - Option 1 (and 3) allows different calling conventions for non-tail-callable functions, which may be reduce constraints on ABIs. + - Option 1 (and 3) allows different calling conventions for non-tail-callable functions, which may reduce constraints on ABIs. - On the other hand, it creates a bifurcated function space, which can lead to difficulties e.g. when using function tables or other forms of dynamic indirection. - - Benefit of option 2 (and 3) unclear. + - Benefit of option 2 (and thus 3) unclear. + - Experimental validation revealed that there isn't a notable performance benefit to option 1 either. + +* CG resolution was to go with option 4 as the conceptually simplest. ## Examples @@ -66,19 +73,19 @@ This can be applied to any form of call, that is: A simple boring example of a tail-recursive factorial funciton. ``` (func $fac (param $x i64) (result i64) - (return_call $fac-aux (get_local $x) (i64.const 1)) + (return_call $fac-aux (get_local $x) (i64.const 1)) ) (func $fac-aux (param $x i64) (param $r i64) (result i64) - (if (i64.eqz (get_local $x)) - (then (return (get_local $r))) - (else - (return_call $fac-aux - (i64.sub (get_local $x) (i64.const 1)) - (i64.mul (get_local $x) (get_local $r)) - ) - ) - ) + (if (i64.eqz (get_local $x)) + (then (return (get_local $r))) + (else + (return_call $fac-aux + (i64.sub (get_local $x) (i64.const 1)) + (i64.mul (get_local $x) (get_local $r)) + ) + ) + ) ) ``` @@ -86,19 +93,15 @@ A simple boring example of a tail-recursive factorial funciton. ## Spec Changes -For now, we assume that separate instructions are introduced. -It is not difficult to adapt the rules to an alternative design with instruction prefixes. - -The details of possible typing refinements to distinguish tail-callers/callees are to be discussed and not yet included. - - ### Structure -Add two instructions (for now): +Add two instructions: * `return_call `, the tail-call version of `call` * `return_call_indirect `, the tail-call version of `call_indirect` +Other language extensions like [typed function refereces](https://github.com/WebAssembly/function-references/blob/master/proposals/function-references/Overview.md) that introduce new call instructions will also introduce tail versions of these new instructions. + ### Validation @@ -142,13 +145,4 @@ The text format is extended with two new instructions in the obvious manner. ## Open Questions -* Which instruction scheme should be picked? - -* Differentiate tail-callers or callees by type? - -* What about tail calls to host functions? - - treat as tail-calling a wrapper, use type distinction, or trap? - - note: cannot distinguish statically without type system support, e.g. with indirect calls - -* Instruction name bikeshedding - +* Can tail calls across module boundaries guarantee tail behaviour? From 5556e50c960bbe73aded5fd11a1d8b7c17fc8512 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 13 Dec 2019 09:41:33 +0100 Subject: [PATCH 07/18] Typo in Overview --- proposals/tail-call/Overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/tail-call/Overview.md b/proposals/tail-call/Overview.md index ec4b2521d6..81753693cc 100644 --- a/proposals/tail-call/Overview.md +++ b/proposals/tail-call/Overview.md @@ -32,8 +32,8 @@ This can be applied to any form of call, that is: * The proposal thus introduces a tail version of every call instruction * An alternative scheme introducing a single instruction prefix applicable to every call instruction was considered but rejected by the CG - - considerations: WebAssembly will likely get a few more call instructions in the future, e.g., `call_ref` - - but instruction prefixes as modifiers re not used anywhere else in Wasm + - WebAssembly will likely get a few more call instructions in the future, e.g., `call_ref` + - otoh, instruction prefixes as modifiers are not used anywhere else in Wasm ### Execution From 4041030768b764f2b6951289dc15b3e8be4eb5de Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 27 Apr 2020 14:22:44 +0200 Subject: [PATCH 08/18] Fix typo --- document/core/exec/instructions.rst | 2 +- interpreter/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index b13e80846a..5399076683 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -1131,7 +1131,7 @@ Tail-invocation of :ref:`function address ` :math:`a` 2. Let :math:`[t_1^n] \to [t_2^m]` be the :ref:`function type ` :math:`S.\SFUNCS[a].\FITYPE`. -3. Assert: due to :ref:`validation `, there are at least :math:`m` values on the top of the stack. +3. Assert: due to :ref:`validation `, there are at least :math:`n` values on the top of the stack. 4. Pop the results :math:`\val^n` from the stack. diff --git a/interpreter/Makefile b/interpreter/Makefile index 69a6b402bd..09f0756cc0 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -19,7 +19,7 @@ WINMAKE = winmake.bat DIRS = util syntax binary text valid runtime exec script host main LIBS = bigarray -FLAGS = -cflags '-w +a-4-27-42-44-45 -warn-error +a' +FLAGS = -cflags '-w +a-4-27-42-44-45 -warn-error +a-3' OCB = ocamlbuild $(FLAGS) $(DIRS:%=-I %) $(LIBS:%=-libs %) JS = # set to JS shell command to run JS tests From 5c4ff46370fd23b82f4347484ab3f18dadce1e40 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 11 Mar 2021 11:09:22 +0100 Subject: [PATCH 09/18] Set up Travis --- .travis.yml | 2 +- deploy_key.enc | Bin 3248 -> 3360 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 403e9dc254..d9a461f856 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,5 +34,5 @@ os: linux env: global: - - ENCRYPTION_LABEL: "304454be9d6c" + - ENCRYPTION_LABEL: "189e52c2c347" - COMMIT_AUTHOR_EMAIL: "noreply@webassembly.org" diff --git a/deploy_key.enc b/deploy_key.enc index b6d3e2f19771652a9711760b1712a77fe1b4e579..a27258e10f3300b078e53c2e98491e0f40a8fd88 100644 GIT binary patch literal 3360 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 literal 3248 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 From 9082bcdda808ce6ac58dce3a5e065f05faaa4e29 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 11 Mar 2021 11:42:55 +0100 Subject: [PATCH 10/18] Fix revision --- document/core/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/document/core/index.rst b/document/core/index.rst index 0b82480b22..2dbbe57049 100644 --- a/document/core/index.rst +++ b/document/core/index.rst @@ -4,7 +4,6 @@ WebAssembly Specification .. only:: html | Release |release| + tail calls (Draft, last updated |today|) - | Release |release| (Draft, |today|) | Editor: Andreas Rossberg From 122e4be181c1ea953f5ce9d1f2bafe40c96b7b19 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 11 Mar 2021 12:35:01 +0100 Subject: [PATCH 11/18] Add a simple test for multiple tables --- test/core/return_call_indirect.wast | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/core/return_call_indirect.wast b/test/core/return_call_indirect.wast index edd63661ae..acf0a72e06 100644 --- a/test/core/return_call_indirect.wast +++ b/test/core/return_call_indirect.wast @@ -144,6 +144,27 @@ ) ) + ;; Multiple tables + + (table $tab2 funcref (elem $tab-f1)) + (table $tab3 funcref (elem $tab-f2)) + + (func $tab-f1 (result i32) (i32.const 0x133)) + (func $tab-f2 (result i32) (i32.const 0x134)) + + (func (export "call-tab") (param $i i32) (result i32) + (if (i32.eq (local.get $i) (i32.const 0)) + (then (return_call_indirect (type $out-i32) (i32.const 0))) + ) + (if (i32.eq (local.get $i) (i32.const 1)) + (then (return_call_indirect 1 (type $out-i32) (i32.const 0))) + ) + (if (i32.eq (local.get $i) (i32.const 2)) + (then (return_call_indirect $tab3 (type $out-i32) (i32.const 0))) + ) + (i32.const 0) + ) + ;; Recursion (func $fac (export "fac") (type $over-i64) @@ -223,6 +244,10 @@ (assert_trap (invoke "dispatch-structural" (i32.const 11)) "indirect call type mismatch") (assert_trap (invoke "dispatch-structural" (i32.const 16)) "indirect call type mismatch") +(assert_return (invoke "call-tab" (i32.const 0)) (i32.const 0x132)) +(assert_return (invoke "call-tab" (i32.const 1)) (i32.const 0x133)) +(assert_return (invoke "call-tab" (i32.const 2)) (i32.const 0x134)) + (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)) From 9c0d7457c88d960975aa0d3a16dfee597659293a Mon Sep 17 00:00:00 2001 From: Thomas Lively <7121787+tlively@users.noreply.github.com> Date: Wed, 15 Dec 2021 07:44:45 -0800 Subject: [PATCH 12/18] Describe correct tail call behavior across modules Whether tail calls across module boundaries would guarantee tail call behavior was previously an open question, but @thibaudmichaud confirmed that they would guarantee tail call behavior in V8 in https://github.com/WebAssembly/tail-call/issues/15#issuecomment-994821853. --- proposals/tail-call/Overview.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/proposals/tail-call/Overview.md b/proposals/tail-call/Overview.md index 81753693cc..ce08d1abcf 100644 --- a/proposals/tail-call/Overview.md +++ b/proposals/tail-call/Overview.md @@ -46,6 +46,8 @@ This can be applied to any form of call, that is: * Tail calls to host functions cannot guarantee tail behaviour (outside the scope of the spec) +* Tail calls across WebAssembly module boundaries *do* guarantee tail behavior + ### Typing @@ -141,8 +143,3 @@ Use the reserved opcodes after existing call instructions, i.e.: ### Text Format The text format is extended with two new instructions in the obvious manner. - - -## Open Questions - -* Can tail calls across module boundaries guarantee tail behaviour? From a906a4d63bf29b8488bd161870e04dc9198dc797 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Tue, 15 Feb 2022 14:15:25 +0100 Subject: [PATCH 13/18] Eps --- document/core/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/core/conf.py b/document/core/conf.py index edbec9c787..ed128ff77d 100644 --- a/document/core/conf.py +++ b/document/core/conf.py @@ -69,7 +69,7 @@ repo = 'tail-call' # The name of the proposal it represents, if any -proposal = 'tail-call' +proposal = 'tail calls' # The draft version string (clear out for release cuts) draft = ' (Draft ' + date.today().strftime("%Y-%m-%d") + ')' From 7258eb959f892b3cc7d21d6d4bb2f034fc8c245b Mon Sep 17 00:00:00 2001 From: mnordine Date: Mon, 20 Jun 2022 08:23:33 -0300 Subject: [PATCH 14/18] Fix typo --- proposals/tail-call/Overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/tail-call/Overview.md b/proposals/tail-call/Overview.md index ce08d1abcf..b422654f04 100644 --- a/proposals/tail-call/Overview.md +++ b/proposals/tail-call/Overview.md @@ -102,7 +102,7 @@ Add two instructions: * `return_call `, the tail-call version of `call` * `return_call_indirect `, the tail-call version of `call_indirect` -Other language extensions like [typed function refereces](https://github.com/WebAssembly/function-references/blob/master/proposals/function-references/Overview.md) that introduce new call instructions will also introduce tail versions of these new instructions. +Other language extensions like [typed function references](https://github.com/WebAssembly/function-references/blob/master/proposals/function-references/Overview.md) that introduce new call instructions will also introduce tail versions of these new instructions. ### Validation From abcab5d37374323f06650cda84aad776212bee65 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 30 Jan 2023 21:08:20 +0100 Subject: [PATCH 15/18] Adjust for multi-return --- document/core/valid/instructions.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index 3cd3295466..5ef57ca1d6 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -1520,17 +1520,17 @@ Control Instructions * The function :math:`C.\CFUNCS[x]` must be defined in the context. -* Let :math:`[t_1^\ast] \to [t_2^?]` be the :ref:`function type ` :math:`C.\CFUNCS[x]`. +* Let :math:`[t_1^\ast] \to [t_2^\ast]` be the :ref:`function type ` :math:`C.\CFUNCS[x]`. -* The :ref:`result type ` :math:`[t_2^?]` must be the same as :math:`C.\CRETURN`. +* The :ref:`result type ` :math:`[t_2^\ast]` must be the same as :math:`C.\CRETURN`. * Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. .. math:: \frac{ - C.\CFUNCS[x] = [t_1^\ast] \to [t_2^?] + C.\CFUNCS[x] = [t_1^\ast] \to [t_2^\ast] \qquad - C.\CRETURN = [t_2^?] + C.\CRETURN = [t_2^\ast] }{ C \vdashinstr \RETURNCALL~x : [t_3^\ast~t_1^\ast] \to [t_4^\ast] } @@ -1554,9 +1554,9 @@ Control Instructions * The type :math:`C.\CTYPES[y]` must be defined in the context. -* Let :math:`[t_1^\ast] \to [t_2^?]` be the :ref:`function type ` :math:`C.\CTYPES[y]`. +* Let :math:`[t_1^\ast] \to [t_2^\ast]` be the :ref:`function type ` :math:`C.\CTYPES[y]`. -* The :ref:`result type ` :math:`[t_2^?]` must be the same as :math:`C.\CRETURN`. +* The :ref:`result type ` :math:`[t_2^\ast]` must be the same as :math:`C.\CRETURN`. * Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. @@ -1565,9 +1565,9 @@ Control Instructions \frac{ C.\CTABLES[x] = \limits~\FUNCREF \qquad - C.\CTYPES[y] = [t_1^\ast] \to [t_2^?] + C.\CTYPES[y] = [t_1^\ast] \to [t_2^\ast] \qquad - C.\CRETURN = [t_2^?] + C.\CRETURN = [t_2^\ast] }{ C \vdashinstr \RETURNCALLINDIRECT~x~y : [t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast] } From 1f02322258b248bf770aa4fc5ea966665eb2acc1 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 30 Jan 2023 21:26:10 +0100 Subject: [PATCH 16/18] Tweak reduction rules --- document/core/exec/instructions.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 3096760d54..75fea3e289 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -2848,7 +2848,7 @@ Control Instructions .. math:: \begin{array}{lcl@{\qquad}l} (\RETURNCALL~x) &\stepto& (\RETURNINVOKE~a) - & (\iff \CALL~x \stepto \INVOKE~a) + & (\iff (\CALL~x) \stepto (\INVOKE~a)) \end{array} @@ -2900,10 +2900,10 @@ Control Instructions .. math:: \begin{array}{lcl@{\qquad}l} - (\RETURNCALLINDIRECT~x) &\stepto& (\RETURNINVOKE~a) - & (\iff \CALLINDIRECT~x \stepto \INVOKE~a) \\ - (\RETURNCALLINDIRECT~x) &\stepto& \TRAP - & (\iff \CALLINDIRECT~x \stepto \TRAP) \\ + \val~(\RETURNCALLINDIRECT~x) &\stepto& (\RETURNINVOKE~a) + & (\iff \val~(\CALLINDIRECT~x) \stepto (\INVOKE~a)) \\ + \val~(\RETURNCALLINDIRECT~x) &\stepto& \TRAP + & (\iff \val~(\CALLINDIRECT~x) \stepto \TRAP) \\ \end{array} From 269f18b7d0ce330b809a4deaf658704c6e735419 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 1 Mar 2023 10:19:20 +0100 Subject: [PATCH 17/18] [ci] deactivate node run for now --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a158de1ee1..7cd34fbcd3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,9 @@ jobs: uses: actions/setup-node@v1 with: node-version: 19.x - - run: cd interpreter && opam exec make JS=node all + # TODO: disable node.js run until it fully implements proposal + # - run: cd interpreter && opam exec make JS=node all + - run: cd interpreter && opam exec make all ref-interpreter-js-library: runs-on: ubuntu-latest From 6f44ca27af411a0f6bc4e07520807d7adfc0de88 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 1 Mar 2023 15:30:12 +0100 Subject: [PATCH 18/18] Fix return_call_indirect for multible tables --- document/core/appendix/index-instructions.py | 2 +- document/core/exec/instructions.rst | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index ff3c7ea675..446b23d249 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -88,7 +88,7 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\CALL~x', r'\hex{10}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-call', r'exec-call'), Instruction(r'\CALLINDIRECT~x~y', r'\hex{11}', r'[t_1^\ast~\I32] \to [t_2^\ast]', r'valid-call_indirect', r'exec-call_indirect'), Instruction(r'\RETURNCALL~x', r'\hex{12}', r'[t_1^\ast] \to [t_2^\ast]', r'valid-return_call', r'exec-return_call'), - Instruction(r'\RETURNCALLINDIRECT~x', r'\hex{13}', r'[t_1^\ast~\I32] \to [t_2^\ast]', r'valid-return_call_indirect', r'exec-return_call_indirect'), + Instruction(r'\RETURNCALLINDIRECT~x~y', r'\hex{13}', r'[t_1^\ast~\I32] \to [t_2^\ast]', r'valid-return_call_indirect', r'exec-return_call_indirect'), Instruction(None, r'\hex{14}'), Instruction(None, r'\hex{15}'), Instruction(None, r'\hex{16}'), diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 44e1ff9c16..546cfca283 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -2854,22 +2854,22 @@ Control Instructions .. _exec-return_call_indirect: -:math:`\RETURNCALLINDIRECT~x` -............................. +:math:`\RETURNCALLINDIRECT~x~y` +............................... 1. Let :math:`F` be the :ref:`current ` :ref:`frame `. -2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITABLES[0]` exists. +2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITABLES[x]` exists. -3. Let :math:`\X{ta}` be the :ref:`table address ` :math:`F.\AMODULE.\MITABLES[0]`. +3. Let :math:`\X{ta}` be the :ref:`table address ` :math:`F.\AMODULE.\MITABLES[x]`. 4. Assert: due to :ref:`validation `, :math:`S.\STABLES[\X{ta}]` exists. 5. Let :math:`\X{tab}` be the :ref:`table instance ` :math:`S.\STABLES[\X{ta}]`. -6. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITYPES[x]` exists. +6. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITYPES[y]` exists. -7. Let :math:`\X{ft}_{\F{expect}}` be the :ref:`function type ` :math:`F.\AMODULE.\MITYPES[x]`. +7. Let :math:`\X{ft}_{\F{expect}}` be the :ref:`function type ` :math:`F.\AMODULE.\MITYPES[y]`. 8. Assert: due to :ref:`validation `, a value with :ref:`value type ` |I32| is on the top of the stack. @@ -2900,10 +2900,10 @@ Control Instructions .. math:: \begin{array}{lcl@{\qquad}l} - \val~(\RETURNCALLINDIRECT~x) &\stepto& (\RETURNINVOKE~a) - & (\iff \val~(\CALLINDIRECT~x) \stepto (\INVOKE~a)) \\ - \val~(\RETURNCALLINDIRECT~x) &\stepto& \TRAP - & (\iff \val~(\CALLINDIRECT~x) \stepto \TRAP) \\ + \val~(\RETURNCALLINDIRECT~x~y) &\stepto& (\RETURNINVOKE~a) + & (\iff \val~(\CALLINDIRECT~x~y) \stepto (\INVOKE~a)) \\ + \val~(\RETURNCALLINDIRECT~x~y) &\stepto& \TRAP + & (\iff \val~(\CALLINDIRECT~x~y) \stepto \TRAP) \\ \end{array}