Skip to content

Commit

Permalink
Add type annotation to call_ref (WebAssembly#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
rossberg committed Aug 25, 2022
1 parent b04e3be commit 2b194ce
Show file tree
Hide file tree
Showing 22 changed files with 148 additions and 141 deletions.
2 changes: 1 addition & 1 deletion document/core/appendix/gen-index-instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat
Instruction(r'\CALLINDIRECT~x~y', r'\hex{11}', r'[t_1^\ast~\I32] \to [t_2^\ast]', r'valid-call_indirect', r'exec-call_indirect'),
Instruction(None, r'\hex{12}'),
Instruction(None, r'\hex{13}'),
Instruction(r'\CALLREF', r'\hex{14}', r'[t_1^\ast~(\REF~\NULL~x)] \to [t_2^\ast]', r'valid-call_ref', r'exec-call_ref'),
Instruction(r'\CALLREF~x', r'\hex{14}', r'[t_1^\ast~(\REF~\NULL~x)] \to [t_2^\ast]', r'valid-call_ref', r'exec-call_ref'),
Instruction(None, r'\hex{15}'),
Instruction(None, r'\hex{16}'),
Instruction(None, r'\hex{17}'),
Expand Down
2 changes: 1 addition & 1 deletion document/core/appendix/index-instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Instruction Binary Opcode
:math:`\CALLINDIRECT~x~y` :math:`\hex{11}` :math:`[t_1^\ast~\I32] \to [t_2^\ast]` :ref:`validation <valid-call_indirect>` :ref:`execution <exec-call_indirect>`
(reserved) :math:`\hex{12}`
(reserved) :math:`\hex{13}`
:math:`\CALLREF` :math:`\hex{14}` :math:`[t_1^\ast~(\REF~\NULL~x)] \to [t_2^\ast]` :ref:`validation <valid-call_ref>` :ref:`execution <exec-call_ref>`
:math:`\CALLREF~x` :math:`\hex{14}` :math:`[t_1^\ast~(\REF~\NULL~x)] \to [t_2^\ast]` :ref:`validation <valid-call_ref>` :ref:`execution <exec-call_ref>`
(reserved) :math:`\hex{15}`
(reserved) :math:`\hex{16}`
(reserved) :math:`\hex{17}`
Expand Down
6 changes: 3 additions & 3 deletions document/core/binary/instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ Control Instructions
\hex{0F} &\Rightarrow& \RETURN \\ &&|&
\hex{10}~~x{:}\Bfuncidx &\Rightarrow& \CALL~x \\ &&|&
\hex{11}~~y{:}\Btypeidx~~x{:}\Btableidx &\Rightarrow& \CALLINDIRECT~x~y \\ &&|&
\hex{14} &\Rightarrow& \CALLREF \\ &&|&
\hex{D4}~~x{:}\Bfuncidx &\Rightarrow& \BRONNULL \\ &&|&
\hex{D6}~~x{:}\Bfuncidx &\Rightarrow& \BRONNONNULL \\
\hex{14}~~x{:}\Btypeidx &\Rightarrow& \CALLREF~x \\ &&|&
\hex{D4}~~l{:}\Blabelidx &\Rightarrow& \BRONNULL~l \\ &&|&
\hex{D6}~~l{:}\Blabelidx &\Rightarrow& \BRONNONNULL~l \\
\end{array}
.. note::
Expand Down
6 changes: 3 additions & 3 deletions document/core/exec/instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2837,8 +2837,8 @@ Control Instructions
.. _exec-call_ref:

:math:`\CALLREF`
................
:math:`\CALLREF~x`
..................

1. Assert: due to :ref:`validation <valid-call_ref>`, a :ref:`function reference <syntax-ref>` is on the top of the stack.

Expand All @@ -2848,7 +2848,7 @@ Control Instructions

.. math::
\begin{array}{lcl@{\qquad}l}
F; (\REFFUNCADDR~a)~\CALLREF &\stepto& F; (\INVOKE~a)
F; (\REFFUNCADDR~a)~\CALLREF~x &\stepto& F; (\INVOKE~a)
\end{array}
Expand Down
2 changes: 1 addition & 1 deletion document/core/syntax/instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ Instructions in this group affect the flow of control.
\BRONNONNULL~\labelidx \\&&|&
\RETURN \\&&|&
\CALL~\funcidx \\&&|&
\CALLREF \\&&|&
\CALLREF~\typeidx \\&&|&
\CALLINDIRECT~\tableidx~\typeidx \\
\end{array}
Expand Down
2 changes: 1 addition & 1 deletion document/core/text/instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ All other control instruction are represented verbatim.
\text{br\_on\_non\_null}~~l{:}\Tlabelidx_I &\Rightarrow& \BRONNONNULL~l \\ &&|&
\text{return} &\Rightarrow& \RETURN \\ &&|&
\text{call}~~x{:}\Tfuncidx_I &\Rightarrow& \CALL~x \\ &&|&
\text{call\_ref} &\Rightarrow& \CALLREF \\ &&|&
\text{call\_ref}~~x{:}\Ttypeidx &\Rightarrow& \CALLREF~x \\ &&|&
\text{call\_indirect}~~x{:}\Ttableidx~~y,I'{:}\Ttypeuse_I &\Rightarrow& \CALLINDIRECT~x~y
& (\iff I' = \{\ILOCALS~(\epsilon)^\ast\}) \\
\end{array}
Expand Down
10 changes: 6 additions & 4 deletions document/core/valid/instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1538,18 +1538,20 @@ Control Instructions
.. _valid-call_ref:

:math:`\CALLREF`
................
:math:`\CALLREF~x`
..................

* The type :math:`C.\CTYPES[x]` must be defined in the context.

* Let :math:`x` be some :ref:`type index <syntax-typeidx>` for which :math:`C.\CTYPES[x]` is a :ref:`function type <syntax-functype>` of the form :math:`[t_1^\ast] \to [t_2^\ast]`.
* Let :math:`[t_1^\ast] \to [t_2^\ast]` be the :ref:`function type <syntax-functype>` :math:`C.\CTYPES[x]`.

* Then the instruction is valid with type :math:`[t_1^\ast~(\REF~\NULL~x)] \to [t_2^\ast]`.

.. math::
\frac{
C.\CTYPES[x] = [t_1^\ast] \to [t_2^\ast]
}{
C \vdashinstr \CALLREF : [t_1^\ast~(\REF~\NULL~x)] \to [t_2^\ast]
C \vdashinstr \CALLREF~x : [t_1^\ast~(\REF~\NULL~x)] \to [t_2^\ast]
}
Expand Down
4 changes: 2 additions & 2 deletions interpreter/binary/decode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ let rec instr s =

| 0x12 | 0x13 as b -> illegal s pos b (* return_call, return_call_indirect *)

| 0x14 -> call_ref
| 0x15 -> return_call_ref
| 0x14 -> call_ref (at var s)
| 0x15 -> return_call_ref (at var s)

| 0x16 as b -> illegal s pos b

Expand Down
4 changes: 2 additions & 2 deletions interpreter/binary/encode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ struct
| BrOnNonNull x -> op 0xd6; var x
| Return -> op 0x0f
| Call x -> op 0x10; var x
| CallRef -> op 0x14
| CallRef x -> op 0x14; var x
| CallIndirect (x, y) -> op 0x11; var y; var x
| ReturnCallRef -> op 0x15
| ReturnCallRef x -> op 0x15; var x

| Drop -> op 0x1a
| Select None -> op 0x1b
Expand Down
10 changes: 5 additions & 5 deletions interpreter/exec/eval.ml
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,10 @@ let rec step (c : config) : config =
| Call x, vs ->
vs, [Invoke (func c.frame.inst x) @@ e.at]

| CallRef, Ref (NullRef _) :: vs ->
| CallRef _x, Ref (NullRef _) :: vs ->
vs, [Trapping "null function reference" @@ e.at]

| CallRef, Ref (FuncRef f) :: vs ->
| CallRef _x, Ref (FuncRef f) :: vs ->
vs, [Invoke f @@ e.at]

| CallIndirect (x, y), Num (I32 i) :: vs ->
Expand All @@ -237,11 +237,11 @@ let rec step (c : config) : config =
else
vs, [Trapping "indirect call type mismatch" @@ e.at]

| ReturnCallRef, Ref (NullRef _) :: vs ->
| ReturnCallRef _x, Ref (NullRef _) :: vs ->
vs, [Trapping "null function reference" @@ e.at]

| ReturnCallRef, vs ->
(match (step {c with code = (vs, [Plain CallRef @@ e.at])}).code with
| ReturnCallRef x, vs ->
(match (step {c with code = (vs, [Plain (CallRef x) @@ e.at])}).code with
| vs', [{it = Invoke a; at}] -> vs', [ReturningInvoke (vs', a) @@ at]
| vs', [{it = Trapping s; at}] -> vs', [Trapping s @@ at]
| _ -> assert false
Expand Down
4 changes: 2 additions & 2 deletions interpreter/syntax/ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ and instr' =
| BrOnNonNull of idx (* break on non-null *)
| Return (* break from function body *)
| Call of idx (* call function *)
| CallRef (* call function through reference *)
| CallRef of idx (* call function through reference *)
| CallIndirect of idx * idx (* call function through table *)
| ReturnCallRef (* tail call through reference *)
| ReturnCallRef of idx (* tail call through reference *)
| LocalGet of idx (* read local idxiable *)
| LocalSet of idx (* write local idxiable *)
| LocalTee of idx (* write local idxiable and keep value *)
Expand Down
3 changes: 2 additions & 1 deletion interpreter/syntax/free.ml
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ let rec instr (e : instr) =
| If (bt, es1, es2) -> block_type bt ++ block es1 ++ block es2
| Br x | BrIf x | BrOnNull x | BrOnNonNull x -> labels (idx x)
| BrTable (xs, x) -> list (fun x -> labels (idx x)) (x::xs)
| Return | CallRef | ReturnCallRef -> empty
| Return -> empty
| Call x -> funcs (idx x)
| CallRef x | ReturnCallRef x -> types (idx x)
| CallIndirect (x, y) -> tables (idx x) ++ types (idx y)
| LocalGet x | LocalSet x | LocalTee x -> locals (idx x)
| GlobalGet x | GlobalSet x -> globals (idx x)
Expand Down
4 changes: 2 additions & 2 deletions interpreter/syntax/operators.ml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ let br_on_non_null x = BrOnNonNull x

let return = Return
let call x = Call x
let call_ref = CallRef
let call_ref x = CallRef x
let call_indirect x y = CallIndirect (x, y)
let return_call_ref = ReturnCallRef
let return_call_ref x = ReturnCallRef x

let local_get x = LocalGet x
let local_set x = LocalSet x
Expand Down
4 changes: 2 additions & 2 deletions interpreter/text/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,10 @@ let rec instr e =
| BrOnNonNull x -> "br_on_non_null " ^ var x, []
| Return -> "return", []
| Call x -> "call " ^ var x, []
| CallRef -> "call_ref", []
| CallRef x -> "call_ref " ^ var x, []
| CallIndirect (x, y) ->
"call_indirect " ^ var x, [Node ("type " ^ var y, [])]
| ReturnCallRef -> "return_call_ref", []
| ReturnCallRef x -> "return_call_ref " ^ var x, []
| LocalGet x -> "local.get " ^ var x, []
| LocalSet x -> "local.set " ^ var x, []
| LocalTee x -> "local.tee " ^ var x, []
Expand Down
4 changes: 2 additions & 2 deletions interpreter/text/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,8 @@ plain_instr :
| BR_ON_NON_NULL var { fun c -> br_on_non_null ($2 c label) }
| RETURN { fun c -> return }
| CALL var { fun c -> call ($2 c func) }
| CALL_REF { fun c -> call_ref }
| RETURN_CALL_REF { fun c -> return_call_ref }
| CALL_REF var { fun c -> call_ref ($2 c type_) }
| RETURN_CALL_REF var { fun c -> return_call_ref ($2 c type_) }
| LOCAL_GET var { fun c -> local_get ($2 c local) }
| LOCAL_SET var { fun c -> local_set ($2 c local) }
| LOCAL_TEE var { fun c -> local_tee ($2 c local) }
Expand Down
38 changes: 10 additions & 28 deletions interpreter/valid/valid.ml
Original file line number Diff line number Diff line change
Expand Up @@ -392,18 +392,9 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in
let FuncT (ts1, ts2) = func c x in
ts1 --> ts2, []

| CallRef ->
(match peek_ref 0 s e.at with
| (_, DefHT x) ->
let FuncT (ts1, ts2) = func_type c (x @@ e.at) in
(ts1 @ [RefT (Null, DefHT x)]) --> ts2, []
| (_, BotHT) ->
[RefT (Null, BotHT)] -->... [], []
| rt ->
error e.at
("type mismatch: instruction requires function reference type" ^
" but stack has " ^ string_of_val_type (RefT rt))
)
| CallRef x ->
let FuncT (ts1, ts2) = func_type c x in
(ts1 @ [RefT (Null, DefHT x.it)]) --> ts2, []

| CallIndirect (x, y) ->
let TableT (_lim, t) = table c x in
Expand All @@ -413,22 +404,13 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in
" but table has element type " ^ string_of_ref_type t);
(ts1 @ [NumT I32T]) --> ts2, []

| ReturnCallRef ->
(match peek_ref 0 s e.at with
| (_, DefHT x) ->
let FuncT (ts1, ts2) = func_type c (x @@ e.at) in
require (match_result_type c.types [] ts2 c.results) e.at
("type mismatch: current function requires result type " ^
string_of_result_type c.results ^
" but callee returns " ^ string_of_result_type ts2);
(ts1 @ [RefT (Null, DefHT x)]) -->... [], []
| (_, BotHT) ->
[RefT (Null, BotHT)] -->... [], []
| rt ->
error e.at
("type mismatch: instruction requires function reference type" ^
" but stack has " ^ string_of_ref_type rt)
)
| ReturnCallRef x ->
let FuncT (ts1, ts2) = func_type c x in
require (match_result_type c.types [] ts2 c.results) e.at
("type mismatch: current function requires result type " ^
string_of_result_type c.results ^
" but callee returns " ^ string_of_result_type ts2);
(ts1 @ [RefT (Null, DefHT x.it)]) -->... [], []

| LocalGet x ->
let LocalT (init, t) = local c x in
Expand Down
24 changes: 12 additions & 12 deletions proposals/function-references/Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The function `$hof` takes a function pointer as parameter, and is invoked by `$c
(type $i32-i32 (func (param i32) (result i32)))
(func $hof (param $f (ref $i32-i32)) (result i32)
(i32.add (i32.const 10) (call_ref (i32.const 42) (local.get $f)))
(i32.add (i32.const 10) (call_ref $i32-i32 (i32.const 42) (local.get $f)))
)
(func $inc (param $i i32) (result i32)
Expand Down Expand Up @@ -204,21 +204,21 @@ Note: Extending block types with index sets to allow initialization status to la
- iff `$f : $t`
- this is a *constant instruction*

* `call_ref` calls a function through a reference
- `call_ref : [t1* (ref null $t)] -> [t2*]`
* `call_ref <typeidx>` calls a function through a reference
- `call_ref $t : [t1* (ref null $t)] -> [t2*]`
- iff `$t = [t1*] -> [t2*]`
- traps on `null`

* With the [tail call proposal](https://github.com/WebAssembly/tail-call/blob/master/proposals/tail-call/Overview.md), there will also be `return_call_ref`:
- `return_call_ref : [t1* (ref null $t)] -> [t2*]`
- `return_call_ref $t : [t1* (ref null $t)] -> [t2*]`
- iff `$t = [t1*] -> [t2*]`
- and `t2* <: C.result`
- traps on `null`


#### Optional References

* `ref.null` is generalised to take a `<heaptype>` immediate
* `ref.null <heaptype>` is generalised to take a `<heaptype>` immediate
- `ref.null ht: [] -> [(ref null ht)]`
- iff `ht ok`

Expand All @@ -227,13 +227,13 @@ Note: Extending block types with index sets to allow initialization status to la
- iff `ht ok`
- traps on `null`

* `br_on_null $l` checks for null and branches if present
* `br_on_null <labelidx>` checks for null and branches if present
- `br_on_null $l : [t* (ref null ht)] -> [t* (ref ht)]`
- iff `$l : [t*]`
- and `ht ok`
- branches to `$l` on `null`, otherwise returns operand as non-null

* `br_on_non_null $l` checks for null and branches if not present
* `br_on_non_null <labelidx>` checks for null and branches if not present
- `br_on_non_null $l : [t* (ref null ht)] -> [t*]`
- iff `$l : [t* (ref ht)]`
- and `ht ok`
Expand All @@ -246,15 +246,15 @@ Note: Extending block types with index sets to allow initialization status to la

Typing of local instructions is updated to account for the initialization status of locals.

* `local.get $x`
* `local.get <localidx>`
- `local.get $x : [] -> [t]`
- iff `$x : set t`

* `local.set $x`
* `local.set <localidx>`
- `local.set $x : [t] -> [] $x`
- iff `$x : set? t`

* `local.tee $x`
* `local.tee <localidx>`
- `local.tee $x : [t] -> [t] $x`
- iff `$x : set? t`

Expand Down Expand Up @@ -319,8 +319,8 @@ The opcode for heap types is encoded as an `s33`.

| Opcode | Instruction | Immediates |
| ------ | ------------------------ | ---------- |
| 0x14 | `call_ref` | |
| 0x15 | `return_call_ref` | |
| 0x14 | `call_ref $t` | `$t : u32` |
| 0x15 | `return_call_ref $t` | `$t : u32` |
| 0xd3 | `ref.as_non_null` | |
| 0xd4 | `br_on_null $l` | `$l : u32` |
| 0xd6 | `br_on_non_null $l` | `$l : u32` |
Expand Down
8 changes: 4 additions & 4 deletions test/core/br_on_non_null.wast
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
(type $t (func (result i32)))

(func $nn (param $r (ref $t)) (result i32)
(call_ref
(call_ref $t
(block $l (result (ref $t))
(br_on_non_null $l (local.get $r))
(return (i32.const -1))
)
)
)
(func $n (param $r (ref null $t)) (result i32)
(call_ref
(call_ref $t
(block $l (result (ref $t))
(br_on_non_null $l (local.get $r))
(return (i32.const -1))
Expand All @@ -27,7 +27,7 @@

(func (export "unreachable") (result i32)
(block $l
(return (call_ref (br_on_null $l (unreachable))))
(return (call_ref $t (br_on_null $l (unreachable))))
)
(i32.const -1)
)
Expand All @@ -53,7 +53,7 @@
(func $f (param i32) (result i32) (i32.mul (local.get 0) (local.get 0)))

(func $a (param $n i32) (param $r (ref null $t)) (result i32)
(call_ref
(call_ref $t
(block $l (result i32 (ref $t))
(return (br_on_non_null $l (local.get $n) (local.get $r)))
)
Expand Down
Loading

0 comments on commit 2b194ce

Please sign in to comment.