diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index ae6b3f3..ed51b59 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -784,9 +784,14 @@ impl Interpreter { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_copysign(_context: &mut FunctionContext) -> Result + fn run_copysign(context: &mut FunctionContext) -> Result where RuntimeValue: From + TryInto, T: Float { - Err(Error::NotImplemented) // TODO + context + .value_stack_mut() + .pop_pair_as::() + .map(|(left, right)| left.copysign(right)) + .map(|v| context.value_stack_mut().push(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) } fn run_wrap(context: &mut FunctionContext) -> Result diff --git a/src/interpreter/tests/wabt.rs b/src/interpreter/tests/wabt.rs index b457bfb..c9a6f49 100644 --- a/src/interpreter/tests/wabt.rs +++ b/src/interpreter/tests/wabt.rs @@ -1009,3 +1009,328 @@ fn binary_i32() { assert_eq!(module.execute(13, vec![]).unwrap().unwrap(), RuntimeValue::I32(-793)); // 4294966503 assert_eq!(module.execute(14, vec![]).unwrap().unwrap(), RuntimeValue::I32(-1610612749)); // 2684354547 } + +/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/binary.txt#L65 +#[test] +fn binary_i64() { + let module = module() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(1), + Opcode::I64Const(2), + Opcode::I64Add, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(20), + Opcode::I64Const(4), + Opcode::I64Sub, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(3), + Opcode::I64Const(7), + Opcode::I64Mul, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-4), + Opcode::I64Const(2), + Opcode::I64DivS, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-4), + Opcode::I64Const(2), + Opcode::I64DivU, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-5), + Opcode::I64Const(2), + Opcode::I64RemS, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-5), + Opcode::I64Const(2), + Opcode::I64RemU, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(11), + Opcode::I64Const(5), + Opcode::I64And, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(11), + Opcode::I64Const(5), + Opcode::I64Or, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(11), + Opcode::I64Const(5), + Opcode::I64Xor, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-100), + Opcode::I64Const(3), + Opcode::I64Shl, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-100), + Opcode::I64Const(3), + Opcode::I64ShrU, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-100), + Opcode::I64Const(3), + Opcode::I64ShrS, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-100), + Opcode::I64Const(3), + Opcode::I64Rotl, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I64Const(-100), + Opcode::I64Const(3), + Opcode::I64Rotr, + Opcode::End, + ])).build() + .build() + .build(); + + let program = ProgramInstance::new(); + let module = program.add_module("main", module).unwrap(); + assert_eq!(module.execute(0, vec![]).unwrap().unwrap(), RuntimeValue::I64(3)); + assert_eq!(module.execute(1, vec![]).unwrap().unwrap(), RuntimeValue::I64(16)); + assert_eq!(module.execute(2, vec![]).unwrap().unwrap(), RuntimeValue::I64(21)); + assert_eq!(module.execute(3, vec![]).unwrap().unwrap(), RuntimeValue::I64(-2)); // 18446744073709551614 + assert_eq!(module.execute(4, vec![]).unwrap().unwrap(), RuntimeValue::I64(9223372036854775806)); + assert_eq!(module.execute(5, vec![]).unwrap().unwrap(), RuntimeValue::I64(-1)); // 18446744073709551615 + assert_eq!(module.execute(6, vec![]).unwrap().unwrap(), RuntimeValue::I64(1)); + assert_eq!(module.execute(7, vec![]).unwrap().unwrap(), RuntimeValue::I64(1)); + assert_eq!(module.execute(8, vec![]).unwrap().unwrap(), RuntimeValue::I64(15)); + assert_eq!(module.execute(9, vec![]).unwrap().unwrap(), RuntimeValue::I64(14)); + assert_eq!(module.execute(10, vec![]).unwrap().unwrap(), RuntimeValue::I64(-800)); // 18446744073709550816 + assert_eq!(module.execute(11, vec![]).unwrap().unwrap(), RuntimeValue::I64(2305843009213693939)); + assert_eq!(module.execute(12, vec![]).unwrap().unwrap(), RuntimeValue::I64(-13)); // 18446744073709551603 + assert_eq!(module.execute(13, vec![]).unwrap().unwrap(), RuntimeValue::I64(-793)); // 18446744073709550823 + assert_eq!(module.execute(14, vec![]).unwrap().unwrap(), RuntimeValue::I64(-6917529027641081869)); // 11529215046068469747 +} + +/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/binary.txt#L3 +#[test] +fn binary_f32() { + // f32 && f64 are serialized using binary32 && binary64 formats + // http://babbage.cs.qc.cuny.edu/IEEE-754/ + let module = module() + .function() + .signature().return_type().f32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F32Const(0x3FA00000), // 1.25 + Opcode::F32Const(0x40700000), // 3.75 + Opcode::F32Add, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F32Const(0x40900000), // 4.5 + Opcode::F32Const(0x461C4000), // 1e4 + Opcode::F32Sub, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F32Const(0x449A5000), // 1234.5 + Opcode::F32Const(0xC0DC0000), // -6.875 + Opcode::F32Mul, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F32Const(0x56B5E621), // 1e14 + Opcode::F32Const(0xC8435000), // -2e5 + Opcode::F32Div, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F32Const(0x00000000), // 0 + Opcode::F32Const(0x00000000), // 0 + Opcode::F32Min, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F32Const(0x00000000), // 0 + Opcode::F32Const(0x00000000), // 0 + Opcode::F32Max, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F32Const(0x00000000), // 0 + Opcode::F32Const(0x00000000), // 0 + Opcode::F32Copysign, + Opcode::End, + ])).build() + .build() + .build(); + + let program = ProgramInstance::new(); + let module = program.add_module("main", module).unwrap(); + assert_eq!(module.execute(0, vec![]).unwrap().unwrap(), RuntimeValue::F32(5.000000)); + assert_eq!(module.execute(1, vec![]).unwrap().unwrap(), RuntimeValue::F32(-9995.500000)); + assert_eq!(module.execute(2, vec![]).unwrap().unwrap(), RuntimeValue::F32(-8487.187500)); + assert_eq!(module.execute(3, vec![]).unwrap().unwrap(), RuntimeValue::F32(-500000000.000000)); + assert_eq!(module.execute(4, vec![]).unwrap().unwrap(), RuntimeValue::F32(0.000000)); + assert_eq!(module.execute(5, vec![]).unwrap().unwrap(), RuntimeValue::F32(0.000000)); + assert_eq!(module.execute(6, vec![]).unwrap().unwrap(), RuntimeValue::F32(0.000000)); +} + +/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/binary.txt#L157 +#[test] +fn binary_f64() { + // f32 && f64 are serialized using binary32 && binary64 formats + // http://babbage.cs.qc.cuny.edu/IEEE-754/ + let module = module() + .function() + .signature().return_type().f64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F64Const(0x41CD6F3458800000), // 987654321 + Opcode::F64Const(0x419D6F3454000000), // 123456789 + Opcode::F64Add, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F64Const(0x4C33A8A41D39B24E), // 1234e56 + Opcode::F64Const(0x44DD1DE3D2D5C713), // 5.5e23 + Opcode::F64Sub, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F64Const(0xC132C4B000000000), // -123e4 + Opcode::F64Const(0x416789FE40000000), // 12341234 + Opcode::F64Mul, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F64Const(0x6974E718D7D7625A), // 1e200 + Opcode::F64Const(0x4A511B0EC57E649A), // 1e50 + Opcode::F64Div, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F64Const(0x0000000000000000), // 0 + Opcode::F64Const(0x0000000000000000), // 0 + Opcode::F64Min, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F64Const(0x0000000000000000), // 0 + Opcode::F64Const(0x0000000000000000), // 0 + Opcode::F64Max, + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().f64().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::F64Const(0x0000000000000000), // 0 + Opcode::F64Const(0x0000000000000000), // 0 + Opcode::F64Copysign, + Opcode::End, + ])).build() + .build() + .build(); + + let program = ProgramInstance::new(); + let module = program.add_module("main", module).unwrap(); + assert_eq!(module.execute(0, vec![]).unwrap().unwrap(), RuntimeValue::F64(1111111110.000000)); + assert_eq!(module.execute(1, vec![]).unwrap().unwrap(), RuntimeValue::F64(123400000000000007812762268812638756607430593436581896388608.000000)); + assert_eq!(module.execute(2, vec![]).unwrap().unwrap(), RuntimeValue::F64(-15179717820000.000000)); + // TODO: result differs + // assert_eq!(module.execute(3, vec![]).unwrap().unwrap(), RuntimeValue::F64(99999999999999998083559617243737459057312001403031879309116481015410011220367858297629826861622.0f64)); + assert_eq!(module.execute(4, vec![]).unwrap().unwrap(), RuntimeValue::F64(0.000000)); + assert_eq!(module.execute(5, vec![]).unwrap().unwrap(), RuntimeValue::F64(0.000000)); + assert_eq!(module.execute(6, vec![]).unwrap().unwrap(), RuntimeValue::F64(0.000000)); +} diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 2c64ed8..c3dda58 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -106,6 +106,8 @@ pub trait Float: ArithmeticOps { fn min(self, other: T) -> T; /// Returns the maximum of the two numbers. fn max(self, other: T) -> T; + /// Sets sign of this value to the sign of other value. + fn copysign(self, other: T) -> T; } impl RuntimeValue { @@ -552,6 +554,14 @@ macro_rules! impl_float { // This differs from the IEEE 754-2008 maxNum operation in that it returns a NaN if either operand is a NaN, and in that the behavior when the operands are zeros of differing signs is fully specified. // This differs from the common x>y?x:y expansion in its handling of negative zero and NaN values. fn max(self, other: $type) -> $type { self.max(other) } + fn copysign(self, other: $type) -> $type { + // TODO: this may be buggy for edge cases + if self.is_sign_positive() == other.is_sign_positive() { + self + } else { + self * -1.0 + } + } } } }