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

Spec abstract syntax for GC types and instructions #373

Merged
merged 2 commits into from
May 11, 2023
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
110 changes: 106 additions & 4 deletions document/core/syntax/instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -415,12 +415,15 @@ Occasionally, it is convenient to group operators together according to the foll
\end{array}


.. index:: ! reference instruction, reference, null
.. index:: ! reference instruction, reference, null, cast, heap type, reference type
pair: abstract syntax; instruction
.. _syntax-ref.null:
.. _syntax-ref.func:
.. _syntax-ref.is_null:
.. _syntax-ref.as_non_null:
.. _syntax-ref.eq:
.. _syntax-ref.test:
.. _syntax-ref.cast:
.. _syntax-instr-ref:

Reference Instructions
Expand All @@ -435,11 +438,102 @@ Instructions in this group are concerned with accessing :ref:`references <syntax
\REFNULL~\heaptype \\&&|&
\REFFUNC~\funcidx \\&&|&
\REFISNULL \\&&|&
\REFASNONNULL \\
\REFASNONNULL \\&&|&
\REFEQ \\&&|&
\REFTEST~\reftype \\&&|&
\REFCAST~\reftype \\
\end{array}

The first three of these instruction produce a :ref:`null <syntax-null>` value, produce a reference to a given function, or check for a null value, respectively.
The |REFASNONNULL| casts a :ref:`nullable <syntax-reftype>` to a non-null one, and :ref:`traps <trap>` if it encounters null.
The |REFNULL| and |REFFUNC| instructions produce a :ref:`null <syntax-null>` value or a reference to a given function, respectively.

The instruction |REFISNULL| checks for null,
while |REFASNONNULL| converts a :ref:`nullable <syntax-reftype>` to a non-null one, and :ref:`traps <trap>` if it encounters null.

The |REFEQ| compares two references.

The instructions |REFTEST| and |REFCAST| test the :ref:`dynamic type <syntax-type-dyn>` of a reference operand.
The former merely returns the result of the test,
while the latter performs a downcast and :ref:`traps <trap>` if the operand's type does not match.

.. note::
The |BR_ON_CAST| and |BR_ON_CAST_FAIL| instructions provides versions of the latter that branch depending on the success of the downcast instead of trapping.


.. index:: reference instruction, reference, null, heap type, reference type
pair: abstract syntax; instruction

.. _syntax-struct.new:
.. _syntax-struct.new_default:
.. _syntax-struct.get:
.. _syntax-struct.get_s:
.. _syntax-struct.get_u:
.. _syntax-struct.set:
.. _syntax-array.new:
.. _syntax-array.new_default:
.. _syntax-array.new_fixed:
.. _syntax-array.new_data:
.. _syntax-array.new_elem:
.. _syntax-array.get:
.. _syntax-array.get_s:
.. _syntax-array.get_u:
.. _syntax-array.set:
.. _syntax-array.len:
.. _syntax-i31.new:
.. _syntax-i31.get_s:
.. _syntax-i31.get_u:
.. _syntax-extern.internalize:
.. _syntax-extern.externalize:
.. _syntax-instr-struct:
.. _syntax-instr-array:
.. _syntax-instr-i31:
.. _syntax-instr-extern:

Aggregate Instructions
~~~~~~~~~~~~~~~~~~~~~~

Instructions in this group are concerned with creating and accessing :ref:`references <syntax-reftype>` to :ref:`aggregate <syntax-type-aggregate>` types.

.. math::
\begin{array}{llcl}
\production{instruction} & \instr &::=&
\dots \\&&|&
\STRUCTNEW~\typeidx \\&&|&
\STRUCTNEWDEFAULT~\typeidx \\&&|&
\STRUCTGET~\typeidx~\u32 \\&&|&
\STRUCTGETS~\typeidx~\u32 \\&&|&
\STRUCTGETU~\typeidx~\u32 \\&&|&
\STRUCTSET~\typeidx~\u32 \\&&|&
\ARRAYNEW~\typeidx \\&&|&
\ARRAYNEWFIXED~\typeidx~\u32 \\&&|&
\ARRAYNEWDEFAULT~\typeidx \\&&|&
\ARRAYNEWDATA~\typeidx~\dataidx \\&&|&
\ARRAYNEWELEM~\typeidx~\elemidx \\&&|&
\ARRAYGET~\typeidx \\&&|&
\ARRAYGETS~\typeidx \\&&|&
\ARRAYGETU~\typeidx \\&&|&
\ARRAYSET~\typeidx \\&&|&
\ARRAYLEN \\&&|&
\I31NEW \\&&|&
\I31GETS \\&&|&
\I31GETU \\&&|&
\EXTERNINTERNALIZE \\&&|&
\EXTERNEXTERNALIZE \\
\end{array}

The instructions |STRUCTNEW| and |STRUCTNEWDEFAULT| allocate a new :ref:`structure <syntax-type-struct>`, initializing them either with operands or with default values.
The remaining instructions on structs access individual fields,
allowing for different sign extension modes in the case of :ref:`packed <syntax-type-packed>` storage types.

Similarly, :ref:`arrays <syntax-type-array>` can be allocated either with an explicit initialization operand or a default value.
Furthermore, |ARRAYNEWFIXED| allocates an array with statically fixed size,
and |ARRAYNEWDATA| and |ARRAYNEWELEM| allocate an array and initialize it from a :ref:`data <syntax-data>` or :ref:`element <syntax-elem>` segement, respectively.
The remaining array instructions access individual slots,
again allowing for different sign extension modes in the case of a :ref:`packed <syntax-type-packed>` storage type.
Last, |ARRAYLEN| produces the length of an array.

The instructions |I31NEW|, |I31GETS|, and |I31GETU| convert between type |I31| and an unboxed :ref:`scalar <syntax-i31>`.

The instructions |EXTERNINTERNALIZE| and |EXTERNEXTERNALIZE| allow lossless conversion between references represented as type :math:`(\REF~\NULL~\EXTERN)`| and as :math:`(\REF~\NULL~\ANY)`.


.. index:: ! parametric instruction, value type
Expand Down Expand Up @@ -627,6 +721,10 @@ The |DATADROP| instruction prevents further use of a passive data segment. This
.. _syntax-br:
.. _syntax-br_if:
.. _syntax-br_table:
.. _syntax-br_on_null:
.. _syntax-br_on_non_null:
.. _syntax-br_on_cast:
.. _syntax-br_on_cast_fail:
.. _syntax-return:
.. _syntax-call:
.. _syntax-call_indirect:
Expand Down Expand Up @@ -654,6 +752,8 @@ Instructions in this group affect the flow of control.
\BRTABLE~\vec(\labelidx)~\labelidx \\&&|&
\BRONNULL~\labelidx \\&&|&
\BRONNONNULL~\labelidx \\&&|&
\BRONCAST~\labelidx~\reftype~\reftype \\&&|&
\BRONCASTFAIL~\labelidx~\reftype~\reftype \\&&|&
\RETURN \\&&|&
\CALL~\funcidx \\&&|&
\CALLREF~\typeidx \\&&|&
Expand Down Expand Up @@ -697,6 +797,8 @@ Branch instructions come in several flavors:
|BRIF| performs a conditional branch,
and |BRTABLE| performs an indirect branch through an operand indexing into the label vector that is an immediate to the instruction, or to a default target if the operand is out of bounds.
The |BRONNULL| and |BRONNONNULL| instructions check whether a reference operand is :ref:`null <syntax-null>` and branch if that is the case or not the case, respectively.
Similarly, |BRONCAST| and |BRONCASTFAIL| attempt a downcast on a reference operand and branch if that succeeds, or fails, respectively.

The |RETURN| instruction is a shortcut for an unconditional branch to the outermost block, which implicitly is the body of the current function.
Taking a branch *unwinds* the operand stack up to the height where the targeted structured control instruction was entered.
However, branches may additionally consume operands themselves, which they push back on the operand stack after unwinding.
Expand Down
65 changes: 56 additions & 9 deletions document/core/syntax/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,39 +73,84 @@ Conventions



.. index:: ! heap type, store, type identifier, ! substitution, ! closed type
.. index:: ! heap type, store, type identifier, ! substitution, ! closed type, ! abstract type, ! concrete type, ! unboxed scalar
pair: abstract syntax; heap type
.. _type-i31:
.. _type-subst:
.. _type-closed:
.. _type-abstract:
.. _type-concrete:
.. _syntax-heaptype:

Heap Types
~~~~~~~~~~

*Heap types* classify objects in the runtime :ref:`store <store>`.
There are three disjoint hierarchies of heap types:

- *function types* classify :ref:`functions <syntax-func>`,
- *aggregate types* classify dynamically allocated *managed* data, such as *structures*, *arrays*, or *unboxed scalars*,
- *external types* classify *external* references possibly owned by the :ref:`embedder <embedder>`.

The values from the latter two hierarchies are interconvertible by ways of the |EXTERNINTERNALIZE| and |EXTERNINTERNALIZE| instructions.
That is, both type hierarchies are inhabited by an isomorphic set of values, but may have different, incompatible representations in practice.

.. math::
\begin{array}{llll}
\production{heap type} & \heaptype &::=&
\FUNC ~|~ \EXTERN ~|~ \typeidx ~|~ \functype ~|~ \BOT \\
\FUNC ~|~ \NOFUNC \\&&|&
\EXTERN ~|~ \NOEXTERN \\&&|&
\ANY ~|~ \EQ ~|~ \I31 ~|~ \STRUCT ~|~ \ARRAY ~|~ \NONE \\&&|&
\typeidx ~|~ \deftype ~|~ \BOT \\
\end{array}

The type |FUNC| denotes the infinite union of all types of :ref:`functions <syntax-func>`, regardless of their concrete :ref:`function types <syntax-functype>`.
A heap type is either *abstract* or *concrete*.

The abstract type |FUNC| denotes the common supertype of all :ref:`function types <syntax-functype>`, regardless of their concrete definition.
Dually, the type |NOFUNC| denotes the common subtype of all :ref:`function types <syntax-functype>`, regardless of their concrete definition.
This type has no values.

The abstract type |EXTERN| denotes the common supertype of all external references received through the :ref:`embedder <embedder>`.
This type has no concrete subtypes.
Dually, the type |NOEXTERN| denotes the common subtype of all forms of external references.
This type has no values.

The abstract type |ANY| denotes the common supertype of all aggregate types, as well as possibly abstract values produced by *internalizing* an external reference of type |EXTERN|.
Dually, the type |NONE| denotes the common subtype of all forms of aggregate types.
This type has no values.

The abstract type |EQ| is a subtype of |ANY| that includes all types for which references can be compared, i.e., aggregate values and |I31|.

The abstract types |STRUCT| and |ARRAY| denote the common supertypes of all :ref:`structure <syntax-structtype>` and :ref:`array <syntax-arraytype>` aggregates, respectively.

The abstract type |I31| denotes *unboxed scalars*, that is, integers injected into references.
Copy link
Member

Choose a reason for hiding this comment

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

Is i31 really abstract? It seems that it should be considered concrete since there are values for which it is the dynamic type. Maybe instead of "concrete" we should be using "defined" or similar?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, fair question. As far as the type system is concerned, it is in the same category as the other abstract types. This is just a syntactic distinction, not a semantic one.

Another alternative would be "specific" vs "nonspecific"/"generic", but that has the same awkwardness wrt i31.

Maybe there is a better pair of adjectives, but so far I can't think of any. I don't think "defined" works, because the opposite would then be "undefined". Plus, I already need "defined type" for something else.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe "declared" and "undeclared"? "undeclared" seems accurate to describe all the built-in types that are not declared/defined in the type section, but idk if we have precedent talking about declarations at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, I think we don't currently talk about "declarations" anywhere. But more importantly, "undeclared" still has this strong connotation of being erroneous, doesn't it?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm maybe, although not nearly as much as "undefined" IMO. Maybe "declared" and "built in" would work?

Anyway, I don't think figuring out the perfect names here should block landing this.

Their observable value range is limited to 31 bits.

.. note::
An |I31| is not actually allocated in the store,
but represented in a way that allows them to be mixed with actual references into the store without ambiguity.
Engines need to perform some form of *pointer tagging* to achieve this,
which is why 1 bit is reserved.

The type |EXTERN| denotes the infinite union of all objects owned by the :ref:`embedder <embedder>` and that can be passed into WebAssembly under this type.

A *concrete* heap type consists of a :ref:`type index <syntax-typeidx>` and classifies an object of the respective :ref:`type <syntax-type>` defined in some module.
A concrete heap type consists of a :ref:`type index <syntax-typeidx>` and classifies an object of the respective :ref:`type <syntax-type>` defined in some module.

A concrete heap type can also consist of a :ref:`function type <syntax-functype>` directly.
A concrete heap type can also consist of a :ref:`defined type <syntax-deftype>` directly.
tlively marked this conversation as resolved.
Show resolved Hide resolved
However, this form is representable in neither the :ref:`binary format <binary-valtype>` nor the :ref:`text format <text-valtype>`, such that it cannot be used in a program;
it only occurs during :ref:`validation <valid>` or :ref:`execution <exec>`, as the result of *substituting* a :ref:`type index <syntax-typeidx>` with its definition.

The type :math:`\BOT` is a :ref:`subtype <match-heaptype>` of all other heap types.
A type of any form is *closed* when it does not contain a heap type that is a :ref:`type index <syntax-typeidx>`,
i.e., all :ref:`type indices <syntax-typeidx>` have been :ref:`substituted <notation-subst>` with their :ref:`defined type <syntax-deftype>`.

The type :math:`\BOT` is a :ref:`subtype <match-heaptype>` of all heap types.
By virtue of being representable in neither the :ref:`binary format <binary-valtype>` nor the :ref:`text format <text-valtype>`, it cannot be used in a program;
it only occurs during :ref:`validation <valid>`, as a part of a possible operand type for instructions.

A type of any form is *closed* when it does not contain a heap type that is a :ref:`type index <syntax-typeidx>`,
i.e., all :ref:`type indices <syntax-typeidx>` have been :ref:`substituted <notation-subst>` with their :ref:`defined type <syntax-deftype>`.
.. note::
Although the types |NONE|, |NOFUNC|, and |NOEXTERN| are not inhabited by any values,
they can be used to form the types of all null :ref:`references <syntax-reftype>` in their respective hierarchy.
For example, :math:`(\REF~\NULL~\NOFUNC)` is the generic type of a null reference compatible with all function reference types.


.. _notation-subst:

Expand Down Expand Up @@ -264,6 +309,8 @@ They are also used to classify the inputs and outputs of :ref:`instructions <syn
Defined Types
~~~~~~~~~~~~~

