Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge multi-value proposal into spec #1145

Merged
merged 1 commit into from
Apr 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 32 additions & 25 deletions document/core/appendix/algorithm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,16 @@ the latter surrounding :ref:`structured control instructions <syntax-instr-contr
type ctrl_stack = stack(ctrl_frame)
type ctrl_frame = {
label_types : list(val_type)
opcode : opcode
start_types : list(val_type)
end_types : list(val_type)
height : nat
unreachable : bool
}
For each value, the operand stack records its :ref:`value type <syntax-valtype>`, or :code:`Unknown` when the type is not known.

For each entered block, the control stack records a *control frame* with the type of the associated :ref:`label <syntax-label>` (used to type-check branches), the result type of the block (used to check its result), the height of the operand stack at the start of the block (used to check that operands do not underflow the current block), and a flag recording whether the remainder of the block is unreachable (used to handle :ref:`stack-polymorphic <polymorphism>` typing after branches).

.. note::
In the presentation of this algorithm, multiple values are supported for the :ref:`result types <syntax-resulttype>` classifying blocks and labels.
With the current version of WebAssembly, the :code:`list` could be simplified to an optional value.
For each entered block, the control stack records a *control frame* with the originating opcode, the types on the top of the operand stack at the start and end of the block (used to check its result as well as branches), the height of the operand stack at the start of the block (used to check that operands do not underflow the current block), and a flag recording whether the remainder of the block is unreachable (used to handle :ref:`stack-polymorphic <polymorphism>` typing after branches).

For the purpose of presenting the algorithm, the operand and control stacks are simply maintained as global variables:

Expand Down Expand Up @@ -98,17 +95,21 @@ The control stack is likewise manipulated through auxiliary functions:

.. code-block:: pseudo
func push_ctrl(label : list(val_type), out : list(val_type)) =
 let frame = ctrl_frame(label, out, opds.size(), false)
func push_ctrl(opcode : opcode, in : list(val_type), out : list(val_type)) =
 let frame = ctrl_frame(opcode, in, out, opds.size(), false)
  ctrls.push(frame)
push_opds(in)
func pop_ctrl() : list(val_type) =
func pop_ctrl() : ctrl_frame =
 error_if(ctrls.is_empty())
 let frame = ctrls[0]
  pop_opds(frame.end_types)
  error_if(opds.size() =/= frame.height)
ctrls.pop()
  return frame.end_types
  return frame
func label_types(frame : ctrl_frame) : list(val_types) =
return (if frame.opcode == loop then frame.start_types else frame.end_types)
func unreachable() =
  opds.resize(ctrls[0].height)
Expand All @@ -121,6 +122,8 @@ Popping a frame first checks that the control stack is not empty.
It then verifies that the operand stack contains the right types of values expected at the end of the exited block and pops them off the operand stack.
Afterwards, it checks that the stack has shrunk back to its initial height.

The type of the :ref:`label <syntax-label>` associated with a control frame is either that of the stack at the start or the end of the frame, determined by the opcode that it originates from.

Finally, the current frame can be marked as unreachable.
In that case, all existing operand types are purged from the operand stack, in order to allow for the :ref:`stack-polymorphism <polymorphism>` logic in :code:`pop_opd` to take effect.

Expand Down Expand Up @@ -163,41 +166,45 @@ Other instructions are checked in a similar manner.
   case (unreachable)
      unreachable()
case (block t*)
push_ctrl([t*], [t*])
case (block t1*->t2*)
pop_opds([t1*])
push_ctrl(block, [t1*], [t2*])
case (loop t*)
push_ctrl([], [t*])
case (loop t1*->t2*)
pop_opds([t1*])
push_ctrl(loop, [t1*], [t2*])
case (if t*)
case (if t1*->t2*)
pop_opd(I32)
push_ctrl([t*], [t*])
pop_opds([t1*])
push_ctrl(if, [t1*], [t2*])
case (end)
let results = pop_ctrl()
push_opds(results)
let frame = pop_ctrl()
push_opds(frame.end_types)
case (else)
let results = pop_ctrl()
push_ctrl(results, results)
let frame = pop_ctrl()
error_if(frame.opcode =/= if)
push_ctrl(else, frame.start_types, frame.end_types)
case (br n)
     error_if(ctrls.size() < n)
      pop_opds(ctrls[n].label_types)
      pop_opds(label_types(ctrls[n]))
      unreachable()
case (br_if n)
     error_if(ctrls.size() < n)
pop_opd(I32)
      pop_opds(ctrls[n].label_types)
      push_opds(ctrls[n].label_types)
      pop_opds(label_types(ctrls[n]))
      push_opds(label_types(ctrls[n]))
   case (br_table n* m)
      error_if(ctrls.size() < m)
      foreach (n in n*)
        error_if(ctrls.size() < n || ctrls[n].label_types =/= ctrls[m].label_types)
        error_if(ctrls.size() < n || label_types(ctrls[n]) =/= label_types(ctrls[m]))
pop_opd(I32)
      pop_opds(ctrls[m].label_types)
      pop_opds(label_types(ctrls[m]))
      unreachable()
.. note::
Expand Down
2 changes: 2 additions & 0 deletions document/core/appendix/implementation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ An implementation may impose restrictions on the following dimensions of a modul
* the number of :ref:`exports <syntax-export>` from a :ref:`module <syntax-module>`
* the number of parameters in a :ref:`function type <syntax-functype>`
* the number of results in a :ref:`function type <syntax-functype>`
* the number of parameters in a :ref:`block type <syntax-blocktype>`
* the number of results in a :ref:`block type <syntax-blocktype>`
* the number of :ref:`locals <syntax-local>` in a :ref:`function <syntax-func>`
* the size of a :ref:`function <syntax-func>` body
* the size of a :ref:`structured control instruction <syntax-instr-control>`
Expand Down
Loading