diff --git a/crates/wasm_interp/src/instance.rs b/crates/wasm_interp/src/instance.rs index 4422c6c6c0..1ccd67ce8f 100644 --- a/crates/wasm_interp/src/instance.rs +++ b/crates/wasm_interp/src/instance.rs @@ -779,19 +779,79 @@ impl<'a> Instance<'a> { self.value_stack.push(Value::I32(result as i32)); } - F32EQ => todo!("{:?} @ {:#x}", op_code, file_offset), - F32NE => todo!("{:?} @ {:#x}", op_code, file_offset), - F32LT => todo!("{:?} @ {:#x}", op_code, file_offset), - F32GT => todo!("{:?} @ {:#x}", op_code, file_offset), - F32LE => todo!("{:?} @ {:#x}", op_code, file_offset), - F32GE => todo!("{:?} @ {:#x}", op_code, file_offset), + F32EQ => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + let result: bool = arg1 == arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F32NE => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + let result: bool = arg1 != arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F32LT => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + let result: bool = arg1 < arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F32GT => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + let result: bool = arg1 > arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F32LE => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + let result: bool = arg1 <= arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F32GE => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + let result: bool = arg1 >= arg2; + self.value_stack.push(Value::I32(result as i32)); + } - F64EQ => todo!("{:?} @ {:#x}", op_code, file_offset), - F64NE => todo!("{:?} @ {:#x}", op_code, file_offset), - F64LT => todo!("{:?} @ {:#x}", op_code, file_offset), - F64GT => todo!("{:?} @ {:#x}", op_code, file_offset), - F64LE => todo!("{:?} @ {:#x}", op_code, file_offset), - F64GE => todo!("{:?} @ {:#x}", op_code, file_offset), + F64EQ => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + let result: bool = arg1 == arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F64NE => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + let result: bool = arg1 != arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F64LT => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + let result: bool = arg1 < arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F64GT => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + let result: bool = arg1 > arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F64LE => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + let result: bool = arg1 <= arg2; + self.value_stack.push(Value::I32(result as i32)); + } + F64GE => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + let result: bool = arg1 >= arg2; + self.value_stack.push(Value::I32(result as i32)); + } I32CLZ => { let arg = self.value_stack.pop_u32(); @@ -990,10 +1050,26 @@ impl<'a> Instance<'a> { F32TRUNC => todo!("{:?} @ {:#x}", op_code, file_offset), F32NEAREST => todo!("{:?} @ {:#x}", op_code, file_offset), F32SQRT => todo!("{:?} @ {:#x}", op_code, file_offset), - F32ADD => todo!("{:?} @ {:#x}", op_code, file_offset), - F32SUB => todo!("{:?} @ {:#x}", op_code, file_offset), - F32MUL => todo!("{:?} @ {:#x}", op_code, file_offset), - F32DIV => todo!("{:?} @ {:#x}", op_code, file_offset), + F32ADD => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + self.value_stack.push(Value::F32(arg1 + arg2)); + } + F32SUB => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + self.value_stack.push(Value::F32(arg1 - arg2)); + } + F32MUL => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + self.value_stack.push(Value::F32(arg1 * arg2)); + } + F32DIV => { + let arg2 = self.value_stack.pop_f32(); + let arg1 = self.value_stack.pop_f32(); + self.value_stack.push(Value::F32(arg1 / arg2)); + } F32MIN => todo!("{:?} @ {:#x}", op_code, file_offset), F32MAX => todo!("{:?} @ {:#x}", op_code, file_offset), F32COPYSIGN => todo!("{:?} @ {:#x}", op_code, file_offset), @@ -1005,10 +1081,26 @@ impl<'a> Instance<'a> { F64TRUNC => todo!("{:?} @ {:#x}", op_code, file_offset), F64NEAREST => todo!("{:?} @ {:#x}", op_code, file_offset), F64SQRT => todo!("{:?} @ {:#x}", op_code, file_offset), - F64ADD => todo!("{:?} @ {:#x}", op_code, file_offset), - F64SUB => todo!("{:?} @ {:#x}", op_code, file_offset), - F64MUL => todo!("{:?} @ {:#x}", op_code, file_offset), - F64DIV => todo!("{:?} @ {:#x}", op_code, file_offset), + F64ADD => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + self.value_stack.push(Value::F64(arg1 + arg2)); + } + F64SUB => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + self.value_stack.push(Value::F64(arg1 - arg2)); + } + F64MUL => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + self.value_stack.push(Value::F64(arg1 * arg2)); + } + F64DIV => { + let arg2 = self.value_stack.pop_f64(); + let arg1 = self.value_stack.pop_f64(); + self.value_stack.push(Value::F64(arg1 / arg2)); + } F64MIN => todo!("{:?} @ {:#x}", op_code, file_offset), F64MAX => todo!("{:?} @ {:#x}", op_code, file_offset), F64COPYSIGN => todo!("{:?} @ {:#x}", op_code, file_offset), diff --git a/crates/wasm_interp/tests/test_f32.rs b/crates/wasm_interp/tests/test_f32.rs new file mode 100644 index 0000000000..cf454d438b --- /dev/null +++ b/crates/wasm_interp/tests/test_f32.rs @@ -0,0 +1,108 @@ +#![cfg(test)] + +use roc_wasm_interp::test_utils::test_op_example; +use roc_wasm_module::{opcodes::OpCode, opcodes::OpCode::*, Value}; + +fn test_f32_comparison(op: OpCode, arg1: f32, arg2: f32, expected: bool) { + test_op_example( + op, + [Value::F32(arg1), Value::F32(arg2)], + Value::I32(expected as i32), + ) +} + +fn test_f32_binop(op: OpCode, arg1: f32, arg2: f32, expected: f32) { + test_op_example( + op, + [Value::F32(arg1), Value::F32(arg2)], + Value::F32(expected), + ) +} + +// fn test_f32_unop(op: OpCode, arg: f32, expected: f32) { +// test_op_example(op, [Value::F32(arg)], Value::F32(expected)) +// } + +#[test] +fn test_f32eq() { + let op = F32EQ; + test_f32_comparison(op, 1.1, 1.1, true); + test_f32_comparison(op, 1.1, -1.1, false); +} + +#[test] +fn test_f32ne() { + let op = F32NE; + test_f32_comparison(op, 1.1, 1.1, false); + test_f32_comparison(op, 1.1, -1.1, true); +} + +#[test] +fn test_f32lt() { + let op = F32LT; + test_f32_comparison(op, 1.1, 1.1, false); + test_f32_comparison(op, 1.1, -1.1, false); + test_f32_comparison(op, -1.1, 1.1, true); +} + +#[test] +fn test_f32gt() { + let op = F32GT; + test_f32_comparison(op, 1.1, 1.1, false); + test_f32_comparison(op, 1.1, -1.1, true); + test_f32_comparison(op, -1.1, 1.1, false); +} + +#[test] +fn test_f32le() { + let op = F32LE; + test_f32_comparison(op, 1.1, 1.1, true); + test_f32_comparison(op, 1.1, -1.1, false); + test_f32_comparison(op, -1.1, 1.1, true); +} + +#[test] +fn test_f32ge() { + let op = F32GE; + test_f32_comparison(op, 1.1, 1.1, true); + test_f32_comparison(op, 1.1, -1.1, true); + test_f32_comparison(op, -1.1, 1.1, false); +} + +#[test] +fn test_f32add() { + let op = F32ADD; + test_f32_binop(op, 0.0, 0.0, 0.0); + test_f32_binop(op, -1.1, -1.1, -2.2); + test_f32_binop(op, 1.1, 1.1, 2.2); + test_f32_binop(op, 1.1, -1.1, 0.0); +} + +#[test] +fn test_f32sub() { + let op = F32SUB; + test_f32_binop(op, 0.0, 0.0, 0.0); + test_f32_binop(op, -1.1, -1.1, 0.0); + test_f32_binop(op, 1.1, -1.1, 2.2); +} + +#[test] +fn test_f32mul() { + let op = F32MUL; + test_f32_binop(op, 1.1, 0.0, 0.0); + test_f32_binop(op, -1.5, -1.5, 2.25); + test_f32_binop(op, 1.5, 1.5, 2.25); + test_f32_binop(op, 1.5, -1.5, -2.25); +} + +#[test] +fn test_f32div() { + let op = F32DIV; + test_f32_binop(op, -1.1, -1.1, 1.0); + test_f32_binop(op, 1.1, -1.1, -1.0); + test_f32_binop(op, 5.0, 2.0, 2.5); + + test_f32_binop(op, 1.0, 0.0, f32::INFINITY); + test_f32_binop(op, -1.0, 0.0, f32::NEG_INFINITY); + // test_f32_binop(op, 0.0, 0.0, f32::NAN); // can't check NaN for equality! LOL +} diff --git a/crates/wasm_interp/tests/test_f64.rs b/crates/wasm_interp/tests/test_f64.rs new file mode 100644 index 0000000000..322e7c7bb8 --- /dev/null +++ b/crates/wasm_interp/tests/test_f64.rs @@ -0,0 +1,108 @@ +#![cfg(test)] + +use roc_wasm_interp::test_utils::test_op_example; +use roc_wasm_module::{opcodes::OpCode, opcodes::OpCode::*, Value}; + +fn test_f64_comparison(op: OpCode, arg1: f64, arg2: f64, expected: bool) { + test_op_example( + op, + [Value::F64(arg1), Value::F64(arg2)], + Value::I32(expected as i32), + ) +} + +fn test_f64_binop(op: OpCode, arg1: f64, arg2: f64, expected: f64) { + test_op_example( + op, + [Value::F64(arg1), Value::F64(arg2)], + Value::F64(expected), + ) +} + +// fn test_f64_unop(op: OpCode, arg: f64, expected: f64) { +// test_op_example(op, [Value::F64(arg)], Value::F64(expected)) +// } + +#[test] +fn test_f64eq() { + let op = F64EQ; + test_f64_comparison(op, 1.1, 1.1, true); + test_f64_comparison(op, 1.1, -1.1, false); +} + +#[test] +fn test_f64ne() { + let op = F64NE; + test_f64_comparison(op, 1.1, 1.1, false); + test_f64_comparison(op, 1.1, -1.1, true); +} + +#[test] +fn test_f64lt() { + let op = F64LT; + test_f64_comparison(op, 1.1, 1.1, false); + test_f64_comparison(op, 1.1, -1.1, false); + test_f64_comparison(op, -1.1, 1.1, true); +} + +#[test] +fn test_f64gt() { + let op = F64GT; + test_f64_comparison(op, 1.1, 1.1, false); + test_f64_comparison(op, 1.1, -1.1, true); + test_f64_comparison(op, -1.1, 1.1, false); +} + +#[test] +fn test_f64le() { + let op = F64LE; + test_f64_comparison(op, 1.1, 1.1, true); + test_f64_comparison(op, 1.1, -1.1, false); + test_f64_comparison(op, -1.1, 1.1, true); +} + +#[test] +fn test_f64ge() { + let op = F64GE; + test_f64_comparison(op, 1.1, 1.1, true); + test_f64_comparison(op, 1.1, -1.1, true); + test_f64_comparison(op, -1.1, 1.1, false); +} + +#[test] +fn test_f64add() { + let op = F64ADD; + test_f64_binop(op, 0.0, 0.0, 0.0); + test_f64_binop(op, -1.1, -1.1, -2.2); + test_f64_binop(op, 1.1, 1.1, 2.2); + test_f64_binop(op, 1.1, -1.1, 0.0); +} + +#[test] +fn test_f64sub() { + let op = F64SUB; + test_f64_binop(op, 0.0, 0.0, 0.0); + test_f64_binop(op, -1.1, -1.1, 0.0); + test_f64_binop(op, 1.1, -1.1, 2.2); +} + +#[test] +fn test_f64mul() { + let op = F64MUL; + test_f64_binop(op, 1.1, 0.0, 0.0); + test_f64_binop(op, -1.5, -1.5, 2.25); + test_f64_binop(op, 1.5, 1.5, 2.25); + test_f64_binop(op, 1.5, -1.5, -2.25); +} + +#[test] +fn test_f64div() { + let op = F64DIV; + test_f64_binop(op, -1.1, -1.1, 1.0); + test_f64_binop(op, 1.1, -1.1, -1.0); + test_f64_binop(op, 5.0, 2.0, 2.5); + + test_f64_binop(op, 1.0, 0.0, f64::INFINITY); + test_f64_binop(op, -1.0, 0.0, f64::NEG_INFINITY); + // test_f64_binop(op, 0.0, 0.0, f64::NAN); // can't check NaN for equality! LOL +}