diff --git a/spec/src/fixtures.rs b/spec/src/fixtures.rs index 41164bf..a32dbc2 100644 --- a/spec/src/fixtures.rs +++ b/spec/src/fixtures.rs @@ -8,3 +8,5 @@ macro_rules! run_test { } run_test!("i32", wasm_i32); +run_test!("f32", wasm_f32); +run_test!("f64", wasm_f64); diff --git a/spec/src/run.rs b/spec/src/run.rs index 5bc5b55..127a6ce 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -10,6 +10,7 @@ use serde_json; use test; use parity_wasm; use parity_wasm::interpreter::{ + RuntimeValue, ProgramInstance, ModuleInstance, ModuleInstanceInterface, Error as InterpreterError, }; @@ -30,6 +31,18 @@ fn runtime_value(test_val: &test::RuntimeValue) -> parity_wasm::RuntimeValue { let unsigned: u32 = test_val.value.parse().expect("Literal parse error"); parity_wasm::RuntimeValue::I32(unsigned as i32) }, + "i64" => { + let unsigned: u64 = test_val.value.parse().expect("Literal parse error"); + parity_wasm::RuntimeValue::I64(unsigned as i64) + }, + "f32" => { + let unsigned: u32 = test_val.value.parse().expect("Literal parse error"); + parity_wasm::RuntimeValue::decode_f32(unsigned) + }, + "f64" => { + let unsigned: u64 = test_val.value.parse().expect("Literal parse error"); + parity_wasm::RuntimeValue::decode_f64(unsigned) + }, _ => panic!("Unknwon runtime value type"), } } @@ -103,6 +116,24 @@ pub fn spec(name: &str) { } } }, + &test::Command::AssertReturnCanonicalNan { line, ref action } | &test::Command::AssertReturnArithmeticNan { line, ref action } => { + let result = run_action(&*module, action); + match result { + Ok(result) => { + for actual_result in result.into_iter().collect::>() { + match actual_result { + RuntimeValue::F32(val) => if !val.is_nan() { panic!("Expected nan value, got {:?}", val) }, + RuntimeValue::F64(val) => if !val.is_nan() { panic!("Expected nan value, got {:?}", val) }, + val @ _ => panic!("Expected action to return float value, got {:?}", val), + } + } + println!("assert_return_nan at line {} - success", line); + }, + Err(e) => { + panic!("Expected action to return value, got error: {:?}", e); + } + } + }, &test::Command::AssertTrap { line, ref action, .. } => { let result = run_action(&*module, action); match result { diff --git a/spec/src/test.rs b/spec/src/test.rs index 614ecca..f2a06fd 100644 --- a/spec/src/test.rs +++ b/spec/src/test.rs @@ -25,6 +25,16 @@ pub enum Command { action: Action, expected: Vec, }, + #[serde(rename = "assert_return_canonical_nan")] + AssertReturnCanonicalNan { + line: u64, + action: Action, + }, + #[serde(rename = "assert_return_arithmetic_nan")] + AssertReturnArithmeticNan { + line: u64, + action: Action, + }, #[serde(rename = "assert_trap")] AssertTrap { line: u64, diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index eedac2b..d3c1150 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -758,7 +758,7 @@ impl Interpreter { context .value_stack_mut() .pop_as::() - .map(|v| v.round()) + .map(|v| v.nearest()) .map(|v| context.value_stack_mut().push(v.into())) .map(|_| InstructionOutcome::RunNextInstruction) } diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 7f1bb51..71f81a9 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -99,6 +99,8 @@ pub trait Float: ArithmeticOps { fn trunc(self) -> T; /// Returns the nearest integer to a number. Round half-way cases away from 0.0. fn round(self) -> T; + /// Returns the nearest integer to a number. Ties are round to even number. + fn nearest(self) -> T; /// Takes the square root of a number. fn sqrt(self) -> T; /// Returns the minimum of the two numbers. @@ -604,17 +606,40 @@ macro_rules! impl_float { fn ceil(self) -> $type { self.ceil() } fn trunc(self) -> $type { self.trunc() } fn round(self) -> $type { self.round() } + fn nearest(self) -> $type { + let round = self.round(); + if self.fract().abs() != 0.5 { + return round; + } + + use std::ops::Rem; + if round.rem(2.0) == 1.0 { + self.floor() + } else if round.rem(2.0) == -1.0 { + self.ceil() + } else { + round + } + } fn sqrt(self) -> $type { self.sqrt() } - // TODO // This instruction corresponds to what is sometimes called "minNaN" in other languages. - // This differs from the IEEE 754-2008 minNum 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 $type { self.min(other) } - // TODO + fn min(self, other: $type) -> $type { + if self.is_nan() || other.is_nan() { + use std::$type; + return $type::NAN; + } + + self.min(other) + } // This instruction corresponds to what is sometimes called "maxNaN" in other languages. - // 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 max(self, other: $type) -> $type { + if self.is_nan() || other.is_nan() { + use std::$type; + return $type::NAN; + } + + 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() {