.. todo:: structured types, recrusive types, etc.

*Defined types* are the ones that can be defined in a :ref:`module <syntax-module>`, assigning them a :ref:`type index <syntax-typeidx>`.

.. math::
Expand Down
65 changes: 61 additions & 4 deletions document/core/util/macros.def
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@

.. |BOT| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{bot}}

.. |I8| mathdef:: \xref{exec/runtime}{syntax-storagetype}{\K{i8}}
.. |I16| mathdef:: \xref{exec/runtime}{syntax-storagetype}{\K{i16}}
.. |I8| mathdef:: \xref{syntax/runtime}{syntax-storagetype}{\K{i8}}
.. |I16| mathdef:: \xref{syntax/runtime}{syntax-storagetype}{\K{i16}}
.. |I32| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i32}}
.. |I64| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i64}}
.. |F32| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{f32}}
Expand All @@ -199,10 +199,30 @@

.. |REF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{ref}}
.. |NULL| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{null}}
.. |STRUCT| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{struct}}
.. |ARRAY| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{array}}
.. |FUNC| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{func}}
.. |EXTERN| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{extern}}
.. |FUNCREF| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{funcref}}
.. |EXTERNREF| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{externref}}
.. |NONE| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{none}}
.. |NOFUNC| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{nofunc}}
.. |NOEXTERN| mathdef:: \xref{syntax/types}{syntax-heaptype}{\K{noextern}}
.. |ANYREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{anyref}}
.. |EQREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{eqref}}
.. |I31REF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{i31ref}}
.. |STRUCTREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{structref}}
.. |ARRAYREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{arrayref}}
.. |FUNCREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{funcref}}
.. |EXTERNREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{externref}}
.. |NULLREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{nullref}}
.. |NULLFUNCREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{nullfuncref}}
.. |NULLEXTERNREF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{nullexternref}}

