diff --git a/benchmarks/wasm/wasmfx/cont_bind4.bin.wast b/benchmarks/wasm/wasmfx/cont_bind4.bin.wast new file mode 100644 index 000000000..3c9368709 --- /dev/null +++ b/benchmarks/wasm/wasmfx/cont_bind4.bin.wast @@ -0,0 +1,12 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\8e\80\80\80\00\04\60" + "\00\01\7f\5d\00\60\01\7f\01\7f\5d\02\03\83\80\80" + "\80\00\02\02\02\07\88\80\80\80\00\01\04\6d\61\69" + "\6e\00\01\09\85\80\80\80\00\01\03\00\01\00\0a\a0" + "\80\80\80\00\02\87\80\80\80\00\00\20\00\41\2c\6a" + "\0b\8e\80\80\80\00\00\20\00\d2\00\e0\03\e1\03\01" + "\e3\01\00\0b" +) +(module instance) +(assert_return (invoke "main" (i32.const 0x16)) (i32.const 0x42)) +(assert_return (invoke "main" (i32.const 0xffff_fe44)) (i32.const 0xffff_fe70)) diff --git a/benchmarks/wasm/wasmfx/cont_bind4.wast b/benchmarks/wasm/wasmfx/cont_bind4.wast new file mode 100644 index 000000000..214a287d2 --- /dev/null +++ b/benchmarks/wasm/wasmfx/cont_bind4.wast @@ -0,0 +1,21 @@ +(module + (type $f1 (func (result i32))) + (type $c1 (cont $f1)) + + (type $f2 (func (param i32) (result i32))) + (type $c2 (cont $f2)) + + (func $add44 (param i32) (result i32) (i32.add (local.get 0) (i32.const 44))) + (elem declare func $add44) + + (func (export "main") (param i32) (result i32) + (resume $c1 + (cont.bind $c2 $c1 + (local.get 0) + (cont.new $c2 (ref.func $add44)))) + ) +) + +(assert_return (invoke "main" (i32.const 22)) (i32.const 66)) +(assert_return (invoke "main" (i32.const -444)) (i32.const -400)) + diff --git a/benchmarks/wasm/wasmfx/cont_bind5-strip.wast b/benchmarks/wasm/wasmfx/cont_bind5-strip.wast new file mode 100644 index 000000000..4e56e37a3 --- /dev/null +++ b/benchmarks/wasm/wasmfx/cont_bind5-strip.wast @@ -0,0 +1,31 @@ +(module + (type (;0;) (func (param i32) (result i32))) + (type (;1;) (cont 0)) + (type (;2;) (func (param i32 i32) (result i32))) + (type (;3;) (cont 2)) + (type (;4;) (func (param i32))) + (type (;5;) (func)) + (import "spectest" "print_i32" (func (;0;) (type 4))) + (export "main" (func 3)) + (start 3) + (elem (;0;) declare func 1) + (func (;1;) (type 2) (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.sub + ) + (func (;2;) (type 2) (param i32 i32) (result i32) + local.get 1 + local.get 0 + ref.func 1 + cont.new 3 + cont.bind 3 1 + resume 1 + ) + (func (;3;) (type 5) + i32.const 22 + i32.const 44 + call 2 + call 0 + ) +) diff --git a/benchmarks/wasm/wasmfx/cont_bind5.bin.wast b/benchmarks/wasm/wasmfx/cont_bind5.bin.wast new file mode 100644 index 000000000..3f710caeb --- /dev/null +++ b/benchmarks/wasm/wasmfx/cont_bind5.bin.wast @@ -0,0 +1,18 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\90\80\80\80\00\04\60" + "\01\7f\01\7f\5d\00\60\02\7f\7f\01\7f\5d\02\03\83" + "\80\80\80\00\02\02\02\07\88\80\80\80\00\01\04\6d" + "\61\69\6e\00\01\09\85\80\80\80\00\01\03\00\01\00" + "\0a\a2\80\80\80\00\02\87\80\80\80\00\00\20\00\20" + "\01\6b\0b\90\80\80\80\00\00\20\01\20\00\d2\00\e0" + "\03\e1\03\01\e3\01\00\0b" +) +(module instance) +(assert_return + (invoke "main" (i32.const 0x16) (i32.const 0x2c)) + (i32.const 0xffff_ffea) +) +(assert_return + (invoke "main" (i32.const 0xffff_fe44) (i32.const 0x6f)) + (i32.const 0xffff_fdd5) +) diff --git a/benchmarks/wasm/wasmfx/cont_bind5.wast b/benchmarks/wasm/wasmfx/cont_bind5.wast new file mode 100644 index 000000000..c00342077 --- /dev/null +++ b/benchmarks/wasm/wasmfx/cont_bind5.wast @@ -0,0 +1,25 @@ +(module +;; (type $f1 (func (result i32))) +;; (type $c1 (cont $f1)) + + (type $f2 (func (param i32) (result i32))) + (type $c2 (cont $f2)) + + (type $f3 (func (param i32 i32) (result i32))) + (type $c3 (cont $f3)) + + (func $sub (param i32 i32) (result i32) (i32.sub (local.get 0) (local.get 1))) + (elem declare func $sub) + + (func (export "main") (param i32 i32) (result i32) + (resume $c2 + (local.get 1) + (cont.bind $c3 $c2 + (local.get 0) + (cont.new $c3 (ref.func $sub)))) + ) +) + +;; (assert_return (invoke "main" (i32.const 22) (i32.const 44)) (i32.const -22)) +;; (assert_return (invoke "main" (i32.const -444) (i32.const 111)) (i32.const -555)) + diff --git a/benchmarks/wasm/wasmfx/diff_resume-strip.wat b/benchmarks/wasm/wasmfx/diff_resume-strip.wat new file mode 100644 index 000000000..964aca389 --- /dev/null +++ b/benchmarks/wasm/wasmfx/diff_resume-strip.wat @@ -0,0 +1,53 @@ +(module + (type (;0;) (func (param i32))) + (type (;1;) (cont 0)) + (type (;2;) (func (result i32))) + (type (;3;) (func)) + (import "spectest" "print_i32" (func (;0;) (type 0))) + (tag (;0;) (type 2) (result i32)) + (export "_start" (func 2)) + (start 2) + (elem (;0;) declare func 1) + (func (;1;) (type 0) (param i32) + local.get 0 + call 0 + suspend 0 + call 0 + ) + (func (;2;) (type 3) + (local i32 (ref 1)) + ref.func 1 + cont.new 1 + local.set 1 + i32.const 10 + local.set 0 + block ;; label = @1 + block (result (ref 1)) ;; label = @2 + local.get 0 + local.get 1 + resume 1 (on 0 0 (;@2;)) + i32.const -2 + call 0 + br 1 (;@1;) + end + local.set 1 + local.get 0 + i32.const 1 + i32.add + local.set 0 + block ;; label = @2 + block (result (ref 1)) ;; label = @3 + local.get 0 + local.get 1 + resume 1 (on 0 0 (;@3;)) + i32.const 42 + call 0 + br 1 (;@2;) + end + i32.const 111 + call 0 + drop + end + end + ) +) diff --git a/benchmarks/wasm/wasmfx/diff_resume.wast b/benchmarks/wasm/wasmfx/diff_resume.wast new file mode 100644 index 000000000..5846e4616 --- /dev/null +++ b/benchmarks/wasm/wasmfx/diff_resume.wast @@ -0,0 +1,52 @@ +(module + (import "spectest" "print_i32" (func $print_i32 (param i32))) + (type $task (func (param i32))) + (type $cont (cont $task)) + + (tag $yield (result i32)) + + (func $task1 (param $x i32) + (call $print_i32 (local.get $x)) + (suspend $yield) ;; DH: levaes a continuation on the stack, jump to the tag $yield + ;; when come back, the stack should contains a i32 value, since the return type of $yield is i32 + (call $print_i32) + ) + + (func $main (export "_start") + (local $i i32) + (local $k (ref $cont)) + (local.set $k (cont.new $cont (ref.func $task1))) + (local.set $i (i32.const 10)) + (block $h + (block $on_yield (result (ref $cont)) + (resume $cont + (on $yield $on_yield) + (local.get $i) + (local.get $k) + ) + (call $print_i32 (i32.const -2)) + (br $h)) + ;; $on_yield lands here, with the continuation on the stack + (local.set $k) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (block $h + (block $on_yield2 (result (ref $cont)) + (resume $cont + (on $yield $on_yield2) + (local.get $i) + (local.get $k) + ) + (call $print_i32 (i32.const 42)) + (br $h) + ) + ;; $on_yield2 lands here, with the continuation on the stack + (call $print_i32 (i32.const 111)) + drop + ) + ) + ) + + (elem declare func $task1) + + (start $main) +) \ No newline at end of file diff --git a/benchmarks/wasm/wasmfx/gen-stripped.wast b/benchmarks/wasm/wasmfx/gen-stripped.wast new file mode 100644 index 000000000..83c9fedcc --- /dev/null +++ b/benchmarks/wasm/wasmfx/gen-stripped.wast @@ -0,0 +1,42 @@ +(module + (type (;0;) (func)) + (type (;1;) (cont 0)) + (type (;2;) (func (param i32))) + (type (;3;) (func (result i32 (ref 1)))) + (import "spectest" "print_i32" (func (;0;) (type 2))) + (tag (;0;) (type 2) (param i32)) + (start 2) + (elem (;0;) declare func 1) + (func (;1;) (type 0) + (local i32) + i32.const 100 + local.set 0 + loop ;; label = @1 + local.get 0 + suspend 0 + local.get 0 + i32.const 1 + i32.sub + local.tee 0 + br_if 0 (;@1;) + end + ) + (func (;2;) (type 0) + (local (ref 1)) + ref.func 1 + cont.new 1 + local.set 0 + loop ;; label = @1 + block (result i32 (ref 1)) ;; label = @2 + local.get 0 + resume 1 (on 0 0 (;@2;)) ;; wasmfx ref interpreter has a bug on this, you can add a bracket around `resume ..` to get around + i32.const 42 + call 0 + br 2 (;@2;) + end + local.set 0 + call 0 + br 0 (;@1;) + end + ) +) diff --git a/benchmarks/wasm/wasmfx/nested_suspend-strip.wat b/benchmarks/wasm/wasmfx/nested_suspend-strip.wat new file mode 100644 index 000000000..ab5e2bd24 --- /dev/null +++ b/benchmarks/wasm/wasmfx/nested_suspend-strip.wat @@ -0,0 +1,39 @@ +(module + (type (;0;) (func)) + (type (;1;) (cont 0)) + (type (;2;) (func (param i32))) + (import "spectest" "print_i32" (func (;0;) (type 2))) + (tag (;0;) (type 0)) + (export "_start" (func 3)) + (start 3) + ;; (elem (;0;) declare func 1 2) + (func (;1;) (type 0) + suspend 0 + ) + (func (;2;) (type 0) + block ;; label = @1 + block (result (ref 1)) ;; label = @2 + ref.func 1 + cont.new 1 + resume 1 (on 0 0 (;@2;)) + br 1 (;@1;) + end + drop + i32.const 0 + call 0 + end + ) + (func (;3;) (type 0) + block ;; label = @1 + block (result (ref 1)) ;; label = @2 + ref.func 2 + cont.new 1 + resume 1 (on 0 0 (;@2;)) + br 1 (;@1;) + end + drop + i32.const 1 + call 0 + end + ) +) diff --git a/benchmarks/wasm/wasmfx/resume2.wat b/benchmarks/wasm/wasmfx/resume2.wat new file mode 100644 index 000000000..86de1faa2 --- /dev/null +++ b/benchmarks/wasm/wasmfx/resume2.wat @@ -0,0 +1,9 @@ +(module + (type $f1 (func (result i32))) + (type $c1 (cont $f1)) + (func $f42 (result i32) (i32.const 42)) + (elem declare func $f42) + (func (export "main") (result i32) + (resume $c1 (cont.new $c1 (ref.func $f42))) + ) +) \ No newline at end of file diff --git a/benchmarks/wasm/wasmfx/resume_chain1-strip.wast b/benchmarks/wasm/wasmfx/resume_chain1-strip.wast new file mode 100644 index 000000000..047bf763f --- /dev/null +++ b/benchmarks/wasm/wasmfx/resume_chain1-strip.wast @@ -0,0 +1,31 @@ +(module + (type (;0;) (func (param i32) (result i32))) + (type (;1;) (cont 0)) + (export "main" (func 2)) + ;; (elem (;0;) declare func 0 1) + (func (;0;) (type 0) (param i32) (result i32) + local.get 0 + i32.const 10 + i32.add + ) + (func (;1;) (type 0) (param i32) (result i32) + local.get 0 + i32.const 100 + i32.add + ref.func 0 + cont.new 1 + resume 1 + i32.const 1000 + i32.add + ) + (func (;2;) (type 0) (param i32) (result i32) + local.get 0 + ref.func 1 + cont.new 1 + resume 1 + ) +) + +(assert_return (invoke "main" (i32.const 4)) (i32.const 1114)) +(assert_return (invoke "main" (i32.const 5)) (i32.const 1115)) +(assert_return (invoke "main" (i32.const 9)) (i32.const 1119)) diff --git a/benchmarks/wasm/wasmfx/test_cont-strip.wast b/benchmarks/wasm/wasmfx/test_cont-strip.wast new file mode 100644 index 000000000..6e7eadba8 --- /dev/null +++ b/benchmarks/wasm/wasmfx/test_cont-strip.wast @@ -0,0 +1,71 @@ +(module + (type (;0;) (func (param i32))) + (type (;1;) (cont 0)) + (type (;2;) (func (result i32))) + (type (;3;) (func (param i32 i32))) + (type (;4;) (func)) + (import "spectest" "print_i32" (func (;0;) (type 0))) + (tag (;0;) (type 2) (result i32)) + (export "_start" (func 4)) + (start 4) + (elem (;0;) declare func 3) + (func (;1;) (type 0) (param i32) + local.get 0 + call 0 + suspend 0 + call 0 + ) + (func (;2;) (type 3) (param i32 i32) + (local i32) + local.get 0 + local.set 2 + block ;; label = @1 + loop ;; label = @2 + local.get 2 + local.get 1 + i32.gt_u + br_if 1 (;@1;) + local.get 2 + call 1 + local.get 2 + i32.const 1 + i32.add + local.set 2 + br 0 (;@2;) + end + end + ) + (func (;3;) (type 0) (param i32) + local.get 0 + i32.const 13 + call 2 + ) + (func (;4;) (type 4) + (local (ref 1) i32) + ref.func 3 + cont.new 1 + local.set 0 + i32.const 10 + local.set 1 + block ;; label = @1 + loop ;; label = @2 + block (result (ref 1)) ;; label = @3 + local.get 1 + local.get 0 + resume 1 (on 0 0 (;@3;)) + i32.const -2 + call 0 + br 2 (;@1;) + end + local.set 0 + local.get 1 + i32.const 1 + i32.add + local.set 1 + i32.const -1 + call 0 + br 0 (;@2;) + end + end + ) +) diff --git a/benchmarks/wasm/wasmfx/test_cont.wast b/benchmarks/wasm/wasmfx/test_cont.wast new file mode 100644 index 000000000..8f5408871 --- /dev/null +++ b/benchmarks/wasm/wasmfx/test_cont.wast @@ -0,0 +1,55 @@ +(module + (import "spectest" "print_i32" (func $print_i32 (param i32))) + (type $task (func (param i32))) + (type $cont (cont $task)) + + (tag $yield (result i32)) + ;; DH: tag's input + continuation + ;; == block's output ((ref $cont)) + ;; == the values on the stack after suspend + ;; tag's output + ;; == continuation's input + ;; == the values on the stack when suspended computation is resumed + + (func $process (param $x i32) + (call $print_i32 (local.get $x)) + (suspend $yield) ;; DH: levaes a continuation on the stack, jump to the tag $yield + ;; when come back, the stack should contains a i32 value, since the return type of $yield is i32 + (call $print_i32)) + + (func $range (param $from i32) (param $to i32) + (local $i i32) + (local.set $i (local.get $from)) + (block $b + (loop $l + (br_if $b (i32.gt_u (local.get $i) (local.get $to))) + (call $process (local.get $i)) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $l)))) + + (func $task1 (param $x i32) (call $range (local.get $x) (i32.const 13))) + + (func $main (export "_start") + (local $k (ref $cont)) + (local $i i32) + (local.set $k (cont.new $cont (ref.func $task1))) + (local.set $i (i32.const 10)) + (block $h + (loop $l + (block $on_yield (result (ref $cont)) + (resume $cont + (on $yield $on_yield) + (local.get $i) + (local.get $k)) + (call $print_i32 (i32.const -2)) ;; this code is executed only when no suspend is called in $k + (br $h)) + ;; $on_yield lands here, with the continuation on the stack + (local.set $k) ;; grab the continuation and save it + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (call $print_i32 (i32.const -1)) + (br $l)))) + + (elem declare func $task1) + + (start $main) +) \ No newline at end of file diff --git a/src/main/scala/wasm/MiniWasm.scala b/src/main/scala/wasm/MiniWasm.scala index c6b6ca775..3493a4fe5 100644 --- a/src/main/scala/wasm/MiniWasm.scala +++ b/src/main/scala/wasm/MiniWasm.scala @@ -227,7 +227,7 @@ object Primtives { case F64Type => F64V(0) } case VecType(kind) => ??? - case RefType(kind) => ??? + case RefType(kind) => RefNullV(kind) } def getFuncType(ty: BlockType): FuncType = diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index bd3de9c8c..bdc71d739 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -17,6 +17,10 @@ case class EvaluatorFX(module: ModuleInstance) { type MCont[A] = Stack => A type Handler[A] = Stack => A + case class ContV[A](k: (Stack, Cont[A], MCont[A], Handler[A]) => A) extends Value { + def tipe(implicit m: ModuleInstance): ValueType = ??? + } + // Only used for resumable try-catch (need refactoring): case class TCContV[A](k: (Stack, Cont[A], MCont[A]) => A) extends Value { def tipe(implicit m: ModuleInstance): ValueType = ??? @@ -69,6 +73,7 @@ case class EvaluatorFX(module: ModuleInstance) { mkont: MCont[Ans], trail: List[Cont[Ans]], h: Handler[Ans]): Ans = { + // Note kont is only used in the base case, or when we are appending k and k1 if (insts.isEmpty) return kont(stack, mkont) val inst = insts.head @@ -159,8 +164,11 @@ case class EvaluatorFX(module: ModuleInstance) { case Block(ty, inner) => val funcTy = getFuncType(ty) val (inputs, restStack) = stack.splitAt(funcTy.inps.size) + // why a block is introducing a new mknot1 + // I feel like we will almost never need to change the mkont for a block val restK: Cont[Ans] = (retStack, mkont1) => { // kont -> mkont -> mkont1 + // println(s"inner: $inner") eval(rest, retStack.take(funcTy.out.size) ++ restStack, frame, kont, mkont1, trail, h) } eval(inner, inputs, frame, restK, mkont, restK :: trail, h) @@ -174,6 +182,8 @@ case class EvaluatorFX(module: ModuleInstance) { eval(rest, retStack.take(funcTy.out.size) ++ restStack, frame, kont, mkont, trail, h) def loop(retStack: List[Value], mkont: MCont[Ans]): Ans = eval(inner, retStack.take(funcTy.inps.size), frame, restK, mkont, loop _ :: trail, h) + // for example, loop here doesn't change the meta-continuation at all + // compare with block loop(inputs, mkont) case If(ty, thn, els) => val funcTy = getFuncType(ty) @@ -230,57 +240,93 @@ case class EvaluatorFX(module: ModuleInstance) { // WasmFX effect handlers: case ContNew(ty) => val RefFuncV(f) :: newStack = stack - // should be similar to the contiuantion thrown by `throw` - // val k: Cont[Ans] = (s, mk) => evalCall(f, List(), s, frame, idK, mk, trail, h, false) - // TODO: where should kont go? - // TODO: this implementation is not right - def kr(s: Stack, k1: Cont[Ans], mk: MCont[Ans]): Ans = { - val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) - evalCall(f, List(), s, frame, kontK, mk, trail, h, false) + def kr(s: Stack, k1: Cont[Ans], mk: MCont[Ans], handler: Handler[Ans]): Ans = { + // k1 is rest for `resume` + // mk holds the handler for `suspend` + + // Q: Do we need to care about kont here?? + // Answer: we don't, because continuation is only activated by `resume` + // and it must be handled by the default handler (k1) or the handler associated + // to `suspend` + + // we can discard trail since it is a new function call (similar to `kont`) + val empty: Cont[Ans] = (s1, m1) => m1(s1) + evalCall(f, List(), s, frame, empty, mk, List(), handler, false) } - eval(rest, TCContV(kr) :: newStack, frame, kont, mkont, trail, h) - // TODO: implement the following + eval(rest, ContV(kr) :: newStack, frame, kont, mkont, trail, h) + case Suspend(tag_id) => { // println(s"${RED}Unimplimented Suspending tag $tag_id") - throw new Exception("Suspend not implemented") - // h(stack) + // add the continuation on the stack + + val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans], handler: Handler[Ans]) => { + // TODO: does the following work? + // val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) + + // Q: is it okay to forget `k1` and `mkont` here? + // Ans: No! Because the resumable continuation might be install by + // a different `resume` with a different set of handlers + val newMk: MCont[Ans] = (s) => k1(s, m) + eval(rest, s, frame, kont, newMk, trail, handler) + } + val newStack = ContV(k) :: stack + h(newStack) } // TODO: resume should create a list of handlers to capture suspend - // TODO: The current implementation doesn't not deal with suspend at all case Resume(kty_id, handler) => { - val (f: TCContV[Ans]) :: newStack = stack + val (f: ContV[Ans]) :: newStack = stack val contTy = module.types(kty_id) val ContType(funcTypeId) = contTy val FuncType(_, inps, out) = module.types(funcTypeId) val (inputs, restStack) = newStack.splitAt(inps.size) if (handler.length == 0) { - val k: Cont[Ans] = (s, m) => eval(rest, newStack, frame, kont, m, trail, h) - f.k(inputs, k, mkont) + // the metacontinuation contains the default handler + val mk: MCont[Ans] = (s) => eval(rest, s, frame, kont, mkont, trail, h) + val emptyK: Cont[Ans] = (s, m) => m(s) + f.k(inputs, emptyK, mk, h) } else { - // TODO: attempt single tag first - throw new Exception("tags not supported") + if (handler.length > 1) { + throw new Exception("only single tag is supported") + } + // if suspend happens, the label id holds the handler + val Handler(tagId, labelId) = handler.head + + val newHandler: Handler[Ans] = (newStack) => trail(labelId)(newStack, mkont) + + // f might be handled by the default handler (namely kont), or by the + // handler specified by tags (newhandler, which has the same type as meta-continuation) + val mk: MCont[Ans] = (s) => eval(rest, s, frame, kont, mkont, trail, h) + val emptyK: Cont[Ans] = (s, m) => m(s) + f.k(inputs, emptyK, mk, newHandler) } } - case ContBind(oldContTy, newConTy) => - // val RefContV(oldContAddr) :: newStack = stack - // // use oldParamTy - newParamTy to get how many values to pop from the stack - // val oldParamTy = module.types(oldContTy).inps - // val newParamTy = module.types(newConTy).inps - // val (inputs, restStack) = newStack.splitAt(oldParamTy.size) - // // partially apply the old continuation - // val oldCont = module.funcs(oldContAddr) match { - // case RefContV(f) => f - // case _ => throw new Exception("Continuation is not a function") - // } - throw new Exception("ContBind unimplemented") + case ContBind(oldContTyId, newConTyId) => + val (f: ContV[Ans]) :: newStack = stack + // use oldParamTy - newParamTy to get how many values to pop from the stack + val ContType(oldId) = module.types(oldContTyId) + val FuncType(_, oldParamTy, _) = module.types(oldId) + val ContType(newId) = module.types(newConTyId) + val FuncType(_, newParamTy, _) = module.types(newId) + + // get oldParamTy - newParamTy (there's no type checking at all) + val inputSize = oldParamTy.size - newParamTy.size + + val (inputs, restStack) = newStack.splitAt(inputSize) + // partially apply the old continuation + def kr(s: Stack, k1: Cont[Ans], mk: MCont[Ans], handler: Handler[Ans]): Ans = { + f.k(s ++ inputs, k1, mk, handler) + } + + eval(rest, ContV(kr) :: restStack, frame, kont, mkont, trail, h) + case CallRef(ty) => val RefFuncV(f) :: newStack = stack evalCall(f, rest, newStack, frame, kont, mkont, trail, h, false) @@ -304,8 +350,31 @@ case class EvaluatorFX(module: ModuleInstance) { case Export(`func_name`, ExportFunc(fid)) => System.err.println(s"Entering function $main") module.funcs(fid) match { - case FuncDef(_, FuncBodyDef(_, _, _, body)) => body - case _ => throw new Exception("Entry function has no concrete body") + case FuncDef(_, FuncBodyDef(_, _, locals, body)) => body + case _ => throw new Exception("Entry function has no concrete body") + } + case _ => List() + }) + case None => + module.defs.flatMap({ + case Start(id) => + System.err.println(s"Entering unnamed function $id") + module.funcs(id) match { + case FuncDef(_, FuncBodyDef(_, _, locals, body)) => body + case _ => + throw new Exception("Entry function has no concrete body") + } + case _ => List() + }) + } + val locals = main match { + case Some(func_name) => + module.defs.flatMap({ + case Export(`func_name`, ExportFunc(fid)) => + System.err.println(s"Entering function $main") + module.funcs(fid) match { + case FuncDef(_, FuncBodyDef(_, _, locals, _)) => locals + case _ => throw new Exception("Entry function has no concrete body") } case _ => List() }) @@ -314,7 +383,7 @@ case class EvaluatorFX(module: ModuleInstance) { case Start(id) => System.err.println(s"Entering unnamed function $id") module.funcs(id) match { - case FuncDef(_, FuncBodyDef(_, _, _, body)) => body + case FuncDef(_, FuncBodyDef(_, _, locals, body)) => locals case _ => throw new Exception("Entry function has no concrete body") } @@ -323,7 +392,9 @@ case class EvaluatorFX(module: ModuleInstance) { } if (instrs.isEmpty) println("Warning: nothing is executed") val handler0: Handler[Ans] = stack => throw new Exception(s"Uncaught exception: $stack") - eval(instrs, List(), Frame(ArrayBuffer(I32V(0))), halt, mhalt, List(halt), handler0) + // initialized locals + val frame = Frame(ArrayBuffer(locals.map(zero(_)): _*)) + eval(instrs, List(), frame, halt, mhalt, List(halt), handler0) } def evalTop(m: ModuleInstance): Unit = { diff --git a/src/main/scala/wasm/MiniWasmScript.scala b/src/main/scala/wasm/MiniWasmScript.scala index 01d3a4691..010ea5f4b 100644 --- a/src/main/scala/wasm/MiniWasmScript.scala +++ b/src/main/scala/wasm/MiniWasmScript.scala @@ -10,8 +10,8 @@ sealed class ScriptRunner { def getInstance(instName: Option[String]): ModuleInstance = { instName match { - case Some(name) => instanceMap(name) - case None => instances.head + case Some(name) => instanceMap(name) + case None => instances.head } } @@ -19,11 +19,13 @@ sealed class ScriptRunner { action match { case Invoke(instName, name, args) => val module = getInstance(instName) - val func = module.exports.collectFirst({ - case Export(`name`, ExportFunc(index)) => - module.funcs(index) - case _ => throw new RuntimeException("Not Supported") - }).get + val func = module.exports + .collectFirst({ + case Export(`name`, ExportFunc(index)) => + module.funcs(index) + case _ => throw new RuntimeException("Not Supported") + }) + .get val instrs = func match { case FuncDef(_, FuncBodyDef(ty, _, locals, body)) => body } @@ -36,16 +38,18 @@ sealed class ScriptRunner { val h0: Handler = stack => throw new Exception(s"Uncaught exception: $stack") // TODO: change this back to Evaluator if we are just testing original stuff val actual = evaluator.eval(instrs, List(), Frame(ArrayBuffer(args: _*)), k, mk, List(k), h0) + println(s"expect = $expect") + println(s"actual = $actual") assert(actual == expect) } } def runCmd(cmd: Cmd): Unit = { cmd match { - case CmdModule(module) => instances += ModuleInstance(module) + case CmdModule(module) => instances += ModuleInstance(module) case AssertReturn(action, expect) => assertReturn(action, expect) - case CMdInstnace() => () - case AssertTrap(action, message) => ??? + case CMdInstnace() => () + case AssertTrap(action, message) => ??? } } @@ -54,4 +58,4 @@ sealed class ScriptRunner { runCmd(cmd) } } -} \ No newline at end of file +} diff --git a/src/main/scala/wasm/Parser.scala b/src/main/scala/wasm/Parser.scala index 7dc934413..5dbed47d7 100644 --- a/src/main/scala/wasm/Parser.scala +++ b/src/main/scala/wasm/Parser.scala @@ -778,7 +778,6 @@ class GSWasmVisitor extends WatParserBaseVisitor[WIR] { val Array(ty, _) = constCtx.CONST.getText.split("\\.") visitLiteralWithType(constCtx.literal, toNumType(ty)) } - println(s"expect = $expect") AssertReturn(action, expect.toList) } else { throw new RuntimeException("Unsupported") diff --git a/src/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index dfd754999..9e6d72e7d 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -24,17 +24,17 @@ class TestFx extends FunSuite { def testFile(filename: String, main: Option[String] = None, expected: ExpResult = Ignore) = { val module = Parser.parseFile(filename) - //println(module) + // println(module) val evaluator = EvaluatorFX(ModuleInstance(module)) type Cont = evaluator.Cont[Unit] type MCont = evaluator.MCont[Unit] val haltK: Cont = (stack, m) => m(stack) val haltMK: MCont = (stack) => { - //println(s"halt cont: $stack") + // println(s"halt cont: $stack") expected match { - case ExpInt(e) => assert(stack(0) == I32V(e)) + case ExpInt(e) => assert(stack(0) == I32V(e)) case ExpStack(e) => assert(stack == e) - case Ignore => () + case Ignore => () } } evaluator.evalTop(haltK, haltMK, main) @@ -94,10 +94,6 @@ class TestFx extends FunSuite { // New effect handler tests: - // test("simple script") { - // TestWastFile("./benchmarks/wasm/wasmfx/cont_bind3.bin.wast") - // } - test("call_ref") { testFile("./benchmarks/wasm/wasmfx/callref-strip.wast") } @@ -146,22 +142,44 @@ class TestFx extends FunSuite { /* REAL WASMFX STUFF */ - // TODO: test after implemented cont_bind3 - // test("simple script") { - // TestWastFile("./benchmarks/wasm/wasmfx/cont_bind3.bin.wast") - // } - - // test("cont") { - // // testFile("./benchmarks/wasm/wasmfx/callcont.wast", None, ExpInt(11)) - // testWastFile("./benchmarks/wasm/wasmfx/callcont.bin.wast") - // } + test("cont") { + // testFile("./benchmarks/wasm/wasmfx/callcont.wast", None, ExpInt(11)) + testWastFile("./benchmarks/wasm/wasmfx/callcont.bin.wast") + } test("resume w/o suspend") { testWastFile("./benchmarks/wasm/wasmfx/resume1.bin.wast") } - // test("test_cont") { - // testFile("./benchmarks/wasm/wasmfx/test_cont-strip.wast") - // } + // wasmfx sec 2.3 like example + test("test_cont") { + testFile("./benchmarks/wasm/wasmfx/test_cont-strip.wast") + } + + test("resume_chain1") { + testWastFile("./benchmarks/wasm/wasmfx/resume_chain1-strip.wast") + } + + // printing 0 not 1 + test("nested suspend") { + testFileOutput("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat", List(0)) + } + + // going to print 100 to 1 and then print 42 + test("gen") { + testFile("./benchmarks/wasm/wasmfx/gen-stripped.wast") + } + + test("diff resume") { + testFileOutput("./benchmarks/wasm/wasmfx/diff_resume-strip.wat", List(10, 11, 42)) + } + + test("cont_bind_4") { + testWastFile("./benchmarks/wasm/wasmfx/cont_bind4.bin.wast") + } + + test("cont_bind_5") { + testWastFile("./benchmarks/wasm/wasmfx/cont_bind5.bin.wast") + } }