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

[spec/interpreter/test] Add table bulk instructions missing from bulk op proposal #35

Merged
merged 7 commits into from
Apr 3, 2019

Conversation

rossberg
Copy link
Member

@rossberg rossberg commented Apr 2, 2019

Adds table.size, table.grow, table.fill to overview, spec, interpreter, and tests, as decided at recent CG meeting. Also adds a few more tests for table.get and table.set.

Edit: Also, change interpreter's segment encoding to match bulk ops proposal, addressing #18. (Not updated in spec yet, since corresponding spec text is still missing from bulk ops proposal.)

@rossberg rossberg requested a review from lars-t-hansen April 2, 2019 08:40
- `table.grow $x : [t i32] -> [i32]`
- iff `$x : table t`
- the first operand of `table.grow` is an initialisation value (for compatibility with future extensions to the type system, such as non-nullable references)

Copy link
Contributor

Choose a reason for hiding this comment

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

Any particular justification for the argument ordering for table.grow? I ask, since we chose the other ordering (init value on top of stack, delta underneath). I assume what you have here is compatible with v8?

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, I tried to follow the convention of the other bulk ops, which always have argument order (dest, source, length). In particular, this would most closely match table.fill, except for omitting the start index.
(AFAIAA, V8 does not implement these instructions yet.)

Copy link
Contributor

Choose a reason for hiding this comment

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

And table.fill follows memory.fill, fair enough. I'll file a bug to fix it on our end.

Though it's a bit odd that FF would be shipping features for which we don't even have a spec draft?

Well, shipping in Nightly only and it won't ride the trains until we're all aligned, but apart from this wrinkle I think we've anticipated the draft fairly well :)

