diff --git a/benchmarks/wasm/count.wat b/benchmarks/wasm/count.wat new file mode 100644 index 000000000..456ad31c6 --- /dev/null +++ b/benchmarks/wasm/count.wat @@ -0,0 +1,23 @@ +(module + (type (;0;) (func (param i32) (result i32))) + (type (;1;) (func)) + (func (;0;) (type 0) (param i32) (result i32) + local.get 0 + i32.eqz + if (result i32) ;; label = @1 + local.get 0 + else + local.get 0 + i32.const 1 + i32.sub + return_call 0 + end + ) + (func (;1;) (type 1) + i32.const 1000000 ;; it will not terminate when it's 1mil + ;; TODO: this doesn't seem like an error in our semantics + ;; but something about sbt. But why? + call 0 + ) + (start 1) +) \ No newline at end of file diff --git a/benchmarks/wasm/spectest/return_call.bin.wast b/benchmarks/wasm/spectest/return_call.bin.wast new file mode 100644 index 000000000..a7ed2aa96 --- /dev/null +++ b/benchmarks/wasm/spectest/return_call.bin.wast @@ -0,0 +1,193 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\c8\80\80\80\00\0e\60" + "\00\01\7f\60\00\01\7e\60\00\01\7d\60\00\01\7c\60" + "\01\7f\01\7f\60\01\7e\01\7e\60\01\7d\01\7d\60\01" + "\7c\01\7c\60\02\7d\7f\01\7f\60\02\7f\7e\01\7e\60" + "\02\7c\7d\01\7d\60\02\7e\7c\01\7c\60\02\7e\7e\01" + "\7e\60\01\7e\01\7f\03\9d\80\80\80\00\1c\00\01\02" + "\03\04\05\06\07\08\09\0a\0b\00\01\02\03\00\01\02" + "\03\00\01\02\03\0c\05\0d\0d\07\d8\81\80\80\00\10" + "\08\74\79\70\65\2d\69\33\32\00\0c\08\74\79\70\65" + "\2d\69\36\34\00\0d\08\74\79\70\65\2d\66\33\32\00" + "\0e\08\74\79\70\65\2d\66\36\34\00\0f\0e\74\79\70" + "\65\2d\66\69\72\73\74\2d\69\33\32\00\10\0e\74\79" + "\70\65\2d\66\69\72\73\74\2d\69\36\34\00\11\0e\74" + "\79\70\65\2d\66\69\72\73\74\2d\66\33\32\00\12\0e" + "\74\79\70\65\2d\66\69\72\73\74\2d\66\36\34\00\13" + "\0f\74\79\70\65\2d\73\65\63\6f\6e\64\2d\69\33\32" + "\00\14\0f\74\79\70\65\2d\73\65\63\6f\6e\64\2d\69" + "\36\34\00\15\0f\74\79\70\65\2d\73\65\63\6f\6e\64" + "\2d\66\33\32\00\16\0f\74\79\70\65\2d\73\65\63\6f" + "\6e\64\2d\66\36\34\00\17\07\66\61\63\2d\61\63\63" + "\00\18\05\63\6f\75\6e\74\00\19\04\65\76\65\6e\00" + "\1a\03\6f\64\64\00\1b\0a\80\83\80\80\00\1c\85\80" + "\80\80\00\00\41\b2\02\0b\85\80\80\80\00\00\42\e4" + "\02\0b\87\80\80\80\00\00\43\00\20\73\45\0b\8b\80" + "\80\80\00\00\44\00\00\00\00\00\c8\ae\40\0b\84\80" + "\80\80\00\00\20\00\0b\84\80\80\80\00\00\20\00\0b" + "\84\80\80\80\00\00\20\00\0b\84\80\80\80\00\00\20" + "\00\0b\84\80\80\80\00\00\20\01\0b\84\80\80\80\00" + "\00\20\01\0b\84\80\80\80\00\00\20\01\0b\84\80\80" + "\80\00\00\20\01\0b\84\80\80\80\00\00\12\00\0b\84" + "\80\80\80\00\00\12\01\0b\84\80\80\80\00\00\12\02" + "\0b\84\80\80\80\00\00\12\03\0b\86\80\80\80\00\00" + "\41\20\12\04\0b\87\80\80\80\00\00\42\c0\00\12\05" + "\0b\89\80\80\80\00\00\43\c3\f5\a8\3f\12\06\0b\8d" + "\80\80\80\00\00\44\3d\0a\d7\a3\70\3d\fa\3f\12\07" + "\0b\8b\80\80\80\00\00\43\66\66\00\42\41\20\12\08" + "\0b\89\80\80\80\00\00\41\20\42\c0\00\12\09\0b\92" + "\80\80\80\00\00\44\00\00\00\00\00\00\50\40\43\00" + "\00\00\42\12\0a\0b\90\80\80\80\00\00\42\c0\00\44" + "\66\66\66\66\66\06\50\40\12\0b\0b\97\80\80\80\00" + "\00\20\00\50\04\7e\20\01\05\20\00\42\01\7d\20\00" + "\20\01\7e\12\18\0b\0b\92\80\80\80\00\00\20\00\50" + "\04\7e\20\00\05\20\00\42\01\7d\12\19\0b\0b\92\80" + "\80\80\00\00\20\00\50\04\7f\41\2c\05\20\00\42\01" + "\7d\12\1b\0b\0b\93\80\80\80\00\00\20\00\50\04\7f" + "\41\e3\00\05\20\00\42\01\7d\12\1a\0b\0b" +) +(module instance) +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +;; (assert_return (invoke "type-f32") (f32.const 0x1.e64p+11)) +;; (assert_return (invoke "type-f64") (f64.const 0x1.ec8p+11)) +(assert_return (invoke "type-first-i32") (i32.const 0x20)) +(assert_return (invoke "type-first-i64") (i64.const 0x40)) +;; (assert_return (invoke "type-first-f32") (f32.const 0x1.51eb_86p+0)) +;; (assert_return (invoke "type-first-f64") (f64.const 0x1.a3d7_0a3d_70a3_dp+0)) +(assert_return (invoke "type-second-i32") (i32.const 0x20)) +(assert_return (invoke "type-second-i64") (i64.const 0x40)) +(assert_return (invoke "type-second-f32") (f32.const 0x1p+5)) +;; (assert_return (invoke "type-second-f64") (f64.const 0x1.0066_6666_6666_6p+6)) +(assert_return + (invoke "fac-acc" (i64.const 0x0) (i64.const 0x1)) + (i64.const 0x1) +) +(assert_return + (invoke "fac-acc" (i64.const 0x1) (i64.const 0x1)) + (i64.const 0x1) +) +(assert_return + (invoke "fac-acc" (i64.const 0x5) (i64.const 0x1)) + (i64.const 0x78) +) +(assert_return (invoke "count" (i64.const 0x0)) (i64.const 0x0)) +(assert_return (invoke "count" (i64.const 0x3e8)) (i64.const 0x0)) +;; See `count.wat` for why this is commented out +;; (assert_return (invoke "count" (i64.const 0xf_4240)) (i64.const 0x0)) +(assert_return (invoke "even" (i64.const 0x0)) (i32.const 0x2c)) +(assert_return (invoke "even" (i64.const 0x1)) (i32.const 0x63)) +(assert_return (invoke "even" (i64.const 0x64)) (i32.const 0x2c)) +(assert_return (invoke "even" (i64.const 0x4d)) (i32.const 0x63)) +(assert_return (invoke "even" (i64.const 0xf_4240)) (i32.const 0x2c)) +(assert_return (invoke "even" (i64.const 0xf_4241)) (i32.const 0x63)) +(assert_return (invoke "odd" (i64.const 0x0)) (i32.const 0x63)) +(assert_return (invoke "odd" (i64.const 0x1)) (i32.const 0x2c)) +(assert_return (invoke "odd" (i64.const 0xc8)) (i32.const 0x63)) +(assert_return (invoke "odd" (i64.const 0x4d)) (i32.const 0x2c)) +(assert_return (invoke "odd" (i64.const 0xf_4240)) (i32.const 0x63)) +(assert_return (invoke "odd" (i64.const 0xf_423f)) (i32.const 0x2c)) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\88\80\80\80\00\02\60" + "\00\01\7f\60\00\00\03\83\80\80\80\00\02\00\01\0a" + "\93\80\80\80\00\02\86\80\80\80\00\00\12\01\41\00" + "\0b\82\80\80\80\00\00\0b" + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\89\80\80\80\00\02\60" + "\00\01\7f\60\00\01\7e\03\83\80\80\80\00\02\00\01" + "\0a\95\80\80\80\00\02\86\80\80\80\00\00\12\01\41" + "\00\0b\84\80\80\80\00\00\42\01\0b" + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\88\80\80\80\00\02\60" + "\00\00\60\01\7f\00\03\83\80\80\80\00\02\00\01\0a" + "\91\80\80\80\00\02\84\80\80\80\00\00\12\01\0b\82" + "\80\80\80\00\00\0b" + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\89\80\80\80\00\02\60" + "\00\00\60\02\7c\7f\00\03\83\80\80\80\00\02\00\01" + "\0a\91\80\80\80\00\02\84\80\80\80\00\00\12\01\0b" + "\82\80\80\80\00\00\0b" + ) + "type mismatch" +) +(module definition binary + "\00\61\73\6d\01\00\00\00\01\84\80\80\80\00\01\60" + "\00\00\03\83\80\80\80\00\02\00\00\0a\93\80\80\80" + "\00\02\86\80\80\80\00\00\41\01\12\01\0b\82\80\80" + "\80\00\00\0b" +) +(module instance) +(module definition binary + "\00\61\73\6d\01\00\00\00\01\84\80\80\80\00\01\60" + "\00\00\03\83\80\80\80\00\02\00\00\0a\9c\80\80\80" + "\00\02\8f\80\80\80\00\00\44\00\00\00\00\00\00\00" + "\40\41\01\12\01\0b\82\80\80\80\00\00\0b" +) +(module instance) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\89\80\80\80\00\02\60" + "\00\00\60\02\7f\7f\00\03\83\80\80\80\00\02\00\01" + "\0a\94\80\80\80\00\02\87\80\80\80\00\00\01\41\01" + "\12\01\0b\82\80\80\80\00\00\0b" + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\89\80\80\80\00\02\60" + "\00\00\60\02\7f\7f\00\03\83\80\80\80\00\02\00\01" + "\0a\94\80\80\80\00\02\87\80\80\80\00\00\41\01\01" + "\12\01\0b\82\80\80\80\00\00\0b" + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\89\80\80\80\00\02\60" + "\00\00\60\02\7f\7c\00\03\83\80\80\80\00\02\00\01" + "\0a\9c\80\80\80\00\02\8f\80\80\80\00\00\44\00\00" + "\00\00\00\00\f0\3f\41\01\12\01\0b\82\80\80\80\00" + "\00\0b" + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\89\80\80\80\00\02\60" + "\00\00\60\02\7c\7f\00\03\83\80\80\80\00\02\00\01" + "\0a\9c\80\80\80\00\02\8f\80\80\80\00\00\41\01\44" + "\00\00\00\00\00\00\f0\3f\12\01\0b\82\80\80\80\00" + "\00\0b" + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\84\80\80\80\00\01\60" + "\00\00\03\82\80\80\80\00\01\00\0a\8a\80\80\80\00" + "\01\84\80\80\80\00\00\12\01\0b" + ) + "unknown function" +) +(assert_invalid + (module binary + "\00\61\73\6d\01\00\00\00\01\84\80\80\80\00\01\60" + "\00\00\03\82\80\80\80\00\01\00\0a\8e\80\80\80\00" + "\01\88\80\80\80\00\00\12\94\98\db\e2\03\0b" + ) + "unknown function" +) diff --git a/benchmarks/wasm/spectest/return_call.wast b/benchmarks/wasm/spectest/return_call.wast new file mode 100644 index 000000000..d1fcf61c6 --- /dev/null +++ b/benchmarks/wasm/spectest/return_call.wast @@ -0,0 +1,198 @@ +;; Test `return_call` operator + +(module + ;; Auxiliary definitions + (func $const-i32 (result i32) (i32.const 0x132)) + (func $const-i64 (result i64) (i64.const 0x164)) + (func $const-f32 (result f32) (f32.const 0xf32)) + (func $const-f64 (result f64) (f64.const 0xf64)) + + (func $id-i32 (param i32) (result i32) (local.get 0)) + (func $id-i64 (param i64) (result i64) (local.get 0)) + (func $id-f32 (param f32) (result f32) (local.get 0)) + (func $id-f64 (param f64) (result f64) (local.get 0)) + + (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) + (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) + (func $f64-f32 (param f64 f32) (result f32) (local.get 1)) + (func $i64-f64 (param i64 f64) (result f64) (local.get 1)) + + ;; Typing + + (func (export "type-i32") (result i32) (return_call $const-i32)) + (func (export "type-i64") (result i64) (return_call $const-i64)) + (func (export "type-f32") (result f32) (return_call $const-f32)) + (func (export "type-f64") (result f64) (return_call $const-f64)) + + (func (export "type-first-i32") (result i32) (return_call $id-i32 (i32.const 32))) + (func (export "type-first-i64") (result i64) (return_call $id-i64 (i64.const 64))) + (func (export "type-first-f32") (result f32) (return_call $id-f32 (f32.const 1.32))) + (func (export "type-first-f64") (result f64) (return_call $id-f64 (f64.const 1.64))) + + (func (export "type-second-i32") (result i32) + (return_call $f32-i32 (f32.const 32.1) (i32.const 32)) + ) + (func (export "type-second-i64") (result i64) + (return_call $i32-i64 (i32.const 32) (i64.const 64)) + ) + (func (export "type-second-f32") (result f32) + (return_call $f64-f32 (f64.const 64) (f32.const 32)) + ) + (func (export "type-second-f64") (result f64) + (return_call $i64-f64 (i64.const 64) (f64.const 64.1)) + ) + + ;; Recursion + + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (return_call $fac-acc + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + ) + ) + ) + ) + + (func $count (export "count") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 0)) + (else (return_call $count (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + + (func $even (export "even") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 44)) + (else (return_call $odd (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + (func $odd (export "odd") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 99)) + (else (return_call $even (i64.sub (local.get 0) (i64.const 1)))) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) + +(assert_return (invoke "count" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000)) (i64.const 0)) +;; (assert_return (invoke "count" (i64.const 1_000_000)) (i64.const 0)) + +(assert_return (invoke "even" (i64.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 1_000_000)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1_000_001)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 1_000_000)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 999_999)) (i32.const 44)) + + +;; Invalid typing + +(assert_invalid + (module + (func $type-void-vs-num (result i32) (return_call 1) (i32.const 0)) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-num-vs-num (result i32) (return_call 1) (i32.const 0)) + (func (result i64) (i64.const 1)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $arity-0-vs-1 (return_call 1)) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-0-vs-2 (return_call 1)) + (func (param f64 i32)) + ) + "type mismatch" +) + +(module + (func $arity-1-vs-0 (i32.const 1) (return_call 1)) + (func) +) + +(module + (func $arity-2-vs-0 (f64.const 2) (i32.const 1) (return_call 1)) + (func) +) + +(assert_invalid + (module + (func $type-first-void-vs-num (return_call 1 (nop) (i32.const 1))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-void-vs-num (return_call 1 (i32.const 1) (nop))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-num-vs-num (return_call 1 (f64.const 1) (i32.const 1))) + (func (param i32 f64)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-num-vs-num (return_call 1 (i32.const 1) (f64.const 1))) + (func (param f64 i32)) + ) + "type mismatch" +) + + +;; Unbound function + +(assert_invalid + (module (func $unbound-func (return_call 1))) + "unknown function" +) +(assert_invalid + (module (func $large-func (return_call 1012321300))) + "unknown function" +) \ No newline at end of file diff --git a/src/main/scala/wasm/AST.scala b/src/main/scala/wasm/AST.scala index 1cd74d8f7..16439ba63 100644 --- a/src/main/scala/wasm/AST.scala +++ b/src/main/scala/wasm/AST.scala @@ -287,6 +287,7 @@ abstract class Action extends WIR case class Invoke(instName: Option[String], name: String, args: List[Value]) extends Action abstract class Assertion extends Cmd +case class AssertInvalid() extends Assertion case class AssertReturn(action: Action, expect: List[Num] /* TODO: support multiple expect result type*/) extends Assertion case class AssertTrap(action: Action, message: String) extends Assertion diff --git a/src/main/scala/wasm/MiniWasmScript.scala b/src/main/scala/wasm/MiniWasmScript.scala index 1590a43d7..8d27c7327 100644 --- a/src/main/scala/wasm/MiniWasmScript.scala +++ b/src/main/scala/wasm/MiniWasmScript.scala @@ -23,7 +23,6 @@ sealed class ScriptRunner { .collectFirst({ case Export(`name`, ExportFunc(index)) => module.funcs(index) - case _ => throw new RuntimeException("Not Supported") }) .get val instrs = func match { diff --git a/src/main/scala/wasm/Parser.scala b/src/main/scala/wasm/Parser.scala index 614030663..ac277abd1 100644 --- a/src/main/scala/wasm/Parser.scala +++ b/src/main/scala/wasm/Parser.scala @@ -237,7 +237,10 @@ class GSWasmVisitor extends WatParserBaseVisitor[WIR] { } case I64Type => { if (ctx.NAT.getText.startsWith("0x")) { - I64V(java.lang.Long.parseLong(ctx.NAT.getText.substring(2), 16)) + // println(s"parsing hex: ${ctx.NAT.getText.substring(2)}") + // also skip underscore + I64V(java.lang.Long.parseLong(ctx.NAT.getText.substring(2).replace("_", ""), 16)) + // I64V(java.lang.Long.parseLong(ctx.NAT.getText.substring(2), 16)) } else { I64V(ctx.NAT.getText.toLong) } @@ -800,6 +803,9 @@ class GSWasmVisitor extends WatParserBaseVisitor[WIR] { visitLiteralWithType(constCtx.literal, toNumType(ty)) } AssertReturn(action, expect.toList) + } else if (ctx.ASSERT_INVALID != null ) { + // we simply ignore assert_invalid for now + AssertInvalid() } else { throw new RuntimeException("Unsupported") } diff --git a/src/test/scala/genwasym/TestTFP.scala b/src/test/scala/genwasym/TestTFP.scala index 46c7c3b24..011700c79 100644 --- a/src/test/scala/genwasym/TestTFP.scala +++ b/src/test/scala/genwasym/TestTFP.scala @@ -163,4 +163,14 @@ class TestTFP extends FunSuite { testFileOutput("./benchmarks/wasm/trycatch/try_catch_catch_br.wat", List(1, 2, 6, 4, 6, 5)) } + // SpecTest + test("spectest_return_call") { + testWastFile("./benchmarks/wasm/spectest/return_call.bin.wast") + } + + // one of the failing test cases + test("count") { + testFile("./benchmarks/wasm/count.wat", None, ExpInt(0)) + } + }