From b764a07294a6c6cea9ae2de6d05a86c9c944a88d Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Wed, 9 Oct 2024 22:22:16 +0800 Subject: [PATCH 1/7] a failed test case --- benchmarks/wasm/tribonacci.rs | 13 +++++ benchmarks/wasm/tribonacci.wat | 68 ++++++++++++++++++++++++++ src/test/scala/genwasym/TestEval.scala | 1 + 3 files changed, 82 insertions(+) create mode 100644 benchmarks/wasm/tribonacci.rs create mode 100644 benchmarks/wasm/tribonacci.wat diff --git a/benchmarks/wasm/tribonacci.rs b/benchmarks/wasm/tribonacci.rs new file mode 100644 index 000000000..ae576056c --- /dev/null +++ b/benchmarks/wasm/tribonacci.rs @@ -0,0 +1,13 @@ +#[no_mangle] +#[inline(never)] +fn tribonacci(n: i32) -> i32 { + if n == 0 { 0 } + else if n == 1 { 1 } + else if n == 2 { 1 } + else { tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3) } +} + +#[no_mangle] +fn real_main() -> i32 { + tribonacci(12) +} diff --git a/benchmarks/wasm/tribonacci.wat b/benchmarks/wasm/tribonacci.wat new file mode 100644 index 000000000..f2c0e678f --- /dev/null +++ b/benchmarks/wasm/tribonacci.wat @@ -0,0 +1,68 @@ +(module + (type (;0;) (func (param i32) (result i32))) + (type (;1;) (func (result i32))) + (func (;0;) (type 0) (param i32) (result i32) + (local i32 i32) + local.get 0 + i32.const 2 + i32.shl + i32.const 1048576 + i32.add + local.set 1 + i32.const 0 + local.set 2 + local.get 0 + local.set 0 + loop (result i32) ;; label = @1 + local.get 2 + local.set 2 + local.get 1 + local.set 1 + block ;; label = @2 + local.get 0 + local.tee 0 + i32.const 2 + i32.gt_u + br_if 0 (;@2;) + local.get 1 + i32.load + local.get 2 + i32.add + return + end + local.get 1 + i32.const -12 + i32.add + local.set 1 + local.get 0 + i32.const -1 + i32.add + call 0 + local.get 0 + i32.const -2 + i32.add + call 0 + i32.add + local.get 2 + i32.add + local.set 2 + local.get 0 + i32.const -3 + i32.add + local.set 0 + br 0 (;@1;) + end) + (func (;1;) (type 1) (result i32) + i32.const 12 + call 0) + (start 1) + (table (;0;) 1 1 funcref) + (memory (;0;) 17) + (global (;0;) (mut i32) (i32.const 1048576)) + (global (;1;) i32 (i32.const 1048588)) + (global (;2;) i32 (i32.const 1048592)) + (export "memory" (memory 0)) + (export "tribonacci" (func 0)) + (export "real_main" (func 1)) + (export "__data_end" (global 1)) + (export "__heap_base" (global 2))) diff --git a/src/test/scala/genwasym/TestEval.scala b/src/test/scala/genwasym/TestEval.scala index f2d66b153..2518ce657 100644 --- a/src/test/scala/genwasym/TestEval.scala +++ b/src/test/scala/genwasym/TestEval.scala @@ -44,6 +44,7 @@ class TestEval extends FunSuite { test("load") { testFile("./benchmarks/wasm/load.wat", None, Some(1)) } test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } test("fib") { testFile("./benchmarks/wasm/fib.wat", None, Some(144)) } + test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, Some(504)) } // TODO: add wasm spec tests? How to utilize wast files? } From a589f8bd6b9509811024a4eb75f116c43e179610 Mon Sep 17 00:00:00 2001 From: Guannan Wei Date: Wed, 9 Oct 2024 23:34:13 +0200 Subject: [PATCH 2/7] update tribonacci.wat --- benchmarks/wasm/tribonacci.wat | 85 ++++++++++++---------------------- 1 file changed, 30 insertions(+), 55 deletions(-) diff --git a/benchmarks/wasm/tribonacci.wat b/benchmarks/wasm/tribonacci.wat index f2c0e678f..01a50848c 100644 --- a/benchmarks/wasm/tribonacci.wat +++ b/benchmarks/wasm/tribonacci.wat @@ -2,67 +2,42 @@ (type (;0;) (func (param i32) (result i32))) (type (;1;) (func (result i32))) (func (;0;) (type 0) (param i32) (result i32) - (local i32 i32) local.get 0 - i32.const 2 - i32.shl - i32.const 1048576 - i32.add - local.set 1 - i32.const 0 - local.set 2 - local.get 0 - local.set 0 - loop (result i32) ;; label = @1 - local.get 2 - local.set 2 - local.get 1 - local.set 1 - block ;; label = @2 + if (result i32) ;; label = @1 + local.get 0 + i32.const 1 + i32.eq + if (result i32) ;; label = @2 + i32.const 1 + else local.get 0 - local.tee 0 i32.const 2 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.load - local.get 2 - i32.add - return + i32.eq + if (result i32) ;; label = @3 + i32.const 1 + else + local.get 0 + i32.const 1 + i32.sub + call 0 + local.get 0 + i32.const 2 + i32.sub + call 0 + i32.add + local.get 0 + i32.const 3 + i32.sub + call 0 + i32.add + end end - local.get 1 - i32.const -12 - i32.add - local.set 1 - local.get 0 - i32.const -1 - i32.add - call 0 - local.get 0 - i32.const -2 - i32.add - call 0 - i32.add - local.get 2 - i32.add - local.set 2 - local.get 0 - i32.const -3 - i32.add - local.set 0 - br 0 (;@1;) + else + i32.const 0 end) (func (;1;) (type 1) (result i32) i32.const 12 call 0) (start 1) - (table (;0;) 1 1 funcref) - (memory (;0;) 17) - (global (;0;) (mut i32) (i32.const 1048576)) - (global (;1;) i32 (i32.const 1048588)) - (global (;2;) i32 (i32.const 1048592)) - (export "memory" (memory 0)) - (export "tribonacci" (func 0)) - (export "real_main" (func 1)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2))) + (memory (;0;) 0) + (export "memory" (memory 0))) From e2315b64aba2186d652ac21e0640e2dd2b278ff4 Mon Sep 17 00:00:00 2001 From: Guannan Wei Date: Thu, 10 Oct 2024 14:16:45 +0200 Subject: [PATCH 3/7] fix and test return behavior --- benchmarks/wasm/return.wat | 12 ++++++------ src/main/scala/wasm/MiniWasm.scala | 6 ++++-- src/test/scala/genwasym/TestEval.scala | 7 ++++++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/benchmarks/wasm/return.wat b/benchmarks/wasm/return.wat index f96091738..c1f4e46a5 100644 --- a/benchmarks/wasm/return.wat +++ b/benchmarks/wasm/return.wat @@ -1,13 +1,13 @@ (module - (type (;0;) (func)) - (func (;0;) (type 0) - block ;; label = @1 + (func (result i32) + block + i32.const 42 return end - unreachable ) - (func (;1;) (type 0) + (func (result i32) call 0 + unreachable ) (start 1) -) \ No newline at end of file +) diff --git a/src/main/scala/wasm/MiniWasm.scala b/src/main/scala/wasm/MiniWasm.scala index b2789e2ed..90e311cb8 100644 --- a/src/main/scala/wasm/MiniWasm.scala +++ b/src/main/scala/wasm/MiniWasm.scala @@ -7,6 +7,8 @@ import gensym.wasm.memory._ import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.HashMap +case class Trap() extends Exception + case class ModuleInstance( types: List[FuncType], funcs: HashMap[Int, WIR], @@ -259,7 +261,7 @@ object Evaluator { eval(rest, I32V(value) :: newStack, frame, kont, trail, ret) case Nop => eval(rest, stack, frame, kont, trail, ret) - case Unreachable => throw new RuntimeException("Unreachable") + case Unreachable => throw Trap() case Block(ty, inner) => val k: Cont[Ans] = (retStack) => eval(rest, retStack.take(ty.toList.size) ++ stack, frame, kont, trail, ret) @@ -302,7 +304,7 @@ object Evaluator { eval(rest, retStack.take(ty.out.size) ++ newStack, frame, kont, trail, ret) // We push newK on the trail since function creates a new block to escape // (more or less like `return`) - eval(body, List(), newFrame, newK, newK :: trail, ret+1) + eval(body, List(), newFrame, newK, newK :: trail, 0) case Call(f) if frame.module.funcs(f).isInstanceOf[Import] => frame.module.funcs(f) match { case Import("console", "log", _) => diff --git a/src/test/scala/genwasym/TestEval.scala b/src/test/scala/genwasym/TestEval.scala index 2518ce657..186c7ded6 100644 --- a/src/test/scala/genwasym/TestEval.scala +++ b/src/test/scala/genwasym/TestEval.scala @@ -40,11 +40,16 @@ class TestEval extends FunSuite { test("fact") { testFile("./benchmarks/wasm/fact.wat", None, Some(120)) } test("loop") { testFile("./benchmarks/wasm/loop.wat", None, Some(10)) } test("even-odd") { testFile("./benchmarks/wasm/even_odd.wat", None, Some(1)) } - test("return") { testFile("./benchmarks/wasm/return.wat", None, None) } test("load") { testFile("./benchmarks/wasm/load.wat", None, Some(1)) } test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } test("fib") { testFile("./benchmarks/wasm/fib.wat", None, Some(144)) } test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, Some(504)) } + test("return") { + intercept[gensym.wasm.miniwasm.Trap] { + testFile("./benchmarks/wasm/return.wat", None, None) + } + } + // TODO: add wasm spec tests? How to utilize wast files? } From 92bd61d06f74f2cd35d165a5e779c751a56213fd Mon Sep 17 00:00:00 2001 From: Guannan Wei Date: Thu, 10 Oct 2024 14:21:13 +0200 Subject: [PATCH 4/7] validation of return.wat --- benchmarks/wasm/return.wat | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/benchmarks/wasm/return.wat b/benchmarks/wasm/return.wat index c1f4e46a5..5bf59d173 100644 --- a/benchmarks/wasm/return.wat +++ b/benchmarks/wasm/return.wat @@ -1,11 +1,14 @@ (module - (func (result i32) + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (func (type 1) block i32.const 42 return end + i32.const 100 ) - (func (result i32) + (func (type 0) call 0 unreachable ) From a7b174972b4bcfa9bb12e3efa1932317f6c459e2 Mon Sep 17 00:00:00 2001 From: butterunderflow <112108686+butterunderflow@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:15:41 +0800 Subject: [PATCH 5/7] `trail` is local jump table (#56) --- 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 90e311cb8..8efe0e12a 100644 --- a/src/main/scala/wasm/MiniWasm.scala +++ b/src/main/scala/wasm/MiniWasm.scala @@ -304,7 +304,7 @@ object Evaluator { eval(rest, retStack.take(ty.out.size) ++ newStack, frame, kont, trail, ret) // We push newK on the trail since function creates a new block to escape // (more or less like `return`) - eval(body, List(), newFrame, newK, newK :: trail, 0) + eval(body, List(), newFrame, newK, List(newK), 0) case Call(f) if frame.module.funcs(f).isInstanceOf[Import] => frame.module.funcs(f) match { case Import("console", "log", _) => From e0c967df7dc96ff87e4d7698712d99b4cc69b517 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Thu, 10 Oct 2024 22:27:19 +0800 Subject: [PATCH 6/7] use rust compiled tribonacci.wat(a data section involved) --- benchmarks/wasm/tribonacci.wat | 95 ++++++++++++++++---------- src/test/scala/genwasym/TestEval.scala | 2 +- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/benchmarks/wasm/tribonacci.wat b/benchmarks/wasm/tribonacci.wat index 01a50848c..88e70e23c 100644 --- a/benchmarks/wasm/tribonacci.wat +++ b/benchmarks/wasm/tribonacci.wat @@ -1,43 +1,68 @@ -(module +(module $tribonacci.wat.temp (type (;0;) (func (param i32) (result i32))) (type (;1;) (func (result i32))) - (func (;0;) (type 0) (param i32) (result i32) + (func $tribonacci (type 0) (param i32) (result i32) + (local i32 i32) local.get 0 - if (result i32) ;; label = @1 - local.get 0 - i32.const 1 - i32.eq - if (result i32) ;; label = @2 - i32.const 1 - else + i32.const 2 + i32.shl + i32.const 1048576 + i32.add + local.set 1 + i32.const 0 + local.set 2 + local.get 0 + local.set 0 + loop (result i32) ;; label = @1 + local.get 2 + local.set 2 + local.get 1 + local.set 1 + block ;; label = @2 local.get 0 + local.tee 0 i32.const 2 - i32.eq - if (result i32) ;; label = @3 - i32.const 1 - else - local.get 0 - i32.const 1 - i32.sub - call 0 - local.get 0 - i32.const 2 - i32.sub - call 0 - i32.add - local.get 0 - i32.const 3 - i32.sub - call 0 - i32.add - end + i32.gt_u + br_if 0 (;@2;) + local.get 1 + i32.load + local.get 2 + i32.add + return end - else - i32.const 0 + local.get 1 + i32.const -12 + i32.add + local.set 1 + local.get 0 + i32.const -1 + i32.add + call $tribonacci + local.get 0 + i32.const -2 + i32.add + call $tribonacci + i32.add + local.get 2 + i32.add + local.set 2 + local.get 0 + i32.const -3 + i32.add + local.set 0 + br 0 (;@1;) end) - (func (;1;) (type 1) (result i32) + (func $real_main (type 1) (result i32) i32.const 12 - call 0) - (start 1) - (memory (;0;) 0) - (export "memory" (memory 0))) + call $tribonacci) + (table (;0;) 1 1 funcref) + (memory (;0;) 17) + (global $__stack_pointer (mut i32) (i32.const 1048576)) + (global (;1;) i32 (i32.const 1048588)) + (global (;2;) i32 (i32.const 1048592)) + (export "memory" (memory 0)) + (export "tribonacci" (func $tribonacci)) + (export "real_main" (func $real_main)) + (export "__data_end" (global 1)) + (export "__heap_base" (global 2)) + (data $.rodata (i32.const 1048576) "\00\00\00\00\01\00\00\00\01\00\00\00")) diff --git a/src/test/scala/genwasym/TestEval.scala b/src/test/scala/genwasym/TestEval.scala index 186c7ded6..86c80a118 100644 --- a/src/test/scala/genwasym/TestEval.scala +++ b/src/test/scala/genwasym/TestEval.scala @@ -43,7 +43,7 @@ class TestEval extends FunSuite { test("load") { testFile("./benchmarks/wasm/load.wat", None, Some(1)) } test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } test("fib") { testFile("./benchmarks/wasm/fib.wat", None, Some(144)) } - test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, Some(504)) } + test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", Some("$real_main"), Some(504)) } test("return") { intercept[gensym.wasm.miniwasm.Trap] { From 225bb765ee500669c734a6a0b3750abb8623f5eb Mon Sep 17 00:00:00 2001 From: Guannan Wei Date: Fri, 11 Oct 2024 15:47:12 +0200 Subject: [PATCH 7/7] keep both versions of tribonacci --- benchmarks/wasm/tribonacci.wat | 95 +++++++------------ .../wasm/{tribonacci.rs => tribonacci_ret.rs} | 0 benchmarks/wasm/tribonacci_ret.wat | 68 +++++++++++++ src/test/scala/genwasym/TestEval.scala | 7 +- 4 files changed, 107 insertions(+), 63 deletions(-) rename benchmarks/wasm/{tribonacci.rs => tribonacci_ret.rs} (100%) create mode 100644 benchmarks/wasm/tribonacci_ret.wat diff --git a/benchmarks/wasm/tribonacci.wat b/benchmarks/wasm/tribonacci.wat index 88e70e23c..01a50848c 100644 --- a/benchmarks/wasm/tribonacci.wat +++ b/benchmarks/wasm/tribonacci.wat @@ -1,68 +1,43 @@ -(module $tribonacci.wat.temp +(module (type (;0;) (func (param i32) (result i32))) (type (;1;) (func (result i32))) - (func $tribonacci (type 0) (param i32) (result i32) - (local i32 i32) + (func (;0;) (type 0) (param i32) (result i32) local.get 0 - i32.const 2 - i32.shl - i32.const 1048576 - i32.add - local.set 1 - i32.const 0 - local.set 2 - local.get 0 - local.set 0 - loop (result i32) ;; label = @1 - local.get 2 - local.set 2 - local.get 1 - local.set 1 - block ;; label = @2 + if (result i32) ;; label = @1 + local.get 0 + i32.const 1 + i32.eq + if (result i32) ;; label = @2 + i32.const 1 + else local.get 0 - local.tee 0 i32.const 2 - i32.gt_u - br_if 0 (;@2;) - local.get 1 - i32.load - local.get 2 - i32.add - return + i32.eq + if (result i32) ;; label = @3 + i32.const 1 + else + local.get 0 + i32.const 1 + i32.sub + call 0 + local.get 0 + i32.const 2 + i32.sub + call 0 + i32.add + local.get 0 + i32.const 3 + i32.sub + call 0 + i32.add + end end - local.get 1 - i32.const -12 - i32.add - local.set 1 - local.get 0 - i32.const -1 - i32.add - call $tribonacci - local.get 0 - i32.const -2 - i32.add - call $tribonacci - i32.add - local.get 2 - i32.add - local.set 2 - local.get 0 - i32.const -3 - i32.add - local.set 0 - br 0 (;@1;) + else + i32.const 0 end) - (func $real_main (type 1) (result i32) + (func (;1;) (type 1) (result i32) i32.const 12 - call $tribonacci) - (table (;0;) 1 1 funcref) - (memory (;0;) 17) - (global $__stack_pointer (mut i32) (i32.const 1048576)) - (global (;1;) i32 (i32.const 1048588)) - (global (;2;) i32 (i32.const 1048592)) - (export "memory" (memory 0)) - (export "tribonacci" (func $tribonacci)) - (export "real_main" (func $real_main)) - (export "__data_end" (global 1)) - (export "__heap_base" (global 2)) - (data $.rodata (i32.const 1048576) "\00\00\00\00\01\00\00\00\01\00\00\00")) + call 0) + (start 1) + (memory (;0;) 0) + (export "memory" (memory 0))) diff --git a/benchmarks/wasm/tribonacci.rs b/benchmarks/wasm/tribonacci_ret.rs similarity index 100% rename from benchmarks/wasm/tribonacci.rs rename to benchmarks/wasm/tribonacci_ret.rs diff --git a/benchmarks/wasm/tribonacci_ret.wat b/benchmarks/wasm/tribonacci_ret.wat new file mode 100644 index 000000000..88e70e23c --- /dev/null +++ b/benchmarks/wasm/tribonacci_ret.wat @@ -0,0 +1,68 @@ +(module $tribonacci.wat.temp + (type (;0;) (func (param i32) (result i32))) + (type (;1;) (func (result i32))) + (func $tribonacci (type 0) (param i32) (result i32) + (local i32 i32) + local.get 0 + i32.const 2 + i32.shl + i32.const 1048576 + i32.add + local.set 1 + i32.const 0 + local.set 2 + local.get 0 + local.set 0 + loop (result i32) ;; label = @1 + local.get 2 + local.set 2 + local.get 1 + local.set 1 + block ;; label = @2 + local.get 0 + local.tee 0 + i32.const 2 + i32.gt_u + br_if 0 (;@2;) + local.get 1 + i32.load + local.get 2 + i32.add + return + end + local.get 1 + i32.const -12 + i32.add + local.set 1 + local.get 0 + i32.const -1 + i32.add + call $tribonacci + local.get 0 + i32.const -2 + i32.add + call $tribonacci + i32.add + local.get 2 + i32.add + local.set 2 + local.get 0 + i32.const -3 + i32.add + local.set 0 + br 0 (;@1;) + end) + (func $real_main (type 1) (result i32) + i32.const 12 + call $tribonacci) + (table (;0;) 1 1 funcref) + (memory (;0;) 17) + (global $__stack_pointer (mut i32) (i32.const 1048576)) + (global (;1;) i32 (i32.const 1048588)) + (global (;2;) i32 (i32.const 1048592)) + (export "memory" (memory 0)) + (export "tribonacci" (func $tribonacci)) + (export "real_main" (func $real_main)) + (export "__data_end" (global 1)) + (export "__heap_base" (global 2)) + (data $.rodata (i32.const 1048576) "\00\00\00\00\01\00\00\00\01\00\00\00")) diff --git a/src/test/scala/genwasym/TestEval.scala b/src/test/scala/genwasym/TestEval.scala index 86c80a118..1c808fad1 100644 --- a/src/test/scala/genwasym/TestEval.scala +++ b/src/test/scala/genwasym/TestEval.scala @@ -14,8 +14,6 @@ import collection.mutable.ArrayBuffer import org.scalatest.FunSuite class TestEval extends FunSuite { - - // Mostly testing the files generated form `benchmarks/wasm/test.rs` def testFile(filename: String, main: Option[String] = None, expected: Option[Int] = None) = { @@ -43,7 +41,7 @@ class TestEval extends FunSuite { test("load") { testFile("./benchmarks/wasm/load.wat", None, Some(1)) } test("btree") { testFile("./benchmarks/wasm/btree/2o1u-unlabeled.wat") } test("fib") { testFile("./benchmarks/wasm/fib.wat", None, Some(144)) } - test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", Some("$real_main"), Some(504)) } + test("tribonacci") { testFile("./benchmarks/wasm/tribonacci.wat", None, Some(504)) } test("return") { intercept[gensym.wasm.miniwasm.Trap] { @@ -51,5 +49,8 @@ class TestEval extends FunSuite { } } + // FIXME: + //test("tribonacci-ret") { testFile("./benchmarks/wasm/tribonacci_ret.wat", None, Some(504)) } + // TODO: add wasm spec tests? How to utilize wast files? }