.. |TFUNC| mathdef:: \xref{syntax/types}{syntax-strtype}{\K{func}}
.. |TSTRUCT| mathdef:: \xref{syntax/types}{syntax-strtype}{\K{struct}}
.. |TARRAY| mathdef:: \xref{syntax/types}{syntax-strtype}{\K{array}}
.. |TSUB| mathdef:: \xref{syntax/types}{syntax-subtype}{\K{sub}}
.. |TREC| mathdef:: \xref{syntax/types}{syntax-rectype}{\K{rec}}
.. |TFINAL| mathdef:: \xref{syntax/types}{syntax-subtype}{\K{filan}}

.. |MVAR| mathdef:: \xref{syntax/types}{syntax-mut}{\K{var}}
.. |MCONST| mathdef:: \xref{syntax/types}{syntax-mut}{\K{const}}
Expand All @@ -228,6 +248,12 @@
.. |valtype| mathdef:: \xref{syntax/types}{syntax-valtype}{\X{valtype}}
.. |resulttype| mathdef:: \xref{syntax/types}{syntax-resulttype}{\X{resulttype}}
.. |functype| mathdef:: \xref{syntax/types}{syntax-functype}{\X{functype}}
.. |structtype| mathdef:: \xref{syntax/types}{syntax-structtype}{\X{structtype}}
.. |arraytype| mathdef:: \xref{syntax/types}{syntax-arraytype}{\X{arraytype}}
.. |fieldtype| mathdef:: \xref{syntax/types}{syntax-fieldtype}{\X{fieldtype}}
.. |strtype| mathdef:: \xref{syntax/types}{syntax-strtype}{\X{strtype}}
.. |subtype| mathdef:: \xref{syntax/types}{syntax-subtype}{\X{subtype}}
.. |rectype| mathdef:: \xref{syntax/types}{syntax-rectype}{\X{rectype}}
.. |deftype| mathdef:: \xref{syntax/types}{syntax-deftype}{\X{deftype}}