(match op s with
| 0x0f -> table_fill (at var s)
| 0x10 -> table_size (at var s)
| 0x11 -> table_grow (at var s)
Copy link
Contributor

Choose a reason for hiding this comment

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

These encodings contradict the table in #29 (first and last comments), which we have used as the basis for the decoder in Firefox. If possible I'd like these to change here since we're shipping already. Proposed encodings there are table_grow == 0x0f, table_size == 0x10, table_fill == 0x11. Ditto the encoder and any other spots.

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, sorry, I completely forgot about #29. Changed. (Though it's a bit odd that FF would be shipping features for which we don't even have a spec draft?)

| TableGet x -> op 0x25; var x
| TableSet x -> op 0x26; var x
| TableSize x -> op 0xfc; op 0x10; var x
| TableGrow x -> op 0xfc; op 0x11; var x
| TableFill x -> op 0xfc; op 0x0f; var x
Copy link
Contributor

Choose a reason for hiding this comment

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

See comments in decode.ml re the encodings here.

val type_of : table -> table_type
val size : table -> size
val grow : table -> size -> unit (* raises SizeOverflow, SizeLimit *)
val grow : table -> size -> ref_ -> unit (* raises SizeOverflow, SizeLimit *)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: add OutOfMemory to the comment 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.

Done.


val alloc : table_type -> table
val alloc : table_type -> ref_ -> table
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: add comment that this can raise OutOfMemory?

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.


(assert_trap
(invoke "fill" (i32.const 8) (ref.host 6) (i32.const 3))
"out of bounds"
Copy link
Contributor

@lars-t-hansen lars-t-hansen Apr 3, 2019

Choose a reason for hiding this comment

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

In this case we also want to check that there was initialization of the elements up to the bound.

Copy link
Member Author

Choose a reason for hiding this comment

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

Isn't that what the next 3 asserts are doing, or did you mean something else?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, my oversight - eyes glazing over...


.. math::
\begin{array}{llclll}
\production{instruction} & \Binstr &::=& \dots \\ &&|&
\hex{25}~~x{:}\Btableidx &\Rightarrow& \TABLEGET~x \\ &&|&
\hex{26}~~x{:}\Btableidx &\Rightarrow& \TABLESET~x \\
\hex{26}~~x{:}\Btableidx &\Rightarrow& \TABLESET~x \\ &&|&
\hex{FC}~\hex{0F}~~x{:}\Btableidx &\Rightarrow& \TABLEFILL~x \\ &&|&
Copy link
Contributor

Choose a reason for hiding this comment

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

See notes later about encodings here.

7. Assert: due to :ref:`validation <valid-table.grow>`, a value of :ref:`value type <syntax-valtype>` |I32| is on the top of the stack.

8. Pop the value :math:`\I32.\CONST~n` from the stack.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we're missing any handling of the initializer argument 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.

Good catch, fixed. That meant also adjusting the meta function and the embedding interface accordingly to support an initialisation value.


5. Let :math:`\X{tab}` be the :ref:`table instance <syntax-tableinst>` :math:`S.\STABLES[a]`.

6. Assert: due to :ref:`validation <valid-table.fill>`, a value of :ref:`value type <syntax-valtype>` |I32| is on the top of the stack.
Copy link
Contributor

Choose a reason for hiding this comment

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

Technically this is possibly a little dodgy (ditto subsequent clauses, ditto clauses for TABLESET): since we're directly manipulating the stack in the recursive call below, asserting that this is true as a consequence of validation is questionable. I'm not sure it's worth fixing though.

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, fair point. I think there are actually a few other such assertions already where, strictly speaking, having the right value on the stack is rather a consequence of soundness.

Copy link
Contributor

@lars-t-hansen lars-t-hansen left a comment

Choose a reason for hiding this comment

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

Looks good in general, thanks for doing this. I'd like to see the opcode encodings changed to values previously agreed upon in #29. I'd like to have a discussion about argument ordering for table.grow, as Firefox is now in conflict with this spec.

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.

Comments addresses. Also applied some minor adaptation to JS API.

See comment below regarding operand order for table.grow.

(match op s with
| 0x0f -> table_fill (at var s)
| 0x10 -> table_size (at var s)
| 0x11 -> table_grow (at var 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, sorry, I completely forgot about #29. Changed. (Though it's a bit odd that FF would be shipping features for which we don't even have a spec draft?)


val alloc : table_type -> table
val alloc : table_type -> ref_ -> table
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.

val type_of : table -> table_type
val size : table -> size
val grow : table -> size -> unit (* raises SizeOverflow, SizeLimit *)
val grow : table -> size -> ref_ -> unit (* raises SizeOverflow, SizeLimit *)
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.

- `table.grow $x : [t i32] -> [i32]`
- iff `$x : table t`
- the first operand of `table.grow` is an initialisation value (for compatibility with future extensions to the type system, such as non-nullable references)

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, I tried to follow the convention of the other bulk ops, which always have argument order (dest, source, length). In particular, this would most closely match table.fill, except for omitting the start index.
(AFAIAA, V8 does not implement these instructions yet.)


(assert_trap
(invoke "fill" (i32.const 8) (ref.host 6) (i32.const 3))
"out of bounds"
Copy link
Member Author

Choose a reason for hiding this comment

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

Isn't that what the next 3 asserts are doing, or did you mean something else?

7. Assert: due to :ref:`validation <valid-table.grow>`, a value of :ref:`value type <syntax-valtype>` |I32| is on the top of the stack.

8. Pop the value :math:`\I32.\CONST~n` from 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.

Good catch, fixed. That meant also adjusting the meta function and the embedding interface accordingly to support an initialisation value.


5. Let :math:`\X{tab}` be the :ref:`table instance <syntax-tableinst>` :math:`S.\STABLES[a]`.

6. Assert: due to :ref:`validation <valid-table.fill>`, a value of :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.

Yes, fair point. I think there are actually a few other such assertions already where, strictly speaking, having the right value on the stack is rather a consequence of soundness.

@rossberg
Copy link
Member Author

rossberg commented Apr 3, 2019

Thanks for reviewing!

@rossberg rossberg merged commit c3d5cbc into master Apr 3, 2019
rossberg pushed a commit that referenced this pull request Nov 20, 2019
This commit fixes #34 by specifying that the flags field (which
indicates if a segment is passive) is a `varuint32` instead of a
`uint8`. It was discovered in #34 that the memory index located at that
position today is a `varuint32`, which can be validly encoded as `0x80
0x00` in addition to `0x00` (in addition to a number of other
encodings). This means that if the first field were repurposed as a
single byte of flags, it would break these existing modules that work
today.

It's not currently known how many modules in the wild actually take
advantage of such an encoding, but it's probably better to be safe than
sorry!

Closes #34
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.

2 participants