Skip to content

Commit 8f790a0

Browse files
authored
Merge pull request #47 from dhil/wasmfx-merge
Merge with stack-switching/wasmfx
2 parents ca746e0 + 8d8942a commit 8f790a0

File tree

22 files changed

+3588
-67
lines changed

22 files changed

+3588
-67
lines changed

README.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
1-
[![CI for specs](https://github.com/wasmfx/specfx/actions/workflows/ci-spec.yml/badge.svg)](https://github.com/wasmfx/specfx/actions/workflows/ci-spec.yml)
2-
[![CI for interpreter & tests](https://github.com/wasmfx/specfx/actions/workflows/ci-interpreter.yml/badge.svg)](https://github.com/wasmfx/specfx/actions/workflows/ci-interpreter.yml)
1+
# Stack-Switching Proposal for WebAssembly
32

4-
# Typed Continuations Proposal for WebAssembly
3+
This repository is a clone of [`WebAssembly/spec`](https://github.com/WebAssembly/spec/). It is meant for discussion, prototype specification, and implementation of a proposal to add
4+
support for stack-switching.
55

6-
This repository is a clone of
7-
[github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/).
8-
It is meant for discussion, prototype specification and implementation
9-
of a proposal to add support for different patterns of non-local
10-
control flow to WebAssembly.
6+
See the [explainer](proposals/stack-switching/Explainer.md) for a high-level summary of the proposal.
117

12-
The proposal is fully implemented as part of the reference interpreter.
8+
## Previous proposals
9+
10+
The current explainer represents the unification of two previous proposals: Typed Continuations (wasmfx) and Bag of Stacks (bos). (The explainers have now been unified. Once the reference interpreter and examples are adapted for the unified proposal this section will be removed from the README.)
11+
12+
#### Typed Continuations
1313

1414
* See the [explainer](proposals/continuations/Explainer.md) for a high-level summary of the proposal.
1515

1616
* See the [overview](proposals/continuations/Overview.md) for a more formal description of the proposal.
1717

18+
* An [implementation](https://github.com/WebAssembly/stack-switching/tree/wasmfx) is available as an extension to the reference interpreter. It is accesible from the `wasmfx` branch of this repository.
19+
1820
* See the [examples](proposals/continuations/examples) for Wasm code for implementing various different features including lightweight threads, actors, and async/await.
1921

20-
Original `README` from upstream repository follows.
22+
#### Bag of Stacks Proposal
23+
24+
* See the [explainer](proposals/bag-o-stacks/Explainer.md) for a high-level summary of the proposal.
25+
26+
Original README from upstream repository follows.
27+
28+
--------------------------------------------------------------------------------
29+
30+
[![CI for specs](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-spec.yml/badge.svg)](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-spec.yml)
31+
[![CI for interpreter & tests](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-interpreter.yml/badge.svg)](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-interpreter.yml)
2132

2233
# spec
2334

document/core/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@
6666
logo = 'static/webassembly.png'
6767

6868
# The name of the GitHub repository this resides in
69-
repo = 'continuations'
69+
repo = 'stack-switching'
7070

7171
# The name of the proposal it represents, if any
72-
proposal = 'continuations'
72+
proposal = 'stack-switching'
7373

7474
# The draft version string (clear out for release cuts)
7575
draft = ' (Draft ' + date.today().strftime("%Y-%m-%d") + ')'

interpreter/binary/decode.ml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -337,11 +337,6 @@ let block_type s =
337337
(fun s -> ValBlockType (Some (val_type s)));
338338
] s
339339

340-
let var_pair s =
341-
let x = at var s in
342-
let y = at var s in
343-
x, y
344-
345340
let local s =
346341
let n = u32 s in
347342
let t = at val_type s in
@@ -355,6 +350,16 @@ let locals s =
355350
s pos "too many locals";
356351
List.flatten (List.map (Lib.Fun.uncurry Lib.List32.make) nts)
357352

353+
let on_clause s =
354+
match byte s with
355+
| 0x00 ->
356+
let x = at var s in
357+
let y = at var s in
358+
(x, OnLabel y)
359+
| 0x01 ->
360+
let x = at var s in
361+
(x, OnSwitch)
362+
| _ -> error s (pos s) "ON opcode expected"
358363

359364
let rec instr s =
360365
let pos = pos s in
@@ -633,14 +638,18 @@ let rec instr s =
633638
| 0xe2 -> suspend (at var s)
634639
| 0xe3 ->
635640
let x = at var s in
636-
let xls = vec var_pair s in
641+
let xls = vec on_clause s in
637642
resume x xls
638643
| 0xe4 ->
639644
let x = at var s in
640645
let tag = at var s in
641-
let xls = vec var_pair s in
646+
let xls = vec on_clause s in
642647
resume_throw x tag xls
643648
| 0xe5 ->
649+
let x = at var s in
650+
let y = at var s in
651+
switch x y
652+
| 0xe6 ->
644653
let bt = block_type s in
645654
let es' = instr_block s in
646655
end_ s;

interpreter/binary/encode.ml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ struct
231231
let end_ () = op 0x0b
232232

233233
let var x = u32 x.it
234-
let var_pair (x, y) = var x; var y
235234

236235
let memop x {align; offset; _} =
237236
let has_var = x.it <> 0l in
@@ -255,6 +254,16 @@ struct
255254
| nlocs -> (1, loc) :: nlocs
256255
in vec local (List.fold_right combine locs [])
257256

257+
let on_clause (x, y) =
258+
match y with
259+
| OnSwitch ->
260+
byte 0x01; var x
261+
| OnLabel y ->
262+
byte 0x00; var x; var y
263+
264+
let resumetable xls =
265+
vec on_clause xls
266+
258267
let rec instr e =
259268
match e.it with
260269
| Unreachable -> op 0x00
@@ -291,9 +300,10 @@ struct
291300
| ContNew x -> op 0xe0; var x
292301
| ContBind (x, y) -> op 0xe1; var x; var y
293302
| Suspend x -> op 0xe2; var x
294-
| Resume (x, xls) -> op 0xe3; var x; vec var_pair xls
295-
| ResumeThrow (x, y, xls) -> op 0xe4; var x; var y; vec var_pair xls
296-
| Barrier (bt, es) -> op 0xe5; block_type bt; list instr es; end_ ()
303+
| Resume (x, xls) -> op 0xe3; var x; resumetable xls
304+
| ResumeThrow (x, y, xls) -> op 0xe4; var x; var y; resumetable xls
305+
| Switch (x, y) -> op 0xe5; var x; var y
306+
| Barrier (bt, es) -> op 0xe6; block_type bt; list instr es; end_ ()
297307

298308
| Throw x -> op 0x08; var x
299309
| ThrowRef -> op 0x0a

interpreter/exec/eval.ml

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ and admin_instr' =
7272
| Label of int * instr list * code
7373
| Frame of int * frame * code
7474
| Handler of int * catch list * code
75-
| Handle of (tag_inst * idx) list option * code
76-
| Suspending of tag_inst * value stack * ctxt
75+
| Handle of handle_table option * code
76+
| Suspending of tag_inst * value stack * ref_ option * ctxt
7777

7878
and ctxt = code -> code
79+
and handle_table = (tag_inst * idx) list * tag_inst list
7980

8081
type cont = int32 * ctxt (* TODO: represent type properly *)
8182
type ref_ += ContRef of cont option ref
@@ -223,6 +224,24 @@ let array_oob a i n =
223224
I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n))
224225
(I64_convert.extend_i32_u (Aggr.array_length a))
225226

227+
let handle_table (c : config) xls : handle_table =
228+
let suspend =
229+
List.filter_map
230+
(fun (x, hdl) ->
231+
match hdl with
232+
| OnLabel l -> Some (tag c.frame.inst x, l)
233+
| _ -> None)
234+
xls
235+
in
236+
let switch =
237+
List.filter_map
238+
(fun (x, hdl) ->
239+
match hdl with
240+
| OnSwitch -> Some (tag c.frame.inst x)
241+
| _ -> None)
242+
xls
243+
in
244+
(suspend, switch)
226245

227246
let rec step (c : config) : config =
228247
let vs, es = c.code in
@@ -358,7 +377,7 @@ let rec step (c : config) : config =
358377
let tagt = tag c.frame.inst x in
359378
let FuncT (ts, _) = func_type_of_tag_type c.frame.inst (Tag.type_of tagt) in
360379
let args, vs' = i32_split (Lib.List32.length ts) vs e.at in
361-
vs', [Suspending (tagt, args, fun code -> code) @@ e.at]
380+
vs', [Suspending (tagt, args, None, fun code -> code) @@ e.at]
362381

363382
| Resume (x, xls), Ref (NullRef _) :: vs ->
364383
vs, [Trapping "null continuation reference" @@ e.at]
@@ -367,7 +386,7 @@ let rec step (c : config) : config =
367386
vs, [Trapping "continuation already consumed" @@ e.at]
368387

369388
| Resume (x, xls), Ref (ContRef ({contents = Some (n, ctxt)} as cont)) :: vs ->
370-
let hs = List.map (fun (x, l) -> tag c.frame.inst x, l) xls in
389+
let hs = handle_table c xls in
371390
let args, vs' = i32_split n vs e.at in
372391
cont := None;
373392
vs', [Handle (Some hs, ctxt (args, [])) @@ e.at]
@@ -381,11 +400,22 @@ let rec step (c : config) : config =
381400
| ResumeThrow (x, y, xls), Ref (ContRef ({contents = Some (n, ctxt)} as cont)) :: vs ->
382401
let tagt = tag c.frame.inst y in
383402
let FuncT (ts, _) = func_type_of_tag_type c.frame.inst (Tag.type_of tagt) in
384-
let hs = List.map (fun (x, l) -> tag c.frame.inst x, l) xls in
385-
let args, vs' = split (List.length ts) vs e.at in
403+
let hs = handle_table c xls in
404+
let args, vs' = i32_split (Lib.List32.length ts) vs e.at in
386405
cont := None;
387406
vs', [Handle (Some hs, ctxt (args, [Plain (Throw x) @@ e.at])) @@ e.at]
388407

408+
| Switch (x, y), Ref (NullRef _) :: vs ->
409+
vs, [Trapping "null continuation reference" @@ e.at]
410+
411+
| Switch (x, y), Ref (ContRef {contents = None}) :: vs ->
412+
vs, [Trapping "continuation already consumed" @@ e.at]
413+
414+
| Switch (x, y), Ref (ContRef {contents = Some (n, ctxt)} as cont) :: vs ->
415+
let tagt = tag c.frame.inst y in
416+
let args, vs' = i32_split (Int32.sub n 1l) vs e.at in
417+
vs', [Suspending (tagt, args, Some cont, fun code -> code) @@ e.at]
418+
389419
| Barrier (bt, es'), vs ->
390420
let InstrT (ts1, _, _xs) = block_type c.frame.inst bt e.at in
391421
let args, vs' = i32_split (Lib.List32.length ts1) vs e.at in
@@ -1126,9 +1156,9 @@ let rec step (c : config) : config =
11261156
| Label (n, es0, (vs', [])), vs ->
11271157
vs' @ vs, []
11281158

1129-
| Label (n, es0, (vs', {it = Suspending (tagt, vs1, ctxt); at} :: es')), vs ->
1159+
| Label (n, es0, (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs ->
11301160
let ctxt' code = [], [Label (n, es0, compose (ctxt code) (vs', es')) @@ e.at] in
1131-
vs, [Suspending (tagt, vs1, ctxt') @@ at]
1161+
vs, [Suspending (tagt, vs1, contref, ctxt') @@ at]
11321162

11331163
| Label (n, es0, (vs', {it = ReturningInvoke (vs0, f); at} :: es')), vs ->
11341164
vs, [ReturningInvoke (vs0, f) @@ at]
@@ -1155,9 +1185,9 @@ let rec step (c : config) : config =
11551185
| Frame (n, frame', (vs', {it = Throwing (a, vs0); at} :: es')), vs ->
11561186
vs, [Throwing (a, vs0) @@ at]
11571187

1158-
| Frame (n, frame', (vs', {it = Suspending (tagt, vs1, ctxt); at} :: es')), vs ->
1188+
| Frame (n, frame', (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs ->
11591189
let ctxt' code = [], [Frame (n, frame', compose (ctxt code) (vs', es')) @@ e.at] in
1160-
vs, [Suspending (tagt, vs1, ctxt') @@ at]
1190+
vs, [Suspending (tagt, vs1, contref, ctxt') @@ at]
11611191

11621192
| Frame (n, frame', (vs', {it = Returning vs0; at} :: es')), vs ->
11631193
take n vs0 e.at @ vs, []
@@ -1197,6 +1227,10 @@ let rec step (c : config) : config =
11971227
| Handler (n, [], (vs', {it = Throwing (a, vs0); at} :: es')), vs ->
11981228
vs, [Throwing (a, vs0) @@ at]
11991229

1230+
| Handler (n, cs, (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs ->
1231+
let ctxt' code = [], [Handler (n, cs, compose (ctxt code) (vs', es')) @@ e.at] in
1232+
vs, [Suspending (tagt, vs1, contref, ctxt') @@ at]
1233+
12001234
| Handler (n, cs, (vs', e' :: es')), vs when is_jumping e' ->
12011235
vs, [e']
12021236

@@ -1233,16 +1267,25 @@ let rec step (c : config) : config =
12331267
| Handle (None, (vs', {it = Suspending _; at} :: es')), vs ->
12341268
vs, [Trapping "barrier hit by suspension" @@ at]
12351269

1236-
| Handle (Some hs, (vs', {it = Suspending (tagt, vs1, ctxt); at} :: es')), vs
1270+
| Handle (Some (hs, _), (vs', {it = Suspending (tagt, vs1, None, ctxt); at} :: es')), vs
12371271
when List.mem_assq tagt hs ->
12381272
let FuncT (_, ts) = func_type_of_tag_type c.frame.inst (Tag.type_of tagt) in
12391273
let ctxt' code = compose (ctxt code) (vs', es') in
12401274
[Ref (ContRef (ref (Some (Lib.List32.length ts, ctxt'))))] @ vs1 @ vs,
12411275
[Plain (Br (List.assq tagt hs)) @@ e.at]
12421276

1243-
| Handle (hso, (vs', {it = Suspending (tagt, vs1, ctxt); at} :: es')), vs ->
1277+
| Handle (Some (_, hs) as hso, (vs', {it = Suspending (tagt, vs1, Some (ContRef ({contents = Some (_, ctxt)} as cont)), ctxt'); at} :: es')), vs
1278+
when List.memq tagt hs ->
1279+
let FuncT (_, ts) = func_type_of_tag_type c.frame.inst (Tag.type_of tagt) in
1280+
let ctxt'' code = compose (ctxt' code) (vs', es') in
1281+
let cont' = Ref (ContRef (ref (Some (Int32.add (Lib.List32.length ts) 1l, ctxt'')))) in
1282+
let args = vs1 @ [cont'] in
1283+
cont := None;
1284+
vs' @ vs, [Handle (hso, ctxt (args, [])) @@ e.at]
1285+
1286+
| Handle (hso, (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs ->
12441287
let ctxt' code = [], [Handle (hso, compose (ctxt code) (vs', es')) @@ e.at] in
1245-
vs, [Suspending (tagt, vs1, ctxt') @@ at]
1288+
vs, [Suspending (tagt, vs1, contref, ctxt') @@ at]
12461289

12471290
| Handle (hso, (vs', e' :: es')), vs when is_jumping e' ->
12481291
vs, [e']
@@ -1251,7 +1294,8 @@ let rec step (c : config) : config =
12511294
let c' = step {c with code = code'} in
12521295
vs, [Handle (hso, c'.code) @@ e.at]
12531296

1254-
| Suspending (_, _, _), _ -> assert false
1297+
| Suspending (_, _, _, _), _ -> assert false
1298+
12551299
in {c with code = vs', es' @ List.tl es}
12561300

12571301

interpreter/syntax/ast.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ type vec = Value.vec Source.phrase
139139
type name = Utf8.unicode
140140

141141
type block_type = VarBlockType of idx | ValBlockType of val_type option
142+
type hdl = OnLabel of idx | OnSwitch
142143

143144
type instr = instr' Source.phrase
144145
and instr' =
@@ -166,8 +167,9 @@ and instr' =
166167
| ContNew of idx (* create continuation *)
167168
| ContBind of idx * idx (* bind continuation arguments *)
168169
| Suspend of idx (* suspend continuation *)
169-
| Resume of idx * (idx * idx) list (* resume continuation *)
170-
| ResumeThrow of idx * idx * (idx * idx) list (* abort continuation *)
170+
| Resume of idx * (idx * hdl) list (* resume continuation *)
171+
| ResumeThrow of idx * idx * (idx * hdl) list (* abort continuation *)
172+
| Switch of idx * idx (* direct switch continuation *)
171173
| Barrier of block_type * instr list (* guard against suspension *)
172174
| Throw of idx (* throw exception *)
173175
| ThrowRef (* rethrow exception *)

interpreter/syntax/free.ml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ let block_type = function
141141
| VarBlockType x -> types (idx x)
142142
| ValBlockType t -> opt val_type t
143143

144+
let hdl = function
145+
| OnLabel x -> labels (idx x)
146+
| OnSwitch -> empty
147+
144148
let rec instr (e : instr) =
145149
match e.it with
146150
| Unreachable | Nop | Drop -> empty
@@ -176,9 +180,10 @@ let rec instr (e : instr) =
176180
tables (idx x) ++ types (idx y)
177181
| ContNew x -> types (idx x)
178182
| ContBind (x, y) -> types (idx x) ++ types (idx y)
179-
| ResumeThrow (x, y, xys) -> types (idx x) ++ tags (idx y) ++ list (fun (x, y) -> tags (idx x) ++ labels (idx y)) xys
180-
| Resume (x, xys) -> types (idx x) ++ list (fun (x, y) -> tags (idx x) ++ labels (idx y)) xys
183+
| ResumeThrow (x, y, xys) -> types (idx x) ++ tags (idx y) ++ list (fun (x, y) -> tags (idx x) ++ hdl y) xys
184+
| Resume (x, xys) -> types (idx x) ++ list (fun (x, y) -> tags (idx x) ++ hdl y) xys
181185
| Suspend x -> tags (idx x)
186+
| Switch (x, z) -> types (idx x) ++ tags (idx z)
182187
| Throw x -> tags (idx x)
183188
| ThrowRef -> empty
184189
| TryTable (bt, cs, es) ->

interpreter/syntax/operators.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ let cont_bind x y = ContBind (x, y)
5050
let suspend x = Suspend x
5151
let resume x xys = Resume (x, xys)
5252
let resume_throw x y xys = ResumeThrow (x, y, xys)
53+
let switch x y = Switch (x, y)
5354
let barrier bt es = Barrier (bt, es)
5455
let throw x = Throw x
5556
let throw_ref = ThrowRef

interpreter/text/arrange.ml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,15 @@ let block_type = function
489489
| VarBlockType x -> [Node ("type " ^ var x, [])]
490490
| ValBlockType ts -> decls "result" (list_of_opt ts)
491491

492+
let hdl = function
493+
| OnLabel x -> var x
494+
| OnSwitch -> "switch"
495+
496+
let resumetable xys =
497+
List.map
498+
(fun (x, y) -> Node ("on " ^ var x ^ " " ^ hdl y, []))
499+
xys
500+
492501
let rec instr e =
493502
let head, inner =
494503
match e.it with
@@ -526,11 +535,11 @@ let rec instr e =
526535
| ContBind (x, y) -> "cont.bind " ^ var x ^ " " ^ var y, []
527536
| Suspend x -> "suspend " ^ var x, []
528537
| Resume (x, xys) ->
529-
"resume " ^ var x,
530-
List.map (fun (x, y) -> Node ("on " ^ var x ^ " " ^ var y, [])) xys
538+
"resume " ^ var x, resumetable xys
531539
| ResumeThrow (x, y, xys) ->
532-
"resume_throw " ^ var x ^ " " ^ var y,
533-
List.map (fun (x, y) -> Node ("on " ^ var x ^ " " ^ var y, [])) xys
540+
"resume_throw " ^ var x ^ " " ^ var y, resumetable xys
541+
| Switch (x, z) ->
542+
"switch " ^ var x ^ " " ^ var z, []
534543
| Barrier (bt, es) -> "barrier", block_type bt @ list instr es
535544
| Throw x -> "throw " ^ var x, []
536545
| ThrowRef -> "throw_ref", []

interpreter/text/lexer.mll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ rule token = parse
231231
| "resume" -> RESUME
232232
| "resume_throw" -> RESUME_THROW
233233
| "barrier" -> BARRIER
234+
| "switch" -> SWITCH
234235

235236

236237
| "local.get" -> LOCAL_GET

0 commit comments

Comments
 (0)