.. |globaltype| mathdef:: \xref{syntax/types}{syntax-globaltype}{\X{globaltype}}
Expand Down Expand Up @@ -383,6 +409,9 @@
.. |BRTABLE| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{br\_table}}
.. |BRONNULL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{br\_on\_null}}
.. |BRONNONNULL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{br\_on\_non\_null}}
.. |BRONCAST| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{br\_on\_cast}}
.. |BRONCASTFAIL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{br\_on\_cast\_fail}}

.. |RETURN| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{return}}
.. |CALL| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{call}}
.. |CALLREF| mathdef:: \xref{syntax/instructions}{syntax-instr-control}{\K{call\_ref}}
Expand Down Expand Up @@ -422,6 +451,34 @@
.. |REFFUNC| mathdef:: \xref{syntax/instructions}{syntax-instr-ref}{\K{ref.func}}
.. |REFISNULL| mathdef:: \xref{syntax/instructions}{syntax-instr-ref}{\K{ref.is\_null}}
.. |REFASNONNULL| mathdef:: \xref{syntax/instructions}{syntax-instr-ref}{\K{ref.as\_non\_null}}
.. |REFEQ| mathdef:: \xref{syntax/instructions}{syntax-instr-ref}{\K{ref.eq}}
.. |REFTEST| mathdef:: \xref{syntax/instructions}{syntax-instr-ref}{\K{ref.test}}
.. |REFCAST| mathdef:: \xref{syntax/instructions}{syntax-instr-ref}{\K{ref.cast}}

