Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit e15eb41

Browse files
authored
[interpreter] Cherry-pick tail call support (#274)
This cherry-picks interpreter changes from https://github.com/WebAssembly/tail-call to run spec tests that mix `try`s with `return_call(_indirect)`s (#275). When tail-call is merged to the main spec repo, we can revert this change and merge the upstream spec. This also adds missing handling for `ReturningInvoke` in the new `Catch`/`Caught`/`Delegate` instructions in the evaluator.
1 parent 35b435a commit e15eb41

File tree

10 files changed

+74
-8
lines changed

10 files changed

+74
-8
lines changed

interpreter/binary/decode.ml

+6-1
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,13 @@ let rec instr s =
303303
let y = at var s in
304304
let x = at var s in
305305
call_indirect x y
306+
| 0x12 -> return_call (at var s)
307+
| 0x13 ->
308+
let y = at var s in
309+
let x = at var s in
310+
return_call_indirect x y
306311

307-
| 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 as b -> illegal s pos b
312+
| 0x14 | 0x15 | 0x16 | 0x17 as b -> illegal s pos b
308313

309314
| 0x18 -> error s pos "misplaced DELEGATE opcode"
310315
| 0x19 -> error s pos "misplaced CATCH_ALL opcode"

interpreter/binary/encode.ml

+2
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ struct
187187
| Return -> op 0x0f
188188
| Call x -> op 0x10; var x
189189
| CallIndirect (x, y) -> op 0x11; var y; var x
190+
| ReturnCall x -> op 0x12; var x
191+
| ReturnCallIndirect (x, y) -> op 0x13; var y; var x
190192
| Throw x -> op 0x08; var x
191193
| Rethrow x -> op 0x09; var x
192194

interpreter/exec/eval.ml

+28-4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ and admin_instr' =
6363
| Invoke of func_inst
6464
| Trapping of string
6565
| Returning of value stack
66+
| ReturningInvoke of value stack * func_inst
6667
| Breaking of int32 * value stack
6768
| Throwing of Tag.t * value stack
6869
| Rethrowing of int32 * (admin_instr -> admin_instr)
@@ -214,6 +215,21 @@ let rec step (c : config) : config =
214215
else
215216
vs, [Invoke func @@ e.at]
216217

218+
| ReturnCall x, vs ->
219+
(match (step {c with code = (vs, [Plain (Call x) @@ e.at])}).code with
220+
| vs', [{it = Invoke a; at}] -> vs', [ReturningInvoke (vs', a) @@ at]
221+
| _ -> assert false
222+
)
223+
224+
| ReturnCallIndirect (x, y), vs ->
225+
(match
226+
(step {c with code = (vs, [Plain (CallIndirect (x, y)) @@ e.at])}).code
227+
with
228+
| vs', [{it = Invoke a; at}] -> vs', [ReturningInvoke (vs', a) @@ at]
229+
| vs', [{it = Trapping s; at}] -> vs', [Trapping s @@ at]
230+
| _ -> assert false
231+
)
232+
217233
| Throw x, vs ->
218234
let t = tag frame.inst x in
219235
let FuncType (ts, _) = Tag.type_of t in
@@ -629,7 +645,8 @@ let rec step (c : config) : config =
629645
| Trapping msg, vs ->
630646
assert false
631647

632-
| Returning vs', vs ->
648+
| Returning _, vs
649+
| ReturningInvoke _, vs ->
633650
Crash.error e.at "undefined frame"
634651

635652
| Breaking (k, vs'), vs ->
@@ -653,6 +670,9 @@ let rec step (c : config) : config =
653670
| Label (n, es0, (vs', {it = Returning vs0; at} :: es')), vs ->
654671
vs, [Returning vs0 @@ at]
655672

673+
| Label (n, es0, (vs', {it = ReturningInvoke (vs0, f); at} :: es')), vs ->
674+
vs, [ReturningInvoke (vs0, f) @@ at]
675+
656676
| Label (n, es0, (vs', {it = Breaking (0l, vs0); at} :: es')), vs ->
657677
take n vs0 e.at @ vs, List.map plain es0
658678

@@ -684,6 +704,10 @@ let rec step (c : config) : config =
684704
| Frame (n, frame', (vs', {it = Returning vs0; at} :: es')), vs ->
685705
take n vs0 e.at @ vs, []
686706

707+
| Frame (n, frame', (vs', {it = ReturningInvoke (vs0, f); at} :: es')), vs ->
708+
let FuncType (ins, out) = Func.type_of f in
709+
take (Lib.List32.length ins) vs0 e.at @ vs, [Invoke f @@ at]
710+
687711
| Frame (n, frame', (vs', {it = Throwing (a, vs0); at} :: es')), vs ->
688712
vs, [Throwing (a, vs0) @@ at]
689713

@@ -694,7 +718,7 @@ let rec step (c : config) : config =
694718
| Catch (n, cts, ca, (vs', [])), vs ->
695719
vs' @ vs, []
696720

697-
| Catch (n, cts, ca, (vs', ({it = Trapping _ | Breaking _ | Returning _ | Delegating _; at} as e) :: es')), vs ->
721+
| Catch (n, cts, ca, (vs', ({it = Trapping _ | Breaking _ | Returning _ | ReturningInvoke _ | Delegating _; at} as e) :: es')), vs ->
698722
vs, [e]
699723

700724
| Catch (n, cts, ca, (vs', {it = Rethrowing (k, cont); at} :: es')), vs ->
@@ -719,7 +743,7 @@ let rec step (c : config) : config =
719743
| Caught (n, a, vs0, (vs', [])), vs ->
720744
vs' @ vs, []
721745

722-
| Caught (n, a, vs0, (vs', ({it = Trapping _ | Breaking _ | Returning _ | Throwing _ | Delegating _; at} as e) :: es')), vs ->
746+
| Caught (n, a, vs0, (vs', ({it = Trapping _ | Breaking _ | Returning _ | ReturningInvoke _ | Throwing _ | Delegating _; at} as e) :: es')), vs ->
723747
vs, [e]
724748

725749
| Caught (n, a, vs0, (vs', {it = Rethrowing (0l, cont); at} :: es')), vs ->
@@ -735,7 +759,7 @@ let rec step (c : config) : config =
735759
| Delegate (l, (vs', [])), vs ->
736760
vs' @ vs, []
737761

738-
| Delegate (l, (vs', ({it = Trapping _ | Breaking _ | Returning _ | Rethrowing _ | Delegating _; at} as e) :: es')), vs ->
762+
| Delegate (l, (vs', ({it = Trapping _ | Breaking _ | Returning _ | ReturningInvoke _ | Rethrowing _ | Delegating _; at} as e) :: es')), vs ->
739763
vs, [e]
740764

741765
| Delegate (l, (vs', {it = Throwing (a, vs0); at} :: es')), vs ->

interpreter/syntax/ast.ml

+2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ and instr' =
149149
| Return (* break from function body *)
150150
| Call of var (* call function *)
151151
| CallIndirect of var * var (* call function through table *)
152+
| ReturnCall of var (* tail-call function *)
153+
| ReturnCallIndirect of var * var (* tail-call function through table *)
152154
| LocalGet of var (* read local variable *)
153155
| LocalSet of var (* write local variable *)
154156
| LocalTee of var (* write local variable and keep value *)

interpreter/syntax/free.ml

+3-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,9 @@ let rec instr (e : instr) =
7979
| Br x | BrIf x -> labels (var x)
8080
| BrTable (xs, x) -> list (fun x -> labels (var x)) (x::xs)
8181
| Return -> empty
82-
| Call x -> funcs (var x)
83-
| CallIndirect (x, y) -> tables (var x) ++ types (var y)
82+
| Call x | ReturnCall x -> funcs (var x)
83+
| CallIndirect (x, y) | ReturnCallIndirect (x, y) ->
84+
tables (var x) ++ types (var y)
8485
| LocalGet x | LocalSet x | LocalTee x -> locals (var x)
8586
| GlobalGet x | GlobalSet x -> globals (var x)
8687
| TableGet x | TableSet x | TableSize x | TableGrow x | TableFill x ->

interpreter/syntax/operators.ml

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ let br_table xs x = BrTable (xs, x)
2929
let return = Return
3030
let call x = Call x
3131
let call_indirect x y = CallIndirect (x, y)
32+
let return_call x = ReturnCall x
33+
let return_call_indirect x y = ReturnCallIndirect (x, y)
3234
let throw x = Throw x
3335
let rethrow x = Rethrow x
3436

interpreter/text/arrange.ml

+3
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,9 @@ let rec instr e =
453453
| Call x -> "call " ^ var x, []
454454
| CallIndirect (x, y) ->
455455
"call_indirect " ^ var x, [Node ("type " ^ var y, [])]
456+
| ReturnCall x -> "return_call " ^ var x, []
457+
| ReturnCallIndirect (x, y) ->
458+
"return_call_indirect " ^ var x, [Node ("type " ^ var y, [])]
456459
| LocalGet x -> "local.get " ^ var x, []
457460
| LocalSet x -> "local.set " ^ var x, []
458461
| LocalTee x -> "local.tee " ^ var x, []

interpreter/text/lexer.mll

+2
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ rule token = parse
168168
| "select" -> SELECT
169169
| "call" -> CALL
170170
| "call_indirect" -> CALL_INDIRECT
171+
| "return_call" -> RETURN_CALL
172+
| "return_call_indirect" -> RETURN_CALL_INDIRECT
171173

172174
| "local.get" -> LOCAL_GET
173175
| "local.set" -> LOCAL_SET

interpreter/text/parser.mly

+15-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ let inline_type_explicit (c : context) x ft at =
218218
%token UNREACHABLE NOP DROP SELECT
219219
%token BLOCK END IF THEN ELSE LOOP BR BR_IF BR_TABLE TRY DO CATCH CATCH_ALL
220220
%token DELEGATE
221-
%token CALL CALL_INDIRECT RETURN
221+
%token CALL CALL_INDIRECT RETURN RETURN_CALL RETURN_CALL_INDIRECT
222222
%token LOCAL_GET LOCAL_SET LOCAL_TEE GLOBAL_GET GLOBAL_SET
223223
%token TABLE_GET TABLE_SET
224224
%token TABLE_SIZE TABLE_GROW TABLE_FILL TABLE_COPY TABLE_INIT ELEM_DROP
@@ -395,6 +395,7 @@ plain_instr :
395395
br_table xs x }
396396
| RETURN { fun c -> return }
397397
| CALL var { fun c -> call ($2 c func) }
398+
| RETURN_CALL var { fun c -> return_call ($2 c func) }
398399
| THROW var { fun c -> throw ($2 c tag) }
399400
| RETHROW var { fun c -> rethrow ($2 c label) }
400401
| LOCAL_GET var { fun c -> local_get ($2 c local) }
@@ -477,6 +478,14 @@ call_instr_instr_list :
477478
{ let at1 = ati 1 in
478479
fun c -> let x, es = $2 c in
479480
(call_indirect (0l @@ at1) x @@ at1) :: es }
481+
| RETURN_CALL_INDIRECT var call_instr_type_instr_list
482+
{ let at1 = ati 1 in
483+
fun c -> let x, es = $3 c in
484+
(return_call_indirect ($2 c table) x @@ at1) :: es }
485+
| RETURN_CALL_INDIRECT call_instr_type_instr_list /* Sugar */
486+
{ let at1 = ati 1 in
487+
fun c -> let x, es = $2 c in
488+
(return_call_indirect (0l @@ at1) x @@ at1) :: es }
480489

481490
call_instr_type_instr_list :
482491
| type_use call_instr_params_instr_list
@@ -600,6 +609,11 @@ expr1 : /* Sugar */
600609
| CALL_INDIRECT call_expr_type /* Sugar */
601610
{ let at1 = ati 1 in
602611
fun c -> let x, es = $2 c in es, call_indirect (0l @@ at1) x }
612+
| RETURN_CALL_INDIRECT var call_expr_type
613+
{ fun c -> let x, es = $3 c in es, return_call_indirect ($2 c table) x }
614+
| RETURN_CALL_INDIRECT call_expr_type /* Sugar */
615+
{ let at1 = ati 1 in
616+
fun c -> let x, es = $2 c in es, return_call_indirect (0l @@ at1) x }
603617
| BLOCK labeling_opt block
604618
{ fun c -> let c' = $2 c [] in let bt, es = $3 c' in [], block bt es }
605619
| LOOP labeling_opt block

interpreter/valid/valid.ml

+11
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,17 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : op_type
302302
" but table has " ^ string_of_ref_type t);
303303
(ts1 @ [NumType I32Type]) --> ts2
304304

305+
| ReturnCall x ->
306+
let FuncType (ins, out) = func c x in
307+
require (out = c.results) e.at "type mismatch in function result";
308+
ins -->... []
309+
310+
| ReturnCallIndirect (x, y) ->
311+
let TableType (lim, t) = table c x in
312+
let FuncType (ins, out) = type_ c y in
313+
require (out = c.results) e.at "type mismatch in function result";
314+
(ins @ [NumType I32Type]) -->... []
315+
305316
| Throw x ->
306317
let TagType y = tag c x in
307318
let FuncType (ts1, _) = type_ c (y @@ e.at) in

0 commit comments

Comments
 (0)