Skip to content

Commit 4a314b8

Browse files
authored
Merge pull request WebAssembly#324 from WebAssembly/merge-funcref3
Merge upstream funcref + main repo
2 parents 65b77cd + e32c930 commit 4a314b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+3802
-2860
lines changed

.github/workflows/main.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ jobs:
2121
- run: opam install --yes ocamlbuild.0.14.0
2222
- run: cd interpreter && opam exec make all
2323

24+
ref-interpreter-js-library:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v2
28+
- name: Setup OCaml
29+
uses: ocaml/setup-ocaml@v2
30+
with:
31+
ocaml-compiler: 4.13.x
32+
- run: opam install --yes ocamlbuild.0.14.0 ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0
33+
- run: cd interpreter && opam exec make wast.js
34+
2435
build-js-api-spec:
2536
runs-on: ubuntu-latest
2637
steps:

document/core/appendix/algorithm.rst

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ The algorithm is expressed in typed pseudo code whose semantics is intended to b
2121
Data Structures
2222
~~~~~~~~~~~~~~~
2323

24-
Types are representable as a set of enumerations.
24+
Types
25+
.....
26+
27+
Value types are representable as a set of enumerations.
2528

2629
.. code-block:: pseudo
2730
@@ -34,11 +37,11 @@ Types are representable as a set of enumerations.
3437
func is_num(t : val_type) : bool =
3538
return t = I32 || t = I64 || t = F32 || t = F64 || t = Bot
3639
37-
func is_vec(t : val_type | Unknown) : bool =
38-
return t = V128 || t = Unknown
40+
func is_vec(t : val_type) : bool =
41+
return t = V128 || t = Bot
3942
40-
func is_ref(t : val_type | Unknown) : bool =
41-
return t = not (is_num t || is_vec t) || t = Bot
43+
func is_ref(t : val_type) : bool =
44+
return not (is_num t || is_vec t) || t = Bot
4245
4346
Equivalence and subtyping checks can be defined on these types.
4447

@@ -65,36 +68,64 @@ Equivalence and subtyping checks can be defined on these types.
6568
return matches_heap(t1.heap, t2.heap) && matches_null(t1.null, t2.null)
6669
6770
func matches(t1 : val_type, t2 : val_type) : bool =
68-
return
69-
(is_num t1 && is_num t2 & t1 = t2) ||
70-
(is_ref t1 && is_ref t2 & matches_ref(t1, t2)) ||
71-
t1 = Bot
71+
switch (t1, t2)
72+
case (Ref(_), Ref(_))
73+
return matches_ref(t1, t2)
74+
case (Bot, _)
75+
return true
76+
case (_, _)
77+
return t1 = t2
78+
79+
Context
80+
.......
81+
82+
Validation requires a :ref:`context <context>` for checking uses of :ref:`indices <syntax-index>`.
83+
For the purpose of presenting the algorithm, it is maintained in a set of global variables:
84+
85+
.. code-block:: pseudo
86+
87+
var locals : array(val_type)
88+
var locals_init : array(bool)
89+
var globals : array(global_type)
90+
var funcs : array(func_type)
91+
var tables : array(table_type)
92+
var mems : array(mem_type)
93+
94+
This assumes suitable representations for the various :ref:`types <syntax-type>` besides :code:`val_type`, which are omitted here.
95+
96+
For locals, there is an additional array recording the initialization status of each local.
97+
98+
Stacks
99+
......
72100

73-
The algorithm uses two separate stacks: the *value stack* and the *control stack*.
74-
The former tracks the :ref:`types <syntax-valtype>` of operand values on the :ref:`stack <stack>`,
75-
the latter surrounding :ref:`structured control instructions <syntax-instr-control>` and their associated :ref:`blocks <syntax-instr-control>`.
101+
The algorithm uses three separate stacks: the *value stack*, the *control stack*, and the *initialization stack*.
102+
The value stack tracks the :ref:`types <syntax-valtype>` of operand values on the :ref:`stack <stack>`.
103+
The control stack tracks surrounding :ref:`structured control instructions <syntax-instr-control>` and their associated :ref:`blocks <syntax-instr-control>`.
104+
The initialization stack records all :ref:`locals <syntax-local>` that have been initialized since the beginning of the function.
76105

77106
.. code-block:: pseudo
78107
79108
type val_stack = stack(val_type)
109+
type init_stack = stack(u32)
80110
81111
type ctrl_stack = stack(ctrl_frame)
82112
type ctrl_frame = {
83113
opcode : opcode
84114
start_types : list(val_type)
85115
end_types : list(val_type)
86-
height : nat
116+
val_height : nat
117+
init_height : nat
87118
unreachable : bool
88119
}
89120
90-
For each value, the value stack records its :ref:`value type <syntax-valtype>`.
91-
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).
121+
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), the height of the initialization stack at the start of the block (used to reset initialization status at the end of the block), and a flag recording whether the remainder of the block is unreachable (used to handle :ref:`stack-polymorphic <polymorphism>` typing after branches).
92122