.. |STRUCTNEW| mathdef:: \xref{syntax/instructions}{syntax-instr-struct}{\K{struct.new}}
.. |STRUCTNEWDEFAULT| mathdef:: \xref{syntax/instructions}{syntax-instr-struct}{\K{struct.new\_default}}
.. |STRUCTGET| mathdef:: \xref{syntax/instructions}{syntax-instr-struct}{\K{struct.get}}
.. |STRUCTGETS| mathdef:: \xref{syntax/instructions}{syntax-instr-struct}{\K{struct.get\_s}}
.. |STRUCTGETU| mathdef:: \xref{syntax/instructions}{syntax-instr-struct}{\K{struct.get\_u}}
.. |STRUCTSET| mathdef:: \xref{syntax/instructions}{syntax-instr-struct}{\K{struct.set}}

.. |ARRAYNEW| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.new}}
.. |ARRAYNEWDEFAULT| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.new\_default}}
.. |ARRAYNEWFIXED| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.new\_fixed}}
.. |ARRAYNEWDATA| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.new\_data}}
.. |ARRAYNEWELEM| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.new\_elem}}
.. |ARRAYGET| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.get}}
.. |ARRAYGETS| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.get\_s}}
.. |ARRAYGETU| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.get\_u}}
.. |ARRAYSET| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.set}}
.. |ARRAYLEN| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.len}}

