From 0bb5f3a2e90dad8b02115ade677b83f34802885c Mon Sep 17 00:00:00 2001 From: nerodesu017 Date: Mon, 26 Aug 2024 16:59:56 +0300 Subject: [PATCH] feat: conversion op codes Signed-off-by: nerodesu017 --- Cargo.lock | 36 +- Cargo.toml | 1 + src/core/error.rs | 3 + src/core/reader/types/opcode.rs | 20 + src/execution/interpreter_loop.rs | 300 ++++- src/execution/value.rs | 70 + src/validation/code.rs | 60 + tests/conversions.rs | 2008 +++++++++++++++++++++++++++++ tests/errors.rs | 6 + tests/i32.rs | 16 + tests/i64.rs | 16 + 11 files changed, 2496 insertions(+), 40 deletions(-) create mode 100644 tests/conversions.rs create mode 100644 tests/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 286b05f3..02a96a65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,6 +238,22 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +[[package]] +name = "hexf" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6618f4550dcd7d9ddb5126ab18d48dfa31aa952159cb832390bda464d3bc827e" +dependencies = [ + "hexf-parse", + "syn 1.0.109", +] + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + [[package]] name = "humantime" version = "2.1.0" @@ -482,7 +498,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -496,6 +512,17 @@ dependencies = [ "serde", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.52" @@ -534,7 +561,7 @@ checksum = "c8f546451eaa38373f549093fe9fd05e7d2bade739e2ddf834b9968621d60107" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -590,7 +617,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.52", "wasm-bindgen-shared", ] @@ -612,7 +639,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -638,6 +665,7 @@ version = "0.1.0" dependencies = [ "criterion", "env_logger 0.10.2", + "hexf", "itertools 0.12.1", "libm", "log", diff --git a/Cargo.toml b/Cargo.toml index 6e38805c..c41468e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ wasmparser = "0.119.0" itertools = "0.12.0" wat = "1.0.83" criterion = { version = "0.5.1", features = ["html_reports"] } +hexf = "0.2.1" [features] default = ["hooks"] diff --git a/src/core/error.rs b/src/core/error.rs index 1b717db4..7e8ad806 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -11,6 +11,8 @@ pub enum RuntimeError { UnrepresentableResult, FunctionNotFound, StackSmash, + // https://github.com/wasmi-labs/wasmi/blob/37d1449524a322817c55026eb21eb97dd693b9ce/crates/core/src/trap.rs#L265C5-L265C27 + BadConversionToInteger, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -122,6 +124,7 @@ impl Display for RuntimeError { RuntimeError::UnrepresentableResult => f.write_str("Result is unrepresentable"), RuntimeError::FunctionNotFound => f.write_str("Function not found"), RuntimeError::StackSmash => f.write_str("Stack smashed"), + RuntimeError::BadConversionToInteger => f.write_str("Bad conversion to integer"), } } } diff --git a/src/core/reader/types/opcode.rs b/src/core/reader/types/opcode.rs index 52cd9d53..5c66b1c8 100644 --- a/src/core/reader/types/opcode.rs +++ b/src/core/reader/types/opcode.rs @@ -115,8 +115,28 @@ pub const F64_DIV: u8 = 0xA3; pub const F64_MIN: u8 = 0xA4; pub const F64_MAX: u8 = 0xA5; pub const F64_COPYSIGN: u8 = 0xA6; +pub const I32_WRAP_I64: u8 = 0xA7; +pub const I32_TRUNC_F32_S: u8 = 0xA8; +pub const I32_TRUNC_F32_U: u8 = 0xA9; +pub const I32_TRUNC_F64_S: u8 = 0xAA; +pub const I32_TRUNC_F64_U: u8 = 0xAB; +pub const I64_EXTEND_I32_S: u8 = 0xAC; +pub const I64_EXTEND_I32_U: u8 = 0xAD; +pub const I64_TRUNC_F32_S: u8 = 0xAE; +pub const I64_TRUNC_F32_U: u8 = 0xAF; +pub const I64_TRUNC_F64_S: u8 = 0xB0; +pub const I64_TRUNC_F64_U: u8 = 0xB1; pub const F32_CONVERT_I32_S: u8 = 0xB2; pub const F32_CONVERT_I32_U: u8 = 0xB3; pub const F32_CONVERT_I64_S: u8 = 0xB4; pub const F32_CONVERT_I64_U: u8 = 0xB5; +pub const F32_DEMOTE_F64: u8 = 0xB6; +pub const F64_CONVERT_I32_S: u8 = 0xB7; +pub const F64_CONVERT_I32_U: u8 = 0xB8; +pub const F64_CONVERT_I64_S: u8 = 0xB9; +pub const F64_CONVERT_I64_U: u8 = 0xBA; +pub const F64_PROMOTE_F32: u8 = 0xBB; +pub const I32_REINTERPRET_F32: u8 = 0xBC; +pub const I64_REINTERPRET_F64: u8 = 0xBD; pub const F32_REINTERPRET_I32: u8 = 0xBE; +pub const F64_REINTERPRET_I64: u8 = 0xBF; diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 331ea64f..6892b52e 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -251,7 +251,7 @@ pub(super) fn run( } F32_CONST => { let constant = f32::from_bits(wasm.read_var_f32().unwrap_validated()); - trace!("Instruction: f32.const [] -> [{constant}]"); + trace!("Instruction: f32.const [] -> [{constant:.7}]"); stack.push_value(constant.into()); } I32_EQZ => { @@ -1033,41 +1033,6 @@ pub(super) fn run( trace!("Instruction: f32.copysign [{v1} {v2}] -> [{res}]"); stack.push_value(res.into()); } - F32_CONVERT_I32_S => { - let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let res: value::F32 = value::F32(v1 as f32); - - trace!("Instruction: f32.convert_i32_s [{v1}] -> [{res}]"); - stack.push_value(res.into()); - } - F32_CONVERT_I32_U => { - let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let res: value::F32 = value::F32(v1 as u32 as f32); - - trace!("Instruction: f32.convert_i32_u [{v1}] -> [{res}]"); - stack.push_value(res.into()); - } - F32_CONVERT_I64_S => { - let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); - let res: value::F32 = value::F32(v1 as f32); - - trace!("Instruction: f32.convert_i64_s [{v1}] -> [{res}]"); - stack.push_value(res.into()); - } - F32_CONVERT_I64_U => { - let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); - let res: value::F32 = value::F32(v1 as u64 as f32); - - trace!("Instruction: f32.convert_i64_u [{v1}] -> [{res}]"); - stack.push_value(res.into()); - } - F32_REINTERPRET_I32 => { - let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); - let res: value::F32 = value::F32::from_bits(v1 as u32); - - trace!("Instruction: f32.reinterpret_i32 [{v1}] -> [{res}]"); - stack.push_value(res.into()); - } F64_ABS => { let v1: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); @@ -1174,7 +1139,270 @@ pub(super) fn run( trace!("Instruction: f64.copysign [{v1} {v2}] -> [{res}]"); stack.push_value(res.into()); } + I32_WRAP_I64 => { + let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let res: i32 = v as i32; + + trace!("Instruction: i32.wrap_i64 [{v}] -> [{res}]"); + stack.push_value(res.into()); + } + I32_TRUNC_F32_S => { + let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F32(2147483648.0) || v <= value::F32(-2147483904.0) { + return Err(RuntimeError::UnrepresentableResult); + } + + let res: i32 = v.as_i32(); + + trace!("Instruction: i32.trunc_f32_s [{v:.7}] -> [{res}]"); + stack.push_value(res.into()); + } + I32_TRUNC_F32_U => { + let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F32(4294967296.0) || v <= value::F32(-1.0) { + return Err(RuntimeError::UnrepresentableResult); + } + + let res: i32 = v.as_u32() as i32; + + trace!("Instruction: i32.trunc_f32_u [{v:.7}] -> [{res}]"); + stack.push_value(res.into()); + } + + I32_TRUNC_F64_S => { + let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F64(2147483648.0) || v <= value::F64(-2147483649.0) { + return Err(RuntimeError::UnrepresentableResult); + } + + let res: i32 = v.as_i32(); + + trace!("Instruction: i32.trunc_f64_s [{v:.7}] -> [{res}]"); + stack.push_value(res.into()); + } + I32_TRUNC_F64_U => { + let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F64(4294967296.0) || v <= value::F64(-1.0) { + return Err(RuntimeError::UnrepresentableResult); + } + + let res: i32 = v.as_u32() as i32; + + trace!("Instruction: i32.trunc_f32_u [{v:.7}] -> [{res}]"); + stack.push_value(res.into()); + } + + I64_EXTEND_I32_S => { + let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let res: i64 = v as i64; + + trace!("Instruction: i64.extend_i32_s [{v}] -> [{res}]"); + stack.push_value(res.into()); + } + + I64_EXTEND_I32_U => { + let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + + let res: i64 = v as u32 as i64; + + trace!("Instruction: i64.extend_i32_u [{v}] -> [{res}]"); + stack.push_value(res.into()); + } + + I64_TRUNC_F32_S => { + let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F32(9223372036854775808.0) || v <= value::F32(-9223373136366403584.0) + { + return Err(RuntimeError::UnrepresentableResult); + } + let res: i64 = v.as_i64(); + + trace!("Instruction: i64.trunc_f32_s [{v:.7}] -> [{res}]"); + stack.push_value(res.into()); + } + I64_TRUNC_F32_U => { + let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F32(18446744073709551616.0) || v <= value::F32(-1.0) { + return Err(RuntimeError::UnrepresentableResult); + } + + let res: i64 = v.as_u64() as i64; + + trace!("Instruction: i64.trunc_f32_u [{v:.7}] -> [{res}]"); + stack.push_value(res.into()); + } + + I64_TRUNC_F64_S => { + let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F64(9223372036854775808.0) || v <= value::F64(-9223372036854777856.0) + { + return Err(RuntimeError::UnrepresentableResult); + } + + let res: i64 = v.as_i64(); + + trace!("Instruction: i64.trunc_f64_s [{v:.17}] -> [{res}]"); + stack.push_value(res.into()); + } + I64_TRUNC_F64_U => { + let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + if v.is_infinity() { + return Err(RuntimeError::UnrepresentableResult); + } + if v.is_nan() { + return Err(RuntimeError::BadConversionToInteger); + } + if v >= value::F64(18446744073709551616.0) || v <= value::F64(-1.0) { + return Err(RuntimeError::UnrepresentableResult); + } + + let res: i64 = v.as_u64() as i64; + + trace!("Instruction: i64.trunc_f64_u [{v:.17}] -> [{res}]"); + stack.push_value(res.into()); + } + F32_CONVERT_I32_S => { + let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let res: value::F32 = value::F32(v as f32); + + trace!("Instruction: f32.convert_i32_s [{v}] -> [{res}]"); + stack.push_value(res.into()); + } + F32_CONVERT_I32_U => { + let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let res: value::F32 = value::F32(v as u32 as f32); + + trace!("Instruction: f32.convert_i32_u [{v}] -> [{res}]"); + stack.push_value(res.into()); + } + F32_CONVERT_I64_S => { + let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let res: value::F32 = value::F32(v as f32); + + trace!("Instruction: f32.convert_i64_s [{v}] -> [{res}]"); + stack.push_value(res.into()); + } + F32_CONVERT_I64_U => { + let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let res: value::F32 = value::F32(v as u64 as f32); + + trace!("Instruction: f32.convert_i64_u [{v}] -> [{res}]"); + stack.push_value(res.into()); + } + F32_DEMOTE_F64 => { + let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + let res: value::F32 = v.as_f32(); + + trace!("Instruction: f32.demote_f64 [{v:.17}] -> [{res:.7}]"); + stack.push_value(res.into()); + } + F64_CONVERT_I32_S => { + let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let res: value::F64 = value::F64(v as f64); + + trace!("Instruction: f64.convert_i32_s [{v}] -> [{res:.17}]"); + stack.push_value(res.into()); + } + F64_CONVERT_I32_U => { + let v: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let res: value::F64 = value::F64(v as u32 as f64); + + trace!("Instruction: f64.convert_i32_u [{v}] -> [{res:.17}]"); + stack.push_value(res.into()); + } + F64_CONVERT_I64_S => { + let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let res: value::F64 = value::F64(v as f64); + + trace!("Instruction: f64.convert_i64_s [{v}] -> [{res:.17}]"); + stack.push_value(res.into()); + } + F64_CONVERT_I64_U => { + let v: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let res: value::F64 = value::F64(v as u64 as f64); + + trace!("Instruction: f64.convert_i64_u [{v}] -> [{res:.17}]"); + stack.push_value(res.into()); + } + F64_PROMOTE_F32 => { + let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + let res: value::F64 = v.as_f32(); + + trace!("Instruction: f64.promote_f32 [{v:.7}] -> [{res:.17}]"); + stack.push_value(res.into()); + } + I32_REINTERPRET_F32 => { + let v: value::F32 = stack.pop_value(ValType::NumType(NumType::F32)).into(); + let res: i32 = v.reinterpret_as_i32(); + + trace!("Instruction: i32.reinterpret_f32 [{v:.7}] -> [{res}]"); + stack.push_value(res.into()); + } + I64_REINTERPRET_F64 => { + let v: value::F64 = stack.pop_value(ValType::NumType(NumType::F64)).into(); + let res: i64 = v.reinterpret_as_i64(); + + trace!("Instruction: i64.reinterpret_f64 [{v:.17}] -> [{res}]"); + stack.push_value(res.into()); + } + F32_REINTERPRET_I32 => { + let v1: i32 = stack.pop_value(ValType::NumType(NumType::I32)).into(); + let res: value::F32 = value::F32::from_bits(v1 as u32); + + trace!("Instruction: f32.reinterpret_i32 [{v1}] -> [{res:.7}]"); + stack.push_value(res.into()); + } + F64_REINTERPRET_I64 => { + let v1: i64 = stack.pop_value(ValType::NumType(NumType::I64)).into(); + let res: value::F64 = value::F64::from_bits(v1 as u64); + + trace!("Instruction: f64.reinterpret_i64 [{v1}] -> [{res:.17}]"); + stack.push_value(res.into()); + } other => { trace!("Unknown instruction {other:#x}, skipping.."); } diff --git a/src/execution/value.rs b/src/execution/value.rs index f17f8acf..c7ae8e25 100644 --- a/src/execution/value.rs +++ b/src/execution/value.rs @@ -23,6 +23,32 @@ impl PartialEq for F32 { } } +// impl PartialOrd for F32 { +// fn ge(&self, other: &Self) -> bool { +// self.0 >= other.0 +// } +// fn gt(&self, other: &Self) -> bool { +// self.0 > other.0 +// } +// fn le(&self, other: &Self) -> bool { +// self.0 <= other.0 +// } +// fn lt(&self, other: &Self) -> bool { +// self.0 < other.0 +// } +// fn partial_cmp(&self, other: &Self) -> Option { +// if self.0.is_nan() && other.0.is_nan() { +// Some(core::cmp::Ordering::Equal) +// } else if self.0 == other.0 { +// Some(core::cmp::Ordering::Equal) +// } else if self.0 < other.0 { +// Some(core::cmp::Ordering::Less) +// } else { +// Some(core::cmp::Ordering::Greater) +// } +// } +// } + impl Add for F32 { type Output = Self; fn add(self, rhs: Self) -> Self::Output { @@ -101,6 +127,28 @@ impl F32 { pub fn is_nan(&self) -> bool { self.0.is_nan() } + pub fn is_infinity(&self) -> bool { + self.0.is_infinite() + } + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } + pub fn as_u32(&self) -> u32 { + self.0 as u32 + } + pub fn as_i64(&self) -> i64 { + self.0 as i64 + } + pub fn as_u64(&self) -> u64 { + self.0 as u64 + } + pub fn as_f32(&self) -> F64 { + F64(self.0 as f64) + } + pub fn reinterpret_as_i32(&self) -> i32 { + self.0.to_bits() as i32 + } } #[derive(Clone, Debug, Copy, PartialOrd)] @@ -197,6 +245,28 @@ impl F64 { pub fn is_nan(&self) -> bool { self.0.is_nan() } + pub fn is_infinity(&self) -> bool { + self.0.is_infinite() + } + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } + pub fn as_u32(&self) -> u32 { + self.0 as u32 + } + pub fn as_i64(&self) -> i64 { + self.0 as i64 + } + pub fn as_u64(&self) -> u64 { + self.0 as u64 + } + pub fn as_f32(&self) -> F32 { + F32(self.0 as f32) + } + pub fn reinterpret_as_i64(&self) -> i64 { + self.0.to_bits() as i64 + } } /// A value at runtime. This is essentially a duplicate of [ValType] just with additional values. diff --git a/src/validation/code.rs b/src/validation/code.rs index fe52efa9..cfb452f3 100644 --- a/src/validation/code.rs +++ b/src/validation/code.rs @@ -353,6 +353,42 @@ fn read_instructions( value_stack.push(ValType::NumType(NumType::I64)); } + I32_WRAP_I64 => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::I64))?; + + value_stack.push(ValType::NumType(NumType::I32)); + } + + I32_TRUNC_F32_S | I32_TRUNC_F32_U | I32_REINTERPRET_F32 => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::F32))?; + + value_stack.push(ValType::NumType(NumType::I32)); + } + + I32_TRUNC_F64_S | I32_TRUNC_F64_U => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::F64))?; + + value_stack.push(ValType::NumType(NumType::I32)); + } + + I64_EXTEND_I32_S | I64_EXTEND_I32_U => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::I32))?; + + value_stack.push(ValType::NumType(NumType::I64)); + } + + I64_TRUNC_F32_S | I64_TRUNC_F32_U => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::F32))?; + + value_stack.push(ValType::NumType(NumType::I64)); + } + + I64_TRUNC_F64_S | I64_TRUNC_F64_U | I64_REINTERPRET_F64 => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::F64))?; + + value_stack.push(ValType::NumType(NumType::I64)); + } + F32_CONVERT_I32_S | F32_CONVERT_I32_U | F32_REINTERPRET_I32 => { assert_pop_value_stack(value_stack, ValType::NumType(NumType::I32))?; @@ -364,6 +400,30 @@ fn read_instructions( value_stack.push(ValType::NumType(NumType::F32)); } + + F32_DEMOTE_F64 => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::F64))?; + + value_stack.push(ValType::NumType(NumType::F32)); + } + + F64_CONVERT_I32_S | F64_CONVERT_I32_U => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::I32))?; + + value_stack.push(ValType::NumType(NumType::F64)); + } + + F64_CONVERT_I64_S | F64_CONVERT_I64_U | F64_REINTERPRET_I64 => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::I64))?; + + value_stack.push(ValType::NumType(NumType::F64)); + } + + F64_PROMOTE_F32 => { + assert_pop_value_stack(value_stack, ValType::NumType(NumType::F32))?; + + value_stack.push(ValType::NumType(NumType::F64)); + } _ => return Err(Error::InvalidInstr(first_instr_byte)), } } diff --git a/tests/conversions.rs b/tests/conversions.rs new file mode 100644 index 00000000..90b09f5e --- /dev/null +++ b/tests/conversions.rs @@ -0,0 +1,2008 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ +use core::{f32, f64}; + +use hexf::{hexf32, hexf64}; +use wasm::{validate, RuntimeError, RuntimeInstance}; + +const WAT: &'static str = r#" + (module + (func (export "{{0}}") (param $x {{1}}) (result {{2}}) + local.get $x + {{0}}) + ) +"#; + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i32_wrap_i64_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.wrap_i64") + .replace("{{1}}", "i32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i32.wrap_i64 implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L51) +#[test_log::test] +pub fn i32_wrap_i64() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.wrap_i64") + .replace("{{1}}", "i64") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1_i64).unwrap()); + assert_eq!(-100000, instance.invoke_func(0, -100000_i64).unwrap()); + assert_eq!( + 0x80000000_u32 as i32, + instance.invoke_func(0, 0x80000000_i64).unwrap() + ); + assert_eq!( + 0x7fffffff, + instance + .invoke_func(0, 0xffffffff7fffffff_u64 as i64) + .unwrap() + ); + assert_eq!( + 0x00000000, + instance + .invoke_func(0, 0xffffffff00000000_u64 as i64) + .unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance + .invoke_func(0, 0xfffffffeffffffff_u64 as i64) + .unwrap() + ); + assert_eq!( + 0x00000001, + instance + .invoke_func(0, 0xffffffff00000001_u64 as i64) + .unwrap() + ); + assert_eq!(0, instance.invoke_func(0, 0_i64).unwrap()); + assert_eq!( + 0x9abcdef0_u32 as i32, + instance.invoke_func(0, 1311768467463790320_i64).unwrap() + ); + assert_eq!( + 0xffffffff_u32 as i32, + instance.invoke_func(0, 0x00000000ffffffff_i64).unwrap() + ); + assert_eq!( + 0x00000000, + instance.invoke_func(0, 0x0000000100000000_i64).unwrap() + ); + assert_eq!( + 0x00000001, + instance.invoke_func(0, 0x0000000100000001_i64).unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i32_trunc_f32_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_f32_s") + .replace("{{1}}", "i32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i32.trunc_f32_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L64) +#[test_log::test] +pub fn i32_trunc_f32_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_f32_s") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0, instance.invoke_func(0, 0.0_f32).unwrap()); + assert_eq!(0, instance.invoke_func(0, -0.0_f32).unwrap()); + assert_eq!(0, instance.invoke_func(0, hexf32!("0x1p-149")).unwrap()); + assert_eq!(0, instance.invoke_func(0, hexf32!("-0x1p-149")).unwrap()); + assert_eq!(1, instance.invoke_func(0, 1.0_f32).unwrap()); + assert_eq!( + 1, + instance.invoke_func(0, hexf32!("0x1.19999ap+0")).unwrap() + ); + assert_eq!(1, instance.invoke_func(0, 1.5_f32).unwrap()); + assert_eq!(-1, instance.invoke_func(0, -1.0_f32).unwrap()); + assert_eq!( + -1, + instance.invoke_func(0, hexf32!("-0x1.19999ap+0")).unwrap() + ); + assert_eq!(-1, instance.invoke_func(0, -1.5_f32).unwrap()); + assert_eq!(-1, instance.invoke_func(0, -1.9_f32).unwrap()); + assert_eq!(-2, instance.invoke_func(0, -2.0_f32).unwrap()); + assert_eq!( + 2147483520, + instance.invoke_func(0, 2147483520.0_f32).unwrap() + ); + assert_eq!( + 2147483648_u32 as i32, + instance.invoke_func(0, -2147483648.0_f32).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 2147483648.0_f32) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, -2147483904.0_f32) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f32::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f32::NAN) + .err() + .unwrap() + ); +} + +/// A function to test the i32.trunc_f32_u implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L87C1-L107C99) +#[test_log::test] +pub fn i32_trunc_f32_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_f32_u") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0, instance.invoke_func(0, 0.0_f32).unwrap()); + assert_eq!(0, instance.invoke_func(0, -0.0_f32).unwrap()); + assert_eq!(0, instance.invoke_func(0, hexf32!("0x1p-149")).unwrap()); + assert_eq!(0, instance.invoke_func(0, hexf32!("-0x1p-149")).unwrap()); + assert_eq!(1, instance.invoke_func(0, 1.0_f32).unwrap()); + assert_eq!( + 1, + instance.invoke_func(0, hexf32!("0x1.19999ap+0")).unwrap() + ); + assert_eq!(1, instance.invoke_func(0, 1.5_f32).unwrap()); + assert_eq!(1, instance.invoke_func(0, 1.9_f32).unwrap()); + assert_eq!(2, instance.invoke_func(0, 2.0_f32).unwrap()); + assert_eq!( + -2147483648, + instance.invoke_func(0, 2147483648_f32).unwrap() + ); + assert_eq!(-256, instance.invoke_func(0, 4294967040.0_f32).unwrap()); + assert_eq!( + 0, + instance.invoke_func(0, hexf32!("-0x1.ccccccp-1")).unwrap() + ); + assert_eq!( + 0, + instance.invoke_func(0, hexf32!("-0x1.fffffep-1")).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 4294967296.0_f32) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance.invoke_func::(0, -1.0).err().unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f32::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f32::NAN) + .err() + .unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i32_trunc_f64_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_f64_s") + .replace("{{1}}", "i32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i32.trunc_f64_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L109) +#[test_log::test] +pub fn i32_trunc_f64_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_f64_s") + .replace("{{1}}", "f64") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0, instance.invoke_func(0, 0.0_f64).unwrap()); + assert_eq!(0, instance.invoke_func(0, -0.0_f64).unwrap()); + assert_eq!( + 0, + instance + .invoke_func(0, hexf64!("0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke_func(0, hexf64!("-0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!(1, instance.invoke_func(0, 1.0_f64).unwrap()); + assert_eq!( + 1, + instance + .invoke_func(0, hexf64!("0x1.199999999999ap+0")) + .unwrap() + ); + assert_eq!(1, instance.invoke_func(0, 1.5_f64).unwrap()); + assert_eq!(-1, instance.invoke_func(0, -1.0_f64).unwrap()); + assert_eq!( + -1, + instance + .invoke_func(0, hexf64!("-0x1.199999999999ap+0")) + .unwrap() + ); + assert_eq!(-1, instance.invoke_func(0, -1.5_f64).unwrap()); + assert_eq!(-1, instance.invoke_func(0, -1.9_f64).unwrap()); + assert_eq!(-2, instance.invoke_func(0, -2.0_f64).unwrap()); + assert_eq!( + 2147483647, + instance.invoke_func(0, 2147483647.0_f64).unwrap() + ); + assert_eq!( + -2147483648, + instance.invoke_func(0, -2147483648.0_f64).unwrap() + ); + assert_eq!( + -2147483648, + instance.invoke_func(0, -2147483648.9_f64).unwrap() + ); + assert_eq!( + -2147483648, + instance.invoke_func(0, -2147483648.9_f64).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 2147483648.0) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, -2147483649.0) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f64::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f64::NAN) + .err() + .unwrap() + ); +} + +/// A function to test the i32.trunc_f64_u implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L134) +#[test_log::test] +pub fn i32_trunc_f64_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.trunc_f64_u") + .replace("{{1}}", "f64") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0, instance.invoke_func(0, 0.0_f64).unwrap()); + assert_eq!(0, instance.invoke_func(0, -0.0_f64).unwrap()); + assert_eq!( + 0, + instance + .invoke_func(0, hexf64!("0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke_func(0, hexf64!("-0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!(1, instance.invoke_func(0, 1.0_f64).unwrap()); + assert_eq!( + 1, + instance + .invoke_func(0, hexf64!("0x1.199999999999ap+0")) + .unwrap() + ); + assert_eq!(1, instance.invoke_func(0, 1.5_f64).unwrap()); + assert_eq!(1, instance.invoke_func(0, 1.9_f64).unwrap()); + assert_eq!(2, instance.invoke_func(0, 2.0_f64).unwrap()); + assert_eq!( + -2147483648, + instance.invoke_func(0, 2147483648_f64).unwrap() + ); + assert_eq!(-1, instance.invoke_func(0, 4294967295.0_f64).unwrap()); + assert_eq!( + 0, + instance + .invoke_func(0, hexf64!("-0x1.ccccccccccccdp-1")) + .unwrap() + ); + assert_eq!( + 0, + instance + .invoke_func(0, hexf64!("-0x1.fffffffffffffp-1")) + .unwrap() + ); + assert_eq!(100000000, instance.invoke_func(0, 1e8_f64).unwrap()); + assert_eq!(0, instance.invoke_func(0, -0.9).unwrap()); + assert_eq!( + 4294967295_u32 as i32, + instance.invoke_func(0, 4294967295.9_f64).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 4294967296.0) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance.invoke_func::(0, -1.0).err().unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance.invoke_func::(0, 1e16).err().unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance.invoke_func::(0, 1e30).err().unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 9223372036854775808_f64) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f64::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f64::NAN) + .err() + .unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i64_extend_i32_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.extend_i32_u") + .replace("{{1}}", "i64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i64.extend_i32_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L37) +#[test_log::test] +pub fn i64_extend_i32_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.extend_i32_s") + .replace("{{1}}", "i32") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0_i64, instance.invoke_func(0, 0).unwrap()); + assert_eq!(10000_i64, instance.invoke_func(0, 10000).unwrap()); + assert_eq!(-10000_i64, instance.invoke_func(0, -10000).unwrap()); + assert_eq!(-1_i64, instance.invoke_func(0, -1).unwrap()); + assert_eq!( + 0x000000007fffffff_i64, + instance.invoke_func(0, 0x7fffffff).unwrap() + ); + assert_eq!( + 0xffffffff80000000_u64 as i64, + instance.invoke_func(0, 0x80000000_u32 as i32).unwrap() + ); +} + +/// A function to test the i64.extend_i32_u implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L44C1-L49C98) +#[test_log::test] +pub fn i64_extend_i32_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.extend_i32_u") + .replace("{{1}}", "i32") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0_i64, instance.invoke_func(0, 0).unwrap()); + assert_eq!(10000_i64, instance.invoke_func(0, 10000).unwrap()); + assert_eq!( + 0x00000000ffffd8f0_i64, + instance.invoke_func(0, -10000).unwrap() + ); + assert_eq!(0xffffffff_i64, instance.invoke_func(0, -1).unwrap()); + assert_eq!( + 0x000000007fffffff_i64, + instance.invoke_func(0, 0x7fffffff).unwrap() + ); + assert_eq!( + 0x0000000080000000_i64, + instance.invoke_func(0, 0x80000000_u32 as i32).unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i64_trunc_f32_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_f32_s") + .replace("{{1}}", "i64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i64.trunc_f32_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L162) +#[test_log::test] +pub fn i64_trunc_f32_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_f32_s") + .replace("{{1}}", "f32") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0_i64, instance.invoke_func(0, 0.0_f32).unwrap()); + assert_eq!(0_i64, instance.invoke_func(0, -0.0_f32).unwrap()); + assert_eq!(0_i64, instance.invoke_func(0, hexf32!("0x1p-149")).unwrap()); + assert_eq!( + 0_i64, + instance.invoke_func(0, hexf32!("-0x1p-149")).unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.0_f32).unwrap()); + assert_eq!( + 1_i64, + instance.invoke_func(0, hexf32!("0x1.19999ap+0")).unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.5_f32).unwrap()); + assert_eq!(-1_i64, instance.invoke_func(0, -1.0_f32).unwrap()); + assert_eq!( + -1_i64, + instance.invoke_func(0, hexf32!("-0x1.19999ap+0")).unwrap() + ); + assert_eq!(-1_i64, instance.invoke_func(0, -1.5_f32).unwrap()); + assert_eq!(-1_i64, instance.invoke_func(0, -1.9_f32).unwrap()); + assert_eq!(-2_i64, instance.invoke_func(0, -2.0_f32).unwrap()); + assert_eq!( + 4294967296_i64, + instance.invoke_func(0, 4294967296_f32).unwrap() + ); + assert_eq!( + -4294967296_i64, + instance.invoke_func(0, -4294967296_f32).unwrap() + ); + assert_eq!( + 9223371487098961920_i64, + instance.invoke_func(0, 9223371487098961920.0_f32).unwrap() + ); + assert_eq!( + -9223372036854775808_i64, + instance.invoke_func(0, -9223372036854775808.0_f32).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 9223372036854775808.0_f32) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, -9223373136366403584.0_f32) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f32::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f32::NAN) + .err() + .unwrap() + ); +} + +/// A function to test the i64.trunc_f32_u implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L187) +#[test_log::test] +pub fn i64_trunc_f32_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_f32_u") + .replace("{{1}}", "f32") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0_i64, instance.invoke_func(0, 0.0_f32).unwrap()); + assert_eq!(0_i64, instance.invoke_func(0, -0.0_f32).unwrap()); + assert_eq!(0_i64, instance.invoke_func(0, hexf32!("0x1p-149")).unwrap()); + assert_eq!( + 0_i64, + instance.invoke_func(0, hexf32!("-0x1p-149")).unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.0_f32).unwrap()); + assert_eq!( + 1_i64, + instance.invoke_func(0, hexf32!("0x1.19999ap+0")).unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.5_f32).unwrap()); + assert_eq!( + 4294967296_i64, + instance.invoke_func(0, 4294967296_f32).unwrap() + ); + assert_eq!( + -1099511627776_i64, + instance.invoke_func(0, 18446742974197923840.0_f32).unwrap() + ); + assert_eq!( + 1_i64, + instance.invoke_func(0, hexf32!("0x1.19999ap+0")).unwrap() + ); + assert_eq!( + 0_i64, + instance.invoke_func(0, hexf32!("-0x1.fffffep-1")).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 18446744073709551616.0_f32) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance.invoke_func::(0, -1.0_f32).err().unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f32::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f32::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f32::NAN) + .err() + .unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i64_trunc_f64_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_f64_s") + .replace("{{1}}", "i64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i64.trunc_f64_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L207) +#[test_log::test] +pub fn i64_trunc_f64_s() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_f64_s") + .replace("{{1}}", "f64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0_i64, instance.invoke_func(0, 0.0_f64).unwrap()); + assert_eq!(0_i64, instance.invoke_func(0, -0.0_f64).unwrap()); + assert_eq!( + 0_i64, + instance + .invoke_func(0, hexf64!("0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke_func(0, hexf64!("-0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.0_f64).unwrap()); + assert_eq!( + 1_i64, + instance + .invoke_func(0, hexf64!("0x1.199999999999ap+0")) + .unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.5_f64).unwrap()); + assert_eq!(-1_i64, instance.invoke_func(0, -1.0_f64).unwrap()); + assert_eq!( + -1_i64, + instance + .invoke_func(0, hexf64!("-0x1.199999999999ap+0")) + .unwrap() + ); + assert_eq!(-1_i64, instance.invoke_func(0, -1.5_f64).unwrap()); + assert_eq!(-1_i64, instance.invoke_func(0, -1.9_f64).unwrap()); + assert_eq!(-2_i64, instance.invoke_func(0, -2.0_f64).unwrap()); + assert_eq!( + 4294967296_i64, + instance.invoke_func(0, 4294967296_f64).unwrap() + ); + assert_eq!( + -4294967296_i64, + instance.invoke_func(0, -4294967296_f64).unwrap() + ); + assert_eq!( + 9223372036854774784_i64, + instance.invoke_func(0, 9223372036854774784.0_f64).unwrap() + ); + assert_eq!( + -9223372036854775808_i64, + instance.invoke_func(0, -9223372036854775808.0_f64).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 9223372036854775808.0_f64) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, -9223372036854777856.0_f64) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f64::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f64::NAN) + .err() + .unwrap() + ); +} + +/// A function to test the i64.trunc_f64_u implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L232) +#[test_log::test] +pub fn i64_trunc_f64_u() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.trunc_f64_u") + .replace("{{1}}", "f64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0_i64, instance.invoke_func(0, 0.0_f64).unwrap()); + assert_eq!(0_i64, instance.invoke_func(0, -0.0_f64).unwrap()); + assert_eq!( + 0_i64, + instance + .invoke_func(0, hexf64!("0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke_func(0, hexf64!("-0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.0_f64).unwrap()); + assert_eq!( + 1_i64, + instance + .invoke_func(0, hexf64!("0x1.199999999999ap+0")) + .unwrap() + ); + assert_eq!(1_i64, instance.invoke_func(0, 1.5_f64).unwrap()); + assert_eq!( + 0xffffffff_i64, + instance.invoke_func(0, 4294967295_f64).unwrap() + ); + assert_eq!( + 0x100000000_i64, + instance.invoke_func(0, 4294967296_f64).unwrap() + ); + assert_eq!( + -2048_i64, + instance.invoke_func(0, 18446744073709549568.0_f64).unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke_func(0, hexf64!("-0x1.ccccccccccccdp-1")) + .unwrap() + ); + assert_eq!( + 0_i64, + instance + .invoke_func(0, hexf64!("-0x1.fffffffffffffp-1")) + .unwrap() + ); + assert_eq!(100000000_i64, instance.invoke_func(0, 1e8_f64).unwrap()); + assert_eq!( + 10000000000000000_i64, + instance.invoke_func(0, 1e16_f64).unwrap() + ); + assert_eq!( + -9223372036854775808_i64, + instance.invoke_func(0, 9223372036854775808_f64).unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, 18446744073709551616.0_f64) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance.invoke_func::(0, -1_f64).err().unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::UnrepresentableResult, + instance + .invoke_func::(0, f64::NEG_INFINITY) + .err() + .unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance.invoke_func::(0, f64::NAN).err().unwrap() + ); + assert_eq!( + RuntimeError::BadConversionToInteger, + instance + .invoke_func::(0, -f64::NAN) + .err() + .unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f32_convert_i32_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.convert_i32_s") + .replace("{{1}}", "i64") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the f32.convert_i32_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L256) +#[test_log::test] +pub fn f32_convert_i32_s() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.convert_i32_s") + .replace("{{1}}", "i32") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f32, instance.invoke_func(0, 1).unwrap()); + assert_eq!(-1.0_f32, instance.invoke_func(0, -1).unwrap()); + assert_eq!(0.0_f32, instance.invoke_func(0, 0).unwrap()); + assert_eq!(2147483648_f32, instance.invoke_func(0, 2147483647).unwrap()); + assert_eq!( + -2147483648_f32, + instance.invoke_func(0, -2147483648).unwrap() + ); + assert_eq!( + hexf32!("0x1.26580cp+30"), + instance.invoke_func(0, 1234567890).unwrap() + ); +} + +/// A function to test the f32.convert_i32_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L495) +#[test_log::test] +pub fn f32_convert_i32_u() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.convert_i32_u") + .replace("{{1}}", "i32") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f32, instance.invoke_func(0, 1).unwrap()); + assert_eq!(0.0_f32, instance.invoke_func(0, 0).unwrap()); + assert_eq!(2147483648_f32, instance.invoke_func(0, 2147483647).unwrap()); + assert_eq!( + 2147483648_f32, + instance.invoke_func(0, -2147483648).unwrap() + ); + assert_eq!( + hexf32!("0x1.234568p+28"), + instance.invoke_func(0, 0x12345678).unwrap() + ); + assert_eq!( + 4294967296.0_f32, + instance.invoke_func(0, 0xffffffff_u32 as i32).unwrap() + ); + assert_eq!( + hexf32!("0x1.000000p+31"), + instance.invoke_func(0, 0x80000080_u32 as i32).unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+31"), + instance.invoke_func(0, 0x80000081_u32 as i32).unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+31"), + instance.invoke_func(0, 0x80000082_u32 as i32).unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffcp+31"), + instance.invoke_func(0, 0xfffffe80_u32 as i32).unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffep+31"), + instance.invoke_func(0, 0xfffffe81_u32 as i32).unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffep+31"), + instance.invoke_func(0, 0xfffffe82_u32 as i32).unwrap() + ); + assert_eq!(16777216.0_f32, instance.invoke_func(0, 16777217).unwrap()); + assert_eq!(16777220.0_f32, instance.invoke_func(0, 16777219).unwrap()); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f32_convert_i64_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.convert_i64_s") + .replace("{{1}}", "i32") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the f32.convert_i64_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L459) +#[test_log::test] +pub fn f32_convert_i64_s() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.convert_i64_s") + .replace("{{1}}", "i64") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f32, instance.invoke_func(0, 1_i64).unwrap()); + assert_eq!(-1.0_f32, instance.invoke_func(0, -1_i64).unwrap()); + assert_eq!(0.0_f32, instance.invoke_func(0, 0_i64).unwrap()); + assert_eq!( + 9223372036854775807_f32, + instance.invoke_func(0, 9223372036854775807_i64).unwrap() + ); + assert_eq!( + -9223372036854775808_f32, + instance.invoke_func(0, -9223372036854775808_i64).unwrap() + ); + assert_eq!( + hexf32!("0x1.1db9e8p+48"), + instance.invoke_func(0, 314159265358979_i64).unwrap() + ); + assert_eq!( + 16777216.0_f32, + instance.invoke_func(0, 16777217_i64).unwrap() + ); + assert_eq!( + -16777216.0_f32, + instance.invoke_func(0, -16777217_i64).unwrap() + ); + assert_eq!( + 16777220.0_f32, + instance.invoke_func(0, 16777219_i64).unwrap() + ); + assert_eq!( + -16777220.0_f32, + instance.invoke_func(0, -16777219_i64).unwrap() + ); + + assert_eq!( + hexf32!("0x1.fffffep+62"), + instance.invoke_func(0, 0x7fffff4000000001_i64).unwrap() + ); + assert_eq!( + hexf32!("-0x1.fffffep+62"), + instance + .invoke_func(0, 0x8000004000000001_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+53"), + instance.invoke_func(0, 0x0020000020000001_i64).unwrap() + ); + assert_eq!( + hexf32!("-0x1.000002p+53"), + instance + .invoke_func(0, 0xffdfffffdfffffff_u64 as i64) + .unwrap() + ); +} + +/// A function to test the f32.convert_i64_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L459) +#[test_log::test] +pub fn f32_convert_i64_u() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.convert_i64_u") + .replace("{{1}}", "i64") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f32, instance.invoke_func(0, 1_i64).unwrap()); + assert_eq!(0.0_f32, instance.invoke_func(0, 0_i64).unwrap()); + assert_eq!( + 9223372036854775807_f32, + instance.invoke_func(0, 9223372036854775807_i64).unwrap() + ); + assert_eq!( + 9223372036854775808_f32, + instance.invoke_func(0, -9223372036854775808_i64).unwrap() + ); + assert_eq!( + 18446744073709551616.0_f32, + instance + .invoke_func(0, 0xffffffffffffffff_u64 as i64) + .unwrap() + ); + // ;; Test rounding directions. + assert_eq!( + 16777216.0_f32, + instance.invoke_func(0, 16777217_i64).unwrap() + ); + assert_eq!( + 16777220.0_f32, + instance.invoke_func(0, 16777219_i64).unwrap() + ); + + assert_eq!( + hexf32!("0x1.000002p+53"), + instance.invoke_func(0, 0x0020000020000001_i64).unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffep+62"), + instance.invoke_func(0, 0x7fffffbfffffffff_i64).unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+63"), + instance + .invoke_func(0, 0x8000008000000001_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffep+63"), + instance + .invoke_func(0, 0xfffffe8000000001_u64 as i64) + .unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f32_demote_f64_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.demote_f64") + .replace("{{1}}", "f32") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0.0_f32, instance.invoke_func(0, 0.0_f64).unwrap()); +} + +/// A function to test the f32.demote_f64 implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L565) +#[test_log::test] +pub fn f32_demote_f64() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.demote_f64") + .replace("{{1}}", "f64") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0.0_f32, instance.invoke_func(0, 0.0_f64).unwrap()); + assert_eq!(-0.0_f32, instance.invoke_func(0, -0.0_f64).unwrap()); + assert_eq!( + 0.0_f32, + instance + .invoke_func(0, hexf64!("0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!( + -0.0_f32, + instance + .invoke_func(0, hexf64!("-0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!(1.0_f32, instance.invoke_func(0, 1.0_f64).unwrap()); + assert_eq!(-1.0_f32, instance.invoke_func(0, -1.0_f64).unwrap()); + assert_eq!( + hexf32!("0x1p-126"), + instance + .invoke_func(0, hexf64!("0x1.fffffe0000000p-127")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1p-126"), + instance + .invoke_func(0, hexf64!("-0x1.fffffe0000000p-127")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffcp-127"), + instance + .invoke_func(0, hexf64!("0x1.fffffdfffffffp-127")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1.fffffcp-127"), + instance + .invoke_func(0, hexf64!("-0x1.fffffdfffffffp-127")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1p-149"), + instance.invoke_func(0, hexf64!("0x1p-149")).unwrap() + ); + assert_eq!( + hexf32!("-0x1p-149"), + instance.invoke_func(0, hexf64!("-0x1p-149")).unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffcp+127"), + instance + .invoke_func(0, hexf64!("0x1.fffffd0000000p+127")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1.fffffcp+127"), + instance + .invoke_func(0, hexf64!("-0x1.fffffd0000000p+127")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffep+127"), + instance + .invoke_func(0, hexf64!("0x1.fffffd0000001p+127")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1.fffffep+127"), + instance + .invoke_func(0, hexf64!("-0x1.fffffd0000001p+127")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffep+127"), + instance.invoke_func(0, hexf64!("0x1.fffffep+127")).unwrap() + ); + assert_eq!( + hexf32!("-0x1.fffffep+127"), + instance + .invoke_func(0, hexf64!("-0x1.fffffep+127")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.fffffep+127"), + instance + .invoke_func(0, hexf64!("0x1.fffffefffffffp+127")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1.fffffep+127"), + instance + .invoke_func(0, hexf64!("-0x1.fffffefffffffp+127")) + .unwrap() + ); + assert_eq!( + f32::INFINITY, + instance.invoke_func(0, hexf64!("0x1.ffffffp+127")).unwrap() + ); + assert_eq!( + f32::NEG_INFINITY, + instance + .invoke_func(0, hexf64!("-0x1.ffffffp+127")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1p-119"), + instance.invoke_func(0, hexf64!("0x1p-119")).unwrap() + ); + assert_eq!( + hexf32!("0x1.8f867ep+125"), + instance.invoke_func(0, hexf64!("0x1.8f867ep+125")).unwrap() + ); + assert_eq!( + f32::INFINITY, + instance.invoke_func(0, f64::INFINITY).unwrap() + ); + assert_eq!( + f32::NEG_INFINITY, + instance.invoke_func(0, f64::NEG_INFINITY).unwrap() + ); + assert_eq!( + 1.0_f32, + instance + .invoke_func(0, hexf64!("0x1.0000000000001p+0")) + .unwrap() + ); + assert_eq!( + 1.0_f32, + instance + .invoke_func(0, hexf64!("0x1.fffffffffffffp-1")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000000p+0"), + instance + .invoke_func(0, hexf64!("0x1.0000010000000p+0")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+0"), + instance + .invoke_func(0, hexf64!("0x1.0000010000001p+0")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+0"), + instance + .invoke_func(0, hexf64!("0x1.000002fffffffp+0")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000004p+0"), + instance + .invoke_func(0, hexf64!("0x1.0000030000000p+0")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000004p+0"), + instance + .invoke_func(0, hexf64!("0x1.0000050000000p+0")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.0p+24"), + instance + .invoke_func(0, hexf64!("0x1.0000010000000p+24")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+24"), + instance + .invoke_func(0, hexf64!("0x1.0000010000001p+24")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000002p+24"), + instance + .invoke_func(0, hexf64!("0x1.000002fffffffp+24")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.000004p+24"), + instance + .invoke_func(0, hexf64!("0x1.0000030000000p+24")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.4eae5p+108"), + instance + .invoke_func(0, hexf64!("0x1.4eae4f7024c7p+108")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.a12e72p-113"), + instance + .invoke_func(0, hexf64!("0x1.a12e71e358685p-113")) + .unwrap() + ); + assert_eq!( + hexf32!("0x1.cb9834p-127"), + instance + .invoke_func(0, hexf64!("0x1.cb98354d521ffp-127")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1.6972b4p+1"), + instance + .invoke_func(0, hexf64!("-0x1.6972b30cfb562p+1")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1.bedbe4p+112"), + instance + .invoke_func(0, hexf64!("-0x1.bedbe4819d4c4p+112")) + .unwrap() + ); + // assert_eq!(f32::NAN, instance.invoke_func(0, f64::NAN).unwrap()); + // (assert_return (invoke "f32.demote_f64" (f64.const nan)) (f32.const nan:canonical)) + // (assert_return (invoke "f32.demote_f64" (f64.const nan:0x4000000000000)) (f32.const nan:arithmetic)) + // assert_eq!(f32::NAN, instance.invoke_func(0, -f64::NAN).unwrap()); + // (assert_return (invoke "f32.demote_f64" (f64.const -nan)) (f32.const nan:canonical)) + // (assert_return (invoke "f32.demote_f64" (f64.const -nan:0x4000000000000)) (f32.const nan:arithmetic)) + assert_eq!( + 0.0_f32, + instance.invoke_func(0, hexf64!("0x1p-1022")).unwrap() + ); + assert_eq!( + -0.0_f32, + instance.invoke_func(0, hexf64!("-0x1p-1022")).unwrap() + ); + assert_eq!( + 0.0_f32, + instance.invoke_func(0, hexf64!("0x1.0p-150")).unwrap() + ); + assert_eq!( + -0.0_f32, + instance.invoke_func(0, hexf64!("-0x1.0p-150")).unwrap() + ); + assert_eq!( + hexf32!("0x1p-149"), + instance + .invoke_func(0, hexf64!("0x1.0000000000001p-150")) + .unwrap() + ); + assert_eq!( + hexf32!("-0x1p-149"), + instance + .invoke_func(0, hexf64!("-0x1.0000000000001p-150")) + .unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f64_convert_i32_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.convert_i32_s") + .replace("{{1}}", "i64") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the f64.convert_i32_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L476) +#[test_log::test] +pub fn f64_convert_i32_s() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.convert_i32_s") + .replace("{{1}}", "i32") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f64, instance.invoke_func(0, 1).unwrap()); + assert_eq!(-1.0_f64, instance.invoke_func(0, -1).unwrap()); + assert_eq!(0.0_f64, instance.invoke_func(0, 0).unwrap()); + assert_eq!(2147483647_f64, instance.invoke_func(0, 2147483647).unwrap()); + assert_eq!( + -2147483648_f64, + instance.invoke_func(0, -2147483648).unwrap() + ); + assert_eq!(987654321_f64, instance.invoke_func(0, 987654321).unwrap()); +} + +/// A function to test the f64.convert_i32_u implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L525) +#[test_log::test] +pub fn f64_convert_i32_u() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.convert_i32_u") + .replace("{{1}}", "i32") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f64, instance.invoke_func(0, 1).unwrap()); + assert_eq!(0.0_f64, instance.invoke_func(0, 0).unwrap()); + assert_eq!(2147483647_f64, instance.invoke_func(0, 2147483647).unwrap()); + assert_eq!( + 2147483648_f64, + instance.invoke_func(0, -2147483648).unwrap() + ); + assert_eq!( + 4294967295.0_f64, + instance.invoke_func(0, 0xffffffff_u32 as i32).unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f64_convert_i64_s_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.convert_i64_s") + .replace("{{1}}", "i32") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the f64.convert_i64_s implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L483) +#[test_log::test] +pub fn f64_convert_i64_s() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.convert_i64_s") + .replace("{{1}}", "i64") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f64, instance.invoke_func(0, 1_i64).unwrap()); + assert_eq!(-1.0_f64, instance.invoke_func(0, -1_i64).unwrap()); + assert_eq!(0.0_f64, instance.invoke_func(0, 0_i64).unwrap()); + assert_eq!( + 9223372036854775807_f64, + instance.invoke_func(0, 9223372036854775807_i64).unwrap() + ); + assert_eq!( + -9223372036854775808_f64, + instance.invoke_func(0, -9223372036854775808_i64).unwrap() + ); + assert_eq!( + 4669201609102990_f64, + instance.invoke_func(0, 4669201609102990_i64).unwrap() + ); + assert_eq!( + 9007199254740992_f64, + instance.invoke_func(0, 9007199254740993_i64).unwrap() + ); + assert_eq!( + -9007199254740992_f64, + instance.invoke_func(0, -9007199254740993_i64).unwrap() + ); + assert_eq!( + 9007199254740996_f64, + instance.invoke_func(0, 9007199254740995_i64).unwrap() + ); + assert_eq!( + -9007199254740996_f64, + instance.invoke_func(0, -9007199254740995_i64).unwrap() + ); +} + +/// A function to test the f64.convert_i64_u implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L531C1-L544C103) +#[test_log::test] +pub fn f64_convert_i64_u() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.convert_i64_u") + .replace("{{1}}", "i64") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(1.0_f64, instance.invoke_func(0, 1_i64).unwrap()); + assert_eq!(0.0_f64, instance.invoke_func(0, 0_i64).unwrap()); + assert_eq!( + 9223372036854775807_f64, + instance.invoke_func(0, 9223372036854775807_i64).unwrap() + ); + assert_eq!( + 9223372036854775808_f64, + instance.invoke_func(0, -9223372036854775808_i64).unwrap() + ); + assert_eq!( + 18446744073709551616.0_f64, + instance + .invoke_func(0, 0xffffffffffffffff_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf64!("0x1.0000000000000p+63"), + instance + .invoke_func(0, 0x8000000000000400_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf64!("0x1.0000000000001p+63"), + instance + .invoke_func(0, 0x8000000000000401_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf64!("0x1.0000000000001p+63"), + instance + .invoke_func(0, 0x8000000000000402_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf64!("0x1.ffffffffffffep+63"), + instance + .invoke_func(0, 0xfffffffffffff400_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf64!("0x1.fffffffffffffp+63"), + instance + .invoke_func(0, 0xfffffffffffff401_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf64!("0x1.fffffffffffffp+63"), + instance + .invoke_func(0, 0xfffffffffffff402_u64 as i64) + .unwrap() + ); + // ;; Test rounding directions. + assert_eq!( + 9007199254740992_f64, + instance.invoke_func(0, 9007199254740993_i64).unwrap() + ); + assert_eq!( + 9007199254740996_f64, + instance.invoke_func(0, 9007199254740995_i64).unwrap() + ); +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f64_promote_f32_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.promote_f32") + .replace("{{1}}", "i64") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the f64.promote_f32 implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L546) +#[test_log::test] +pub fn f64_promote_f32() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.promote_f32") + .replace("{{1}}", "f32") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0.0_f64, instance.invoke_func(0, 0.0_f32).unwrap()); + assert_eq!(-0.0_f64, instance.invoke_func(0, -0.0_f32).unwrap()); + assert_eq!( + hexf64!("0x1p-149"), + instance.invoke_func(0, hexf32!("0x1p-149")).unwrap() + ); + assert_eq!( + hexf64!("-0x1p-149"), + instance.invoke_func(0, hexf32!("-0x1p-149")).unwrap() + ); + assert_eq!(1.0_f64, instance.invoke_func(0, 1.0_f32).unwrap()); + assert_eq!(-1.0_f64, instance.invoke_func(0, -1.0_f32).unwrap()); + assert_eq!( + hexf64!("-0x1.fffffep+127"), + instance + .invoke_func(0, hexf32!("-0x1.fffffep+127")) + .unwrap() + ); + assert_eq!( + hexf64!("0x1.fffffep+127"), + instance.invoke_func(0, hexf32!("0x1.fffffep+127")).unwrap() + ); + assert_eq!( + hexf64!("0x1p-119"), + instance.invoke_func(0, hexf32!("0x1p-119")).unwrap() + ); + assert_eq!( + 6.6382536710104395e+37_f64, + instance.invoke_func(0, hexf32!("0x1.8f867ep+125")).unwrap() + ); + assert_eq!( + f64::INFINITY, + instance.invoke_func(0, f32::INFINITY).unwrap() + ); + assert_eq!( + f64::NEG_INFINITY, + instance.invoke_func(0, f32::NEG_INFINITY).unwrap() + ); + // (assert_return (invoke "f64.promote_f32" (f32.const nan)) (f64.const nan:canonical)) + // (assert_return (invoke "f64.promote_f32" (f32.const nan:0x200000)) (f64.const nan:arithmetic)) + // (assert_return (invoke "f64.promote_f32" (f32.const -nan)) (f64.const nan:canonical)) + // (assert_return (invoke "f64.promote_f32" (f32.const -nan:0x200000)) (f64.const nan:arithmetic)) +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i32_reinterpret_f32_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.reinterpret_f32") + .replace("{{1}}", "i64") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i32.reinterpret_f32 implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L644) +#[test_log::test] +pub fn i32_reinterpret_f32() { + let wat = String::from(WAT) + .replace("{{0}}", "i32.reinterpret_f32") + .replace("{{1}}", "f32") + .replace("{{2}}", "i32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0, instance.invoke_func(0, 0.0_f32).unwrap()); + assert_eq!( + 0x80000000_u32 as i32, + instance.invoke_func(0, -0.0_f32).unwrap() + ); + assert_eq!(1, instance.invoke_func(0, hexf32!("0x1p-149")).unwrap()); + // (assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x7fffff)) (i32.const -1)) + assert_eq!( + 0x80000001_u32 as i32, + instance.invoke_func(0, hexf32!("-0x1p-149")).unwrap() + ); + assert_eq!(1065353216, instance.invoke_func(0, 1.0_f32).unwrap()); + assert_eq!(1078530010, instance.invoke_func(0, 3.1415926_f32).unwrap()); + assert_eq!( + 2139095039, + instance.invoke_func(0, hexf32!("0x1.fffffep+127")).unwrap() + ); + assert_eq!( + -8388609, + instance + .invoke_func(0, hexf32!("-0x1.fffffep+127")) + .unwrap() + ); + assert_eq!(0x7f800000, instance.invoke_func(0, f32::INFINITY).unwrap()); + assert_eq!( + 0xff800000_u32 as i32, + instance.invoke_func(0, f32::NEG_INFINITY).unwrap() + ); + assert_eq!(0x7fc00000, instance.invoke_func(0, f32::NAN).unwrap()); + assert_eq!( + 0xffc00000_u32 as i32, + instance.invoke_func(0, -f32::NAN).unwrap() + ); + // (assert_return (invoke "i32.reinterpret_f32" (f32.const -nan)) (i32.const 0xffc00000)) + // (assert_return (invoke "i32.reinterpret_f32" (f32.const nan:0x200000)) (i32.const 0x7fa00000)) + // (assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x200000)) (i32.const 0xffa00000)) +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn i64_reinterpret_f64_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.reinterpret_f64") + .replace("{{1}}", "i32") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i64.reinterpret_f64 implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L660) +#[test_log::test] +pub fn i64_reinterpret_f64() { + let wat = String::from(WAT) + .replace("{{0}}", "i64.reinterpret_f64") + .replace("{{1}}", "f64") + .replace("{{2}}", "i64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0_i64, instance.invoke_func(0, 0.0_f64).unwrap()); + assert_eq!( + 0x8000000000000000_u64 as i64, + instance.invoke_func(0, -0.0_f64).unwrap() + ); + assert_eq!( + 1_i64, + instance + .invoke_func(0, hexf64!("0x0.0000000000001p-1022")) + .unwrap() + ); + // (assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0xfffffffffffff)) (i64.const -1)) + assert_eq!( + 0x8000000000000001_u64 as i64, + instance + .invoke_func(0, hexf64!("-0x0.0000000000001p-1022")) + .unwrap() + ); + assert_eq!( + 4607182418800017408_i64, + instance.invoke_func(0, 1.0_f64).unwrap() + ); + assert_eq!( + 4614256656552045841_i64, + instance.invoke_func(0, 3.14159265358979_f64).unwrap() + ); + assert_eq!( + 9218868437227405311_i64, + instance + .invoke_func(0, hexf64!("0x1.fffffffffffffp+1023")) + .unwrap() + ); + assert_eq!( + -4503599627370497_i64, + instance + .invoke_func(0, hexf64!("-0x1.fffffffffffffp+1023")) + .unwrap() + ); + assert_eq!( + 0x7ff0000000000000_u64 as i64, + instance.invoke_func(0, f64::INFINITY).unwrap() + ); + assert_eq!( + 0xfff0000000000000_u64 as i64, + instance.invoke_func(0, f64::NEG_INFINITY).unwrap() + ); + assert_eq!( + 0x7ff8000000000000_u64 as i64, + instance.invoke_func(0, f64::NAN).unwrap() + ); + assert_eq!( + 0xfff8000000000000_u64 as i64, + instance.invoke_func(0, -f64::NAN).unwrap() + ); + // (assert_return (invoke "i64.reinterpret_f64" (f64.const nan:0x4000000000000)) (i64.const 0x7ff4000000000000)) + // (assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0x4000000000000)) (i64.const 0xfff4000000000000)) +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f32_reinterpret_i32_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.reinterpret_i32") + .replace("{{1}}", "i64") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the f32.reinterpret_i32 implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L618) +#[test_log::test] +pub fn f32_reinterpret_i32() { + let wat = String::from(WAT) + .replace("{{0}}", "f32.reinterpret_i32") + .replace("{{1}}", "i32") + .replace("{{2}}", "f32"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0.0_f32, instance.invoke_func(0, 0).unwrap()); + assert_eq!( + -0.0_f32, + instance.invoke_func(0, 0x80000000_u32 as i32).unwrap() + ); + assert_eq!(hexf32!("0x1p-149"), instance.invoke_func(0, 1).unwrap()); + // (assert_return (invoke "f32.reinterpret_i32" (i32.const -1)) (f32.const -nan:0x7fffff)) + assert_eq!( + hexf32!("0x1.b79a2ap-113"), + instance.invoke_func(0, 123456789).unwrap() + ); + assert_eq!( + hexf32!("-0x1p-149"), + instance.invoke_func(0, -2147483647).unwrap() + ); + assert_eq!(f32::INFINITY, instance.invoke_func(0, 0x7f800000).unwrap()); + assert_eq!( + f32::NEG_INFINITY, + instance.invoke_func(0, 0xff800000_u32 as i32).unwrap() + ); + { + let result: f32 = instance.invoke_func(0, 0x7fc00000).unwrap(); + assert!(result.is_nan()); + assert!(result.is_sign_positive()); + } + { + let result: f32 = instance.invoke_func(0, 0xffc00000_u32 as i32).unwrap(); + assert!(result.is_nan()); + assert!(result.is_sign_negative()); + } + // (assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7fa00000)) (f32.const nan:0x200000)) + // (assert_return (invoke "f32.reinterpret_i32" (i32.const 0xffa00000)) (f32.const -nan:0x200000)) +} + +#[should_panic(expected = "InvalidValueStackType")] +#[test_log::test] +pub fn f64_reinterpret_i64_let_it_die() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.reinterpret_i64") + .replace("{{1}}", "i32") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(-1, instance.invoke_func(0, -1).unwrap()); +} + +/// A function to test the i64.reinterpret_f64 implementation using the [WASM TestSuite](https://github.com/WebAssembly/testsuite/blob/7570678ade1244ae69c9fefc990f4534c63ffaec/conversions.wast#L660) +#[test_log::test] +pub fn f64_reinterpret_i64() { + let wat = String::from(WAT) + .replace("{{0}}", "f64.reinterpret_i64") + .replace("{{1}}", "i64") + .replace("{{2}}", "f64"); + let wasm_bytes = wat::parse_str(wat).unwrap(); + let validation_info = validate(&wasm_bytes).expect("validation failed"); + let mut instance = RuntimeInstance::new(&validation_info).expect("instantiation failed"); + + assert_eq!(0.0_f64, instance.invoke_func(0, 0_i64).unwrap()); + assert_eq!( + hexf64!("0x0.0000000000001p-1022"), + instance.invoke_func(0, 1_i64).unwrap() + ); + // (assert_return (invoke "f64.reinterpret_i64" (i64.const -1)) (f64.const -nan:0xfffffffffffff)) + assert_eq!( + -0.0_f64, + instance + .invoke_func(0, 0x8000000000000000_u64 as i64) + .unwrap() + ); + assert_eq!( + hexf64!("0x0.00000499602d2p-1022"), + instance.invoke_func(0, 1234567890_i64).unwrap() + ); + assert_eq!( + hexf64!("-0x0.0000000000001p-1022"), + instance.invoke_func(0, -9223372036854775807_i64).unwrap() + ); + assert_eq!( + f64::INFINITY, + instance + .invoke_func(0, 0x7ff0000000000000_u64 as i64) + .unwrap() + ); + assert_eq!( + f64::NEG_INFINITY, + instance + .invoke_func(0, 0xfff0000000000000_u64 as i64) + .unwrap() + ); + { + let result: f64 = instance + .invoke_func(0, 0x7ff8000000000000_u64 as i64) + .unwrap(); + assert!(result.is_nan()); + assert!(result.is_sign_positive()); + } + { + let result: f64 = instance + .invoke_func(0, 0xfff8000000000000_u64 as i64) + .unwrap(); + assert!(result.is_nan()); + assert!(result.is_sign_negative()); + } + // (assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff4000000000000)) (f64.const nan:0x4000000000000)) + // (assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff4000000000000)) (f64.const -nan:0x4000000000000)) +} diff --git a/tests/errors.rs b/tests/errors.rs new file mode 100644 index 00000000..82c3f759 --- /dev/null +++ b/tests/errors.rs @@ -0,0 +1,6 @@ +use wasm::RuntimeError; + +#[test_log::test] +pub fn runtime_error_bad_conversion_to_integer() { + println!("{}", RuntimeError::BadConversionToInteger) +} diff --git a/tests/i32.rs b/tests/i32.rs index d260145f..2438b3b0 100644 --- a/tests/i32.rs +++ b/tests/i32.rs @@ -1,3 +1,19 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ use wasm::{validate, RuntimeInstance}; const WAT: &'static str = r#" diff --git a/tests/i64.rs b/tests/i64.rs index 26cb936c..f1f0aa81 100644 --- a/tests/i64.rs +++ b/tests/i64.rs @@ -1,3 +1,19 @@ +/* +# This file incorporates code from the WebAssembly testsuite, originally +# available at https://github.com/WebAssembly/testsuite. +# +# The original code is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ use wasm::{validate, RuntimeInstance}; const WAT: &'static str = r#"