93-
For the purpose of presenting the algorithm, the operand and control stacks are simply maintained as global variables:
123+
For the purpose of presenting the algorithm, these stacks are simply maintained as global variables:
94124

95125
.. code-block:: pseudo
96126
97127
var vals : val_stack
128+
var inits : init_stack
98129
var ctrls : ctrl_stack
99130
100131
However, these variables are not manipulated directly by the main checking function, but through a set of auxiliary functions:
@@ -122,7 +153,7 @@ However, these variables are not manipulated directly by the main checking funct
122153
func pop_ref() : ref_type =
123154
let actual = pop_val()
124155
error_if(not is_ref(actual))
125-
if actual = Bot then return Ref(Bot, false)
156+
if (actual = Bot) return Ref(Bot, false)
126157
return actual
127158
128159
func push_vals(types : list(val_type)) = foreach (t in types) push_val(t)
@@ -149,25 +180,49 @@ Finally, there are accumulative functions for pushing or popping multiple operan
149180
so that, e.g., :code:`ctrls[0]` accesses the element pushed last.
150181

151182

183+
The initialization stack and the initialization status of locals is manipulated through the following functions:
184+
185+
.. code-block:: pseudo
186+
187+
func get_local(idx : u32) =
188+
error_if(not locals_init[idx])
189+
190+
func set_local(idx : u32) =
191+
if (not locals_init[idx])
192+
inits.push(idx)
193+
locals_init[idx] := true
194+
195+
func reset_locals(height : nat) =
196+
while (inits.size() > height)
197+
locals_init[inits.pop()] := false
198+
199+
Getting a local verifies that it is known to be initialized.
200+
When a local is set that was not set already,
201+
then its initialization status is updated and the change is recorded in the initialization stack.
202+
Thus, the initialization status of all locals can be reset to a previous state by denoting a specific height in the initialization stack.
203+
204+
The size of the initialization stack is bounded by the number of (non-defaultable) locals in a function, so can be preallocated by an algorithm.
205+
152206
The control stack is likewise manipulated through auxiliary functions:
153207

154208
.. code-block:: pseudo
155209
156210
func push_ctrl(opcode : opcode, in : list(val_type), out : list(val_type)) =
157-
let frame = ctrl_frame(opcode, in, out, vals.size(), false)
211+
let frame = ctrl_frame(opcode, in, out, vals.size(), inits.size(), false)
158212
ctrls.push(frame)
159213
push_vals(in)
160214
161215
func pop_ctrl() : ctrl_frame =
162216
error_if(ctrls.is_empty())
163217
let frame = ctrls[0]
164218
pop_vals(frame.end_types)
165-
error_if(vals.size() =/= frame.height)
219+
error_if(vals.size() =/= frame.val_height)
220+
reset_locals(frame.init_height)
166221
ctrls.pop()
167222
return frame
168223
169224
func label_types(frame : ctrl_frame) : list(val_types) =
170-
return (if frame.opcode == loop then frame.start_types else frame.end_types)
225+
return (if (frame.opcode = loop) frame.start_types else frame.end_types)
171226
172227
func unreachable() =
173228
vals.resize(ctrls[0].height)
@@ -179,18 +234,22 @@ It allocates a new frame record recording them along with the current height of
179234
Popping a frame first checks that the control stack is not empty.
180235
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.
181236
Afterwards, it checks that the stack has shrunk back to its initial height.
237+
Finally, it undoes all changes to the initialization status of locals that happend inside the block.
182238

183239
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.
184240

185241
Finally, the current frame can be marked as unreachable.
186242
In that case, all existing operand types are purged from the value stack, in order to allow for the :ref:`stack-polymorphism <polymorphism>` logic in :code:`pop_val` to take effect.
243+
Because every function has an implicit outermost label that corresponds to an implicit block frame,
244+
it is an invariant of the validation algorithm that there always is at least one frame on the control stack when validating an instruction, and hence, `ctrls[0]` is always defined.
187245

188246
.. note::
189247
Even with the unreachable flag set, consecutive operands are still pushed to and popped from the operand stack.
190248
That is necessary to detect invalid :ref:`examples <polymorphism>` like :math:`(\UNREACHABLE~(\I32.\CONST)~\I64.\ADD)`.
191249
However, a polymorphic stack cannot underflow, but instead generates :code:`Bot` types as needed.
192250

193251

252+
194253
.. index:: opcode
195254

