From c2ffebb453da51ef67eb4669f950e679236470a9 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 03:14:09 -0500 Subject: [PATCH 01/15] stuck --- benchmarks/wasm/wasmfx/test_cont-strip.wast | 71 +++++++++++ benchmarks/wasm/wasmfx/test_cont.wast | 55 +++++++++ src/main/scala/wasm/AST.scala | 4 +- src/main/scala/wasm/MiniWasm.scala | 2 +- src/main/scala/wasm/MiniWasmFX.scala | 67 ++++++++-- src/test/scala/genwasym/TestFx.scala | 128 ++++++++++---------- 6 files changed, 252 insertions(+), 75 deletions(-) create mode 100644 benchmarks/wasm/wasmfx/test_cont-strip.wast create mode 100644 benchmarks/wasm/wasmfx/test_cont.wast 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/AST.scala b/src/main/scala/wasm/AST.scala index ec1bd19d7..acc0842ae 100644 --- a/src/main/scala/wasm/AST.scala +++ b/src/main/scala/wasm/AST.scala @@ -318,8 +318,8 @@ trait Callable // https://webassembly.github.io/function-references/core/exec/runtime.html abstract class Ref extends Value with Callable -case class RefNullV(t: HeapType) extends Ref { - def tipe(implicit m: ModuleInstance): ValueType = RefType(t) +case class RefNullV() extends Ref { + def tipe(implicit m: ModuleInstance): ValueType = ??? } case class RefFuncV(funcAddr: Int) extends Ref { def tipe(implicit m: ModuleInstance): ValueType = diff --git a/src/main/scala/wasm/MiniWasm.scala b/src/main/scala/wasm/MiniWasm.scala index c6b6ca775..00c9f0a62 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() } def getFuncType(ty: BlockType): FuncType = diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index bd3de9c8c..00a654ec3 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -69,13 +69,14 @@ 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 val rest = insts.tail // TODO: uncommenting this will fail tests that uses `testFileOutput` - // println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) + println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) inst match { case Drop => eval(rest, stack.tail, frame, kont, mkont, trail, h) @@ -159,6 +160,8 @@ 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 eval(rest, retStack.take(funcTy.out.size) ++ restStack, frame, kont, mkont1, trail, h) @@ -174,6 +177,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) @@ -232,10 +237,10 @@ case class EvaluatorFX(module: ModuleInstance) { 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 = { + // k1 is rest for `resume` + // mk holds the handler for `suspend` val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) evalCall(f, List(), s, frame, kontK, mk, trail, h, false) } @@ -244,7 +249,16 @@ case class EvaluatorFX(module: ModuleInstance) { // TODO: implement the following case Suspend(tag_id) => { // println(s"${RED}Unimplimented Suspending tag $tag_id") - throw new Exception("Suspend not implemented") + // add the continuation on the stack + + val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans]) => { + // k1 is the default handler + // so it should be konk ++ k1 + eval(rest, s, frame, kont, m, trail, h) + } + val newStack = TCContV(k) :: stack + mkont(newStack) + // throw new Exception("Suspend not implemented") // h(stack) } @@ -262,7 +276,19 @@ case class EvaluatorFX(module: ModuleInstance) { f.k(inputs, k, mkont) } else { // TODO: attempt single tag first - throw new Exception("tags not supported") + if (handler.length > 1) { + throw new Exception("only single tag is supported") + } + val Handler(tagId, labelId) = handler.head + // if suspend happens, the label id holds the handler + // TODO: should handler be passed in an mkont?? + 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) + f.k(inputs, kont, newHandler) + + // throw new Exception("tags not supported") } @@ -304,7 +330,30 @@ 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 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 +363,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 +372,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/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index dfd754999..ba184181e 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -24,7 +24,7 @@ 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] @@ -56,41 +56,41 @@ class TestFx extends FunSuite { } // non-effect tests should still pass: - test("ack") { testFile("./benchmarks/wasm/ack.wat", Some("real_main"), ExpInt(7)) } - test("power") { testFile("./benchmarks/wasm/pow.wat", Some("real_main"), ExpInt(1024)) } - test("start") { testFile("./benchmarks/wasm/start.wat") } - test("fact") { testFile("./benchmarks/wasm/fact.wat", None, ExpInt(120)) } - test("loop") { testFile("./benchmarks/wasm/loop.wat", None, ExpInt(10)) } - test("even-odd") { testFile("./benchmarks/wasm/even_odd.wat", None, ExpInt(1)) } - test("load") { testFile("./benchmarks/wasm/load.wat", None, ExpInt(1)) } - test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } - test("fib") { testFile("./benchmarks/wasm/fib.wat", None, ExpInt(144)) } - test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, ExpInt(504)) } - - test("return") { - intercept[gensym.wasm.miniwasm.Trap] { - testFile("./benchmarks/wasm/return.wat", Some("$real_main")) - } - } - test("return_call") { - testFile("./benchmarks/wasm/sum.wat", Some("sum10"), ExpInt(55)) - } + // test("ack") { testFile("./benchmarks/wasm/ack.wat", Some("real_main"), ExpInt(7)) } + // test("power") { testFile("./benchmarks/wasm/pow.wat", Some("real_main"), ExpInt(1024)) } + // test("start") { testFile("./benchmarks/wasm/start.wat") } + // test("fact") { testFile("./benchmarks/wasm/fact.wat", None, ExpInt(120)) } + // test("loop") { testFile("./benchmarks/wasm/loop.wat", None, ExpInt(10)) } + // test("even-odd") { testFile("./benchmarks/wasm/even_odd.wat", None, ExpInt(1)) } + // test("load") { testFile("./benchmarks/wasm/load.wat", None, ExpInt(1)) } + // test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } + // test("fib") { testFile("./benchmarks/wasm/fib.wat", None, ExpInt(144)) } + // test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, ExpInt(504)) } + + // test("return") { + // intercept[gensym.wasm.miniwasm.Trap] { + // testFile("./benchmarks/wasm/return.wat", Some("$real_main")) + // } + // } + // test("return_call") { + // testFile("./benchmarks/wasm/sum.wat", Some("sum10"), ExpInt(55)) + // } - test("block input") { - testFile("./benchmarks/wasm/block.wat", Some("real_main"), ExpInt(9)) - } - test("loop block input") { - testFile("./benchmarks/wasm/block.wat", Some("test_loop_input"), ExpInt(55)) - } - test("if block input") { - testFile("./benchmarks/wasm/block.wat", Some("test_if_input"), ExpInt(25)) - } - test("block input - poly br") { - testFile("./benchmarks/wasm/block.wat", Some("test_poly_br"), ExpInt(0)) - } - test("loop block - poly br") { - testFile("./benchmarks/wasm/loop_poly.wat", None, ExpStack(List(2, 1))) - } + // test("block input") { + // testFile("./benchmarks/wasm/block.wat", Some("real_main"), ExpInt(9)) + // } + // test("loop block input") { + // testFile("./benchmarks/wasm/block.wat", Some("test_loop_input"), ExpInt(55)) + // } + // test("if block input") { + // testFile("./benchmarks/wasm/block.wat", Some("test_if_input"), ExpInt(25)) + // } + // test("block input - poly br") { + // testFile("./benchmarks/wasm/block.wat", Some("test_poly_br"), ExpInt(0)) + // } + // test("loop block - poly br") { + // testFile("./benchmarks/wasm/loop_poly.wat", None, ExpStack(List(2, 1))) + // } // New effect handler tests: @@ -98,39 +98,39 @@ class TestFx extends FunSuite { // TestWastFile("./benchmarks/wasm/wasmfx/cont_bind3.bin.wast") // } - test("call_ref") { - testFile("./benchmarks/wasm/wasmfx/callref-strip.wast") - } + // test("call_ref") { + // testFile("./benchmarks/wasm/wasmfx/callref-strip.wast") + // } - test("try-catch") { - testFileOutput("./benchmarks/wasm/trycatch/try_catch.wat", List(1, 2, 3, 4, 5)) - } + // test("try-catch") { + // testFileOutput("./benchmarks/wasm/trycatch/try_catch.wat", List(1, 2, 3, 4, 5)) + // } - test("try-catch-succ") { - // no exception was thrown - testFileOutput("./benchmarks/wasm/trycatch/try_catch_succ.wat", List(1, 3, 5)) - } + // test("try-catch-succ") { + // // no exception was thrown + // testFileOutput("./benchmarks/wasm/trycatch/try_catch_succ.wat", List(1, 3, 5)) + // } - test("try-catch-discard") { - // discard the resumption in the catch block - testFileOutput("./benchmarks/wasm/trycatch/try_catch_discard.wat", List(1, 42, 4, 5)) - } + // test("try-catch-discard") { + // // discard the resumption in the catch block + // testFileOutput("./benchmarks/wasm/trycatch/try_catch_discard.wat", List(1, 42, 4, 5)) + // } - test("nested-try-catch") { - testFileOutput("./benchmarks/wasm/trycatch/nested_try_catch.wat", List(1, 2, 3, 4, 5, 6, 7, 8, 9)) - } + // test("nested-try-catch") { + // testFileOutput("./benchmarks/wasm/trycatch/nested_try_catch.wat", List(1, 2, 3, 4, 5, 6, 7, 8, 9)) + // } - test("try-catch-multishot") { - testFileOutput("./benchmarks/wasm/trycatch/multishot.wat", List(1, 2, 3, 4, 3, 5)) - } + // test("try-catch-multishot") { + // testFileOutput("./benchmarks/wasm/trycatch/multishot.wat", List(1, 2, 3, 4, 3, 5)) + // } - test("try-catch-deep-handler") { - testFileOutput("./benchmarks/wasm/trycatch/deep.wat", List(1, 2, 3, 2, 4, 4, 5)) - } + // test("try-catch-deep-handler") { + // testFileOutput("./benchmarks/wasm/trycatch/deep.wat", List(1, 2, 3, 2, 4, 4, 5)) + // } - test("try-catch-block") { - testFileOutput("./benchmarks/wasm/trycatch/try_catch_block.wat", List(1, 2, 3, 4, 5)) - } + // test("try-catch-block") { + // testFileOutput("./benchmarks/wasm/trycatch/try_catch_block.wat", List(1, 2, 3, 4, 5)) + // } // Note: the interaction between try-catch and block is not well-defined yet @@ -160,8 +160,8 @@ class TestFx extends FunSuite { testWastFile("./benchmarks/wasm/wasmfx/resume1.bin.wast") } - // test("test_cont") { - // testFile("./benchmarks/wasm/wasmfx/test_cont-strip.wast") - // } + test("test_cont") { + testFile("./benchmarks/wasm/wasmfx/test_cont-strip.wast") + } } From f2d4fb5f9e0c8528a9e60778c8426edc18a27c9a Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 03:30:06 -0500 Subject: [PATCH 02/15] im blind --- src/main/scala/wasm/MiniWasmFX.scala | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index 00a654ec3..d79f9c950 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 = ??? @@ -238,26 +242,26 @@ case class EvaluatorFX(module: ModuleInstance) { // should be similar to the contiuantion thrown by `throw` // TODO: this implementation is not right - def kr(s: Stack, k1: Cont[Ans], mk: MCont[Ans]): Ans = { + 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` val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) - evalCall(f, List(), s, frame, kontK, mk, trail, h, false) + evalCall(f, List(), s, frame, kontK, mk, trail, handler, false) } - eval(rest, TCContV(kr) :: newStack, frame, kont, mkont, trail, h) + eval(rest, ContV(kr) :: newStack, frame, kont, mkont, trail, h) // TODO: implement the following case Suspend(tag_id) => { // println(s"${RED}Unimplimented Suspending tag $tag_id") // add the continuation on the stack - val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans]) => { + val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans], handler: Handler[Ans]) => { // k1 is the default handler // so it should be konk ++ k1 - eval(rest, s, frame, kont, m, trail, h) + eval(rest, s, frame, kont, m, trail, handler) } - val newStack = TCContV(k) :: stack - mkont(newStack) + val newStack = ContV(k) :: stack + h(newStack) // throw new Exception("Suspend not implemented") // h(stack) } @@ -265,7 +269,7 @@ case class EvaluatorFX(module: ModuleInstance) { // 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) @@ -273,7 +277,7 @@ case class EvaluatorFX(module: ModuleInstance) { if (handler.length == 0) { val k: Cont[Ans] = (s, m) => eval(rest, newStack, frame, kont, m, trail, h) - f.k(inputs, k, mkont) + f.k(inputs, k, mkont, h) } else { // TODO: attempt single tag first if (handler.length > 1) { @@ -286,7 +290,8 @@ case class EvaluatorFX(module: ModuleInstance) { // 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) - f.k(inputs, kont, newHandler) + val k: Cont[Ans] = (s, m) => eval(rest, newStack, frame, kont, m, trail, h) + f.k(inputs, k, mkont, newHandler) // throw new Exception("tags not supported") From bcad72a0bea077bbe83b145469be5af0a5861924 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 03:39:41 -0500 Subject: [PATCH 03/15] minor editing --- src/main/scala/wasm/MiniWasmFX.scala | 2 +- src/test/scala/genwasym/TestFx.scala | 126 +++++++++++++-------------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index d79f9c950..26db102fd 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -80,7 +80,7 @@ case class EvaluatorFX(module: ModuleInstance) { val rest = insts.tail // TODO: uncommenting this will fail tests that uses `testFileOutput` - println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) + // println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) inst match { case Drop => eval(rest, stack.tail, frame, kont, mkont, trail, h) diff --git a/src/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index ba184181e..3c07183d9 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -56,81 +56,81 @@ class TestFx extends FunSuite { } // non-effect tests should still pass: - // test("ack") { testFile("./benchmarks/wasm/ack.wat", Some("real_main"), ExpInt(7)) } - // test("power") { testFile("./benchmarks/wasm/pow.wat", Some("real_main"), ExpInt(1024)) } - // test("start") { testFile("./benchmarks/wasm/start.wat") } - // test("fact") { testFile("./benchmarks/wasm/fact.wat", None, ExpInt(120)) } - // test("loop") { testFile("./benchmarks/wasm/loop.wat", None, ExpInt(10)) } - // test("even-odd") { testFile("./benchmarks/wasm/even_odd.wat", None, ExpInt(1)) } - // test("load") { testFile("./benchmarks/wasm/load.wat", None, ExpInt(1)) } - // test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } - // test("fib") { testFile("./benchmarks/wasm/fib.wat", None, ExpInt(144)) } - // test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, ExpInt(504)) } - - // test("return") { - // intercept[gensym.wasm.miniwasm.Trap] { - // testFile("./benchmarks/wasm/return.wat", Some("$real_main")) - // } - // } - // test("return_call") { - // testFile("./benchmarks/wasm/sum.wat", Some("sum10"), ExpInt(55)) - // } + test("ack") { testFile("./benchmarks/wasm/ack.wat", Some("real_main"), ExpInt(7)) } + test("power") { testFile("./benchmarks/wasm/pow.wat", Some("real_main"), ExpInt(1024)) } + test("start") { testFile("./benchmarks/wasm/start.wat") } + test("fact") { testFile("./benchmarks/wasm/fact.wat", None, ExpInt(120)) } + test("loop") { testFile("./benchmarks/wasm/loop.wat", None, ExpInt(10)) } + test("even-odd") { testFile("./benchmarks/wasm/even_odd.wat", None, ExpInt(1)) } + test("load") { testFile("./benchmarks/wasm/load.wat", None, ExpInt(1)) } + test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } + test("fib") { testFile("./benchmarks/wasm/fib.wat", None, ExpInt(144)) } + test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, ExpInt(504)) } + + test("return") { + intercept[gensym.wasm.miniwasm.Trap] { + testFile("./benchmarks/wasm/return.wat", Some("$real_main")) + } + } + test("return_call") { + testFile("./benchmarks/wasm/sum.wat", Some("sum10"), ExpInt(55)) + } - // test("block input") { - // testFile("./benchmarks/wasm/block.wat", Some("real_main"), ExpInt(9)) - // } - // test("loop block input") { - // testFile("./benchmarks/wasm/block.wat", Some("test_loop_input"), ExpInt(55)) - // } - // test("if block input") { - // testFile("./benchmarks/wasm/block.wat", Some("test_if_input"), ExpInt(25)) - // } - // test("block input - poly br") { - // testFile("./benchmarks/wasm/block.wat", Some("test_poly_br"), ExpInt(0)) - // } - // test("loop block - poly br") { - // testFile("./benchmarks/wasm/loop_poly.wat", None, ExpStack(List(2, 1))) - // } + test("block input") { + testFile("./benchmarks/wasm/block.wat", Some("real_main"), ExpInt(9)) + } + test("loop block input") { + testFile("./benchmarks/wasm/block.wat", Some("test_loop_input"), ExpInt(55)) + } + test("if block input") { + testFile("./benchmarks/wasm/block.wat", Some("test_if_input"), ExpInt(25)) + } + test("block input - poly br") { + testFile("./benchmarks/wasm/block.wat", Some("test_poly_br"), ExpInt(0)) + } + test("loop block - poly br") { + testFile("./benchmarks/wasm/loop_poly.wat", None, ExpStack(List(2, 1))) + } // New effect handler tests: - // test("simple script") { - // TestWastFile("./benchmarks/wasm/wasmfx/cont_bind3.bin.wast") - // } + test("simple script") { + TestWastFile("./benchmarks/wasm/wasmfx/cont_bind3.bin.wast") + } - // test("call_ref") { - // testFile("./benchmarks/wasm/wasmfx/callref-strip.wast") - // } + test("call_ref") { + testFile("./benchmarks/wasm/wasmfx/callref-strip.wast") + } - // test("try-catch") { - // testFileOutput("./benchmarks/wasm/trycatch/try_catch.wat", List(1, 2, 3, 4, 5)) - // } + test("try-catch") { + testFileOutput("./benchmarks/wasm/trycatch/try_catch.wat", List(1, 2, 3, 4, 5)) + } - // test("try-catch-succ") { - // // no exception was thrown - // testFileOutput("./benchmarks/wasm/trycatch/try_catch_succ.wat", List(1, 3, 5)) - // } + test("try-catch-succ") { + // no exception was thrown + testFileOutput("./benchmarks/wasm/trycatch/try_catch_succ.wat", List(1, 3, 5)) + } - // test("try-catch-discard") { - // // discard the resumption in the catch block - // testFileOutput("./benchmarks/wasm/trycatch/try_catch_discard.wat", List(1, 42, 4, 5)) - // } + test("try-catch-discard") { + // discard the resumption in the catch block + testFileOutput("./benchmarks/wasm/trycatch/try_catch_discard.wat", List(1, 42, 4, 5)) + } - // test("nested-try-catch") { - // testFileOutput("./benchmarks/wasm/trycatch/nested_try_catch.wat", List(1, 2, 3, 4, 5, 6, 7, 8, 9)) - // } + test("nested-try-catch") { + testFileOutput("./benchmarks/wasm/trycatch/nested_try_catch.wat", List(1, 2, 3, 4, 5, 6, 7, 8, 9)) + } - // test("try-catch-multishot") { - // testFileOutput("./benchmarks/wasm/trycatch/multishot.wat", List(1, 2, 3, 4, 3, 5)) - // } + test("try-catch-multishot") { + testFileOutput("./benchmarks/wasm/trycatch/multishot.wat", List(1, 2, 3, 4, 3, 5)) + } - // test("try-catch-deep-handler") { - // testFileOutput("./benchmarks/wasm/trycatch/deep.wat", List(1, 2, 3, 2, 4, 4, 5)) - // } + test("try-catch-deep-handler") { + testFileOutput("./benchmarks/wasm/trycatch/deep.wat", List(1, 2, 3, 2, 4, 4, 5)) + } - // test("try-catch-block") { - // testFileOutput("./benchmarks/wasm/trycatch/try_catch_block.wat", List(1, 2, 3, 4, 5)) - // } + test("try-catch-block") { + testFileOutput("./benchmarks/wasm/trycatch/try_catch_block.wat", List(1, 2, 3, 4, 5)) + } // Note: the interaction between try-catch and block is not well-defined yet From 7af3cf2c754e02b79f40aa7c2a48129e2668e9b1 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 03:53:40 -0500 Subject: [PATCH 04/15] minor fix --- src/test/scala/genwasym/TestFx.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index 3c07183d9..ce6200049 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -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") } @@ -148,7 +144,7 @@ class TestFx extends FunSuite { // TODO: test after implemented cont_bind3 // test("simple script") { - // TestWastFile("./benchmarks/wasm/wasmfx/cont_bind3.bin.wast") + // testWastFile("./benchmarks/wasm/wasmfx/cont_bind3.bin.wast") // } // test("cont") { From d57a91af4119682bd7a6a47d4d98d858717b952b Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 04:35:14 -0500 Subject: [PATCH 05/15] correct behavior for stack --- .../wasm/wasmfx/resume_chain1-strip.wast | 31 +++++++++++++++++++ src/main/scala/wasm/MiniWasmFX.scala | 6 ++-- src/test/scala/genwasym/TestFx.scala | 4 +++ 3 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 benchmarks/wasm/wasmfx/resume_chain1-strip.wast 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/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index 26db102fd..ce997fb7a 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -80,7 +80,7 @@ case class EvaluatorFX(module: ModuleInstance) { val rest = insts.tail // TODO: uncommenting this will fail tests that uses `testFileOutput` - // println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) + println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) inst match { case Drop => eval(rest, stack.tail, frame, kont, mkont, trail, h) @@ -276,7 +276,7 @@ case class EvaluatorFX(module: ModuleInstance) { 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) + val k: Cont[Ans] = (s, m) => eval(rest, s, frame, kont, m, trail, h) f.k(inputs, k, mkont, h) } else { // TODO: attempt single tag first @@ -290,7 +290,7 @@ case class EvaluatorFX(module: ModuleInstance) { // 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 k: Cont[Ans] = (s, m) => eval(rest, newStack, frame, kont, m, trail, h) + val k: Cont[Ans] = (s, m) => eval(rest, s, frame, kont, m, trail, h) f.k(inputs, k, mkont, newHandler) // throw new Exception("tags not supported") diff --git a/src/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index ce6200049..797013186 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -160,4 +160,8 @@ class TestFx extends FunSuite { testFile("./benchmarks/wasm/wasmfx/test_cont-strip.wast") } + test("resume_chain1") { + testWastFile("./benchmarks/wasm/wasmfx/resume_chain1-strip.wast") + } + } From a2c6f9bd20ef1bd867034b3fc1312cd93640895a Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 05:37:30 -0500 Subject: [PATCH 06/15] correct behavior for nested resume --- .../wasm/wasmfx/nested_suspend-strip.wat | 39 +++++++++++++++++++ src/main/scala/wasm/MiniWasmFX.scala | 13 +++++-- src/test/scala/genwasym/TestFx.scala | 6 +++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 benchmarks/wasm/wasmfx/nested_suspend-strip.wat 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/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index ce997fb7a..dcbc7d990 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -80,7 +80,7 @@ case class EvaluatorFX(module: ModuleInstance) { val rest = insts.tail // TODO: uncommenting this will fail tests that uses `testFileOutput` - println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) + // println(s"inst: ${inst} \t | ${frame.locals} | ${stack.reverse}" ) inst match { case Drop => eval(rest, stack.tail, frame, kont, mkont, trail, h) @@ -168,6 +168,7 @@ case class EvaluatorFX(module: ModuleInstance) { // 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) @@ -245,8 +246,14 @@ case class EvaluatorFX(module: ModuleInstance) { 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` - val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) - evalCall(f, List(), s, frame, kontK, mk, trail, handler, false) + + // TODO: Do we need to care about kont here?? + // Possible 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` + + // val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) + evalCall(f, List(), s, frame, k1, mk, trail, handler, false) } eval(rest, ContV(kr) :: newStack, frame, kont, mkont, trail, h) diff --git a/src/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index 797013186..953a2d8c0 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -164,4 +164,10 @@ class TestFx extends FunSuite { testWastFile("./benchmarks/wasm/wasmfx/resume_chain1-strip.wast") } + test("nested suspend") { + testFile("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat") + + // testFileOutput("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat", List(0)) + } + } From a5750b78f817753ce3457add064414c0dee96a32 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 05:47:01 -0500 Subject: [PATCH 07/15] edit comments --- src/main/scala/wasm/MiniWasmFX.scala | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index dcbc7d990..02f8fdf96 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -17,6 +17,7 @@ case class EvaluatorFX(module: ModuleInstance) { type MCont[A] = Stack => A type Handler[A] = Stack => A + // TODO: is MCont and Handler together redundant? case class ContV[A](k: (Stack, Cont[A], MCont[A], Handler[A]) => A) extends Value { def tipe(implicit m: ModuleInstance): ValueType = ??? } @@ -240,9 +241,7 @@ 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` - // TODO: this implementation is not right 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` @@ -257,7 +256,7 @@ case class EvaluatorFX(module: ModuleInstance) { } eval(rest, ContV(kr) :: newStack, frame, kont, mkont, trail, h) - // TODO: implement the following + case Suspend(tag_id) => { // println(s"${RED}Unimplimented Suspending tag $tag_id") // add the continuation on the stack @@ -265,6 +264,8 @@ case class EvaluatorFX(module: ModuleInstance) { val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans], handler: Handler[Ans]) => { // k1 is the default handler // so it should be konk ++ k1 + + // TODO: is it okay to forget `k1` and `mkont` here? eval(rest, s, frame, kont, m, trail, handler) } val newStack = ContV(k) :: stack @@ -274,7 +275,6 @@ case class EvaluatorFX(module: ModuleInstance) { } // 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: ContV[Ans]) :: newStack = stack val contTy = module.types(kty_id) @@ -286,13 +286,12 @@ case class EvaluatorFX(module: ModuleInstance) { val k: Cont[Ans] = (s, m) => eval(rest, s, frame, kont, m, trail, h) f.k(inputs, k, mkont, h) } else { - // TODO: attempt single tag first if (handler.length > 1) { throw new Exception("only single tag is supported") } val Handler(tagId, labelId) = handler.head // if suspend happens, the label id holds the handler - // TODO: should handler be passed in an mkont?? + val newHandler: Handler[Ans] = (newStack) => trail(labelId)(newStack, mkont) // f might be handled by the default handler (namely kont), or by the @@ -318,7 +317,7 @@ case class EvaluatorFX(module: ModuleInstance) { // case _ => throw new Exception("Continuation is not a function") // } throw new Exception("ContBind unimplemented") - + case CallRef(ty) => val RefFuncV(f) :: newStack = stack evalCall(f, rest, newStack, frame, kont, mkont, trail, h, false) @@ -343,7 +342,7 @@ case class EvaluatorFX(module: ModuleInstance) { System.err.println(s"Entering function $main") module.funcs(fid) match { case FuncDef(_, FuncBodyDef(_, _, locals, body)) => body - case _ => throw new Exception("Entry function has no concrete body") + case _ => throw new Exception("Entry function has no concrete body") } case _ => List() }) @@ -366,7 +365,7 @@ case class EvaluatorFX(module: ModuleInstance) { 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 _ => throw new Exception("Entry function has no concrete body") } case _ => List() }) From 5c38f2d46334f8301480c8827b571af31a29114b Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Mon, 25 Nov 2024 05:54:56 -0500 Subject: [PATCH 08/15] gen --- benchmarks/wasm/wasmfx/gen-stripped.wast | 42 ++++++++++++++++++++++++ src/test/scala/genwasym/TestFx.scala | 4 +++ 2 files changed, 46 insertions(+) create mode 100644 benchmarks/wasm/wasmfx/gen-stripped.wast 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/src/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index 953a2d8c0..b0d6f4167 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -170,4 +170,8 @@ class TestFx extends FunSuite { // testFileOutput("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat", List(0)) } + test("gen") { + testFile("./benchmarks/wasm/wasmfx/gen-stripped.wast") + } + } From c20456cf9844e2b1be4b52083f5aed802c296903 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Wed, 27 Nov 2024 12:09:38 -0500 Subject: [PATCH 09/15] failing exmaple, k1 in suspend should not be forgotten --- benchmarks/wasm/wasmfx/diff_resume-strip.wat | 53 ++++++++++++++++++++ benchmarks/wasm/wasmfx/diff_resume.wast | 52 +++++++++++++++++++ src/test/scala/genwasym/TestFx.scala | 15 ++++-- 3 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 benchmarks/wasm/wasmfx/diff_resume-strip.wat create mode 100644 benchmarks/wasm/wasmfx/diff_resume.wast 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/src/test/scala/genwasym/TestFx.scala b/src/test/scala/genwasym/TestFx.scala index b0d6f4167..0abdb22fe 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -147,15 +147,16 @@ class TestFx extends FunSuite { // 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") } + // wasmfx sec 2.3 like example test("test_cont") { testFile("./benchmarks/wasm/wasmfx/test_cont-strip.wast") } @@ -164,14 +165,20 @@ class TestFx extends FunSuite { testWastFile("./benchmarks/wasm/wasmfx/resume_chain1-strip.wast") } + // printing 0 not 1 test("nested suspend") { testFile("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat") // 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)) + } + } From 2c8121c8a428f73d4be7984bc19b0d5404ca8742 Mon Sep 17 00:00:00 2001 From: ahuoguo <52595524+ahuoguo@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:10:35 -0500 Subject: [PATCH 10/15] mk is back! (#71) --- src/main/scala/wasm/MiniWasmFX.scala | 33 +++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index 02f8fdf96..06432077c 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -246,13 +246,14 @@ case class EvaluatorFX(module: ModuleInstance) { // k1 is rest for `resume` // mk holds the handler for `suspend` - // TODO: Do we need to care about kont here?? - // Possible answer: we don't, because continuation is only activated by `resume` + // 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` - // val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) - evalCall(f, List(), s, frame, k1, mk, trail, handler, false) + // 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, ContV(kr) :: newStack, frame, kont, mkont, trail, h) @@ -262,11 +263,16 @@ case class EvaluatorFX(module: ModuleInstance) { // add the continuation on the stack val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans], handler: Handler[Ans]) => { + // the 3 lines below doens't work // k1 is the default handler // so it should be konk ++ k1 + // val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) - // TODO: is it okay to forget `k1` and `mkont` here? - eval(rest, s, frame, kont, m, trail, handler) + // 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) @@ -283,23 +289,24 @@ case class EvaluatorFX(module: ModuleInstance) { val (inputs, restStack) = newStack.splitAt(inps.size) if (handler.length == 0) { - val k: Cont[Ans] = (s, m) => eval(rest, s, frame, kont, m, trail, h) - f.k(inputs, k, mkont, h) + // 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 { if (handler.length > 1) { throw new Exception("only single tag is supported") } - val Handler(tagId, labelId) = handler.head // 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 k: Cont[Ans] = (s, m) => eval(rest, s, frame, kont, m, trail, h) - f.k(inputs, k, mkont, newHandler) - - // throw new Exception("tags not supported") + 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) } From f347473ce08a1ecc544aa3d000e614cecd108263 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Wed, 27 Nov 2024 13:55:07 -0500 Subject: [PATCH 11/15] implement cont bind --- benchmarks/wasm/wasmfx/cont_bind4.bin.wast | 12 ++++++ benchmarks/wasm/wasmfx/cont_bind4.wast | 21 ++++++++++ benchmarks/wasm/wasmfx/cont_bind5-strip.wast | 31 +++++++++++++++ benchmarks/wasm/wasmfx/cont_bind5.bin.wast | 18 +++++++++ benchmarks/wasm/wasmfx/cont_bind5.wast | 25 ++++++++++++ src/main/scala/wasm/MiniWasmFX.scala | 41 +++++++++++--------- src/main/scala/wasm/MiniWasmScript.scala | 26 +++++++------ src/main/scala/wasm/Parser.scala | 1 - src/test/scala/genwasym/TestFx.scala | 23 +++++------ 9 files changed, 157 insertions(+), 41 deletions(-) create mode 100644 benchmarks/wasm/wasmfx/cont_bind4.bin.wast create mode 100644 benchmarks/wasm/wasmfx/cont_bind4.wast create mode 100644 benchmarks/wasm/wasmfx/cont_bind5-strip.wast create mode 100644 benchmarks/wasm/wasmfx/cont_bind5.bin.wast create mode 100644 benchmarks/wasm/wasmfx/cont_bind5.wast 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/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index 06432077c..af7e47c6e 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -263,10 +263,8 @@ case class EvaluatorFX(module: ModuleInstance) { // add the continuation on the stack val k = (s: Stack, k1: Cont[Ans], m: MCont[Ans], handler: Handler[Ans]) => { - // the 3 lines below doens't work - // k1 is the default handler - // so it should be konk ++ k1 - // val kontK: Cont[Ans] = (s1, m1) => kont(s1, s2 => k1(s2, m1)) + // 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 @@ -290,7 +288,7 @@ case class EvaluatorFX(module: ModuleInstance) { if (handler.length == 0) { // the metacontinuation contains the default handler - val mk: MCont[Ans] = (s) => eval(rest, s, frame, kont, mkont, trail, h) + 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 { @@ -304,7 +302,7 @@ case class EvaluatorFX(module: ModuleInstance) { // 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 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) @@ -312,18 +310,25 @@ case class EvaluatorFX(module: ModuleInstance) { } - 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 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 0abdb22fe..9e6d72e7d 100644 --- a/src/test/scala/genwasym/TestFx.scala +++ b/src/test/scala/genwasym/TestFx.scala @@ -30,11 +30,11 @@ class TestFx extends FunSuite { 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) @@ -142,11 +142,6 @@ 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") @@ -167,9 +162,7 @@ class TestFx extends FunSuite { // printing 0 not 1 test("nested suspend") { - testFile("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat") - - // testFileOutput("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat", List(0)) + testFileOutput("./benchmarks/wasm/wasmfx/nested_suspend-strip.wat", List(0)) } // going to print 100 to 1 and then print 42 @@ -181,4 +174,12 @@ class TestFx extends FunSuite { 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") + } + } From 1079b284baf2a4847d7a33038cc22678ab5dbc86 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Wed, 27 Nov 2024 17:59:09 -0500 Subject: [PATCH 12/15] revert change on refnull --- benchmarks/wasm/wasmfx/resume2.wat | 9 +++++++++ src/main/scala/wasm/AST.scala | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 benchmarks/wasm/wasmfx/resume2.wat 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/src/main/scala/wasm/AST.scala b/src/main/scala/wasm/AST.scala index acc0842ae..ec1bd19d7 100644 --- a/src/main/scala/wasm/AST.scala +++ b/src/main/scala/wasm/AST.scala @@ -318,8 +318,8 @@ trait Callable // https://webassembly.github.io/function-references/core/exec/runtime.html abstract class Ref extends Value with Callable -case class RefNullV() extends Ref { - def tipe(implicit m: ModuleInstance): ValueType = ??? +case class RefNullV(t: HeapType) extends Ref { + def tipe(implicit m: ModuleInstance): ValueType = RefType(t) } case class RefFuncV(funcAddr: Int) extends Ref { def tipe(implicit m: ModuleInstance): ValueType = From 3e5a8445f0a663b964a29ffffbf96ffd50022313 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Wed, 27 Nov 2024 17:59:24 -0500 Subject: [PATCH 13/15] one more change --- src/main/scala/wasm/MiniWasm.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/wasm/MiniWasm.scala b/src/main/scala/wasm/MiniWasm.scala index 00c9f0a62..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) => RefNullV() + case RefType(kind) => RefNullV(kind) } def getFuncType(ty: BlockType): FuncType = From 5941b5700f7e29af00710c9cd2f339365740b744 Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Wed, 27 Nov 2024 18:00:56 -0500 Subject: [PATCH 14/15] rm useless comment --- src/main/scala/wasm/MiniWasmFX.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index af7e47c6e..21bb80e64 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -17,7 +17,6 @@ case class EvaluatorFX(module: ModuleInstance) { type MCont[A] = Stack => A type Handler[A] = Stack => A - // TODO: is MCont and Handler together redundant? case class ContV[A](k: (Stack, Cont[A], MCont[A], Handler[A]) => A) extends Value { def tipe(implicit m: ModuleInstance): ValueType = ??? } From 467709eeb958c44ff71e09877d0be9f30b80df0c Mon Sep 17 00:00:00 2001 From: ahuoguo Date: Wed, 27 Nov 2024 18:01:34 -0500 Subject: [PATCH 15/15] two more extra line --- src/main/scala/wasm/MiniWasmFX.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/scala/wasm/MiniWasmFX.scala b/src/main/scala/wasm/MiniWasmFX.scala index 21bb80e64..bdc71d739 100644 --- a/src/main/scala/wasm/MiniWasmFX.scala +++ b/src/main/scala/wasm/MiniWasmFX.scala @@ -273,8 +273,6 @@ case class EvaluatorFX(module: ModuleInstance) { } val newStack = ContV(k) :: stack h(newStack) - // throw new Exception("Suspend not implemented") - // h(stack) } // TODO: resume should create a list of handlers to capture suspend