Add support for SIMD NaN directives (#686)

* Add support for SIMD NaN directives

* Use is_*_nan helper methods for scalar NaN directives
This commit is contained in:
Andrew Brown
2019-12-06 19:47:29 -08:00
committed by Alex Crichton
parent e134505b90
commit ec8144b87d
2 changed files with 156 additions and 5 deletions

View File

@@ -14,7 +14,7 @@ edition = "2018"
wasmtime-jit = { path = "../jit" } wasmtime-jit = { path = "../jit" }
wasmtime-runtime = { path = "../runtime" } wasmtime-runtime = { path = "../runtime" }
wasmtime-environ = { path = "../environ" } wasmtime-environ = { path = "../environ" }
wast = "3.0.0" wast = "4.0.0"
anyhow = "1.0.19" anyhow = "1.0.19"
target-lexicon = "0.9.0" target-lexicon = "0.9.0"

View File

@@ -1,5 +1,6 @@
use crate::spectest::instantiate_spectest; use crate::spectest::instantiate_spectest;
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
use std::convert::TryInto;
use std::path::Path; use std::path::Path;
use std::str; use std::str;
use wasmtime_jit::{ use wasmtime_jit::{
@@ -254,12 +255,12 @@ impl WastContext {
bail!("{}\nunexpected vector in NaN test", context(span)) bail!("{}\nunexpected vector in NaN test", context(span))
} }
RuntimeValue::F32(x) => { RuntimeValue::F32(x) => {
if (x & 0x7fffffff) != 0x7fc00000 { if !is_canonical_f32_nan(x) {
bail!("{}\nexpected canonical NaN", context(span)) bail!("{}\nexpected canonical NaN", context(span))
} }
} }
RuntimeValue::F64(x) => { RuntimeValue::F64(x) => {
if (x & 0x7fffffffffffffff) != 0x7ff8000000000000 { if !is_canonical_f64_nan(x) {
bail!("{}\nexpected canonical NaN", context(span)) bail!("{}\nexpected canonical NaN", context(span))
} }
} }
@@ -271,6 +272,68 @@ impl WastContext {
} }
} }
} }
AssertReturnCanonicalNanF32x4 { span, invoke } => {
match self.perform_invoke(invoke).with_context(|| context(span))? {
ActionOutcome::Returned { values } => {
for v in values.iter() {
match v {
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
bail!("{}\nunexpected integer in NaN test", context(span))
}
RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!(
"{}\nunexpected scalar float in vector NaN test",
context(span)
),
RuntimeValue::V128(x) => {
for l in 0..4 {
if !is_canonical_f32_nan(&extract_lane_as_u32(x, l)?) {
bail!(
"{}\nexpected f32x4 canonical NaN in lane {}",
context(span),
l
)
}
}
}
};
}
}
ActionOutcome::Trapped { message } => {
bail!("{}\nunexpected trap: {}", context(span), message)
}
}
}
AssertReturnCanonicalNanF64x2 { span, invoke } => {
match self.perform_invoke(invoke).with_context(|| context(span))? {
ActionOutcome::Returned { values } => {
for v in values.iter() {
match v {
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
bail!("{}\nunexpected integer in NaN test", context(span))
}
RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!(
"{}\nunexpected scalar float in vector NaN test",
context(span)
),
RuntimeValue::V128(x) => {
for l in 0..2 {
if !is_canonical_f64_nan(&extract_lane_as_u64(x, l)?) {
bail!(
"{}\nexpected f64x2 canonical NaN in lane {}",
context(span),
l
)
}
}
}
};
}
}
ActionOutcome::Trapped { message } => {
bail!("{}\nunexpected trap: {}", context(span), message)
}
}
}
AssertReturnArithmeticNan { span, invoke } => { AssertReturnArithmeticNan { span, invoke } => {
match self.perform_invoke(invoke).with_context(|| context(span))? { match self.perform_invoke(invoke).with_context(|| context(span))? {
ActionOutcome::Returned { values } => { ActionOutcome::Returned { values } => {
@@ -283,12 +346,12 @@ impl WastContext {
bail!("{}\nunexpected vector in NaN test", context(span)) bail!("{}\nunexpected vector in NaN test", context(span))
} }
RuntimeValue::F32(x) => { RuntimeValue::F32(x) => {
if (x & 0x00400000) != 0x00400000 { if !is_arithmetic_f32_nan(x) {
bail!("{}\nexpected arithmetic NaN", context(span)) bail!("{}\nexpected arithmetic NaN", context(span))
} }
} }
RuntimeValue::F64(x) => { RuntimeValue::F64(x) => {
if (x & 0x0008000000000000) != 0x0008000000000000 { if !is_arithmetic_f64_nan(x) {
bail!("{}\nexpected arithmetic NaN", context(span)) bail!("{}\nexpected arithmetic NaN", context(span))
} }
} }
@@ -300,6 +363,68 @@ impl WastContext {
} }
} }
} }
AssertReturnArithmeticNanF32x4 { span, invoke } => {
match self.perform_invoke(invoke).with_context(|| context(span))? {
ActionOutcome::Returned { values } => {
for v in values.iter() {
match v {
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
bail!("{}\nunexpected integer in NaN test", context(span))
}
RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!(
"{}\nunexpected scalar float in vector NaN test",
context(span)
),
RuntimeValue::V128(x) => {
for l in 0..4 {
if !is_arithmetic_f32_nan(&extract_lane_as_u32(x, l)?) {
bail!(
"{}\nexpected f32x4 arithmetic NaN in lane {}",
context(span),
l
)
}
}
}
};
}
}
ActionOutcome::Trapped { message } => {
bail!("{}\nunexpected trap: {}", context(span), message)
}
}
}
AssertReturnArithmeticNanF64x2 { span, invoke } => {
match self.perform_invoke(invoke).with_context(|| context(span))? {
ActionOutcome::Returned { values } => {
for v in values.iter() {
match v {
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
bail!("{}\nunexpected integer in NaN test", context(span))
}
RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!(
"{}\nunexpected scalar float in vector NaN test",
context(span)
),
RuntimeValue::V128(x) => {
for l in 0..2 {
if !is_arithmetic_f64_nan(&extract_lane_as_u64(x, l)?) {
bail!(
"{}\nexpected f64x2 arithmetic NaN in lane {}",
context(span),
l
)
}
}
}
};
}
}
ActionOutcome::Trapped { message } => {
bail!("{}\nunexpected trap: {}", context(span), message)
}
}
}
AssertInvalid { AssertInvalid {
span, span,
mut module, mut module,
@@ -384,3 +509,29 @@ impl WastContext {
self.run_buffer(path.to_str().unwrap(), &bytes) self.run_buffer(path.to_str().unwrap(), &bytes)
} }
} }
fn extract_lane_as_u32(bytes: &[u8; 16], lane: usize) -> Result<u32> {
let i = lane * 4;
Ok(u32::from_le_bytes(bytes[i..i + 4].try_into()?))
}
fn extract_lane_as_u64(bytes: &[u8; 16], lane: usize) -> Result<u64> {
let i = lane * 8;
Ok(u64::from_le_bytes(bytes[i..i + 8].try_into()?))
}
fn is_canonical_f32_nan(bits: &u32) -> bool {
return (bits & 0x7fffffff) == 0x7fc00000;
}
fn is_canonical_f64_nan(bits: &u64) -> bool {
return (bits & 0x7fffffffffffffff) == 0x7ff8000000000000;
}
fn is_arithmetic_f32_nan(bits: &u32) -> bool {
return (bits & 0x00400000) == 0x00400000;
}
fn is_arithmetic_f64_nan(bits: &u64) -> bool {
return (bits & 0x0008000000000000) == 0x0008000000000000;
}