196255
Validation of Opcode Sequences
@@ -199,10 +258,6 @@ Validation of Opcode Sequences
199258
The following function shows the validation of a number of representative instructions that manipulate the stack.
200259
Other instructions are checked in a similar manner.
201260

202-
.. note::
203-
Various instructions not shown here will additionally require the presence of a validation :ref:`context <context>` for checking uses of :ref:`indices <syntax-index>`.
204-
That is an easy addition and therefore omitted from this presentation.
205-
206261
.. code-block:: pseudo
207262
208263
func validate(opcode) =
@@ -221,7 +276,7 @@ Other instructions are checked in a similar manner.
221276
let t2 = pop_val()
222277
error_if(not (is_num(t1) && is_num(t2) || is_vec(t1) && is_vec(t2)))
223278
error_if(t1 =/= t2 && t1 =/= Bot && t2 =/= Bot)
224-
push_val(if (t1 == Bot) t2 else t1)
279+
push_val(if (t1 = Bot) t2 else t1)
225280
226281
case (select t)
227282
pop_val(I32)
@@ -237,6 +292,14 @@ Other instructions are checked in a similar manner.
237292
let rt = pop_ref()
238293
push_val(Ref(rt.heap, false))
239294
295+
case (local.get x)
296+
get_local(x)
297+
push_val(locals[x])
298+
299+
case (local.set x)
300+
pop_val(locals[x])
301+
set_local(x)
302+
240303
case (unreachable)
241304
unreachable()
242305
@@ -295,7 +358,7 @@ Other instructions are checked in a similar manner.
295358
let rt = pop_ref()
296359
if (rt.heap =/= Bot)
297360
error_if(not is_def(rt.heap))
298-
let ft = lookup_func_type(rt.heap.idx) // TODO
361+
let ft = funcs[rt.heap.idx]
299362
pop_vals(ft.params)
300363
push_vals(ft.results)
301364

document/core/appendix/changes.rst

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Added |FUNCREF| and |EXTERNREF| as new value types and respective instructions [
5353

5454
* New :ref:`reference instructions <syntax-instr-ref>`: |REFNULL|, |REFFUNC|, |REFISNULL|
5555

56-
* Enrich :ref:`parametric instruction <syntax-instr-parametric>`: |SELECT| with optional type immediate
56+
* Extended :ref:`parametric instruction <syntax-instr-parametric>`: |SELECT| with optional type immediate
5757

5858
* New :ref:`declarative <syntax-elemmode>` form of :ref:`element segment <syntax-elem>`
5959

@@ -138,6 +138,33 @@ Added vector type and instructions that manipulate multiple numeric values in pa
138138
* New injection/projection :ref:`vector instructions <syntax-instr-vec>`: :math:`\K{i}\!N\!\K{x}\!M\!\K{.splat}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.splat}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.bitmask}`
139139

140140

141+
Release 2.?
142+
~~~~~~~~~~~
143+
144+
.. index:: reference, reference type, heap type, value type, local, local type, instruction, instruction type, table, function, function type, matching, subtyping
145+
146+
Typeful References
147+
..................
148+
149+
Added more precise types for references [#proposal-typedref]_.
150+
151+
* New generalised form of :ref:`reference types <syntax-reftype>`: :math:`(\REF~\NULL^?~\heaptype)`
152+
153+
* New class of :ref:`heap types <syntax-heaptype>`: |FUNC|, |EXTERN|, :math:`\typeidx`
154+
155+
* Basic :ref:`subtyping <match>` on :ref:`reference <match-reftype>` and :ref:`value <match-valtype>` types
156+
157+
* New :ref:`reference instructions <syntax-instr-ref>`: |REFASNONNULL|, |BRONNULL|, |BRONNONNULL|
158+
159+
* New :ref:`control instruction <syntax-instr-control>`: |CALLREF|
160+
161+
* Refined typing of :ref:`reference instruction <syntax-instr-ref>` |REFFUNC| with more precise result type
162+
163+
* Refined typing of :ref:`local instructions <valid-instr-variable>` and :ref:`instruction sequences <valid-instr-seq>` to track the :ref:`initialization status <syntax-init>` of :ref:`locals <syntax-local>` with non-:ref:`defaultable <valid-defaultable>` type
164+
165+
* Extended :ref:`table definitions <syntax-table>` with optional initializer expression
166+
167+
141168
.. [#proposal-signext]
142169
https://github.com/WebAssembly/spec/tree/main/proposals/sign-extension-ops/
143170
@@ -155,3 +182,6 @@ Added vector type and instructions that manipulate multiple numeric values in pa
155182
156183
.. [#proposal-vectype]
157184
https://github.com/WebAssembly/spec/tree/main/proposals/simd/
185+
186+
.. [#proposal-typedref]
187+
https://github.com/WebAssembly/spec/tree/main/proposals/function-references/

0 commit comments

Comments
 (0)