Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

[spec] First go at specification #3

Merged
merged 8 commits into from
Apr 4, 2018
Merged

[spec] First go at specification #3

merged 8 commits into from
Apr 4, 2018

Conversation

rossberg
Copy link
Member

@rossberg rossberg commented Mar 7, 2018

First go at specifying the baseline proposal, analogous to #2:

  • add types anyref and eqref (and internal nullref) and respective subtyping (splitting valtype into numtype and reftype)
  • add instructions ref.null, ref.isnull, ref.eq, get_table, set_table
  • allow multiple tables
  • validation, execution, binary format and text format
  • adjust appendix

This change should already be complete, but opcodes are preliminary; names of table instruction might also be subject to change, depending on pending main spec PR.

Edit: Rendered version


7. Pop the value :math:`\I32.\CONST~i` from the stack.

8. If :math:`i` is larger than the length of :math:`\X{tab}.\TIELEM`, then:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "is not smaller than", right? This matches the behavior in call_indirect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done.


9. Pop the value :math:`\I32.\CONST~i` from the stack.

10. If :math:`i` is larger than the length of :math:`\X{tab}.\TIELEM`, then:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here w/ "not smaller"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


7. Let :math:`\X{ft}_{\F{expect}}` be the :ref:`function type <syntax-functype>` :math:`F.\AMODULE.\MITYPES[x]`.
7. Let :math:`\X{ft}_{\F{expect}}` be the :ref:`function type <syntax-functype>` :math:`F.\AMODULE.\MITYPES[y]`.

8. Assert: due to :ref:`validation <valid-call_indirect>`, a value with :ref:`value type <syntax-valtype>` |I32| is on the top of the stack.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't mark the proper line, but shouldn't "if tab.elem[i] is uninitialized" be changed to "is REFNULL"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Also needed to change the consecutive line, since the table no longer contains a function address directly. The semantics of elem segments required a similar tweak.

.. _syntax-val:

Values
~~~~~~

WebAssembly computations manipulate *values* of the four basic :ref:`value types <syntax-valtype>`: :ref:`integers <syntax-int>` and :ref:`floating-point data <syntax-float>` of 32 or 64 bit width each, respectively.
WebAssembly computations manipulate *values* of the four basic :ref:`value types <syntax-valtype>`: :ref:`integers <syntax-int>` and :ref:`floating-point data <syntax-float>` of 32 or 64 bit width each, or of references, respectively.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"respectively" sounds strange to me here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the wording.


References other than null are represented with additional :ref:`administrative instructions <syntax-instr-admin>`.
They either are *function references*, pointing to a specific :ref:`function address <syntax-funcaddr>`,
or *host references* pointing to an unintrpreted form of :ref:`host address <syntax-hostaddr>` that can be defined by the :ref:`embedder <embedder>`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sp: uninterpreted

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


* Let :math:`\limits~t` be the :ref:`table type <syntax-tabletype>` :math:`C.\CTABLES[x]`.

* Then the instruction is valid with type :math:`[t] \to []`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[\I32~t] \to []?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


* Let :math:`\limits~t` be the :ref:`table type <syntax-tabletype>` :math:`C.\CTABLES[x]`.

* Then the instruction is valid with type :math:`[] \to [t]`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[\I32] \to [t]?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Reference Types
~~~~~~~~~~~~~~~

*Reference types* classify
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove newlines

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this actually was a half-finished edit.

The type |ANYFUNC| denotes the infinite union of all references to :ref:`functions <syntax-func>`, regardless of their :ref:`function types <syntax-functype>`.

The type |EQREF| denotes the infinite union of all references that can be compared for equality;
in order to avoid exposing implementation details, some reference types, such as |ANYFUNC|, do not admit equality, and therefor are not :ref:`subtypes <match-reftype>` of |EQREF|.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sp: therefore

@@ -147,7 +147,7 @@ The |MTABLES| component of a module defines a vector of *tables* described by th
\{ \TTYPE~\tabletype \} \\
\end{array}

A table is a vector of opaque values of a particular table :ref:`element type <syntax-elemtype>`.
A table is a vector of opaque values of a particular :ref:`reference type <syntax-reftype>`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just so I understand, of the instructions that operate on a table:

  • All table types allow use of get_table, set_table.
  • A table of ANYFUNC allows call_indirect.
  • A table of EQREF allows ref.eq.
  • A table of ANYREF allows no other instructions.

Is that correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right, except that ref.eq does not operate on tables but on references directly.

Copy link
Member Author

@rossberg rossberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review!


7. Pop the value :math:`\I32.\CONST~i` from the stack.

8. If :math:`i` is larger than the length of :math:`\X{tab}.\TIELEM`, then:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done.


9. Pop the value :math:`\I32.\CONST~i` from the stack.

10. If :math:`i` is larger than the length of :math:`\X{tab}.\TIELEM`, then:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


