From 1d67c4d5749431c6a9524316db8175507d795d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Fri, 12 Apr 2024 16:44:23 +0200 Subject: [PATCH] WasmFX documents&examples (#46) --- proposals/continuations/Explainer.md | 196 +++++++++--------- proposals/continuations/Overview.md | 54 +++-- .../continuations/examples/actor-lwt.wast | 80 +++---- proposals/continuations/examples/actor.wast | 74 +++---- .../continuations/examples/async-await.wast | 79 +++---- .../continuations/examples/control-lwt.wast | 95 +++++---- .../continuations/examples/fun-actor-lwt.wast | 146 +++++++------ proposals/continuations/examples/fun-lwt.wast | 42 ++-- .../continuations/examples/fun-pipes.wast | 8 +- .../continuations/examples/fun-state.wast | 6 +- .../continuations/examples/generators.wast | 166 +++++++++++++++ proposals/continuations/examples/lwt.wast | 26 +-- proposals/continuations/examples/pipes.wast | 8 +- .../continuations/examples/static-lwt.wast | 10 +- 14 files changed, 589 insertions(+), 401 deletions(-) create mode 100644 proposals/continuations/examples/generators.wast diff --git a/proposals/continuations/Explainer.md b/proposals/continuations/Explainer.md index f5cd25ef8..904a52bf6 100644 --- a/proposals/continuations/Explainer.md +++ b/proposals/continuations/Explainer.md @@ -15,7 +15,7 @@ single new reference type for *continuations*. 3. [Instruction set](#instruction-set) 1. [Declaring control tags](#declaring-control-tags) 2. [Creating continuations](#creating-continuations) - 3. [Resuming continuations](#resuming-continuations) + 3. [Invoking continuations](#invoking-continuations) 4. [Suspending continuations](#suspending-continuations) 5. [Binding continuations](#binding-continuations) 6. [Trapping continuations](#trapping-continuations) @@ -158,7 +158,7 @@ stacks, but other implementations are also possible. The proposal adds a new reference type for continuations. -```wasm +```wast (cont $t) ``` @@ -168,7 +168,7 @@ continuation, and whose return types `tr*` describes the stack shape after the continuation has run to completion. As a shorthand, we will often write the function type inline and write a continuation type as -```wasm +```wast (cont [tp*] -> [tr*]) ``` @@ -179,7 +179,7 @@ A control tag is similar to an exception extended with a result type *resumable* exception. A tag declaration provides the type signature of a control tag. -```wasm +```wast (tag $e (param tp*) (result tr*)) ``` @@ -195,7 +195,7 @@ for indicating that such a declaration is in scope. The following instruction creates a continuation in *suspended state* from a function. -```wasm +```wast cont.new $ct : [(ref $ft)] -> [(ref $ct)] where: - $ft = func [t1*] -> [t2*] @@ -215,38 +215,40 @@ The first way to invoke a continuation resumes the continuation under a *handler*, which handles subsequent control suspensions within the continuation. -```wasm - resume (tag $e $l)* : [tp* (ref $ct)] -> [tr*] +```wast + resume $ct (tag $e $l)* : [tp* (ref $ct)] -> [tr*] where: - $ct = cont [tp*] -> [tr*] ``` -The `resume` instruction is parameterised by a handler defined by a -collection of pairs of control tags and labels. Each pair maps a -control tag to a label pointing to its corresponding handler code. The -`resume` instruction consumes its continuation argument, meaning a -continuation may be resumed only once. +The `resume` instruction is parameterised by a continuation type and a +handler dispatch table defined by a collection of pairs of control +tags and labels. Each pair maps a control tag to a label pointing to +its corresponding handler code. The `resume` instruction consumes its +continuation argument, meaning a continuation may be resumed only +once. The second way to invoke a continuation is to raise an exception at the control tag invocation site. This amounts to performing "an abortive action" which causes the stack to be unwound. -```wasm - resume_throw $exn : [tp* (ref $ct)])] -> [tr*] +```wast + resume_throw $ct $exn (tag $e $l)* : [tp* (ref $ct)])] -> [tr*] where: - $ct = cont [ta*] -> [tr*] - $exn : [tp*] -> [] ``` -The instruction `resume_throw` is parameterised by the exception to be -raised at the control tag invocation site. As with `resume`, this -instruction also fully consumes its continuation -argument. Operationally, this instruction raises the exception `$exn` -with parameters of type `tp*` at the control tag invocation point in -the context of the supplied continuation. As an exception is being -raised (the continuation is not actually being supplied a value) the -parameter types for the continuation `ta*` are unconstrained. +The instruction `resume_throw` is parameterised by a continuation +type, the exception to be raised at the control tag invocation site, +and a handler dispatch table. As with `resume`, this instruction also +fully consumes its continuation argument. Operationally, this +instruction raises the exception `$exn` with parameters of type `tp*` +at the control tag invocation point in the context of the supplied +continuation. As an exception is being raised (the continuation is not +actually being supplied a value) the parameter types for the +continuation `ta*` are unconstrained. ### Suspending continuations @@ -254,7 +256,7 @@ A computation running inside a continuation can suspend itself by invoking one of the declared control tags. -```wasm +```wast suspend $e : [tp*] -> [tr*] where: - $e : [tp*] -> [tr*] @@ -282,8 +284,8 @@ continuation with compatible type (the [Examples](#examples) section provides several example usages of `cont.bind`). -```wasm - cont.bind $ct2 : [tp1* (ref $ct1)] -> [(ref $ct2)] +```wast + cont.bind $ct1 $ct2 : [tp1* (ref $ct1)] -> [(ref $ct2)] where: $ct1 = cont [tp1* tp2*] -> [tr*] $ct2 = cont [tp2*] -> [tr*] @@ -302,7 +304,7 @@ certain abstraction or language boundaries, we provide an instruction for explicitly trapping attempts at reifying stacks across a certain point. -```wasm +```wast barrier $l bt instr* end : [t1*] -> [t2*] where: - bt = [t1*] -> [t2*] @@ -353,7 +355,7 @@ continuations. In their most basic *static* form we assume a fixed collection of cooperative threads with a single tag that allows a thread to signal that it is willing to yield. -```wasm +```wast (module $lwt (tag $yield (export "yield")) ) @@ -363,7 +365,7 @@ thread to signal that it is willing to yield. The `$yield` tag takes no parameter and has no result. Having declared it, we can now write some cooperative threads as functions. -```wasm +```wast (module $example (tag $yield (import "lwt" "yield")) (func $log (import "spectest" "print_i32") (param i32)) @@ -405,7 +407,7 @@ tag, because we have not yet specified how to handle it. We now define a scheduler. -```wasm +```wast (module $scheduler (type $func (func)) (type $cont (cont $func)) @@ -421,8 +423,8 @@ We now define a scheduler. (loop $l (if (call $queue-empty) (then (return))) (block $on_yield (result (ref $cont)) - (resume (tag $yield $on_yield) - (call $dequeue) + (resume $cont (tag $yield $on_yield) + (call $dequeue) ) (br $l) ;; thread terminated ) ;; $on_yield (result (ref $cont)) @@ -452,7 +454,7 @@ new continuation for each, enqueue the continuations, and invoke the scheduler. The `cont.new` operation turns a function reference into a corresponding continuation reference. -```wasm +```wast (module (type $func (func)) (type $cont (cont $func)) @@ -469,9 +471,9 @@ corresponding continuation reference. (elem declare func $thread1 $thread2 $thread3) (func (export "run") - (call $enqueue (cont.new (type $cont) (ref.func $thread1))) - (call $enqueue (cont.new (type $cont) (ref.func $thread2))) - (call $enqueue (cont.new (type $cont) (ref.func $thread3))) + (call $enqueue (cont.new $cont (ref.func $thread1))) + (call $enqueue (cont.new $cont (ref.func $thread2))) + (call $enqueue (cont.new $cont (ref.func $thread3))) (call $log (i32.const -1)) (call $scheduler) @@ -505,7 +507,7 @@ The threads are interleaved as expected. We can make our lightweight threads functionality considerably more expressive by allowing new threads to be forked dynamically. -```wasm +```wast (module $lwt (type $func (func)) (type $cont (cont $func)) @@ -520,7 +522,7 @@ We declare a new `$fork` tag that takes a continuation as a parameter and (like `$yield`) returns no result. Now we modify our example to fork each of the three threads from a single main thread. -```wasm +```wast (module $example (type $func (func)) (type $cont (cont $func)) @@ -534,11 +536,11 @@ example to fork each of the three threads from a single main thread. (func $main (export "main") (call $log (i32.const 0)) - (suspend $fork (cont.new (type $cont) (ref.func $thread1))) + (suspend $fork (cont.new $cont (ref.func $thread1))) (call $log (i32.const 1)) - (suspend $fork (cont.new (type $cont) (ref.func $thread2))) + (suspend $fork (cont.new $cont (ref.func $thread2))) (call $log (i32.const 2)) - (suspend $fork (cont.new (type $cont) (ref.func $thread3))) + (suspend $fork (cont.new $cont (ref.func $thread3))) (call $log (i32.const 3)) ) @@ -570,7 +572,7 @@ example to fork each of the three threads from a single main thread. ``` As with the static example we define a scheduler module. -```wasm +```wast (module $scheduler (type $func (func)) (type $cont (cont $func)) @@ -590,15 +592,15 @@ In this example we illustrate five different schedulers. First, we write a baseline synchronous scheduler which simply runs the current thread to completion without actually yielding. -```wasm +```wast (func $sync (export "sync") (param $nextk (ref null $cont)) (loop $l (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) - (tag $fork $on_fork) - (local.get $nextk) + (resume $cont (tag $yield $on_yield) + (tag $fork $on_fork) + (local.get $nextk) ) (local.set $nextk (call $dequeue)) (br $l) ;; thread terminated @@ -656,7 +658,7 @@ threads in sequence. Following a similar pattern, we define four different asynchronous schedulers. -```wasm +```wast ;; four asynchronous schedulers: ;; * kt and tk don't yield on encountering a fork ;; 1) kt runs the continuation, queuing up the new thread for later @@ -671,9 +673,9 @@ schedulers. (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) - (tag $fork $on_fork) - (local.get $nextk) + (resume $cont (tag $yield $on_yield) + (tag $fork $on_fork) + (local.get $nextk) ) (local.set $nextk (call $dequeue)) (br $l) ;; thread terminated @@ -695,9 +697,9 @@ schedulers. (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) - (tag $fork $on_fork) - (local.get $nextk) + (resume $cont (tag $yield $on_yield) + (tag $fork $on_fork) + (local.get $nextk) ) (local.set $nextk (call $dequeue)) (br $l) ;; thread terminated @@ -719,9 +721,9 @@ schedulers. (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) - (tag $fork $on_fork) - (local.get $nextk) + (resume $cont (tag $yield $on_yield) + (tag $fork $on_fork) + (local.get $nextk) ) (local.set $nextk (call $dequeue)) (br $l) ;; thread terminated @@ -744,9 +746,9 @@ schedulers. (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) - (tag $fork $on_fork) - (local.get $nextk) + (resume $cont (tag $yield $on_yield) + (tag $fork $on_fork) + (local.get $nextk) ) (local.set $nextk (call $dequeue)) (br $l) ;; thread terminated @@ -772,7 +774,7 @@ current and newly forked threads. We run our example using each of the five schedulers. -```wasm +```wast (module (type $func (func)) (type $cont (cont $func)) @@ -791,15 +793,15 @@ We run our example using each of the five schedulers. (func (export "run") (call $log (i32.const -1)) - (call $scheduler1 (cont.new (type $cont) (ref.func $main))) + (call $scheduler1 (cont.new $cont (ref.func $main))) (call $log (i32.const -2)) - (call $scheduler2 (cont.new (type $cont) (ref.func $main))) + (call $scheduler2 (cont.new $cont (ref.func $main))) (call $log (i32.const -3)) - (call $scheduler3 (cont.new (type $cont) (ref.func $main))) + (call $scheduler3 (cont.new $cont (ref.func $main))) (call $log (i32.const -4)) - (call $scheduler4 (cont.new (type $cont) (ref.func $main))) + (call $scheduler4 (cont.new $cont (ref.func $main))) (call $log (i32.const -5)) - (call $scheduler5 (cont.new (type $cont) (ref.func $main))) + (call $scheduler5 (cont.new $cont (ref.func $main))) (call $log (i32.const -6)) ) ) @@ -901,7 +903,7 @@ delimited control operators. First we implement control/prompt. -```wasm +```wast ;; interface to control/prompt (module $control (type $func (func)) ;; [] -> [] @@ -931,8 +933,8 @@ First we implement control/prompt. (tag $control (export "control") (param (ref $cont-func))) ;; control : [([contref ([] -> [])] -> [])] -> [] (func $prompt (export "prompt") (param $nextk (ref null $cont)) ;; prompt : [(contref ([] -> []))] -> [] (block $on_control (result (ref $cont-func) (ref $cont)) - (resume (tag $control $on_control) - (local.get $nextk)) + (resume $cont (tag $control $on_control) + (local.get $nextk)) (return) ) ;; $on_control (param (ref $cont-func) (ref $cont)) (let (local $h (ref $cont-func)) (local $k (ref $cont)) @@ -964,7 +966,7 @@ handlers for defining different schedulers. Here instead we parameterise the whole example by the behaviour of yielding and forking as `$yield` and `$fork` functions. -```wasm +```wast (module $example (type $func (func)) ;; [] -> [] (type $cont (cont $func)) ;; cont ([] -> []) @@ -982,18 +984,18 @@ forking as `$yield` and `$fork` functions. (func $main (export "main") (param $yield (ref $func)) (param $fork (ref $cont-func)) (call $log (i32.const 0)) (call_ref - (cont.bind (type $cont) (local.get $yield) (local.get $fork) - (cont.new (type $func-cont-func-cont) (ref.func $thread1))) + (cont.bind $func-cont-func-cont $cont (local.get $yield) (local.get $fork) + (cont.new $func-cont-func-cont (ref.func $thread1))) (local.get $fork)) (call $log (i32.const 1)) (call_ref - (cont.bind (type $cont) (local.get $yield) (local.get $fork) - (cont.new (type $func-cont-func-cont) (ref.func $thread2))) + (cont.bind $func-cont-func-cont $cont (local.get $yield) (local.get $fork) + (cont.new $func-cont-func-cont (ref.func $thread2))) (local.get $fork)) (call $log (i32.const 2)) (call_ref - (cont.bind (type $cont) (local.get $yield) (local.get $fork) - (cont.new (type $func-cont-func-cont) (ref.func $thread3))) + (cont.bind $func-cont-func-cont $cont (local.get $yield) (local.get $fork) + (cont.new $func-cont-func-cont (ref.func $thread3))) (local.get $fork)) (call $log (i32.const 3)) ) @@ -1034,7 +1036,7 @@ We now define a scheduler module analogous to that of the previous dynamic lightweight thread example. As before, we will implement five different schedulers. -```wasm +```wast (module (type $func (func)) ;; [] -> [] (type $cont (cont $func)) ;; cont ([] -> []) @@ -1067,7 +1069,7 @@ Unlike before, with control/prompt a generic scheduler loop must be decoupled from the implementations of each operation (yield / fork) as the latter are passed in as arguments to user code -```wasm +```wast ;; generic boilerplate scheduler (func $scheduler (param $nextk (ref null $cont)) (loop $loop @@ -1088,7 +1090,7 @@ fork. First, we do the baseline synchronous scheduler. -```wasm +```wast ;; synchronous scheduler (func $handle-yield-sync (param $k (ref $cont)) (call $scheduler (local.get $k)) @@ -1105,7 +1107,7 @@ First, we do the baseline synchronous scheduler. ) (func $sync (export "sync") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-sync) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-sync) (local.get $k))) ) ``` @@ -1119,7 +1121,7 @@ All of the asynchronous schedulers make use of the same implementation of yield, which enqueues the continuation of the current thread and dequeues the next available thread. -```wasm +```wast ;; asynchronous yield (used by all asynchronous schedulers) (func $handle-yield (param $k (ref $cont)) (call $enqueue (local.get $k)) @@ -1132,7 +1134,7 @@ dequeues the next available thread. Each asynchronous scheduler uses its own implementation of fork. -```wasm +```wast ;; four asynchronous implementations of fork: ;; * kt and tk don't yield on encountering a fork ;; 1) kt runs the continuation, queuing up the new thread for later @@ -1151,7 +1153,7 @@ Each asynchronous scheduler uses its own implementation of fork. ) (func $kt (export "kt") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-kt) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-kt) (local.get $k))) ) ;; no yield on fork, new thread first @@ -1164,7 +1166,7 @@ Each asynchronous scheduler uses its own implementation of fork. ) (func $tk (export "tk") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-tk) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-tk) (local.get $k))) ) ;; yield on fork, continuation first @@ -1178,7 +1180,7 @@ Each asynchronous scheduler uses its own implementation of fork. ) (func $ykt (export "ykt") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-ykt) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-ykt) (local.get $k))) ) ;; yield on fork, new thread first @@ -1192,7 +1194,7 @@ Each asynchronous scheduler uses its own implementation of fork. ) (func $ytk (export "ytk") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-ytk) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-ytk) (local.get $k))) ) ) (register "scheduler") @@ -1203,7 +1205,7 @@ lightweight threads example, but the types are more complex due to the need to index the handled computation (`$main` in this case) by the implementations of forking and yielding. -```wasm +```wast (module (type $func (func)) ;; [] -> [] (type $cont (cont $func)) ;; cont ([] -> []) @@ -1228,15 +1230,15 @@ implementations of forking and yielding. (func $run (export "run") (call $log (i32.const -1)) - (call $scheduler-sync (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-sync (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -2)) - (call $scheduler-kt (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-kt (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -3)) - (call $scheduler-tk (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-tk (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -4)) - (call $scheduler-ykt (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-ykt (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -5)) - (call $scheduler-ytk (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-ytk (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -6)) ) ) @@ -1436,8 +1438,8 @@ We can accommodate named handlers by introducing a new reference type executing a variant of the `resume` instruction and is passed to the continuation: -```wasm - resume_with (tag $e $l)* : [ t1* (ref $ct) ] -> [ t2* ] +```wast + resume_with $ht $ct (tag $e $l)* : [ t1* (ref $ht) (ref $ct) ] -> [ t2* ] where: - $ht = handler t2* - $ct = cont ([ (ref $ht) t1* ] -> [ t2* ]) @@ -1451,8 +1453,8 @@ construction. This instruction is complemented by an instruction for suspending to a specific handler: -```wasm - suspend_to $e : [ s* (ref $ht) ] -> [ t* ] +```wast + suspend_to $ht $e : [ s* (ref $ht) ] -> [ t* ] where: - $ht = handler tr* - $e : [ s* ] -> [ t* ] @@ -1478,7 +1480,7 @@ symmetric `switch_to` primitive. Given named handlers, it is possible to introduce a somewhat magic instruction for switching directly to another continuation: -```wasm +```wast switch_to : [ t1* (ref $ct1) (ref $ht) ] -> [ t2* ] where: - $ht = handler t3* @@ -1488,7 +1490,7 @@ instruction for switching directly to another continuation: This behaves as if there was a built-in tag -```wasm +```wast (tag $Switch (param t1* (ref $ct1)) (result t3*)) ``` @@ -1510,7 +1512,7 @@ In fact, symmetric switching need not necessarily be tied to named handlers, since there could also be an indirect version with dynamic handler lookup: -```wasm +```wast switch : [ t1* (ref $ct1) ] -> [ t2* ] where: - $ct1 = cont ([ (ref $ct2) t1* ] -> [ t3* ]) diff --git a/proposals/continuations/Overview.md b/proposals/continuations/Overview.md index 568c6783b..7b4e5fc09 100644 --- a/proposals/continuations/Overview.md +++ b/proposals/continuations/Overview.md @@ -19,33 +19,40 @@ Based on [typed reference proposal](https://github.com/WebAssembly/function-refe - `cont.new $ct : [(ref null? $ft)] -> [(ref $ct)]` - iff `$ct = cont $ft` -* `cont.bind ` binds a continuation to (partial) arguments - - `cont.bind $ct : [t3* (ref null? $ct')] -> [(ref $ct)]` +* `cont.bind ` binds a continuation to (partial) arguments + - `cont.bind $ct $ct' : [t3* (ref null? $ct)] -> [(ref $ct')]` - iff `$ct = cont $ft` - - and `$ft = [t1*] -> [t2*]` + - and `$ft = [t3* t1*] -> [t2*]` - and `$ct' = cont $ft'` - - and `$ft' = [t3* t1'*] -> [t2'*]` - - and `[t1'*] -> [t2'*] <: [t1*] -> [t2*]` + - and `$ft' = [t1'*] -> [t2'*]` + - and `[t1*] -> [t2*] <: [t1'*] -> [t2'*]` * `suspend ` suspends the current continuation - `suspend $t : [t1*] -> [t2*]` - iff `tag $t : [t1*] -> [t2*]` -* `resume (tag )*` resumes a continuation - - `resume (tag $e $l)* : [t1* (ref null? $ct)] -> [t2*]` +* `resume (tag )*` resumes a continuation + - `resume $ct (tag $t $l)* : [t1* (ref null? $ct)] -> [t2*]` - iff `$ct = cont $ft` - and `$ft = [t1*] -> [t2*]` - and `(tag $t : [te1*] -> [te2*])*` - and `(label $l : [te1'* (ref null? $ct')])*` - and `([te1*] <: [te1'*])*` - and `($ct' = cont $ft')*` - - and `([te2*] -> [t2*] <: $ft')*` + - and `$ft' = [t1'*] -> [t2'*]` + - and `([te2*] -> [t2*] <: [t1'*] -> [t2'*])*` -* `resume_throw ` aborts a continuation - - `resume_throw $e : [te* (ref null? $ct)] -> [t2*]` - - iff `exception $e : [te*]` +* `resume_throw (tag )` aborts a continuation + - `resume_throw $ct $e (tag $t $l): [te* (ref null? $ct)] -> [t2*]` + - iff `(tag $e : [te*] -> [])` - and `$ct = cont $ft` - and `$ft = [t1*] -> [t2*]` + - and `(tag $t : [te1*] -> [te2*])*` + - and `(label $l : [te1'* (ref null? $ct')])*` + - and `([te1*] <: [te1'*])*` + - and `($ct' = cont $ft')*` + - and `$ft' = [t1'*] -> [t2'*]` + - and `([te2*] -> [t2*] <: [t1'*] -> [t2'*])*` * `barrier * end` blocks suspension - `barrier $l bt instr* end : [t1*] -> [t2*]` @@ -111,36 +118,37 @@ H^ea ::= - and `$ct = cont $ft` - and `$ft = [t1^n] -> [t2*]` -* `S; F; (ref.null t) (cont.bind $ct) --> S; F; trap` +* `S; F; (ref.null t) (cont.bind $ct $ct') --> S; F; trap` -* `S; F; (ref.cont ca) (cont.bind $ct) --> S'; F; trap` +* `S; F; (ref.cont ca) (cont.bind $ct $ct') --> S'; F; trap` - iff `S.conts[ca] = epsilon` -* `S; F; v^n (ref.cont ca) (cont.bind $ct) --> S'; F; (ref.const |S.conts|)` +* `S; F; v^n (ref.cont ca) (cont.bind $ct $ct') --> S'; F; (ref.const |S.conts|)` - iff `S.conts[ca] = (E' : n')` - - and `$ct = cont $ft` - - and `$ft = [t1'*] -> [t2'*]` + - and `$ct' = cont $ft'` + - and `$ft' = [t1'*] -> [t2'*]` - and `n = n' - |t1'*|` - and `S' = S with conts[ca] = epsilon with conts += (E : |t1'*|)` - and `E = E'[v^n _]` -* `S; F; (ref.null t) (resume (tag $e $l)*) --> S; F; trap` +* `S; F; (ref.null t) (resume $ct (tag $e $l)*) --> S; F; trap` -* `S; F; (ref.cont ca) (resume (tag $e $l)*) --> S; F; trap` +* `S; F; (ref.cont ca) (resume $ct (tag $e $l)*) --> S; F; trap` - iff `S.conts[ca] = epsilon` -* `S; F; v^n (ref.cont ca) (resume (tag $e $l)*) --> S'; F; handle{(ea $l)*} E[v^n] end` +* `S; F; v^n (ref.cont ca) (resume $ct (tag $t $l)*) --> S'; F; handle{(ea $l)*} E[v^n] end` - iff `S.conts[ca] = (E : n)` - - and `(ea = F.tags[$e])*` + - and `(ea = F.tags[$t])*` - and `S' = S with conts[ca] = epsilon` -* `S; F; (ref.null t) (resume_throw $e) --> S; F; trap` +* `S; F; (ref.null t) (resume_throw $ct $e (tag $t $l)*) --> S; F; trap` -* `S; F; (ref.cont ca) (resume_throw $e) --> S; F; trap` +* `S; F; (ref.cont ca) (resume_throw $ct $e (tag $t $l)*) --> S; F; trap` - iff `S.conts[ca] = epsilon` -* `S; F; v^m (ref.cont ca) (resume_throw $e) --> S'; F; E[v^m (throw $e)]` +* `S; F; v^m (ref.cont ca) (resume_throw $ct $e (tag $t $l)*) --> S'; F; handle{(ea $l)*} E[v^m (throw $e)] end` - iff `S.conts[ca] = (E : n)` + - and `(ea = F.tags[$t])*` - and `S.tags[F.tags[$e]].type = [t1^m] -> [t2*]` - and `S' = S with conts[ca] = epsilon` diff --git a/proposals/continuations/examples/actor-lwt.wast b/proposals/continuations/examples/actor-lwt.wast index ead651eea..60d879591 100644 --- a/proposals/continuations/examples/actor-lwt.wast +++ b/proposals/continuations/examples/actor-lwt.wast @@ -52,7 +52,7 @@ (loop $l (if (i32.eqz (local.get $n)) (then (suspend $send (i32.const 42) (local.get $p))) - (else (local.set $p (suspend $spawn (cont.bind (type $cont) (local.get $p) (cont.new (type $i-cont) (ref.func $next))))) + (else (local.set $p (suspend $spawn (cont.bind $i-cont $cont (local.get $p) (cont.new $i-cont (ref.func $next))))) (local.set $n (i32.sub (local.get $n) (i32.const 1))) (br $l)) ) @@ -73,7 +73,7 @@ (table $queue 0 (ref null $cont)) (memory 1) - (exception $too-many-mailboxes) + (tag $too-many-mailboxes) (global $qdelta i32 (i32.const 10)) @@ -190,8 +190,8 @@ (func $log (import "spectest" "print_i32") (param i32)) - (exception $too-many-mailboxes) - (exception $too-many-messages) + (tag $too-many-mailboxes) + (tag $too-many-messages) (memory 1) @@ -274,7 +274,7 @@ (loop $l (if (i32.eqz (local.get $n)) (then (suspend $send (i32.const 42) (local.get $p))) - (else (local.set $p (suspend $spawn (cont.bind (type $cont) (local.get $p) (cont.new (type $i-cont) (ref.func $next))))) + (else (local.set $p (suspend $spawn (cont.bind $i-cont $cont (local.get $p) (cont.new $i-cont (ref.func $next))))) (local.set $n (i32.sub (local.get $n) (i32.const 1))) (br $l)) ) @@ -371,7 +371,7 @@ (if (call $queue-empty) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) (tag $fork $on_fork) + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (call $dequeue) ) (br $l) ;; thread terminated @@ -397,8 +397,8 @@ ;; -1 means empty - (exception $too-many-mailboxes) - (exception $too-many-messages) + (tag $too-many-mailboxes) + (tag $too-many-messages) (memory 1) @@ -489,52 +489,52 @@ (elem declare func $actk) (func $actk (param $mine i32) (param $nextk (ref $cont)) + (local $ik (ref $i-cont)) + (local $k (ref $cont)) + (local $you (ref $cont)) + (local $yours i32) (loop $l (block $on_self (result (ref $i-cont)) (block $on_spawn (result (ref $cont) (ref $i-cont)) (block $on_send (result i32 i32 (ref $cont)) (block $on_recv (result (ref $i-cont)) - (resume (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) - (local.get $nextk) + (resume $cont (tag $self $on_self) + (tag $spawn $on_spawn) + (tag $send $on_send) + (tag $recv $on_recv) + (local.get $nextk) ) (return) ) ;; $on_recv (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - ;; block this thread until the mailbox is non-empty - (loop $blocked - (if (call $empty-mb (local.get $mine)) - (then (suspend $yield) - (br $blocked)) - ) + (local.set $ik) + ;; block this thread until the mailbox is non-empty + (loop $blocked + (if (call $empty-mb (local.get $mine)) + (then (suspend $yield) + (br $blocked)) ) - (local.set $nextk (cont.bind (type $cont) (call $recv-from-mb (local.get $mine)) (local.get $ik))) ) + (local.set $nextk (cont.bind $i-cont $cont (call $recv-from-mb (local.get $mine)) (local.get $ik))) (br $l) ) ;; $on_send (result i32 i32 (ref $cont)) - (let (param i32 i32) (local $k (ref $cont)) - (call $send-to-mb) - (local.set $nextk (local.get $k)) - ) + (local.set $k) + (call $send-to-mb) + (local.set $nextk (local.get $k)) (br $l) ) ;; $on_spawn (result (ref $cont) (ref $i-cont)) - (let (local $you (ref $cont)) (local $ik (ref $i-cont)) - (call $new-mb) - (let (local $yours i32) - (suspend $fork (cont.bind (type $cont) - (local.get $yours) - (local.get $you) - (cont.new (type $ic-cont) (ref.func $actk)))) - (local.set $nextk (cont.bind (type $cont) (local.get $yours) (local.get $ik))) - ) - ) + (local.set $ik) + (local.set $you) + (call $new-mb) + (local.set $yours) + (suspend $fork (cont.bind $ic-cont $cont + (local.get $yours) + (local.get $you) + (cont.new $ic-cont (ref.func $actk)))) + (local.set $nextk (cont.bind $i-cont $cont (local.get $yours) (local.get $ik))) (br $l) ) ;; $on_self (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - (local.set $nextk (cont.bind (type $cont) (local.get $mine) (local.get $ik))) - ) + (local.set $ik) + (local.set $nextk (cont.bind $i-cont $cont (local.get $mine) (local.get $ik))) (br $l) ) ) @@ -560,7 +560,7 @@ (func $scheduler (import "scheduler" "run") (param $k (ref $cont))) (func $run-actor (export "run-actor") (param $k (ref $cont)) - (call $scheduler (cont.bind (type $cont) (local.get $k) (cont.new (type $cont-cont) (ref.func $act)))) + (call $scheduler (cont.bind $cont-cont $cont (local.get $k) (cont.new $cont-cont (ref.func $act)))) ) ) (register "actor-scheduler") @@ -578,7 +578,7 @@ (func $chain (import "chain" "chain") (param $n i32)) (func $run-chain (export "run-chain") (param $n i32) - (call $run-actor (cont.bind (type $cont) (local.get $n) (cont.new (type $i-cont) (ref.func $chain)))) + (call $run-actor (cont.bind $i-cont $cont (local.get $n) (cont.new $i-cont (ref.func $chain)))) ) ) diff --git a/proposals/continuations/examples/actor.wast b/proposals/continuations/examples/actor.wast index 3a8d36f48..151c08d58 100644 --- a/proposals/continuations/examples/actor.wast +++ b/proposals/continuations/examples/actor.wast @@ -52,7 +52,7 @@ (loop $l (if (i32.eqz (local.get $n)) (then (suspend $send (i32.const 42) (local.get $p))) - (else (local.set $p (suspend $spawn (cont.bind (type $cont) (local.get $p) (cont.new (type $i-cont) (ref.func $next))))) + (else (local.set $p (suspend $spawn (cont.bind $i-cont $cont (local.get $p) (cont.new $i-cont (ref.func $next))))) (local.set $n (i32.sub (local.get $n) (i32.const 1))) (br $l)) ) @@ -73,7 +73,7 @@ (table $queue 0 (ref null $cont)) (memory 1) - (exception $too-many-mailboxes) + (tag $too-many-mailboxes) (global $qdelta i32 (i32.const 10)) @@ -190,8 +190,8 @@ (func $log (import "spectest" "print_i32") (param i32)) - (exception $too-many-mailboxes) - (exception $too-many-messages) + (tag $too-many-mailboxes) + (tag $too-many-messages) (memory 1) @@ -296,10 +296,10 @@ (local $res i32) (suspend $recv) (local.set $res) - (resume (local.get $res) (local.get $ik)) + (resume $i-cont (local.get $res) (local.get $ik)) ) (func $recv-again (param $ik (ref $i-cont)) (result (ref $cont)) - (cont.bind (type $cont) (local.get $ik) (cont.new (type $i-cont-cont) (ref.func $recv-againf))) + (cont.bind $i-cont-cont $cont (local.get $ik) (cont.new $i-cont-cont (ref.func $recv-againf))) ) ;; There are multiple ways of avoiding the need for @@ -317,6 +317,10 @@ (func $run (export "run") (param $nextk (ref null $cont)) (local $mine i32) ;; current mailbox + (local $ik (ref $i-cont)) + (local $k (ref $cont)) + (local $you (ref $cont)) + (local $yours i32) (call $init) (local.set $mine (call $new-mb)) (loop $l @@ -325,48 +329,44 @@ (block $on_spawn (result (ref $cont) (ref $i-cont)) (block $on_send (result i32 i32 (ref $cont)) (block $on_recv (result (ref $i-cont)) - (resume (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) - (local.get $nextk) + (resume $cont (tag $self $on_self) + (tag $spawn $on_spawn) + (tag $send $on_send) + (tag $recv $on_recv) + (local.get $nextk) ) (local.set $mine (call $dequeue-mb)) (local.set $nextk (call $dequeue-k)) (br $l) ) ;; $on_recv (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - ;; block this thread until the mailbox is non-empty - (if (call $empty-mb (local.get $mine)) - (then (call $enqueue-mb (local.get $mine)) - (call $enqueue-k (call $recv-again (local.get $ik))) - (local.set $mine (call $dequeue-mb)) - (local.set $nextk (call $dequeue-k)) - (br $l)) - ) - (local.set $nextk (cont.bind (type $cont) (call $recv-from-mb (local.get $mine)) (local.get $ik))) + (local.set $ik) + ;; block this thread until the mailbox is non-empty + (if (call $empty-mb (local.get $mine)) + (then (call $enqueue-mb (local.get $mine)) + (call $enqueue-k (call $recv-again (local.get $ik))) + (local.set $mine (call $dequeue-mb)) + (local.set $nextk (call $dequeue-k)) + (br $l)) ) + (local.set $nextk (cont.bind $i-cont $cont (call $recv-from-mb (local.get $mine)) (local.get $ik))) (br $l) ) ;; $on_send (result i32 i32 (ref $cont)) - (let (param i32 i32) (local $k (ref $cont)) - (call $send-to-mb) - (local.set $nextk (local.get $k)) - ) + (local.set $k) + (call $send-to-mb) + (local.set $nextk (local.get $k)) (br $l) ) ;; $on_spawn (result (ref $cont) (ref $i-cont)) - (let (local $you (ref $cont)) (local $ik (ref $i-cont)) - (call $new-mb) - (let (local $yours i32) - (call $enqueue-mb (local.get $yours)) - (call $enqueue-k (local.get $you)) - (local.set $nextk (cont.bind (type $cont) (local.get $yours) (local.get $ik))) - ) - ) + (local.set $ik) + (local.set $you) + (call $new-mb) + (local.set $yours) + (call $enqueue-mb (local.get $yours)) + (call $enqueue-k (local.get $you)) + (local.set $nextk (cont.bind $i-cont $cont (local.get $yours) (local.get $ik))) (br $l) ) ;; $on_self (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - (local.set $nextk (cont.bind (type $cont) (local.get $mine) (local.get $ik))) - ) + (local.set $ik) + (local.set $nextk (cont.bind $i-cont $cont (local.get $mine) (local.get $ik))) (br $l) ) ) @@ -388,7 +388,7 @@ (func $chain (import "chain" "chain") (param $n i32)) (func $run-chain (export "run-chain") (param $n i32) - (call $act (cont.bind (type $cont) (local.get $n) (cont.new (type $i-cont) (ref.func $chain)))) + (call $act (cont.bind $i-cont $cont (local.get $n) (cont.new $i-cont (ref.func $chain)))) ) ) diff --git a/proposals/continuations/examples/async-await.wast b/proposals/continuations/examples/async-await.wast index 514ed4170..53570c3bc 100644 --- a/proposals/continuations/examples/async-await.wast +++ b/proposals/continuations/examples/async-await.wast @@ -72,11 +72,11 @@ (local $y i32) (call $log (i32.const -1)) - (local.set $p (suspend $async (cont.bind (type $i-cont) (i32.const 1) (i32.const 3) (cont.new (type $iii-cont) (ref.func $sum))))) + (local.set $p (suspend $async (cont.bind $iii-cont $i-cont (i32.const 1) (i32.const 3) (cont.new $iii-cont (ref.func $sum))))) (call $log (i32.const -2)) - (local.set $q (suspend $async (cont.bind (type $i-cont) (i32.const 5) (i32.const 7) (cont.new (type $iii-cont) (ref.func $sum))))) + (local.set $q (suspend $async (cont.bind $iii-cont $i-cont (i32.const 5) (i32.const 7) (cont.new $iii-cont (ref.func $sum))))) (call $log (i32.const -3)) - (local.set $r (suspend $async (cont.bind (type $i-cont) (i32.const 10) (i32.const 15) (cont.new (type $iii-cont) (ref.func $sum))))) + (local.set $r (suspend $async (cont.bind $iii-cont $i-cont (i32.const 10) (i32.const 15) (cont.new $iii-cont (ref.func $sum))))) (call $log (i32.const -4)) (local.set $x (i32.mul (suspend $await (local.get $p)) @@ -163,8 +163,8 @@ ;; a simplistic implementation of promises that assumes a maximum of ;; 1000 promises and a maximum of one observer per promise - (exception $too-many-promises) - (exception $too-many-observers) + (tag $too-many-promises) + (tag $too-many-observers) (global $num-promises (mut i32) (i32.const 0)) (global $max-promises i32 (i32.const 1000)) @@ -217,7 +217,7 @@ (if (ref.is_null (local.get $k)) (then (return (ref.null $cont))) ) - (return (cont.bind (type $cont) (local.get $v) (local.get $k))) + (return (cont.bind $i-cont $cont (local.get $v) (local.get $k))) ) ) (register "promise") @@ -254,53 +254,56 @@ (func $fulfill-promise (import "promise" "fulfill") (param $p i32) (param $v i32) (result (ref null $cont))) (func $run (export "run") (param $nextk (ref null $cont)) + (local $p i32) + (local $v i32) + (local $ik (ref $i-cont)) + (local $ak (ref $i-cont)) + (local $k (ref null $cont)) (loop $l (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fulfill (result i32 i32 (ref $cont)) (block $on_async (result (ref $i-cont) (ref $i-cont)) (block $on_await (result i32 (ref $i-cont)) - (resume (tag $yield $on_yield) - (tag $fulfill $on_fulfill) - (tag $async $on_async) - (tag $await $on_await) - (local.get $nextk) + (resume $cont (tag $yield $on_yield) + (tag $fulfill $on_fulfill) + (tag $async $on_async) + (tag $await $on_await) + (local.get $nextk) ) (local.set $nextk (call $dequeue)) (br $l) ;; thread terminated ) ;; $on_await (result i32 (ref $i-cont)) - (let (local $p i32) (local $ik (ref $i-cont)) - (if (call $promise-fulfilled (local.get $p)) - ;; if promise fulfilled then run continuation partially applied to value - (then (local.set $nextk (cont.bind (type $cont) (call $promise-value (local.get $p)) (local.get $ik)))) - ;; else add continuation to promise and run next continuation from the queue - (else (call $await-promise (local.get $p) (local.get $ik)) - (local.set $nextk (call $dequeue))) - ) + (local.set $ik) + (local.set $p) + (if (call $promise-fulfilled (local.get $p)) + ;; if promise fulfilled then run continuation partially applied to value + (then (local.set $nextk (cont.bind $i-cont $cont (call $promise-value (local.get $p)) (local.get $ik)))) + ;; else add continuation to promise and run next continuation from the queue + (else (call $await-promise (local.get $p) (local.get $ik)) + (local.set $nextk (call $dequeue))) ) (br $l) ) ;; $on_async (result (ref $i-func) (ref $i-cont)) - (let (local $ak (ref $i-cont)) (local $ik (ref $i-cont)) - ;; create new promise - (call $new-promise) - (let (local $p i32) - ;; enqueue continuation partially applied to promise - (call $enqueue (cont.bind (type $cont) (local.get $p) (local.get $ik))) - ;; run computation partially applied to promise - (local.set $nextk (cont.bind (type $cont) (local.get $p) (local.get $ak))) - ) - ) + (local.set $ik) + (local.set $ak) + ;; create new promise + (call $new-promise) + (local.set $p) + ;; enqueue continuation partially applied to promise + (call $enqueue (cont.bind $i-cont $cont (local.get $p) (local.get $ik))) + ;; run computation partially applied to promise + (local.set $nextk (cont.bind $i-cont $cont (local.get $p) (local.get $ak))) (br $l) ) ;; $on_fulfill (result i32 i32 (ref $cont)) (local.set $nextk) - (let (local $p i32) (local $v i32) - (call $fulfill-promise (local.get $p) (local.get $v)) - (let (local $k (ref null $cont)) - (if (ref.is_null (local.get $k)) - (then) - (else (call $enqueue (local.get $k))) - ) - ) + (local.set $v) + (local.set $p) + (call $fulfill-promise (local.get $p) (local.get $v)) + (local.set $k) + (if (ref.is_null (local.get $k)) + (then) + (else (call $enqueue (local.get $k))) ) (br $l) ) ;; $on_yield (result (ref $cont)) @@ -325,7 +328,7 @@ (elem declare func $run-example) (func (export "run") - (call $scheduler (cont.new (type $cont) (ref.func $run-example))) + (call $scheduler (cont.new $cont (ref.func $run-example))) ) ) diff --git a/proposals/continuations/examples/control-lwt.wast b/proposals/continuations/examples/control-lwt.wast index 1c1e6496b..d7c00cb3c 100644 --- a/proposals/continuations/examples/control-lwt.wast +++ b/proposals/continuations/examples/control-lwt.wast @@ -24,16 +24,18 @@ ;; ;; (Technically this is control0/prompt0 rather than ;; control/prompt.) - (tag $control (export "control") (param (ref $cont-func))) ;; control : ([cont ([] -> [])] -> []) -> [] + (tag $control (export "control") (param (ref $cont-cont))) ;; control : ([cont ([] -> [])] -> []) -> [] (func $prompt (export "prompt") (param $nextk (ref null $cont)) ;; prompt : cont ([] -> []) -> [] - (block $on_control (result (ref $cont-func) (ref $cont)) - (resume (tag $control $on_control) - (local.get $nextk)) + (local $h (ref $cont-cont)) + (local $k (ref $cont)) + (block $on_control (result (ref $cont-cont) (ref $cont)) + (resume $cont (tag $control $on_control) + (local.get $nextk)) (return) ) ;; $on_control (param (ref $cont-func) (ref $cont)) - (let (local $h (ref $cont-func)) (local $k (ref $cont)) - (call_ref (local.get $k) (local.get $h)) - ) + (local.set $k) + (local.set $h) + (resume $cont-cont (local.get $k) (local.get $h)) ) ) (register "control") @@ -57,44 +59,44 @@ (func $main (export "main") (param $yield (ref $func)) (param $fork (ref $cont-func)) (call $log (i32.const 0)) - (call_ref - (cont.bind (type $cont) (local.get $yield) (local.get $fork) - (cont.new (type $func-cont-func-cont) (ref.func $thread1))) + (call_ref $cont-func + (cont.bind $func-cont-func-cont $cont (local.get $yield) (local.get $fork) + (cont.new $func-cont-func-cont (ref.func $thread1))) (local.get $fork)) (call $log (i32.const 1)) - (call_ref - (cont.bind (type $cont) (local.get $yield) (local.get $fork) - (cont.new (type $func-cont-func-cont) (ref.func $thread2))) + (call_ref $cont-func + (cont.bind $func-cont-func-cont $cont (local.get $yield) (local.get $fork) + (cont.new $func-cont-func-cont (ref.func $thread2))) (local.get $fork)) (call $log (i32.const 2)) - (call_ref - (cont.bind (type $cont) (local.get $yield) (local.get $fork) - (cont.new (type $func-cont-func-cont) (ref.func $thread3))) + (call_ref $cont-func + (cont.bind $func-cont-func-cont $cont (local.get $yield) (local.get $fork) + (cont.new $func-cont-func-cont (ref.func $thread3))) (local.get $fork)) (call $log (i32.const 3)) ) (func $thread1 (param $yield (ref $func)) (param $fork (ref $cont-func)) (call $log (i32.const 10)) - (call_ref (local.get $yield)) + (call_ref $func (local.get $yield)) (call $log (i32.const 11)) - (call_ref (local.get $yield)) + (call_ref $func (local.get $yield)) (call $log (i32.const 12)) ) (func $thread2 (param $yield (ref $func)) (param $fork (ref $cont-func)) (call $log (i32.const 20)) - (call_ref (local.get $yield)) + (call_ref $func (local.get $yield)) (call $log (i32.const 21)) - (call_ref (local.get $yield)) + (call_ref $func (local.get $yield)) (call $log (i32.const 22)) ) (func $thread3 (param $yield (ref $func)) (param $fork (ref $cont-func)) (call $log (i32.const 30)) - (call_ref (local.get $yield)) + (call_ref $func (local.get $yield)) (call $log (i32.const 31)) - (call_ref (local.get $yield)) + (call_ref $func (local.get $yield)) (call $log (i32.const 32)) ) ) @@ -170,6 +172,9 @@ (type $func-cont-func-func (func (param (ref $func)) (param (ref $cont-func)))) ;; ([] -> []) -> ([cont ([] -> [])] -> []) -> [] (type $func-cont-func-cont (cont $func-cont-func-func)) ;; cont (([] -> []) -> ([cont ([] -> [])] -> []) -> []) + (type $func-cont-cont (func (param (ref $cont)) (param (ref $cont)))) + (type $cont-cont-func (cont $func-cont-cont)) + (func $log (import "spectest" "print_i32") (param i32)) ;; queue interface @@ -184,7 +189,7 @@ $fork-sync $fork-kt $fork-tk $fork-ykt $fork-ytk) ;; control/prompt interface - (tag $control (import "control" "control") (param (ref $cont-func))) ;; control : ([cont ([] -> [])] -> []) -> [] + (tag $control (import "control" "control") (param (ref $cont-cont))) ;; control : ([cont ([] -> [])] -> []) -> [] (func $prompt (import "control" "prompt") (param $nextk (ref null $cont))) ;; prompt : cont ([] -> []) -> [] ;; generic boilerplate scheduler @@ -215,18 +220,20 @@ (call $scheduler (local.get $k)) ) (func $yield-sync - (suspend $control (ref.func $handle-yield)) + (suspend $control (cont.new $cont-cont (ref.func $handle-yield))) ) (func $handle-fork-sync (param $t (ref $cont)) (param $k (ref $cont)) (call $enqueue (local.get $t)) (call $scheduler (local.get $k)) ) (func $fork-sync (param $t (ref $cont)) - (suspend $control (func.bind (type $cont-func) (local.get $t) (ref.func $handle-fork-sync))) + (suspend $control + (cont.bind $cont-cont-func $cont-cont (local.get $t) + (cont.new $cont-cont-func (ref.func $handle-fork-sync)))) ) (func $sync (export "sync") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-sync) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-sync) (local.get $k))) ) ;; asynchronous yield (used by all asynchronous schedulers) @@ -235,7 +242,7 @@ (call $scheduler (call $dequeue)) ) (func $yield - (suspend $control (ref.func $handle-yield)) + (suspend $control (cont.new $cont-cont (ref.func $handle-yield))) ) ;; four asynchronous implementations of fork: ;; * kt and tk don't yield on encountering a fork @@ -251,11 +258,13 @@ (call $scheduler (local.get $k)) ) (func $fork-kt (param $t (ref $cont)) - (suspend $control (func.bind (type $cont-func) (local.get $t) (ref.func $handle-fork-kt))) + (suspend $control + (cont.bind $cont-cont-func $cont-cont (local.get $t) + (cont.new $cont-cont-func (ref.func $handle-fork-kt)))) ) (func $kt (export "kt") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-kt) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-kt) (local.get $k))) ) ;; no yield on fork, new thread first @@ -264,11 +273,13 @@ (call $scheduler (local.get $t)) ) (func $fork-tk (param $t (ref $cont)) - (suspend $control (func.bind (type $cont-func) (local.get $t) (ref.func $handle-fork-tk))) + (suspend $control + (cont.bind $cont-cont-func $cont-cont (local.get $t) + (cont.new $cont-cont-func (ref.func $handle-fork-tk)))) ) (func $tk (export "tk") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-tk) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-tk) (local.get $k))) ) ;; yield on fork, continuation first @@ -278,11 +289,13 @@ (call $scheduler (call $dequeue)) ) (func $fork-ykt (param $t (ref $cont)) - (suspend $control (func.bind (type $cont-func) (local.get $t) (ref.func $handle-fork-ykt))) + (suspend $control + (cont.bind $cont-cont-func $cont-cont (local.get $t) + (cont.new $cont-cont-func (ref.func $handle-fork-ykt)))) ) (func $ykt (export "ykt") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-ykt) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-ykt) (local.get $k))) ) ;; yield on fork, new thread first @@ -292,11 +305,13 @@ (call $scheduler (call $dequeue)) ) (func $fork-ytk (param $t (ref $cont)) - (suspend $control (func.bind (type $cont-func) (local.get $t) (ref.func $handle-fork-ytk))) + (suspend $control + (cont.bind $cont-cont-func $cont-cont (local.get $t) + (cont.new $cont-cont-func (ref.func $handle-fork-ytk)))) ) (func $ytk (export "ytk") (param $k (ref $func-cont-func-cont)) (call $scheduler - (cont.bind (type $cont) (ref.func $yield) (ref.func $fork-ytk) (local.get $k))) + (cont.bind $func-cont-func-cont $cont (ref.func $yield) (ref.func $fork-ytk) (local.get $k))) ) ) (register "scheduler") @@ -325,15 +340,15 @@ (func $run (export "run") (call $log (i32.const -1)) - (call $scheduler-sync (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-sync (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -2)) - (call $scheduler-kt (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-kt (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -3)) - (call $scheduler-tk (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-tk (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -4)) - (call $scheduler-ykt (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-ykt (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -5)) - (call $scheduler-ytk (cont.new (type $func-cont-func-cont) (ref.func $main))) + (call $scheduler-ytk (cont.new $func-cont-func-cont (ref.func $main))) (call $log (i32.const -6)) ) ) diff --git a/proposals/continuations/examples/fun-actor-lwt.wast b/proposals/continuations/examples/fun-actor-lwt.wast index 2b1f95fd8..a9cfff911 100644 --- a/proposals/continuations/examples/fun-actor-lwt.wast +++ b/proposals/continuations/examples/fun-actor-lwt.wast @@ -45,7 +45,7 @@ (loop $l (if (i32.eqz (local.get $n)) (then (suspend $send (i32.const 42) (local.get $p))) - (else (local.set $p (suspend $spawn (cont.bind (type $cont) (local.get $p) (cont.new (type $i-cont) (ref.func $next))))) + (else (local.set $p (suspend $spawn (cont.bind $i-cont $cont (local.get $p) (cont.new $i-cont (ref.func $next))))) (local.set $n (i32.sub (local.get $n) (i32.const 1))) (br $l)) ) @@ -143,7 +143,7 @@ (if (call $queue-empty) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) (tag $fork $on_fork) + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (call $dequeue) ) (br $l) ;; thread terminated @@ -169,8 +169,8 @@ ;; -1 means empty - (exception $too-many-mailboxes) - (exception $too-many-messages) + (tag $too-many-mailboxes) + (tag $too-many-messages) (memory 1) @@ -253,107 +253,101 @@ ;; resume with $ik applied to $res (func $act-res (param $mine i32) (param $res i32) (param $ik (ref $i-cont)) + (local $yours i32) + (local $k (ref $cont)) + (local $you (ref $cont)) (block $on_self (result (ref $i-cont)) (block $on_spawn (result (ref $cont) (ref $i-cont)) (block $on_send (result i32 i32 (ref $cont)) (block $on_recv (result (ref $i-cont)) ;; this should really be a tail call to the continuation ;; do we need a 'return_resume' operator? - (resume (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) - (local.get $res) (local.get $ik) + (resume $i-cont (tag $self $on_self) + (tag $spawn $on_spawn) + (tag $send $on_send) + (tag $recv $on_recv) + (local.get $res) (local.get $ik) ) (return) ) ;; $on_recv (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - ;; block this thread until the mailbox is non-empty - (loop $l - (if (call $empty-mb (local.get $mine)) - (then (suspend $yield) - (br $l)) - ) + (local.set $ik) + ;; block this thread until the mailbox is non-empty + (loop $l + (if (call $empty-mb (local.get $mine)) + (then (suspend $yield) + (br $l)) ) - (call $recv-from-mb (local.get $mine)) - (local.set $res) - (return_call $act-res (local.get $mine) (local.get $res) (local.get $ik))) - (unreachable) + ) + (call $recv-from-mb (local.get $mine)) + (local.set $res) + (return_call $act-res (local.get $mine) (local.get $res) (local.get $ik)) ) ;; $on_send (result i32 i32 (ref $cont)) - (let (param i32 i32) (local $k (ref $cont)) - (call $send-to-mb) - (return_call $act-nullary (local.get $mine) (local.get $k))) - (unreachable) + (local.set $k) + (call $send-to-mb) + (return_call $act-nullary (local.get $mine) (local.get $k)) ) ;; $on_spawn (result (ref $cont) (ref $i-cont)) - (let (local $you (ref $cont)) (local $ik (ref $i-cont)) - (call $new-mb) - (let (local $yours i32) - (suspend $fork (cont.bind (type $cont) - (local.get $yours) - (local.get $you) - (cont.new (type $icont-cont) (ref.func $act-nullary)))) - (return_call $act-res (local.get $mine) (local.get $yours) (local.get $ik)) - ) - ) - (unreachable) + (local.set $ik) + (local.set $you) + (call $new-mb) + (local.set $yours) + (suspend $fork (cont.bind $icont-cont $cont + (local.get $yours) + (local.get $you) + (cont.new $icont-cont (ref.func $act-nullary)))) + (return_call $act-res (local.get $mine) (local.get $yours) (local.get $ik)) ) ;; $on_self (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - (return_call $act-res (local.get $mine) (local.get $mine) (local.get $ik)) - ) - (unreachable) + (local.set $ik) + (return_call $act-res (local.get $mine) (local.get $mine) (local.get $ik)) ) ;; resume with nullary continuation (func $act-nullary (param $mine i32) (param $k (ref $cont)) (local $res i32) + (local $ik (ref $i-cont)) + (local $you (ref $cont)) + (local $yours i32) (block $on_self (result (ref $i-cont)) (block $on_spawn (result (ref $cont) (ref $i-cont)) (block $on_send (result i32 i32 (ref $cont)) (block $on_recv (result (ref $i-cont)) ;; this should really be a tail call to the continuation ;; do we need a 'return_resume' operator? - (resume (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) - (local.get $k) + (resume $cont (tag $self $on_self) + (tag $spawn $on_spawn) + (tag $send $on_send) + (tag $recv $on_recv) + (local.get $k) ) (return) ) ;; $on_recv (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - ;; block this thread until the mailbox is non-empty - (loop $l - (if (call $empty-mb (local.get $mine)) - (then (suspend $yield) - (br $l)) - ) + (local.set $ik) + ;; block this thread until the mailbox is non-empty + (loop $l + (if (call $empty-mb (local.get $mine)) + (then (suspend $yield) + (br $l)) ) - (call $recv-from-mb (local.get $mine)) - (local.set $res) - (return_call $act-res (local.get $mine) (local.get $res) (local.get $ik))) - (unreachable) + ) + (call $recv-from-mb (local.get $mine)) + (local.set $res) + (return_call $act-res (local.get $mine) (local.get $res) (local.get $ik)) ) ;; $on_send (result i32 i32 (ref $cont)) - (let (param i32 i32) (local $k (ref $cont)) - (call $send-to-mb) - (return_call $act-nullary (local.get $mine) (local.get $k))) - (unreachable) + (local.set $k) + (call $send-to-mb) + (return_call $act-nullary (local.get $mine) (local.get $k)) ) ;; $on_spawn (result (ref $cont) (ref $i-cont)) - (let (local $you (ref $cont)) (local $ik (ref $i-cont)) - (call $new-mb) - (let (local $yours i32) - (suspend $fork (cont.bind (type $cont) - (local.get $yours) - (local.get $you) - (cont.new (type $icont-cont) (ref.func $act-nullary)))) - (return_call $act-res (local.get $mine) (local.get $yours) (local.get $ik)) - ) - ) - (unreachable) + (local.set $ik) + (local.set $you) + (call $new-mb) + (local.set $yours) + (suspend $fork (cont.bind $icont-cont $cont + (local.get $yours) + (local.get $you) + (cont.new $icont-cont (ref.func $act-nullary)))) + (return_call $act-res (local.get $mine) (local.get $yours) (local.get $ik)) ) ;; $on_self (result (ref $i-cont)) - (let (local $ik (ref $i-cont)) - (return_call $act-res (local.get $mine) (local.get $mine) (local.get $ik)) - ) - (unreachable) + (local.set $ik) + (return_call $act-res (local.get $mine) (local.get $mine) (local.get $ik)) ) (func $act (export "act") (param $k (ref $cont)) @@ -379,7 +373,7 @@ (func $scheduler (import "scheduler" "run") (param $k (ref $cont))) (func $run-actor (export "run-actor") (param $k (ref $cont)) - (call $scheduler (cont.bind (type $cont) (local.get $k) (cont.new (type $cont-cont) (ref.func $act)))) + (call $scheduler (cont.bind $cont-cont $cont (local.get $k) (cont.new $cont-cont (ref.func $act)))) ) ) (register "actor-scheduler") @@ -397,7 +391,7 @@ (func $chain (import "chain" "chain") (param $n i32)) (func $run-chain (export "run-chain") (param $n i32) - (call $run-actor (cont.bind (type $cont) (local.get $n) (cont.new (type $i-cont) (ref.func $chain)))) + (call $run-actor (cont.bind $i-cont $cont (local.get $n) (cont.new $i-cont (ref.func $chain)))) ) ) diff --git a/proposals/continuations/examples/fun-lwt.wast b/proposals/continuations/examples/fun-lwt.wast index 0da82ee55..2b57b53df 100644 --- a/proposals/continuations/examples/fun-lwt.wast +++ b/proposals/continuations/examples/fun-lwt.wast @@ -23,11 +23,11 @@ (func $main (export "main") (call $log (i32.const 0)) - (suspend $fork (cont.new (type $cont) (ref.func $thread1))) + (suspend $fork (cont.new $cont (ref.func $thread1))) (call $log (i32.const 1)) - (suspend $fork (cont.new (type $cont) (ref.func $thread2))) + (suspend $fork (cont.new $cont (ref.func $thread2))) (call $log (i32.const 2)) - (suspend $fork (cont.new (type $cont) (ref.func $thread3))) + (suspend $fork (cont.new $cont (ref.func $thread3))) (call $log (i32.const 3)) ) @@ -132,16 +132,16 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) ) (return_call $sync (call $dequeue)) ) ;; $on_fork (result (ref $func) (ref $cont)) - (let (param (ref $cont)) (result (ref $cont)) (local $nextk (ref $cont)) + (local.set $nextk) (call $enqueue) - (return_call $sync (local.get $nextk))) + (return_call $sync (local.get $nextk)) ) ;; $on_yield (result (ref $cont)) (return_call $sync) ) @@ -159,16 +159,16 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) ) (return_call $tk (call $dequeue)) ) ;; $on_fork (result (ref $func) (ref $cont)) - (let (param (ref $cont)) (result (ref $cont)) (local $nextk (ref $cont)) + (local.set $nextk) (call $enqueue) - (return_call $tk (local.get $nextk))) + (return_call $tk (local.get $nextk)) ) ;; $on_yield (result (ref $cont)) (call $enqueue) (return_call $tk (call $dequeue)) @@ -179,7 +179,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk)) @@ -196,7 +196,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) @@ -213,16 +213,16 @@ ;; yield on fork, new thread first (func $ytk (export "ytk") (param $nextk (ref null $cont)) + (local $k (ref $cont)) (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk)) + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk)) (return_call $ytk (call $dequeue)) ) ;; $on_fork (result (ref $cont) (ref $cont)) - (let (param (ref $cont)) (local $k (ref $cont)) - (call $enqueue) - (call $enqueue (local.get $k)) - ) + (local.set $k) + (call $enqueue) + (call $enqueue (local.get $k)) (return_call $ytk (call $dequeue)) ) ;; $on_yield (result (ref $cont)) (call $enqueue) @@ -250,15 +250,15 @@ (func (export "run") (call $log (i32.const -1)) - (call $scheduler-sync (cont.new (type $cont) (ref.func $main))) + (call $scheduler-sync (cont.new $cont (ref.func $main))) (call $log (i32.const -2)) - (call $scheduler-kt (cont.new (type $cont) (ref.func $main))) + (call $scheduler-kt (cont.new $cont (ref.func $main))) (call $log (i32.const -3)) - (call $schedule-tk (cont.new (type $cont) (ref.func $main))) + (call $schedule-tk (cont.new $cont (ref.func $main))) (call $log (i32.const -4)) - (call $scheduler-ykt (cont.new (type $cont) (ref.func $main))) + (call $scheduler-ykt (cont.new $cont (ref.func $main))) (call $log (i32.const -5)) - (call $scheduler-ytk (cont.new (type $cont) (ref.func $main))) + (call $scheduler-ytk (cont.new $cont (ref.func $main))) (call $log (i32.const -6)) ) ) diff --git a/proposals/continuations/examples/fun-pipes.wast b/proposals/continuations/examples/fun-pipes.wast index 55697ad21..4c4008de7 100644 --- a/proposals/continuations/examples/fun-pipes.wast +++ b/proposals/continuations/examples/fun-pipes.wast @@ -10,7 +10,7 @@ (func $piper (param $n i32) (param $p (ref $producer)) (param $c (ref $consumer)) (block $on-receive (result (ref $consumer)) - (resume (tag $receive $on-receive) (local.get $n) (local.get $c)) + (resume $consumer (tag $receive $on-receive) (local.get $n) (local.get $c)) (return) ) ;; receive (local.set $c) @@ -20,7 +20,7 @@ (func $copiper (param $c (ref $consumer)) (param $p (ref $producer)) (local $n i32) (block $on-send (result i32 (ref $producer)) - (resume (tag $send $on-send) (local.get $p)) + (resume $producer (tag $send $on-send) (local.get $p)) (return) ) ;; send (local.set $p) @@ -79,8 +79,8 @@ ) (func (export "run") (param $n i32) - (call $pipe (cont.bind (type $producer) (local.get $n) (cont.new (type $consumer) (ref.func $nats))) - (cont.new (type $consumer) (ref.func $sum)) + (call $pipe (cont.bind $consumer $producer (local.get $n) (cont.new $consumer (ref.func $nats))) + (cont.new $consumer (ref.func $sum)) ) ) ) diff --git a/proposals/continuations/examples/fun-state.wast b/proposals/continuations/examples/fun-state.wast index 23d6c62a9..440aaedfb 100644 --- a/proposals/continuations/examples/fun-state.wast +++ b/proposals/continuations/examples/fun-state.wast @@ -12,7 +12,7 @@ (func $getting (param $k (ref $gk)) (param $s i32) (result i32) (block $on_get (result (ref $gk)) (block $on_set (result i32 (ref $sk)) - (resume (tag $get $on_get) (tag $set $on_set) + (resume $gk (tag $get $on_get) (tag $set $on_set) (local.get $s) (local.get $k) ) (return) @@ -26,7 +26,7 @@ (func $setting (param $s i32) (param $k (ref $sk)) (result i32) (block $on_get (result (ref $gk)) (block $on_set (result i32 (ref $sk)) - (resume (tag $get $on_get) (tag $set $on_set) + (resume $sk (tag $get $on_get) (tag $set $on_set) (local.get $k) ) (return) @@ -54,7 +54,7 @@ (elem declare func $f) (func (export "run") (result i32) - (call $setting (i32.const 0) (cont.new (type $sk) (ref.func $f))) + (call $setting (i32.const 0) (cont.new $sk (ref.func $f))) ) ) diff --git a/proposals/continuations/examples/generators.wast b/proposals/continuations/examples/generators.wast new file mode 100644 index 000000000..a7ce4e057 --- /dev/null +++ b/proposals/continuations/examples/generators.wast @@ -0,0 +1,166 @@ +;; Generators + +;; generator interface +(module $generator + (tag $yield (export "yield") (param i32)) +) +(register "generator") + +(module $examples + (type $func (func)) + (type $cont (cont $func)) + + (tag $yield (import "generator" "yield") (param i32)) + + (func $log (import "spectest" "print_i32") (param i32)) + + ;; yields successive natural numbers + (func $naturals (export "naturals") + (local $n i32) + (loop $l + (suspend $yield (local.get $n)) + (local.set $n (i32.add (local.get $n) (i32.const 1))) + (br $l) + ) + ) + + ;; yields 1-2-3 + (func $one-two-three (export "one-two-three") + (suspend $yield (i32.const 1)) + (suspend $yield (i32.const 2)) + (suspend $yield (i32.const 3)) + ) + + ;; yields successive Fibonacci numbers + (func $fibonacci (export "fibonacci") + (local $a i32) + (local $b i32) + (local $t i32) + (local.set $b (i32.const 1)) + (loop $l + (suspend $yield (local.get $a)) + (local.set $t (local.get $a)) + (local.set $a (local.get $b)) + (local.set $b (i32.add (local.get $t) (local.get $a))) + (br $l) + ) + ) + + (func $print-first (export "print-first") (param $n i32) (param $k (ref $cont)) + (loop $l + (block $on_yield (result i32 (ref $cont)) + (if (local.get $n) + (then (resume $cont (tag $yield $on_yield) (local.get $k))) + ) + (return) + ) ;; $on_yield (result i32 (ref $cont)) + (local.set $k) + (call $log) + (local.set $n (i32.add (local.get $n) (i32.const -1))) + (br $l) + ) + (unreachable) + ) + + (func $sum-first (export "sum-first") (param $n i32) (param $k (ref $cont)) (result i32) + (local $sum i32) + (loop $l + (block $on_yield (result i32 (ref $cont)) + (if (local.get $n) + (then (resume $cont (tag $yield $on_yield) (local.get $k))) + ) + (return (local.get $sum)) + ) ;; $on_yield (result i32 (ref $cont)) + (local.set $k) + (local.set $sum (i32.add (local.get $sum))) + (local.set $n (i32.add (local.get $n) (i32.const -1))) + (br $l) + ) + (unreachable) + ) +) +(register "examples") + +;; storing generators in a global table and then accessing them through i32 handles +;; without knowledge of handlers +(module $manager + (type $func (func)) + (type $cont (cont $func)) + + (tag $yield (import "generator" "yield") (param i32)) + + (table $active 0 (ref null $cont)) + + (func $init (export "init") (param $k (ref $cont)) (result i32) + (table.grow $active (local.get $k) (i32.const 1)) + ) + + (func $next (export "next") (param $g i32) (result i32) + (local $next_k (ref $cont)) + (local $next_v i32) + (block $on_yield (result i32 (ref $cont)) + (resume $cont (tag $yield $on_yield) + (table.get $active (local.get $g)) + ) + (return (i32.const -1)) + ) ;; $on_yield (result i32 (ref $cont)) + (local.set $next_k) + (local.set $next_v) + (table.set (local.get $g) (local.get $next_k)) + (return (local.get $next_v)) + ) +) +(register "manager") + +(module + (type $func (func)) + (type $cont (cont $func)) + + (elem declare func $naturals $fibonacci $one-two-three) + + (func $log (import "spectest" "print_i32") (param i32)) + (func $naturals (import "examples" "naturals")) + (func $fibonacci (import "examples" "fibonacci")) + (func $one-two-three (import "examples" "one-two-three")) + (func $print-first (import "examples" "print-first") (param $n i32) (param $k (ref $cont))) + (func $sum-first (import "examples" "sum-first") (param $n i32) (param $k (ref $cont)) (result i32)) + (func $init (import "manager" "init") (param $k (ref $cont)) (result i32)) + (func $next (import "manager" "next") (param i32) (result i32)) + + (func $print-with-next (param $n i32) (param $gen i32) + (loop $l + (if (i32.eqz (local.get $n)) (then (return))) + (call $next (local.get $gen)) + (call $log) + (local.set $n (i32.add (local.get $n) (i32.const -1))) + (br $l) + ) + ) + + (func $interleave-naturals-and-fib + (local $gen1 i32) + (local $gen2 i32) + (local.set $gen1 (call $init (cont.new $cont (ref.func $naturals)))) + (local.set $gen2 (call $init (cont.new $cont (ref.func $fibonacci)))) + (call $print-with-next (i32.const 5) (local.get $gen1)) + (call $print-with-next (i32.const 5) (local.get $gen2)) + (call $print-with-next (i32.const 5) (local.get $gen1)) + (call $print-with-next (i32.const 5) (local.get $gen2)) + (call $print-with-next (i32.const 5) (local.get $gen1)) + (call $print-with-next (i32.const 5) (local.get $gen2)) + (call $print-with-next (i32.const 5) (local.get $gen1)) + (call $print-with-next (i32.const 5) (local.get $gen2)) + ) + + (func $main (export "main") + (call $interleave-naturals-and-fib) + (call $print-first (i32.const 42) (cont.new $cont (ref.func $naturals))) + (call $print-first (i32.const 42) (cont.new $cont (ref.func $fibonacci))) + (call $sum-first (i32.const 101) (cont.new $cont (ref.func $naturals))) + (call $log) + (call $sum-first (i32.const 10) (cont.new $cont (ref.func $one-two-three))) + (call $log) + ) +) + +(invoke "main") diff --git a/proposals/continuations/examples/lwt.wast b/proposals/continuations/examples/lwt.wast index 6a5955a58..65232d5bc 100644 --- a/proposals/continuations/examples/lwt.wast +++ b/proposals/continuations/examples/lwt.wast @@ -23,11 +23,11 @@ (func $main (export "main") (call $log (i32.const 0)) - (suspend $fork (cont.new (type $cont) (ref.func $thread1))) + (suspend $fork (cont.new $cont (ref.func $thread1))) (call $log (i32.const 1)) - (suspend $fork (cont.new (type $cont) (ref.func $thread2))) + (suspend $fork (cont.new $cont (ref.func $thread2))) (call $log (i32.const 2)) - (suspend $fork (cont.new (type $cont) (ref.func $thread3))) + (suspend $fork (cont.new $cont (ref.func $thread3))) (call $log (i32.const 3)) ) @@ -134,7 +134,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) @@ -165,7 +165,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) @@ -189,7 +189,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) @@ -213,7 +213,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) @@ -238,7 +238,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume + (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk) @@ -278,15 +278,15 @@ (func (export "run") (call $log (i32.const -1)) - (call $scheduler-sync (cont.new (type $cont) (ref.func $main))) + (call $scheduler-sync (cont.new $cont (ref.func $main))) (call $log (i32.const -2)) - (call $scheduler-kt (cont.new (type $cont) (ref.func $main))) + (call $scheduler-kt (cont.new $cont (ref.func $main))) (call $log (i32.const -3)) - (call $schedule-tk (cont.new (type $cont) (ref.func $main))) + (call $schedule-tk (cont.new $cont (ref.func $main))) (call $log (i32.const -4)) - (call $scheduler-ykt (cont.new (type $cont) (ref.func $main))) + (call $scheduler-ykt (cont.new $cont (ref.func $main))) (call $log (i32.const -5)) - (call $scheduler-ytk (cont.new (type $cont) (ref.func $main))) + (call $scheduler-ytk (cont.new $cont (ref.func $main))) (call $log (i32.const -6)) ) ) diff --git a/proposals/continuations/examples/pipes.wast b/proposals/continuations/examples/pipes.wast index 573b9491a..e35817856 100644 --- a/proposals/continuations/examples/pipes.wast +++ b/proposals/continuations/examples/pipes.wast @@ -19,7 +19,7 @@ (if (local.get $consuming) (then (block $on-receive (result (ref $consumer)) - (resume (tag $receive $on-receive) (local.get $n) (local.get $c)) + (resume $consumer (tag $receive $on-receive) (local.get $n) (local.get $c)) (return) ) ;; receive (local.set $c) @@ -28,7 +28,7 @@ ) ) ;; else producing (block $on-send (result i32 (ref $producer)) - (resume (tag $send $on-send) (local.get $p)) + (resume $producer (tag $send $on-send) (local.get $p)) (return) ) ;; send (local.set $p) @@ -86,8 +86,8 @@ ) (func (export "run") (param $n i32) - (call $pipe (cont.bind (type $producer) (local.get $n) (cont.new (type $consumer) (ref.func $nats))) - (cont.new (type $consumer) (ref.func $sum)) + (call $pipe (cont.bind $consumer $producer (local.get $n) (cont.new $consumer (ref.func $nats))) + (cont.new $consumer (ref.func $sum)) ) ) ) diff --git a/proposals/continuations/examples/static-lwt.wast b/proposals/continuations/examples/static-lwt.wast index 0bd0b376b..22bd0f34d 100644 --- a/proposals/continuations/examples/static-lwt.wast +++ b/proposals/continuations/examples/static-lwt.wast @@ -110,8 +110,8 @@ (loop $l (if (call $queue-empty) (then (return))) (block $on_yield (result (ref $cont)) - (resume (tag $yield $on_yield) - (call $dequeue) + (resume $cont (tag $yield $on_yield) + (call $dequeue) ) (br $l) ;; thread terminated ) ;; $on_yield (result (ref $cont)) @@ -138,9 +138,9 @@ (elem declare func $thread1 $thread2 $thread3) (func (export "run") - (call $enqueue (cont.new (type $cont) (ref.func $thread1))) - (call $enqueue (cont.new (type $cont) (ref.func $thread2))) - (call $enqueue (cont.new (type $cont) (ref.func $thread3))) + (call $enqueue (cont.new $cont (ref.func $thread1))) + (call $enqueue (cont.new $cont (ref.func $thread2))) + (call $enqueue (cont.new $cont (ref.func $thread3))) (call $log (i32.const -1)) (call $scheduler)