.. |I31NEW| mathdef:: \xref{syntax/instructions}{syntax-instr-i31}{\K{i31.new}}
.. |I31GETS| mathdef:: \xref{syntax/instructions}{syntax-instr-i31}{\K{i31.get\_s}}
.. |I31GETU| mathdef:: \xref{syntax/instructions}{syntax-instr-i31}{\K{i31.get\_u}}

.. |EXTERNINTERNALIZE| mathdef:: \xref{syntax/instructions}{syntax-instr-extern}{\K{extern.internalize}}
.. |EXTERNEXTERNALIZE| mathdef:: \xref{syntax/instructions}{syntax-instr-extern}{\K{extern.externalize}}

.. |CONST| mathdef:: \xref{syntax/instructions}{syntax-instr-numeric}{\K{const}}
.. |EQZ| mathdef:: \xref{syntax/instructions}{syntax-instr-numeric}{\K{eqz}}
Expand Down
2 changes: 1 addition & 1 deletion proposals/gc/MVP.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ The opcode for heap types is encoded as an `s33`.
| Opcode | Type | Parameters |
| ------ | --------------- | ---------- |
| 0xd5 | `ref.eq` | |
| 0xd6 | `br_on_non_null` | |
| 0xd6 | `br_on_non_null $l` | `$l : labelidx` |
| 0xfb01 | `struct.new $t` | `$t : typeidx` |
| 0xfb02 | `struct.new_default $t` | `$t : typeidx` |
| 0xfb03 | `struct.get $t i` | `$t : typeidx`, `i : fieldidx` |
Expand Down