7. Let :math:`\X{ft}_{\F{expect}}` be the :ref:`function type <syntax-functype>` :math:`F.\AMODULE.\MITYPES[x]`.
7. Let :math:`\X{ft}_{\F{expect}}` be the :ref:`function type <syntax-functype>` :math:`F.\AMODULE.\MITYPES[y]`.

8. Assert: due to :ref:`validation <valid-call_indirect>`, a value with :ref:`value type <syntax-valtype>` |I32| is on the top of the stack.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Also needed to change the consecutive line, since the table no longer contains a function address directly. The semantics of elem segments required a similar tweak.

.. _syntax-val:

Values
~~~~~~

WebAssembly computations manipulate *values* of the four basic :ref:`value types <syntax-valtype>`: :ref:`integers <syntax-int>` and :ref:`floating-point data <syntax-float>` of 32 or 64 bit width each, respectively.
WebAssembly computations manipulate *values* of the four basic :ref:`value types <syntax-valtype>`: :ref:`integers <syntax-int>` and :ref:`floating-point data <syntax-float>` of 32 or 64 bit width each, or of references, respectively.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the wording.


References other than null are represented with additional :ref:`administrative instructions <syntax-instr-admin>`.
They either are *function references*, pointing to a specific :ref:`function address <syntax-funcaddr>`,
or *host references* pointing to an unintrpreted form of :ref:`host address <syntax-hostaddr>` that can be defined by the :ref:`embedder <embedder>`.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -147,7 +147,7 @@ The |MTABLES| component of a module defines a vector of *tables* described by th
\{ \TTYPE~\tabletype \} \\
\end{array}

A table is a vector of opaque values of a particular table :ref:`element type <syntax-elemtype>`.
A table is a vector of opaque values of a particular :ref:`reference type <syntax-reftype>`.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right, except that ref.eq does not operate on tables but on references directly.

Reference Types
~~~~~~~~~~~~~~~

*Reference types* classify
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this actually was a half-finished edit.


* Let :math:`\limits~t` be the :ref:`table type <syntax-tabletype>` :math:`C.\CTABLES[x]`.

* Then the instruction is valid with type :math:`[] \to [t]`.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


* Let :math:`\limits~t` be the :ref:`table type <syntax-tabletype>` :math:`C.\CTABLES[x]`.

