You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 3, 2021. It is now read-only.
Copy file name to clipboardExpand all lines: document/core/appendix/algorithm.rst
+69-79Lines changed: 69 additions & 79 deletions
Original file line number
Diff line number
Diff line change
@@ -22,48 +22,29 @@ Data Structures
22
22
~~~~~~~~~~~~~~~
23
23
24
24
Types are representable as an enumeration.
25
-
In addition to the plain types from the WebAssembly validation semantics, an extended notion of operand type includes a bottom type `Unknown` that is used as the type of :ref:`polymorphic <polymorphism>` operands.
26
-
27
25
A simple subtyping check can be defined on these types.
28
-
In addition, corresponding functions computing the join (least upper bound) and meet (greatest lower bound) of two types are used.
29
-
Because there is no top type, a join does not exist in all cases.
30
-
Similarly, a meet must always be defined on known value types that exclude the auxiliary bottom type `Unknown`,
31
-
hence a meet does not exist in all cases either.
32
-
A type error is encountered if a join or meet is required when it does not exist.
The algorithm uses two separate stacks: the *operand stack* and the *control stack*.
41
+
The algorithm uses two separate stacks: the *value stack* and the *control stack*.
61
42
The former tracks the :ref:`types <syntax-valtype>` of operand values on the :ref:`stack <stack>`,
62
43
the latter surrounding :ref:`structured control instructions <syntax-instr-control>` and their associated :ref:`blocks <syntax-instr-control>`.
63
44
64
45
.. code-block:: pseudo
65
46
66
-
type opd_stack = stack(opd_type)
47
+
type val_stack = stack(val_type)
67
48
68
49
type ctrl_stack = stack(ctrl_frame)
69
50
type ctrl_frame = {
@@ -73,7 +54,7 @@ the latter surrounding :ref:`structured control instructions <syntax-instr-contr
73
54
unreachable : bool
74
55
}
75
56
76
-
For each value, the operand stack records its :ref:`value type <syntax-valtype>`, or :code:`Unknown` when the type is not known.
57
+
For each value, the value stack records its :ref:`value type <syntax-valtype>`.
77
58
78
59
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).
79
60
@@ -85,63 +66,68 @@ For the purpose of presenting the algorithm, the operand and control stacks are
85
66
86
67
.. code-block:: pseudo
87
68
88
-
var opds : opd_stack
69
+
var vals : val_stack
89
70
var ctrls : ctrl_stack
90
71
91
72
However, these variables are not manipulated directly by the main checking function, but through a set of auxiliary functions:
92
73
93
74
.. code-block:: pseudo
94
75
95
-
func push_opd(type : opd_type) =
96
-
opds.push(type)
76
+
func push_val(type : val_type) =
77
+
vals.push(type)
97
78
98
-
func pop_opd() : opd_type =
99
-
if (opds.size() = ctrls[0].height && ctrls[0].unreachable) return Unknown
100
-
error_if(opds.size() = ctrls[0].height)
101
-
return opds.pop()
79
+
func pop_val() : val_type =
80
+
if (vals.size() = ctrls[0].height && ctrls[0].unreachable) return Bot
81
+
error_if(vals.size() = ctrls[0].height)
82
+
return vals.pop()
102
83
103
-
func pop_opd(expect : val_type) =
104
-
let actual = pop_opd()
84
+
func pop_val(expect : val_type) : val_type =
85
+
let actual = pop_val()
105
86
error_if(not matches(actual, expect))
87
+
return actual
106
88
107
-
func push_opds(types : list(val_type)) = foreach (t in types) push_opd(t)
108
-
func pop_opds(types : list(val_type)) = foreach (t in reverse(types)) pop_opd(t)
89
+
func push_vals(types : list(val_type)) = foreach (t in types) push_val(t)
foreach (t in reverse(types)) popped.append(pop_val(t))
93
+
return popped
109
94
110
-
Pushing an operand simply pushes the respective type to the operand stack.
95
+
Pushing an operand value simply pushes the respective type to the value stack.
111
96
112
-
Popping an operand checks that the operand stack does not underflow the current block and then removes one type.
113
-
But first, a special case is handled where the block contains no known operands, but has been marked as unreachable.
97
+
Popping an operand value checks that the value stack does not underflow the current block and then removes one type.
98
+
But first, a special case is handled where the block contains no known values, but has been marked as unreachable.
114
99
That can occur after an unconditional branch, when the stack is typed :ref:`polymorphically <polymorphism>`.
115
-
In that case, an unknown type is returned.
100
+
In that case, the :code:`Bot` type is returned, because that is a *principal* choice trivially satisfying all use constraints.
116
101
117
-
A second function for popping an operand takes an expected (known) type, which the actual operand type is checked against.
118
-
The types may differ by subtyping, inlcuding the case where the actual type is unknown.
102
+
A second function for popping an operand value takes an expected type, which the actual operand type is checked against.
103
+
The types may differ by subtyping, including the case where the actual type is :code:`Bot`, and thereby matches unconditionally.
104
+
The function returns the actual type popped from the stack.
119
105
120
106
Finally, there are accumulative functions for pushing or popping multiple operand types.
121
107
122
108
.. note::
123
109
The notation :code:`stack[i]` is meant to index the stack from the top,
124
-
so that :code:`ctrls[0]` accesses the element pushed last.
110
+
so that, e.g.,:code:`ctrls[0]` accesses the element pushed last.
125
111
126
112
127
113
The control stack is likewise manipulated through auxiliary functions:
128
114
129
115
.. code-block:: pseudo
130
116
131
117
func push_ctrl(label : list(val_type), out : list(val_type)) =
132
-
let frame = ctrl_frame(label, out, opds.size(), false)
118
+
let frame = ctrl_frame(label, out, vals.size(), false)
133
119
ctrls.push(frame)
134
120
135
121
func pop_ctrl() : list(val_type) =
136
122
error_if(ctrls.is_empty())
137
123
let frame = ctrls[0]
138
-
pop_opds(frame.end_types)
139
-
error_if(opds.size() =/= frame.height)
124
+
pop_vals(frame.end_types)
125
+
error_if(vals.size() =/= frame.height)
140
126
ctrls.pop()
141
127
return frame.end_types
142
128
143
129
func unreachable() =
144
-
opds.resize(ctrls[0].height)
130
+
vals.resize(ctrls[0].height)
145
131
ctrls[0].unreachable := true
146
132
147
133
Pushing a control frame takes the types of the label and result values.
@@ -152,18 +138,18 @@ It then verifies that the operand stack contains the right types of values expec
152
138
Afterwards, it checks that the stack has shrunk back to its initial height.
153
139
154
140
Finally, the current frame can be marked as unreachable.
155
-
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.
141
+
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.
156
142
157
143
.. note::
158
144
Even with the unreachable flag set, consecutive operands are still pushed to and popped from the operand stack.
159
145
That is necessary to detect invalid :ref:`examples <polymorphism>` like :math:`(\UNREACHABLE~(\I32.\CONST)~\I64.\ADD)`.
160
-
However, a polymorphic stack cannot underflow, but instead generates :code:`Unknown` types as needed.
146
+
However, a polymorphic stack cannot underflow, but instead generates :code:`Bot` types as needed.
161
147
162
148
163
149
.. index:: opcode
164
150
165
-
Validation of Opcode Sequences
166
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
151
+
Validation of Instruction Sequences
152
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167
153
168
154
The following function shows the validation of a number of representative instructions that manipulate the stack.
169
155
Other instructions are checked in a similar manner.
@@ -177,18 +163,26 @@ Other instructions are checked in a similar manner.
177
163
func validate(opcode) =
178
164
switch (opcode)
179
165
case (i32.add)
180
-
pop_opd(I32)
181
-
pop_opd(I32)
182
-
push_opd(I32)
166
+
pop_val(I32)
167
+
pop_val(I32)
168
+
push_val(I32)
183
169
184
170
case (drop)
185
-
pop_opd()
171
+
pop_val()
186
172
187
173
case (select)
188
-
pop_opd(I32)
189
-
let t1 = pop_opd()
190
-
let t2 = pop_opd()
191
-
push_opd(join(t1, t2))
174
+
pop_val(I32)
175
+
let t1 = pop_val()
176
+
let t2 = pop_val()
177
+
error_if(not (is_num(t1) && is_num(t2)))
178
+
error_if(t1 =/= t2 && t1 =/= Bot && t2 =/= Bot)
179
+
push_val(if (t1 = Bot) t2 else t1)
180
+
181
+
case (select t)
182
+
pop_val(I32)
183
+
pop_val(t)
184
+
pop_val(t)
185
+
push_val(t)
192
186
193
187
case (unreachable)
194
188
unreachable()
@@ -200,39 +194,35 @@ Other instructions are checked in a similar manner.
200
194
push_ctrl([], [t*])
201
195
202
196
case (if t*)
203
-
pop_opd(I32)
197
+
pop_val(I32)
204
198
push_ctrl([t*], [t*])
205
199
206
200
case (end)
207
201
let results = pop_ctrl()
208
-
push_opds(results)
202
+
push_vals(results)
209
203
210
204
case (else)
211
205
let results = pop_ctrl()
212
206
push_ctrl(results, results)
213
207
214
208
case (br n)
215
209
error_if(ctrls.size() < n)
216
-
pop_opds(ctrls[n].label_types)
210
+
pop_vals(ctrls[n].label_types)
217
211
unreachable()
218
212
219
213
case (br_if n)
220
214
error_if(ctrls.size() < n)
221
-
pop_opd(I32)
222
-
pop_opds(ctrls[n].label_types)
223
-
push_opds(ctrls[n].label_types)
215
+
pop_val(I32)
216
+
pop_vals(ctrls[n].label_types)
217
+
push_vals(ctrls[n].label_types)
224
218
225
219
case (br_table n* m)
220
+
pop_val(I32)
226
221
error_if(ctrls.size() < m)
227
-
var ts = ctrls[m].label_types
222
+
let arity = ctrls[m].label_types.size()
228
223
foreach (n in n*)
229
224
error_if(ctrls.size() < n)
230
-
ts := meet(ts, ctrls[n].label_types)
231
-
pop_opd(I32)
232
-
pop_opds(ts)
225
+
error_if(ctrls[n].label_types.size() =/= arity)
226
+
push_vals(pop_vals(ctrls[n].label_types))
227
+
pop_vals(ctrls[m].label_types)
233
228
unreachable()
234
-
235
-
.. note::
236
-
It is an invariant under the current WebAssembly instruction set that an operand of :code:`Unknown` type is never duplicated on the stack.
237
-
This would change if the language were extended with stack operators like :code:`dup`.
238
-
Under such an extension, the above algorithm would need to be refined by replacing the :code:`Unknown` type with proper *type variables* to ensure that all uses are consistent.
Copy file name to clipboardExpand all lines: document/core/syntax/instructions.rst
+5-1Lines changed: 5 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -205,12 +205,16 @@ Instructions in this group can operate on operands of any :ref:`value type <synt
205
205
\production{instruction} & \instr &::=&
206
206
\dots \\&&|&
207
207
\DROP \\&&|&
208
-
\SELECT
208
+
\SELECT~(\valtype^\ast)^? \\
209
209
\end{array}
210
210
211
211
The |DROP| operator simply throws away a single operand.
212
212
213
213
The |SELECT| operator selects one of its first two operands based on whether its third operand is zero or not.
214
+
It may include a :ref:`value type <syntax-valtype>` determining the type of these operands. If missing, the operands must be of :ref:`numeric type <syntax-numtype>`.
215
+
216
+
.. note::
217
+
In future versions of WebAssembly, the type annotation on |SELECT| may allow for more than a single value being selected at the same time.
214
218
215
219
216
220
.. index:: ! variable instruction, local, global, local index, global index
Copy file name to clipboardExpand all lines: document/core/syntax/types.rst
+8-3Lines changed: 8 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -63,7 +63,7 @@ The type |FUNCREF| denotes the infinite union of all references to :ref:`functio
63
63
64
64
The type |NULLREF| only contains a single value: the :ref:`null <syntax-ref.null>` reference.
65
65
It is a :ref:`subtype <match-reftype>` of all other reference types.
66
-
By virtue of not being representable in either the :ref:`binary format <binary-reftype>` nor the :ref:`text format <text-reftype>`, the |NULLREF| type cannot be used in a program;
66
+
By virtue of being representable in neither the :ref:`binary format <binary-reftype>` nor the :ref:`text format <text-reftype>`, the |NULLREF| type cannot be used in a program;
67
67
it only occurs during :ref:`validation <valid>`.
68
68
69
69
.. note::
@@ -73,7 +73,7 @@ Reference types are *opaque*, meaning that neither their size nor their bit patt
73
73
Values of reference type can be stored in :ref:`tables <syntax-table>`.
74
74
75
75
76
-
.. index:: ! value type, number type, reference type
76
+
.. index:: ! value type, number type, reference type, ! bottom type
77
77
pair: abstract syntax; value type
78
78
pair: value; type
79
79
.. _syntax-valtype:
@@ -82,11 +82,16 @@ Value Types
82
82
~~~~~~~~~~~
83
83
84
84
*Value types* classify the individual values that WebAssembly code can compute with and the values that a variable accepts.
85
+
They are either :ref:`number types <syntax-numtype>`, :ref:`reference type <syntax-reftype>`, or the unique *bottom type*, written :math:`\BOT`.
86
+
87
+
The type :math:`\BOT` is a :ref:`subtype <match-valtype>` of all other types.
88
+
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;
0 commit comments