diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 03b1ba07df..af24b7e51e 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -14,7 +14,7 @@ edition = "2018" wasmtime-jit = { path = "../jit" } wasmtime-runtime = { path = "../runtime" } wasmtime-environ = { path = "../environ" } -wast = "3.0.0" +wast = "4.0.0" anyhow = "1.0.19" target-lexicon = "0.9.0" diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index d2d5305148..17f353bf12 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -1,5 +1,6 @@ use crate::spectest::instantiate_spectest; use anyhow::{bail, Context as _, Result}; +use std::convert::TryInto; use std::path::Path; use std::str; use wasmtime_jit::{ @@ -254,12 +255,12 @@ impl WastContext { bail!("{}\nunexpected vector in NaN test", context(span)) } RuntimeValue::F32(x) => { - if (x & 0x7fffffff) != 0x7fc00000 { + if !is_canonical_f32_nan(x) { bail!("{}\nexpected canonical NaN", context(span)) } } RuntimeValue::F64(x) => { - if (x & 0x7fffffffffffffff) != 0x7ff8000000000000 { + if !is_canonical_f64_nan(x) { 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 } => { match self.perform_invoke(invoke).with_context(|| context(span))? { ActionOutcome::Returned { values } => { @@ -283,12 +346,12 @@ impl WastContext { bail!("{}\nunexpected vector in NaN test", context(span)) } RuntimeValue::F32(x) => { - if (x & 0x00400000) != 0x00400000 { + if !is_arithmetic_f32_nan(x) { bail!("{}\nexpected arithmetic NaN", context(span)) } } RuntimeValue::F64(x) => { - if (x & 0x0008000000000000) != 0x0008000000000000 { + if !is_arithmetic_f64_nan(x) { 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 { span, mut module, @@ -384,3 +509,29 @@ impl WastContext { self.run_buffer(path.to_str().unwrap(), &bytes) } } + +fn extract_lane_as_u32(bytes: &[u8; 16], lane: usize) -> Result { + 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 { + 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; +}