* Then the instruction is valid with type :math:`[t] \to []`.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_0^\ast~t^\ast]
C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_0^\ast~{t'}^\ast]
\qquad
(\vdashvaltypematch t' \leq t)^\ast
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced it with a hyperlinked version.


type val_type = I32 | I64 | F32 | F64 | Anyref | Anyfunc | Eqref | Nullref

func is_ref(t : valtype) : bool =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: val_type (function parameter)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


.. math::
\begin{array}{llclll}
\production{element segment} & \Telem_I &::=&
\text{(}~\text{elem}~~(x{:}\Ttableidx_I)^?~~\text{(}~\text{offset}~~e{:}\Texpr_I~\text{)}~~y^\ast{:}\Tvec(\Tfuncidx_I)~\text{)} \\ &&& \qquad
\Rightarrow\quad \{ \ETABLE~x', \EOFFSET~e, \EINIT~y^\ast \} \\
&&& \qquad\qquad\qquad (\iff x' = x^? \neq \epsilon \vee x' = 0) \\
Copy link
Member

@lukewagner lukewagner Mar 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to uplift this change to the v.1 spec as an editorial fix. The new way is much cleaner and I'm not even sure if the original expression parses...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will do.

@@ -70,7 +81,7 @@ However, these variables are not manipulated directly by the main checking funct
let actual = pop_opd()
if (actual = Unknown) return expect
if (expect = Unknown) return actual
error_if(actual =/= expect)
error_if(not matches(actual, expect))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, according to the validation rules, (select (ref.null) (get_table ...) (...)) is supposed to validate, since subtyping is applied to ref.null when validating select with t = anyref. To implement this, I think select now needs special treatment below; I expect taking a meet of the two pop_opd()s.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! This is not just for nullref, subtyping generally changes the way select needs to be handled. To simplify things, I refactored the pseudo code somewhat to make Unknown act more like a bottom type (which has been your view all along, I think :) ).

While fixing this I realised that subtyping also affects how br_table needs to be handled, so I incorporated respective changes as well.


The type |ANYFUNC| denotes the infinite union of all references to :ref:`functions <syntax-func>`, regardless of their :ref:`function types <syntax-functype>`.

The type |EQREF| denotes the infinite union of all references that can be compared for equality;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bikeshed: for symmetry with the other any*, could this perhaps be anyeqref?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


The type |NULLREF| only contains a single value: the :ref:`null <syntax-ref_null>` reference.
It is a :ref:`subtype <match-reftype>` of all other reference types.
The |NULLREF| type cannot be used in a program, it only occurs during :ref:`validation <valid>`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to see how this is the case: as is, it appears that nullref is a valtype which means it can appear in function signatures and as the type of locals. So you can have silly expressions like:

(local $x nulltype)
(call $foo (tee_local $x (get_local $x)))

Instead of trying to introduce a validation-only type, can't we just have an operator reftype.null where reftype is later constrained by validation to only allow nullable types?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is prevented simply by not making nullref part of the "concrete" syntax, i.e., it cannot be expressed in either the binary or the text format. That could be made more explicit by separating it out from value types into a separate class used only for validation, but is it worth it? Alternatively, would a respective note suffice?

A syntax like <reftype>.null cannot nicely be generalised to less primitive reference types. And it seems a bit wasteful to have separate null instructions for each type constructor, especially when they all necessarily produce the same value per subtyping.

We could type-annotate the instruction instead, but that also just seems unnecessary given that we're already buying into subtyping anyway. What the proposed semantics does mirrors the treatment of null expressions in most languages, e.g. Java or C++.

This seems like a point worth discussing at the April meeting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah hah, I hadn't noticed nullref wasn't part of concrete syntax! Yes, that does solve the problem rather elegantly in the spec. In the implementation, though, what will still be annoying is that we'll probably need separate valtype enums for what can appear in locals/signatures (the latter of which takes part in serialization and indirect call ABI matching so we need to be crystal clear about what the cases are) and for what can appear as part of validation. And we'll have to go back and forth between them and ask "which valtype enum do I use here?" and it's not a huge deal, but it'd be nice to avoid if there's not a downside.

And it seems a bit wasteful to have separate null instructions for each type constructor

For this I was assuming a single bytecode "null" followed by the reftype as an immediate (which handles both the primitive and compound types in the encoding)...

We could type-annotate the instruction instead, but that also just seems unnecessary given that we're already buying into subtyping anyway.

... which is what I think you mean here. Agreed we are necessarily buying into subtyping, the only difference here is that the other types will be present in the concrete syntax and so there's not really another option (other than, I suppose, removing subtyping in lieu of a static upcast operator). With null, it seems like we have an option and the non-subtype route seems mildly simpler.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a point worth discussing at the April meeting.

Yeah, happy to discuss in the group. In the short-term, we were wanting to get going on a prototype impl of this proposal and, because of aforementioned valtype warts, we'd prefer to implement the type-parameterized null. Could you perhaps flip the proposal to that so we can directly implement the proposal (knowing we might have to change if the proposal flips back)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's a few hours work on tests and interpreter, too, that I probably want to postpone until it's clear that I won't have to undo it shortly after. But I'm happy to hold back landing these PRs until the meeting. I don’t think that needs to block you from prototyping either version.

Copy link
Member Author

@rossberg rossberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review!

@@ -70,7 +81,7 @@ However, these variables are not manipulated directly by the main checking funct
let actual = pop_opd()
if (actual = Unknown) return expect
if (expect = Unknown) return actual
error_if(actual =/= expect)
error_if(not matches(actual, expect))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch! This is not just for nullref, subtyping generally changes the way select needs to be handled. To simplify things, I refactored the pseudo code somewhat to make Unknown act more like a bottom type (which has been your view all along, I think :) ).

While fixing this I realised that subtyping also affects how br_table needs to be handled, so I incorporated respective changes as well.


The type |ANYFUNC| denotes the infinite union of all references to :ref:`functions <syntax-func>`, regardless of their :ref:`function types <syntax-functype>`.

The type |EQREF| denotes the infinite union of all references that can be compared for equality;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


.. math::
\begin{array}{llclll}
\production{element segment} & \Telem_I &::=&
\text{(}~\text{elem}~~(x{:}\Ttableidx_I)^?~~\text{(}~\text{offset}~~e{:}\Texpr_I~\text{)}~~y^\ast{:}\Tvec(\Tfuncidx_I)~\text{)} \\ &&& \qquad
\Rightarrow\quad \{ \ETABLE~x', \EOFFSET~e, \EINIT~y^\ast \} \\
&&& \qquad\qquad\qquad (\iff x' = x^? \neq \epsilon \vee x' = 0) \\
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will do.


The type |NULLREF| only contains a single value: the :ref:`null <syntax-ref_null>` reference.
It is a :ref:`subtype <match-reftype>` of all other reference types.
The |NULLREF| type cannot be used in a program, it only occurs during :ref:`validation <valid>`.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is prevented simply by not making nullref part of the "concrete" syntax, i.e., it cannot be expressed in either the binary or the text format. That could be made more explicit by separating it out from value types into a separate class used only for validation, but is it worth it? Alternatively, would a respective note suffice?

A syntax like <reftype>.null cannot nicely be generalised to less primitive reference types. And it seems a bit wasteful to have separate null instructions for each type constructor, especially when they all necessarily produce the same value per subtyping.

We could type-annotate the instruction instead, but that also just seems unnecessary given that we're already buying into subtyping anyway. What the proposed semantics does mirrors the treatment of null expressions in most languages, e.g. Java or C++.

This seems like a point worth discussing at the April meeting.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants