diff --git a/crates/lightbeam/Cargo.toml b/crates/lightbeam/Cargo.toml index c3572b1c0e..a4560ea8ad 100644 --- a/crates/lightbeam/Cargo.toml +++ b/crates/lightbeam/Cargo.toml @@ -30,6 +30,3 @@ quickcheck = "0.9.0" [badges] maintenance = { status = "experimental" } - -[features] -bench = [] diff --git a/crates/lightbeam/src/benches.rs b/crates/lightbeam/src/benches.rs new file mode 100644 index 0000000000..2676c9431c --- /dev/null +++ b/crates/lightbeam/src/benches.rs @@ -0,0 +1,112 @@ +use crate::translate; + +const FIBONACCI: &str = r#" +(module + (func $fib (param $n i32) (result i32) + (if (result i32) + (i32.eq + (i32.const 0) + (get_local $n) + ) + (then + (i32.const 1) + ) + (else + (if (result i32) + (i32.eq + (i32.const 1) + (get_local $n) + ) + (then + (i32.const 1) + ) + (else + (i32.add + ;; fib(n - 1) + (call $fib + (i32.add + (get_local $n) + (i32.const -1) + ) + ) + ;; fib(n - 2) + (call $fib + (i32.add + (get_local $n) + (i32.const -2) + ) + ) + ) + ) + ) + ) + ) + ) +) + "#; + +// Generated by Rust for the `fib` function in `bench_fibonacci_baseline` +const FIBONACCI_OPT: &str = r" +(module + (func $fib (param $p0 i32) (result i32) + (local $l1 i32) + (set_local $l1 + (i32.const 1)) + (block $B0 + (br_if $B0 + (i32.lt_u + (get_local $p0) + (i32.const 2))) + (set_local $l1 + (i32.const 1)) + (loop $L1 + (set_local $l1 + (i32.add + (call $fib + (i32.add + (get_local $p0) + (i32.const -1))) + (get_local $l1))) + (br_if $L1 + (i32.gt_u + (tee_local $p0 + (i32.add + (get_local $p0) + (i32.const -2))) + (i32.const 1))))) + (get_local $l1)))"; + +#[bench] +fn bench_fibonacci_compile(b: &mut test::Bencher) { + let wasm = wat::parse_str(FIBONACCI).unwrap(); + + b.iter(|| test::black_box(translate(&wasm).unwrap())); +} + +#[bench] +fn bench_fibonacci_run(b: &mut test::Bencher) { + let wasm = wat::parse_str(FIBONACCI_OPT).unwrap(); + let module = translate(&wasm).unwrap(); + + b.iter(|| module.execute_func::<_, u32>(0, (20,))); +} + +#[bench] +fn bench_fibonacci_compile_run(b: &mut test::Bencher) { + let wasm = wat::parse_str(FIBONACCI).unwrap(); + + b.iter(|| translate(&wasm).unwrap().execute_func::<_, u32>(0, (20,))); +} + +#[bench] +fn bench_fibonacci_baseline(b: &mut test::Bencher) { + fn fib(n: i32) -> i32 { + if n == 0 || n == 1 { + 1 + } else { + fib(n - 1) + fib(n - 2) + } + } + + b.iter(|| test::black_box(fib(test::black_box(20)))); +} diff --git a/crates/lightbeam/src/lib.rs b/crates/lightbeam/src/lib.rs index e4ccd6d633..a1f3617eb4 100644 --- a/crates/lightbeam/src/lib.rs +++ b/crates/lightbeam/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "bench", feature(test))] +#![cfg_attr(test, feature(test))] #![feature(proc_macro_hygiene)] #[macro_use] @@ -11,15 +11,11 @@ extern crate memoffset; extern crate dynasm; extern crate dynasmrt; extern crate itertools; -#[cfg(test)] -#[macro_use] -extern crate lazy_static; -#[cfg(test)] -#[macro_use] -extern crate quickcheck; // Just so we can implement `Signature` for `cranelift_codegen::ir::Signature` extern crate cranelift_codegen; extern crate multi_mut; +#[cfg(test)] +extern crate test; mod backend; mod disassemble; @@ -30,8 +26,10 @@ mod module; mod translate_sections; #[cfg(test)] -mod tests; +mod benches; pub use crate::backend::CodeGenSession; pub use crate::function_body::translate_wasm as translate_function; -pub use crate::module::{translate, ExecutableModule, ModuleContext, Signature, TranslatedModule}; +pub use crate::module::{ + translate, ExecutableModule, ExecutionError, ModuleContext, Signature, TranslatedModule, +}; diff --git a/crates/lightbeam/src/tests.rs b/crates/lightbeam/tests/quickchecks.rs similarity index 71% rename from crates/lightbeam/src/tests.rs rename to crates/lightbeam/tests/quickchecks.rs index 1fa6551106..78d124cd32 100644 --- a/crates/lightbeam/src/tests.rs +++ b/crates/lightbeam/tests/quickchecks.rs @@ -1,4 +1,6 @@ -use super::{module::ExecutionError, translate, ExecutableModule}; +use lazy_static::lazy_static; +use lightbeam::{translate, ExecutableModule}; +use quickcheck::quickcheck; fn translate_wat(wat: &str) -> ExecutableModule { let wasm = wat::parse_str(wat).unwrap(); @@ -6,25 +8,13 @@ fn translate_wat(wat: &str) -> ExecutableModule { compiled } -/// Execute the first function in the module. -fn execute_wat(wat: &str, a: u32, b: u32) -> u32 { - let translated = translate_wat(wat); - translated.disassemble(); - translated.execute_func(0, (a, b)).unwrap() -} - -#[test] -fn empty() { - let _ = translate_wat("(module (func))"); -} - mod op32 { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; macro_rules! binop_test { ($op:ident, $func:expr) => { mod $op { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; use std::sync::Once; const OP: &str = stringify!($op); @@ -82,7 +72,7 @@ mod op32 { macro_rules! unop_test { ($name:ident, $func:expr) => { mod $name { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; use std::sync::Once; lazy_static! { @@ -143,7 +133,7 @@ mod op32 { } mod op64 { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; macro_rules! binop_test { ($op:ident, $func:expr) => { @@ -151,7 +141,7 @@ mod op64 { }; ($op:ident, $func:expr, $retty:ident) => { mod $op { - use super::{translate_wat, ExecutableModule}; + use super::{translate_wat, ExecutableModule, quickcheck, lazy_static}; const RETTY: &str = stringify!($retty); const OP: &str = stringify!($op); @@ -211,7 +201,7 @@ mod op64 { }; ($name:ident, $func:expr, $out_ty:ty) => { mod $name { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; use std::sync::Once; lazy_static! { @@ -290,7 +280,7 @@ mod op64 { } mod opf32 { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; macro_rules! binop_test { ($op:ident, $func:expr) => { @@ -298,7 +288,7 @@ mod opf32 { }; ($op:ident, $func:expr, $retty:ident) => { mod $op { - use super::{translate_wat, ExecutableModule}; + use super::{translate_wat, ExecutableModule, quickcheck, lazy_static}; const RETTY: &str = stringify!($retty); const OP: &str = stringify!($op); @@ -358,7 +348,7 @@ mod opf32 { }; ($name:ident, $func:expr, $out_ty:ty) => { mod $name { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; use std::sync::Once; lazy_static! { @@ -407,7 +397,7 @@ mod opf32 { } mod opf64 { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; macro_rules! binop_test { ($op:ident, $func:expr) => { @@ -415,7 +405,7 @@ mod opf64 { }; ($op:ident, $func:expr, $retty:ident) => { mod $op { - use super::{translate_wat, ExecutableModule}; + use super::{translate_wat, ExecutableModule, quickcheck, lazy_static}; const RETTY: &str = stringify!($retty); const OP: &str = stringify!($op); @@ -475,7 +465,7 @@ mod opf64 { }; ($name:ident, $func:expr, $out_ty:ty) => { mod $name { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; use std::sync::Once; lazy_static! { @@ -549,107 +539,6 @@ quickcheck! { out == Ok(if a == b { a } else { b }) } } -#[test] -fn if_without_result() { - let code = r#" -(module - (func (param i32) (param i32) (result i32) - (if - (i32.eq - (get_local 0) - (get_local 1) - ) - (then (unreachable)) - ) - - (get_local 0) - ) -) - "#; - - assert_eq!(execute_wat(code, 2, 3), 2); -} - -#[test] -fn block() { - let code = r#" -(module - (func (param i32) (param i32) (result i32) - (block (result i32) - get_local 0 - ) - ) -) - "#; - assert_eq!(execute_wat(code, 10, 20), 10); -} - -#[test] -fn br_block() { - let code = r#" -(module - (func (param i32) (param i32) (result i32) - get_local 1 - (block (result i32) - get_local 0 - get_local 0 - br 0 - unreachable - ) - i32.add - ) -) - "#; - - let translated = translate_wat(code); - translated.disassemble(); - - assert_eq!( - translated.execute_func::<(i32, i32), i32>(0, (5, 7)), - Ok(12) - ); -} - -// Tests discarding values on the value stack, while -// carrying over the result using a conditional branch. -#[test] -fn brif_block() { - let code = r#" -(module - (func (param i32) (param i32) (result i32) - get_local 1 - (block (result i32) - get_local 0 - get_local 0 - br_if 0 - unreachable - ) - i32.add - ) -) - "#; - assert_eq!(execute_wat(code, 5, 7), 12); -} - -// Tests that br_if keeps values in the case if the branch -// hasn't been taken. -#[test] -fn brif_block_passthru() { - let code = r#" -(module - (func (param i32) (param i32) (result i32) - (block (result i32) - get_local 1 - get_local 0 - br_if 0 - get_local 1 - i32.add - ) - ) -) - "#; - assert_eq!(execute_wat(code, 0, 3), 6); -} quickcheck! { #[test] @@ -715,270 +604,11 @@ quickcheck! { true } } -#[test] -fn wrong_type() { - let code = r#" -(module - (func (param i32) (param i64) (result i32) - (i32.const 228) - ) -) - "#; - - let translated = translate_wat(code); - assert_eq!( - translated - .execute_func::<_, ()>(0, (0u32, 0u32)) - .unwrap_err(), - ExecutionError::TypeMismatch - ); -} - -#[test] -fn wrong_index() { - let code = r#" -(module - (func (param i32) (param i64) (result i32) - (i32.const 228) - ) -) - "#; - - let translated = translate_wat(code); - assert_eq!( - translated - .execute_func::<_, ()>(10, (0u32, 0u32)) - .unwrap_err(), - ExecutionError::FuncIndexOutOfBounds - ); -} - -fn iterative_fib_baseline(n: u32) -> u32 { - let (mut a, mut b) = (1, 1); - - for _ in 0..n { - let old_a = a; - a = b; - b += old_a; - } - - a -} - -const FIBONACCI: &str = r#" -(module - (func $fib (param $n i32) (result i32) - (if (result i32) - (i32.eq - (i32.const 0) - (get_local $n) - ) - (then - (i32.const 1) - ) - (else - (if (result i32) - (i32.eq - (i32.const 1) - (get_local $n) - ) - (then - (i32.const 1) - ) - (else - (i32.add - ;; fib(n - 1) - (call $fib - (i32.add - (get_local $n) - (i32.const -1) - ) - ) - ;; fib(n - 2) - (call $fib - (i32.add - (get_local $n) - (i32.const -2) - ) - ) - ) - ) - ) - ) - ) - ) -) - "#; - -#[test] -fn fib_unopt() { - let translated = translate_wat(FIBONACCI); - translated.disassemble(); - - for x in 0..30 { - assert_eq!( - translated.execute_func::<_, u32>(0, (x,)), - Ok(iterative_fib_baseline(x)), - "Failed for x={}", - x - ); - } -} - -// Generated by Rust for the `fib` function in `bench_fibonacci_baseline` -const FIBONACCI_OPT: &str = r" -(module - (func $fib (param $p0 i32) (result i32) - (local $l1 i32) - (set_local $l1 - (i32.const 1)) - (block $B0 - (br_if $B0 - (i32.lt_u - (get_local $p0) - (i32.const 2))) - (set_local $l1 - (i32.const 1)) - (loop $L1 - (set_local $l1 - (i32.add - (call $fib - (i32.add - (get_local $p0) - (i32.const -1))) - (get_local $l1))) - (br_if $L1 - (i32.gt_u - (tee_local $p0 - (i32.add - (get_local $p0) - (i32.const -2))) - (i32.const 1))))) - (get_local $l1)))"; - -#[test] -fn fib_opt() { - let translated = translate_wat(FIBONACCI_OPT); - translated.disassemble(); - - for x in 0..30 { - assert_eq!( - translated.execute_func::<_, u32>(0, (x,)), - Ok(iterative_fib_baseline(x)), - "Failed for x={}", - x - ); - } -} - -#[test] -fn i32_div() { - const CODE: &str = r" - (module - (func (param i32) (param i32) (result i32) - (i32.div_s (get_local 0) (get_local 1)) - ) - )"; - - let translated = translate_wat(CODE); - translated.disassemble(); - - assert_eq!(translated.execute_func::<_, u32>(0, (-1, -1)), Ok(1)); -} - -#[test] -fn i32_rem() { - const CODE: &str = r" - (module - (func (param i32) (param i32) (result i32) - (i32.rem_s (get_local 0) (get_local 1)) - ) - )"; - - let translated = translate_wat(CODE); - translated.disassemble(); - - assert_eq!(translated.execute_func::<_, u32>(0, (123121, -1)), Ok(0)); -} - -#[test] -fn i64_div() { - const CODE: &str = r" - (module - (func (param i64) (param i64) (result i64) - (i64.div_s (get_local 0) (get_local 1)) - ) - )"; - - let translated = translate_wat(CODE); - translated.disassemble(); - - assert_eq!(translated.execute_func::<_, u64>(0, (-1i64, -1i64)), Ok(1)); -} - -#[test] -fn i64_rem() { - const CODE: &str = r" - (module - (func (param i64) (param i64) (result i64) - (i64.rem_s (get_local 0) (get_local 1)) - ) - )"; - - let translated = translate_wat(CODE); - translated.disassemble(); - - assert_eq!( - translated.execute_func::<_, u64>(0, (123121i64, -1i64)), - Ok(0) - ); -} - -#[test] -fn br_table() { - const CODE: &str = r" -(func (param $i i32) (result i32) - (return - (block $2 (result i32) - (i32.add (i32.const 10) - (block $1 (result i32) - (i32.add (i32.const 100) - (block $0 (result i32) - (i32.add (i32.const 1000) - (block $default (result i32) - (br_table $0 $1 $2 $default - (i32.mul (i32.const 2) (get_local $i)) - (i32.and (i32.const 3) (get_local $i)) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) -"; - - let translated = translate_wat(CODE); - translated.disassemble(); - - assert_eq!(translated.execute_func::<_, u32>(0, (0u32,)), Ok(110)); - assert_eq!(translated.execute_func::<_, u32>(0, (1u32,)), Ok(12)); - assert_eq!(translated.execute_func::<_, u32>(0, (2u32,)), Ok(4)); - assert_eq!(translated.execute_func::<_, u32>(0, (3u32,)), Ok(1116)); - assert_eq!(translated.execute_func::<_, u32>(0, (4u32,)), Ok(118)); - assert_eq!(translated.execute_func::<_, u32>(0, (5u32,)), Ok(20)); - assert_eq!(translated.execute_func::<_, u32>(0, (6u32,)), Ok(12)); - assert_eq!(translated.execute_func::<_, u32>(0, (7u32,)), Ok(1124)); - assert_eq!(translated.execute_func::<_, u32>(0, (8u32,)), Ok(126)); -} macro_rules! test_select { ($name:ident, $ty:ident) => { mod $name { - use super::{translate_wat, ExecutableModule}; + use super::{lazy_static, quickcheck, translate_wat, ExecutableModule}; use std::sync::Once; lazy_static! { @@ -1022,45 +652,3 @@ macro_rules! test_select { test_select!(select32, i32); test_select!(select64, i64); - -#[cfg(feature = "bench")] -mod benches { - extern crate test; - - use super::{translate, FIBONACCI, FIBONACCI_OPT}; - - #[bench] - fn bench_fibonacci_compile(b: &mut test::Bencher) { - let wasm = wat::parse_str(FIBONACCI).unwrap(); - - b.iter(|| test::black_box(translate(&wasm).unwrap())); - } - - #[bench] - fn bench_fibonacci_run(b: &mut test::Bencher) { - let wasm = wat::parse_str(FIBONACCI_OPT).unwrap(); - let module = translate(&wasm).unwrap(); - - b.iter(|| module.execute_func::<_, u32>(0, (20,))); - } - - #[bench] - fn bench_fibonacci_compile_run(b: &mut test::Bencher) { - let wasm = wat::parse_str(FIBONACCI).unwrap(); - - b.iter(|| translate(&wasm).unwrap().execute_func::<_, u32>(0, (20,))); - } - - #[bench] - fn bench_fibonacci_baseline(b: &mut test::Bencher) { - fn fib(n: i32) -> i32 { - if n == 0 || n == 1 { - 1 - } else { - fib(n - 1) + fib(n - 2) - } - } - - b.iter(|| test::black_box(fib(test::black_box(20)))); - } -} diff --git a/crates/lightbeam/tests/wrongs.rs b/crates/lightbeam/tests/wrongs.rs new file mode 100644 index 0000000000..4f36ec5a28 --- /dev/null +++ b/crates/lightbeam/tests/wrongs.rs @@ -0,0 +1,45 @@ +use lightbeam::{translate, ExecutableModule, ExecutionError}; + +fn translate_wat(wat: &str) -> ExecutableModule { + let wasm = wat::parse_str(wat).unwrap(); + let compiled = translate(&wasm).unwrap(); + compiled +} + +#[test] +fn wrong_type() { + let code = r#" +(module + (func (param i32) (param i64) (result i32) + (i32.const 228) + ) +) + "#; + + let translated = translate_wat(code); + assert_eq!( + translated + .execute_func::<_, ()>(0, (0u32, 0u32)) + .unwrap_err(), + ExecutionError::TypeMismatch + ); +} + +#[test] +fn wrong_index() { + let code = r#" +(module + (func (param i32) (param i64) (result i32) + (i32.const 228) + ) +) + "#; + + let translated = translate_wat(code); + assert_eq!( + translated + .execute_func::<_, ()>(10, (0u32, 0u32)) + .unwrap_err(), + ExecutionError::FuncIndexOutOfBounds + ); +} diff --git a/filetests/arith.wat b/filetests/arith.wat deleted file mode 100644 index fa7115696b..0000000000 --- a/filetests/arith.wat +++ /dev/null @@ -1,13 +0,0 @@ -(module - (memory 1) - (func $main (local i32) - (set_local 0 (i32.sub (i32.const 4) (i32.const 4))) - (if - (get_local 0) - (then unreachable) - (else (drop (i32.mul (i32.const 6) (get_local 0)))) - ) - ) - (start $main) - (data (i32.const 0) "abcdefgh") -) diff --git a/filetests/call.wat b/filetests/call.wat deleted file mode 100644 index e8640d2342..0000000000 --- a/filetests/call.wat +++ /dev/null @@ -1,10 +0,0 @@ -(module - (func $main (local i32) - (set_local 0 (i32.const 0)) - (drop (call $inc)) - ) - (func $inc (result i32) - (i32.const 1) - ) - (start $main) -) diff --git a/filetests/call_indirect.wat b/filetests/call_indirect.wat deleted file mode 100644 index 8bd8ed58c1..0000000000 --- a/filetests/call_indirect.wat +++ /dev/null @@ -1,50 +0,0 @@ -(module - (type $indirect_sig (func (param i64) (result i64))) - - (func $assert (param i32) - (block $ok - (br_if $ok - (get_local 0) - ) - (unreachable) - ) - ) - - (func $plus_1 (param i64) (result i64) - get_local 0 - i64.const 1 - i64.add - ) - (func $minus_1 (param i64) (result i64) - get_local 0 - i64.const 1 - i64.sub - ) - - (func $main - (call $call_indirect - (i32.const 0) - (i64.const 2) - ) - (call $call_indirect - (i32.const 1) - (i64.const 0) - ) - ) - - (func $call_indirect (param $func i32) (param $expected i64) - (call $assert - (i64.eq - (call_indirect (type $indirect_sig) - (i64.const 1) - (get_local $func) - ) - (get_local $expected) - ) - ) - ) - (start $main) - - (table 2 2 anyfunc) - (elem (i32.const 0) $plus_1 $minus_1) -) diff --git a/filetests/fibonacci.wat b/filetests/fibonacci.wat deleted file mode 100644 index 1788a467ca..0000000000 --- a/filetests/fibonacci.wat +++ /dev/null @@ -1,22 +0,0 @@ -(module - (memory 1) - (func $main (local i32 i32 i32 i32) - (set_local 0 (i32.const 0)) - (set_local 1 (i32.const 1)) - (set_local 2 (i32.const 1)) - (set_local 3 (i32.const 0)) - (block - (loop - (br_if 1 (i32.gt_s (get_local 0) (i32.const 5))) - (set_local 3 (get_local 2)) - (set_local 2 (i32.add (get_local 2) (get_local 1))) - (set_local 1 (get_local 3)) - (set_local 0 (i32.add (get_local 0) (i32.const 1))) - (br 0) - ) - ) - (i32.store (i32.const 0) (get_local 2)) - ) - (start $main) - (data (i32.const 0) "0000") -) diff --git a/filetests/globals.wat b/filetests/globals.wat deleted file mode 100644 index 646e5f0f45..0000000000 --- a/filetests/globals.wat +++ /dev/null @@ -1,8 +0,0 @@ -(module - (global $x (mut i32) (i32.const 4)) - (memory 1) - (func $main (local i32) - (i32.store (i32.const 0) (get_global $x)) - ) - (start $main) -) diff --git a/filetests/grow.wat b/filetests/grow.wat deleted file mode 100644 index f28da1d1fa..0000000000 --- a/filetests/grow.wat +++ /dev/null @@ -1,28 +0,0 @@ -(module - (memory 1) - (func $assert (param i32) - (block $ok - (br_if $ok - (get_local 0) - ) - (unreachable) - ) - ) - (func $main (local i32) - (call $assert - (i32.eq - (memory.grow (i32.const 1)) - (i32.const 1) - ) - ) - (call $assert - (i32.eq - (memory.size) - (i32.const 2) - ) - ) - ) - (start $main) - (data (i32.const 0) "\04\03\02\01") -) - diff --git a/filetests/memory.wat b/filetests/memory.wat deleted file mode 100644 index 0c81bad174..0000000000 --- a/filetests/memory.wat +++ /dev/null @@ -1,11 +0,0 @@ -(module - (memory 1) - (func $main (local i32) - (i32.store (i32.const 0) (i32.const 0x0)) - (if (i32.load (i32.const 0)) - (then (i32.store (i32.const 0) (i32.const 0xa))) - (else (i32.store (i32.const 0) (i32.const 0xb)))) - ) - (start $main) - (data (i32.const 0) "0000") -) diff --git a/misc_testsuite/call_indirect.wast b/misc_testsuite/call_indirect.wast new file mode 100644 index 0000000000..aa242d6222 --- /dev/null +++ b/misc_testsuite/call_indirect.wast @@ -0,0 +1,187 @@ +;; Test `call_indirect` calls between modules. + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (type $over-i32 (func (param i32) (result i32))) + (type $over-i64 (func (param i64) (result i64))) + (type $over-f32 (func (param f32) (result f32))) + (type $over-f64 (func (param f64) (result f64))) + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + (func $const-i64 (type $out-i64) (i64.const 0x164)) + (func $const-f32 (type $out-f32) (f32.const 0xf32)) + (func $const-f64 (type $out-f64) (f64.const 0xf64)) + + (func $id-i32 (type $over-i32) (local.get 0)) + (func $id-i64 (type $over-i64) (local.get 0)) + (func $id-f32 (type $over-f32) (local.get 0)) + (func $id-f64 (type $over-f64) (local.get 0)) + + (func $i32-i64 (type $i32-i64) (local.get 1)) + (func $i64-f64 (type $i64-f64) (local.get 1)) + (func $f32-i32 (type $f32-i32) (local.get 1)) + (func $f64-f32 (type $f64-f32) (local.get 1)) + + (func $over-i32-duplicate (type $over-i32-duplicate) (local.get 0)) + (func $over-i64-duplicate (type $over-i64-duplicate) (local.get 0)) + (func $over-f32-duplicate (type $over-f32-duplicate) (local.get 0)) + (func $over-f64-duplicate (type $over-f64-duplicate) (local.get 0)) + + (table (export "table") funcref + (elem + $const-i32 $const-i64 $const-f32 $const-f64 + $id-i32 $id-i64 $id-f32 $id-f64 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 + $over-i32-duplicate $over-i64-duplicate + $over-f32-duplicate $over-f64-duplicate + ) + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (call_indirect (type $out-f64) (i32.const 3)) + ) + + (func (export "type-index") (result i64) + (call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (call_indirect (type $f32-i32) (f32.const 32.1) (i32.const 32) (i32.const 8)) + ) + (func (export "type-second-i64") (result i64) + (call_indirect (type $i32-i64) (i32.const 32) (i64.const 64) (i32.const 9)) + ) + (func (export "type-second-f32") (result f32) + (call_indirect (type $f64-f32) (f64.const 64) (f32.const 32) (i32.const 10)) + ) + (func (export "type-second-f64") (result f64) + (call_indirect (type $i64-f64) (i64.const 64) (f64.const 64.1) (i32.const 11)) + ) +) + +(register "test") + +(module + ;; Auxiliary definitions. These are the same types as "test"'s, but in a + ;; different order, since call_indirect types are compared structurally. + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (type $over-i32 (func (param i32) (result i32))) + (type $over-i64 (func (param i64) (result i64))) + (type $over-f32 (func (param f32) (result f32))) + (type $over-f64 (func (param f64) (result f64))) + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + (type $proc (func)) + + ;; Import the table from "test". + + (import "test" "table" (table 16 funcref)) + + ;; Typing + + (func (export "type-i32") (result i32) + (call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (call_indirect (type $out-f64) (i32.const 3)) + ) + + (func (export "type-index") (result i64) + (call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (call_indirect (type $f32-i32) (f32.const 32.1) (i32.const 32) (i32.const 8)) + ) + (func (export "type-second-i64") (result i64) + (call_indirect (type $i32-i64) (i32.const 32) (i64.const 64) (i32.const 9)) + ) + (func (export "type-second-f32") (result f32) + (call_indirect (type $f64-f32) (f64.const 64) (f32.const 32) (i32.const 10)) + ) + (func (export "type-second-f64") (result f64) + (call_indirect (type $i64-f64) (i64.const 64) (f64.const 64.1) (i32.const 11)) + ) +) + +(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-index") (i64.const 100)) + +(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)) diff --git a/misc_testsuite/control-flow.wast b/misc_testsuite/control-flow.wast new file mode 100644 index 0000000000..8535564a13 --- /dev/null +++ b/misc_testsuite/control-flow.wast @@ -0,0 +1,116 @@ +(module (func)) + +(module + (func (export "if-without-result") (param i32) (param i32) (result i32) + (if + (i32.eq + (local.get 0) + (local.get 1) + ) + (then (unreachable)) + ) + + (local.get 0) + ) +) + +(assert_return (invoke "if-without-result" (i32.const 2) (i32.const 3)) (i32.const 2)) + +(module + (func (export "block") (param i32) (param i32) (result i32) + (block (result i32) + local.get 0 + ) + ) +) + +(assert_return (invoke "block" (i32.const 10) (i32.const 20)) (i32.const 10)) + +(module + (func (export "br_block") (param i32) (param i32) (result i32) + local.get 1 + (block (result i32) + local.get 0 + local.get 0 + br 0 + unreachable + ) + i32.add + ) +) + +(assert_return (invoke "br_block" (i32.const 5) (i32.const 7)) (i32.const 12)) + +;; Tests discarding values on the value stack, while +;; carrying over the result using a conditional branch. +(module + (func (export "brif_block") (param i32) (param i32) (result i32) + local.get 1 + (block (result i32) + local.get 0 + local.get 0 + br_if 0 + unreachable + ) + i32.add + ) +) + +(assert_return (invoke "brif_block" (i32.const 5) (i32.const 7)) (i32.const 12)) + +;; Tests that br_if keeps values in the case if the branch +;; hasn't been taken. +(module + (func (export "brif_block_passthru") (param i32) (param i32) (result i32) + (block (result i32) + local.get 1 + local.get 0 + br_if 0 + local.get 1 + i32.add + ) + ) +) + +(assert_return (invoke "brif_block_passthru" (i32.const 0) (i32.const 3)) (i32.const 6)) + +(module + (func (export "i32.div_s") (param i32) (param i32) (result i32) + (i32.div_s (local.get 0) (local.get 1)) + ) +) + +(module + (func (export "br_table") (param $i i32) (result i32) + (return + (block $2 (result i32) + (i32.add (i32.const 10) + (block $1 (result i32) + (i32.add (i32.const 100) + (block $0 (result i32) + (i32.add (i32.const 1000) + (block $default (result i32) + (br_table $0 $1 $2 $default + (i32.mul (i32.const 2) (local.get $i)) + (i32.and (i32.const 3) (local.get $i)) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) +) + +(assert_return (invoke "br_table" (i32.const 0)) (i32.const 110)) +(assert_return (invoke "br_table" (i32.const 1)) (i32.const 12)) +(assert_return (invoke "br_table" (i32.const 2)) (i32.const 4)) +(assert_return (invoke "br_table" (i32.const 3)) (i32.const 1116)) +(assert_return (invoke "br_table" (i32.const 4)) (i32.const 118)) +(assert_return (invoke "br_table" (i32.const 5)) (i32.const 20)) +(assert_return (invoke "br_table" (i32.const 6)) (i32.const 12)) +(assert_return (invoke "br_table" (i32.const 7)) (i32.const 1124)) +(assert_return (invoke "br_table" (i32.const 8)) (i32.const 126)) diff --git a/misc_testsuite/div-rem.wast b/misc_testsuite/div-rem.wast new file mode 100644 index 0000000000..3bcfae85d6 --- /dev/null +++ b/misc_testsuite/div-rem.wast @@ -0,0 +1,31 @@ +(module + (func (export "i32.div_s") (param i32) (param i32) (result i32) + (i32.div_s (local.get 0) (local.get 1)) + ) +) + +(assert_return (invoke "i32.div_s" (i32.const -1) (i32.const -1)) (i32.const 1)) + +(module + (func (export "i32.rem_s") (param i32) (param i32) (result i32) + (i32.rem_s (local.get 0) (local.get 1)) + ) +) + +(assert_return (invoke "i32.rem_s" (i32.const 123121) (i32.const -1)) (i32.const 0)) + +(module + (func (export "i64.div_s") (param i64) (param i64) (result i64) + (i64.div_s (local.get 0) (local.get 1)) + ) +) + +(assert_return (invoke "i64.div_s" (i64.const -1) (i64.const -1)) (i64.const 1)) + +(module + (func (export "i64.rem_s") (param i64) (param i64) (result i64) + (i64.rem_s (local.get 0) (local.get 1)) + ) +) + +(assert_return (invoke "i64.rem_s" (i64.const 123121) (i64.const -1)) (i64.const 0)) diff --git a/misc_testsuite/empty.wast b/misc_testsuite/empty.wast new file mode 100644 index 0000000000..5f26cd22c2 --- /dev/null +++ b/misc_testsuite/empty.wast @@ -0,0 +1,3 @@ +(module (func (export "empty"))) + +(invoke "empty") diff --git a/misc_testsuite/fib.wast b/misc_testsuite/fib.wast new file mode 100644 index 0000000000..0ea19ee3bc --- /dev/null +++ b/misc_testsuite/fib.wast @@ -0,0 +1,97 @@ +(module + (func $fib (export "fib") (param $n i32) (result i32) + (if (result i32) + (i32.eq + (i32.const 0) + (local.get $n) + ) + (then + (i32.const 1) + ) + (else + (if (result i32) + (i32.eq + (i32.const 1) + (local.get $n) + ) + (then + (i32.const 1) + ) + (else + (i32.add + ;; fib(n - 1) + (call $fib + (i32.add + (local.get $n) + (i32.const -1) + ) + ) + ;; fib(n - 2) + (call $fib + (i32.add + (local.get $n) + (i32.const -2) + ) + ) + ) + ) + ) + ) + ) + ) +) + +(assert_return (invoke "fib" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fib" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fib" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "fib" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "fib" (i32.const 4)) (i32.const 5)) +(assert_return (invoke "fib" (i32.const 5)) (i32.const 8)) +(assert_return (invoke "fib" (i32.const 6)) (i32.const 13)) +(assert_return (invoke "fib" (i32.const 7)) (i32.const 21)) +(assert_return (invoke "fib" (i32.const 8)) (i32.const 34)) +(assert_return (invoke "fib" (i32.const 9)) (i32.const 55)) +(assert_return (invoke "fib" (i32.const 10)) (i32.const 89)) + +;; Generated by Rust. +(module + (func $fib (export "fib") (param $p0 i32) (result i32) + (local $l1 i32) + (local.set $l1 + (i32.const 1)) + (block $B0 + (br_if $B0 + (i32.lt_u + (local.get $p0) + (i32.const 2))) + (local.set $l1 + (i32.const 1)) + (loop $L1 + (local.set $l1 + (i32.add + (call $fib + (i32.add + (local.get $p0) + (i32.const -1))) + (local.get $l1))) + (br_if $L1 + (i32.gt_u + (local.tee $p0 + (i32.add + (local.get $p0) + (i32.const -2))) + (i32.const 1))))) + (local.get $l1)) +) + +(assert_return (invoke "fib" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fib" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fib" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "fib" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "fib" (i32.const 4)) (i32.const 5)) +(assert_return (invoke "fib" (i32.const 5)) (i32.const 8)) +(assert_return (invoke "fib" (i32.const 6)) (i32.const 13)) +(assert_return (invoke "fib" (i32.const 7)) (i32.const 21)) +(assert_return (invoke "fib" (i32.const 8)) (i32.const 34)) +(assert_return (invoke "fib" (i32.const 9)) (i32.const 55)) +(assert_return (invoke "fib" (i32.const 10)) (i32.const 89))