Preserve signalling bit in NaNs (#87)

* Preserve signalling bit in NaNs

* Fix warnings
This commit is contained in:
Jef 2018-04-25 09:18:14 +02:00 committed by Sergey Pepyakin
parent b95e11c414
commit 3890dd379f
8 changed files with 591 additions and 435 deletions

View File

@ -14,13 +14,8 @@ exclude = [ "/res/*", "/tests/*", "/fuzz/*" ]
parity-wasm = "0.27"
byteorder = "1.0"
memory_units = "0.3.0"
nan-preserving-float = "0.1.0"
[dev-dependencies]
wabt = "~0.2.2"
assert_matches = "1.1"
[features]
# 32-bit platforms are not supported and not tested. Use this flag if you really want to use
# wasmi on these platforms.
# See https://github.com/pepyakin/wasmi/issues/43
opt-in-32bit = []
wabt = "^0.2.3"

View File

@ -64,8 +64,8 @@ fn main() {
function_type.params().iter().enumerate().map(|(i, value)| match value {
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i]))),
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i]))),
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i])).into()),
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i])).into()),
}).collect::<Vec<RuntimeValue>>()
};

View File

@ -105,13 +105,7 @@ extern crate assert_matches;
extern crate parity_wasm;
extern crate byteorder;
extern crate memory_units as memory_units_crate;
#[cfg(all(not(feature = "opt-in-32bit"), target_pointer_width = "32"))]
compile_error! {
"32-bit targets are not supported at the moment.
You can use 'opt-in-32bit' feature.
See https://github.com/pepyakin/wasmi/issues/43"
}
extern crate nan_preserving_float;
use std::fmt;
use std::error;

View File

@ -369,6 +369,7 @@ mod tests {
#[test]
fn alloc() {
#[cfg(target_pointer_width = "64")]
let fixtures = &[
(0, None, true),
(0, Some(0), true),
@ -381,6 +382,17 @@ mod tests {
(65536, Some(0), false),
(65536, None, true),
];
#[cfg(target_pointer_width = "32")]
let fixtures = &[
(0, None, true),
(0, Some(0), true),
(1, None, true),
(1, Some(1), true),
(0, Some(1), true),
(1, Some(0), false),
];
for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() {
let initial: Pages = Pages(initial);
let maximum: Option<Pages> = maybe_max.map(|m| Pages(m));

View File

@ -420,7 +420,7 @@ impl ModuleInstance {
// This check is not only for bailing out early, but also to check the case when
// segment consist of 0 members.
if offset_val as usize + element_segment.members().len() > table_inst.current_size() as usize {
if offset_val as u64 + element_segment.members().len() as u64 > table_inst.current_size() as u64 {
return Err(
Error::Instantiation("elements segment does not fit".to_string())
);

View File

@ -16,6 +16,7 @@ use host::Externals;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType};
use common::stack::StackWithLimit;
use memory_units::Pages;
use nan_preserving_float::{F32, F64};
/// Maximum number of entries in value stack.
pub const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
@ -196,8 +197,8 @@ impl<'a, E: Externals> Interpreter<'a, E> {
&Opcode::I32Load(align, offset) => self.run_load::<i32>(context, align, offset),
&Opcode::I64Load(align, offset) => self.run_load::<i64>(context, align, offset),
&Opcode::F32Load(align, offset) => self.run_load::<f32>(context, align, offset),
&Opcode::F64Load(align, offset) => self.run_load::<f64>(context, align, offset),
&Opcode::F32Load(align, offset) => self.run_load::<F32>(context, align, offset),
&Opcode::F64Load(align, offset) => self.run_load::<F64>(context, align, offset),
&Opcode::I32Load8S(align, offset) => self.run_load_extend::<i8, i32>(context, align, offset),
&Opcode::I32Load8U(align, offset) => self.run_load_extend::<u8, i32>(context, align, offset),
&Opcode::I32Load16S(align, offset) => self.run_load_extend::<i16, i32>(context, align, offset),
@ -211,8 +212,8 @@ impl<'a, E: Externals> Interpreter<'a, E> {
&Opcode::I32Store(align, offset) => self.run_store::<i32>(context, align, offset),
&Opcode::I64Store(align, offset) => self.run_store::<i64>(context, align, offset),
&Opcode::F32Store(align, offset) => self.run_store::<f32>(context, align, offset),
&Opcode::F64Store(align, offset) => self.run_store::<f64>(context, align, offset),
&Opcode::F32Store(align, offset) => self.run_store::<F32>(context, align, offset),
&Opcode::F64Store(align, offset) => self.run_store::<F64>(context, align, offset),
&Opcode::I32Store8(align, offset) => self.run_store_wrap::<i32, i8>(context, align, offset),
&Opcode::I32Store16(align, offset) => self.run_store_wrap::<i32, i16>(context, align, offset),
&Opcode::I64Store8(align, offset) => self.run_store_wrap::<i64, i8>(context, align, offset),
@ -251,19 +252,19 @@ impl<'a, E: Externals> Interpreter<'a, E> {
&Opcode::I64GeS => self.run_gte::<i64>(context),
&Opcode::I64GeU => self.run_gte::<u64>(context),
&Opcode::F32Eq => self.run_eq::<f32>(context),
&Opcode::F32Ne => self.run_ne::<f32>(context),
&Opcode::F32Lt => self.run_lt::<f32>(context),
&Opcode::F32Gt => self.run_gt::<f32>(context),
&Opcode::F32Le => self.run_lte::<f32>(context),
&Opcode::F32Ge => self.run_gte::<f32>(context),
&Opcode::F32Eq => self.run_eq::<F32>(context),
&Opcode::F32Ne => self.run_ne::<F32>(context),
&Opcode::F32Lt => self.run_lt::<F32>(context),
&Opcode::F32Gt => self.run_gt::<F32>(context),
&Opcode::F32Le => self.run_lte::<F32>(context),
&Opcode::F32Ge => self.run_gte::<F32>(context),
&Opcode::F64Eq => self.run_eq::<f64>(context),
&Opcode::F64Ne => self.run_ne::<f64>(context),
&Opcode::F64Lt => self.run_lt::<f64>(context),
&Opcode::F64Gt => self.run_gt::<f64>(context),
&Opcode::F64Le => self.run_lte::<f64>(context),
&Opcode::F64Ge => self.run_gte::<f64>(context),
&Opcode::F64Eq => self.run_eq::<F64>(context),
&Opcode::F64Ne => self.run_ne::<F64>(context),
&Opcode::F64Lt => self.run_lt::<F64>(context),
&Opcode::F64Gt => self.run_gt::<F64>(context),
&Opcode::F64Le => self.run_lte::<F64>(context),
&Opcode::F64Ge => self.run_gte::<F64>(context),
&Opcode::I32Clz => self.run_clz::<i32>(context),
&Opcode::I32Ctz => self.run_ctz::<i32>(context),
@ -303,62 +304,62 @@ impl<'a, E: Externals> Interpreter<'a, E> {
&Opcode::I64Rotl => self.run_rotl::<i64>(context),
&Opcode::I64Rotr => self.run_rotr::<i64>(context),
&Opcode::F32Abs => self.run_abs::<f32>(context),
&Opcode::F32Neg => self.run_neg::<f32>(context),
&Opcode::F32Ceil => self.run_ceil::<f32>(context),
&Opcode::F32Floor => self.run_floor::<f32>(context),
&Opcode::F32Trunc => self.run_trunc::<f32>(context),
&Opcode::F32Nearest => self.run_nearest::<f32>(context),
&Opcode::F32Sqrt => self.run_sqrt::<f32>(context),
&Opcode::F32Add => self.run_add::<f32>(context),
&Opcode::F32Sub => self.run_sub::<f32>(context),
&Opcode::F32Mul => self.run_mul::<f32>(context),
&Opcode::F32Div => self.run_div::<f32, f32>(context),
&Opcode::F32Min => self.run_min::<f32>(context),
&Opcode::F32Max => self.run_max::<f32>(context),
&Opcode::F32Copysign => self.run_copysign::<f32>(context),
&Opcode::F32Abs => self.run_abs::<F32>(context),
&Opcode::F32Neg => self.run_neg::<F32>(context),
&Opcode::F32Ceil => self.run_ceil::<F32>(context),
&Opcode::F32Floor => self.run_floor::<F32>(context),
&Opcode::F32Trunc => self.run_trunc::<F32>(context),
&Opcode::F32Nearest => self.run_nearest::<F32>(context),
&Opcode::F32Sqrt => self.run_sqrt::<F32>(context),
&Opcode::F32Add => self.run_add::<F32>(context),
&Opcode::F32Sub => self.run_sub::<F32>(context),
&Opcode::F32Mul => self.run_mul::<F32>(context),
&Opcode::F32Div => self.run_div::<F32, F32>(context),
&Opcode::F32Min => self.run_min::<F32>(context),
&Opcode::F32Max => self.run_max::<F32>(context),
&Opcode::F32Copysign => self.run_copysign::<F32>(context),
&Opcode::F64Abs => self.run_abs::<f64>(context),
&Opcode::F64Neg => self.run_neg::<f64>(context),
&Opcode::F64Ceil => self.run_ceil::<f64>(context),
&Opcode::F64Floor => self.run_floor::<f64>(context),
&Opcode::F64Trunc => self.run_trunc::<f64>(context),
&Opcode::F64Nearest => self.run_nearest::<f64>(context),
&Opcode::F64Sqrt => self.run_sqrt::<f64>(context),
&Opcode::F64Add => self.run_add::<f64>(context),
&Opcode::F64Sub => self.run_sub::<f64>(context),
&Opcode::F64Mul => self.run_mul::<f64>(context),
&Opcode::F64Div => self.run_div::<f64, f64>(context),
&Opcode::F64Min => self.run_min::<f64>(context),
&Opcode::F64Max => self.run_max::<f64>(context),
&Opcode::F64Copysign => self.run_copysign::<f64>(context),
&Opcode::F64Abs => self.run_abs::<F64>(context),
&Opcode::F64Neg => self.run_neg::<F64>(context),
&Opcode::F64Ceil => self.run_ceil::<F64>(context),
&Opcode::F64Floor => self.run_floor::<F64>(context),
&Opcode::F64Trunc => self.run_trunc::<F64>(context),
&Opcode::F64Nearest => self.run_nearest::<F64>(context),
&Opcode::F64Sqrt => self.run_sqrt::<F64>(context),
&Opcode::F64Add => self.run_add::<F64>(context),
&Opcode::F64Sub => self.run_sub::<F64>(context),
&Opcode::F64Mul => self.run_mul::<F64>(context),
&Opcode::F64Div => self.run_div::<F64, F64>(context),
&Opcode::F64Min => self.run_min::<F64>(context),
&Opcode::F64Max => self.run_max::<F64>(context),
&Opcode::F64Copysign => self.run_copysign::<F64>(context),
&Opcode::I32WrapI64 => self.run_wrap::<i64, i32>(context),
&Opcode::I32TruncSF32 => self.run_trunc_to_int::<f32, i32, i32>(context),
&Opcode::I32TruncUF32 => self.run_trunc_to_int::<f32, u32, i32>(context),
&Opcode::I32TruncSF64 => self.run_trunc_to_int::<f64, i32, i32>(context),
&Opcode::I32TruncUF64 => self.run_trunc_to_int::<f64, u32, i32>(context),
&Opcode::I32TruncSF32 => self.run_trunc_to_int::<F32, i32, i32>(context),
&Opcode::I32TruncUF32 => self.run_trunc_to_int::<F32, u32, i32>(context),
&Opcode::I32TruncSF64 => self.run_trunc_to_int::<F64, i32, i32>(context),
&Opcode::I32TruncUF64 => self.run_trunc_to_int::<F64, u32, i32>(context),
&Opcode::I64ExtendSI32 => self.run_extend::<i32, i64, i64>(context),
&Opcode::I64ExtendUI32 => self.run_extend::<u32, u64, i64>(context),
&Opcode::I64TruncSF32 => self.run_trunc_to_int::<f32, i64, i64>(context),
&Opcode::I64TruncUF32 => self.run_trunc_to_int::<f32, u64, i64>(context),
&Opcode::I64TruncSF64 => self.run_trunc_to_int::<f64, i64, i64>(context),
&Opcode::I64TruncUF64 => self.run_trunc_to_int::<f64, u64, i64>(context),
&Opcode::F32ConvertSI32 => self.run_extend::<i32, f32, f32>(context),
&Opcode::F32ConvertUI32 => self.run_extend::<u32, f32, f32>(context),
&Opcode::F32ConvertSI64 => self.run_wrap::<i64, f32>(context),
&Opcode::F32ConvertUI64 => self.run_wrap::<u64, f32>(context),
&Opcode::F32DemoteF64 => self.run_wrap::<f64, f32>(context),
&Opcode::F64ConvertSI32 => self.run_extend::<i32, f64, f64>(context),
&Opcode::F64ConvertUI32 => self.run_extend::<u32, f64, f64>(context),
&Opcode::F64ConvertSI64 => self.run_extend::<i64, f64, f64>(context),
&Opcode::F64ConvertUI64 => self.run_extend::<u64, f64, f64>(context),
&Opcode::F64PromoteF32 => self.run_extend::<f32, f64, f64>(context),
&Opcode::I64TruncSF32 => self.run_trunc_to_int::<F32, i64, i64>(context),
&Opcode::I64TruncUF32 => self.run_trunc_to_int::<F32, u64, i64>(context),
&Opcode::I64TruncSF64 => self.run_trunc_to_int::<F64, i64, i64>(context),
&Opcode::I64TruncUF64 => self.run_trunc_to_int::<F64, u64, i64>(context),
&Opcode::F32ConvertSI32 => self.run_extend::<i32, F32, F32>(context),
&Opcode::F32ConvertUI32 => self.run_extend::<u32, F32, F32>(context),
&Opcode::F32ConvertSI64 => self.run_wrap::<i64, F32>(context),
&Opcode::F32ConvertUI64 => self.run_wrap::<u64, F32>(context),
&Opcode::F32DemoteF64 => self.run_wrap::<F64, F32>(context),
&Opcode::F64ConvertSI32 => self.run_extend::<i32, F64, F64>(context),
&Opcode::F64ConvertUI32 => self.run_extend::<u32, F64, F64>(context),
&Opcode::F64ConvertSI64 => self.run_extend::<i64, F64, F64>(context),
&Opcode::F64ConvertUI64 => self.run_extend::<u64, F64, F64>(context),
&Opcode::F64PromoteF32 => self.run_extend::<F32, F64, F64>(context),
&Opcode::I32ReinterpretF32 => self.run_reinterpret::<f32, i32>(context),
&Opcode::I64ReinterpretF64 => self.run_reinterpret::<f64, i64>(context),
&Opcode::F32ReinterpretI32 => self.run_reinterpret::<i32, f32>(context),
&Opcode::F64ReinterpretI64 => self.run_reinterpret::<i64, f64>(context),
&Opcode::I32ReinterpretF32 => self.run_reinterpret::<F32, i32>(context),
&Opcode::I64ReinterpretF64 => self.run_reinterpret::<F64, i64>(context),
&Opcode::F32ReinterpretI32 => self.run_reinterpret::<i32, F32>(context),
&Opcode::F64ReinterpretI64 => self.run_reinterpret::<i64, F64>(context),
}
}

View File

@ -1,6 +1,7 @@
use std::{i32, i64, u32, u64, f32};
use std::io;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use nan_preserving_float::{F32, F64};
use std::io;
use std::{f32, i32, i64, u32, u64};
use TrapKind;
#[derive(Debug)]
@ -22,9 +23,9 @@ pub enum RuntimeValue {
/// Value of 64-bit signed or unsigned integer.
I64(i64),
/// Value of 32-bit IEEE 754-2008 floating point number.
F32(f32),
F32(F32),
/// Value of 64-bit IEEE 754-2008 floating point number.
F64(f64),
F64(F64),
}
/// Trait for creating value from a [`RuntimeValue`].
@ -136,19 +137,19 @@ impl RuntimeValue {
match value_type {
::types::ValueType::I32 => RuntimeValue::I32(0),
::types::ValueType::I64 => RuntimeValue::I64(0),
::types::ValueType::F32 => RuntimeValue::F32(0f32),
::types::ValueType::F64 => RuntimeValue::F64(0f64),
::types::ValueType::F32 => RuntimeValue::F32(0f32.into()),
::types::ValueType::F64 => RuntimeValue::F64(0f64.into()),
}
}
/// Creates new value by interpreting passed u32 as f32.
pub fn decode_f32(val: u32) -> Self {
RuntimeValue::F32(f32::from_bits(val))
RuntimeValue::F32(F32::from_bits(val))
}
/// Creates new value by interpreting passed u64 as f64.
pub fn decode_f64(val: u64) -> Self {
RuntimeValue::F64(f64::from_bits(val))
RuntimeValue::F64(F64::from_bits(val))
}
/// Get variable type for this value.
@ -197,14 +198,14 @@ impl From<u64> for RuntimeValue {
}
}
impl From<f32> for RuntimeValue {
fn from(val: f32) -> Self {
impl From<F32> for RuntimeValue {
fn from(val: F32) -> Self {
RuntimeValue::F32(val)
}
}
impl From<f64> for RuntimeValue {
fn from(val: f64) -> Self {
impl From<F64> for RuntimeValue {
fn from(val: F64) -> Self {
RuntimeValue::F64(val)
}
}
@ -214,7 +215,7 @@ macro_rules! impl_from_runtime_value {
impl FromRuntimeValue for $into {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
match val {
RuntimeValue::$expected_rt_ty(val) => Some(val as $into),
RuntimeValue::$expected_rt_ty(val) => Some(val.transmute_into()),
_ => None,
}
}
@ -237,19 +238,26 @@ impl FromRuntimeValue for bool {
impl_from_runtime_value!(I32, i32);
impl_from_runtime_value!(I64, i64);
impl_from_runtime_value!(F32, f32);
impl_from_runtime_value!(F64, f64);
impl_from_runtime_value!(F32, F32);
impl_from_runtime_value!(F64, F64);
impl_from_runtime_value!(I32, u32);
impl_from_runtime_value!(I64, u64);
macro_rules! impl_wrap_into {
($from: ident, $into: ident) => {
($from:ident, $into:ident) => {
impl WrapInto<$into> for $from {
fn wrap_into(self) -> $into {
self as $into
}
}
}
};
($from:ident, $intermediate:ident, $into:ident) => {
impl WrapInto<$into> for $from {
fn wrap_into(self) -> $into {
$into::from(self as $intermediate)
}
}
};
}
impl_wrap_into!(i32, i8);
@ -257,13 +265,19 @@ impl_wrap_into!(i32, i16);
impl_wrap_into!(i64, i8);
impl_wrap_into!(i64, i16);
impl_wrap_into!(i64, i32);
impl_wrap_into!(i64, f32);
impl_wrap_into!(u64, f32);
impl_wrap_into!(i64, f32, F32);
impl_wrap_into!(u64, f32, F32);
// Casting from an f64 to an f32 will produce the closest possible value (rounding strategy unspecified)
// NOTE: currently this will cause Undefined Behavior if the value is finite but larger or smaller than the
// largest or smallest finite value representable by f32. This is a bug and will be fixed.
impl_wrap_into!(f64, f32);
impl WrapInto<F32> for F64 {
fn wrap_into(self) -> F32 {
(f64::from(self) as f32).into()
}
}
macro_rules! impl_try_truncate_into {
($from: ident, $into: ident) => {
impl TryTruncateInto<$into, TrapKind> for $from {
@ -284,7 +298,14 @@ macro_rules! impl_try_truncate_into {
Ok(self as $into)
}
}
}
};
($from:ident, $intermediate:ident, $into:ident) => {
impl TryTruncateInto<$into, TrapKind> for $from {
fn try_truncate_into(self) -> Result<$into, TrapKind> {
$intermediate::from(self).try_truncate_into()
}
}
};
}
impl_try_truncate_into!(f32, i32);
@ -295,15 +316,30 @@ impl_try_truncate_into!(f32, u32);
impl_try_truncate_into!(f32, u64);
impl_try_truncate_into!(f64, u32);
impl_try_truncate_into!(f64, u64);
impl_try_truncate_into!(F32, f32, i32);
impl_try_truncate_into!(F32, f32, i64);
impl_try_truncate_into!(F64, f64, i32);
impl_try_truncate_into!(F64, f64, i64);
impl_try_truncate_into!(F32, f32, u32);
impl_try_truncate_into!(F32, f32, u64);
impl_try_truncate_into!(F64, f64, u32);
impl_try_truncate_into!(F64, f64, u64);
macro_rules! impl_extend_into {
($from: ident, $into: ident) => {
($from:ident, $into:ident) => {
impl ExtendInto<$into> for $from {
fn extend_into(self) -> $into {
self as $into
}
}
}
};
($from:ident, $intermediate:ident, $into:ident) => {
impl ExtendInto<$into> for $from {
fn extend_into(self) -> $into {
$into::from(self as $intermediate)
}
}
};
}
impl_extend_into!(i8, i32);
@ -325,6 +361,20 @@ impl_extend_into!(i64, f64);
impl_extend_into!(u64, f64);
impl_extend_into!(f32, f64);
impl_extend_into!(i32, f32, F32);
impl_extend_into!(i32, f64, F64);
impl_extend_into!(u32, f32, F32);
impl_extend_into!(u32, f64, F64);
impl_extend_into!(i64, f64, F64);
impl_extend_into!(u64, f64, F64);
impl_extend_into!(f32, f64, F64);
impl ExtendInto<F64> for F32 {
fn extend_into(self) -> F64 {
(f32::from(self) as f64).into()
}
}
macro_rules! impl_transmute_into_self {
($type: ident) => {
impl TransmuteInto<$type> for $type {
@ -339,6 +389,8 @@ impl_transmute_into_self!(i32);
impl_transmute_into_self!(i64);
impl_transmute_into_self!(f32);
impl_transmute_into_self!(f64);
impl_transmute_into_self!(F32);
impl_transmute_into_self!(F64);
macro_rules! impl_transmute_into_as {
($from: ident, $into: ident) => {
@ -357,6 +409,49 @@ impl_transmute_into_as!(u32, i32);
impl_transmute_into_as!(i64, u64);
impl_transmute_into_as!(u64, i64);
macro_rules! impl_transmute_into_npf {
($npf:ident, $float:ident, $signed:ident, $unsigned:ident) => {
impl TransmuteInto<$float> for $npf {
fn transmute_into(self) -> $float {
self.into()
}
}
impl TransmuteInto<$npf> for $float {
fn transmute_into(self) -> $npf {
self.into()
}
}
impl TransmuteInto<$signed> for $npf {
fn transmute_into(self) -> $signed {
self.to_bits() as _
}
}
impl TransmuteInto<$unsigned> for $npf {
fn transmute_into(self) -> $unsigned {
self.to_bits()
}
}
impl TransmuteInto<$npf> for $signed {
fn transmute_into(self) -> $npf {
$npf::from_bits(self as _)
}
}
impl TransmuteInto<$npf> for $unsigned {
fn transmute_into(self) -> $npf {
$npf::from_bits(self)
}
}
};
}
impl_transmute_into_npf!(F32, f32, i32, u32);
impl_transmute_into_npf!(F64, f64, i64, u64);
impl TransmuteInto<i32> for f32 {
fn transmute_into(self) -> i32 { self.to_bits() as i32 }
}
@ -497,6 +592,26 @@ impl LittleEndianConvert for f64 {
}
}
impl LittleEndianConvert for F32 {
fn into_little_endian(self) -> Vec<u8> {
(self.to_bits() as i32).into_little_endian()
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
i32::from_little_endian(buffer).map(|val| Self::from_bits(val as _))
}
}
impl LittleEndianConvert for F64 {
fn into_little_endian(self) -> Vec<u8> {
(self.to_bits() as i64).into_little_endian()
}
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
i64::from_little_endian(buffer).map(|val| Self::from_bits(val as _))
}
}
macro_rules! impl_integer_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
@ -538,6 +653,8 @@ macro_rules! impl_float_arithmetic_ops {
impl_float_arithmetic_ops!(f32);
impl_float_arithmetic_ops!(f64);
impl_float_arithmetic_ops!(F32);
impl_float_arithmetic_ops!(F64);
macro_rules! impl_integer {
($type: ident) => {
@ -561,13 +678,26 @@ impl_integer!(i64);
impl_integer!(u64);
macro_rules! impl_float {
($type: ident, $int_type: ident) => {
($type:ident, $int_type:ident) => {
impl_float!($type, $type, $int_type);
};
($type:ident, $intermediate:ident, $int_type:ident) => {
impl Float<$type> for $type {
fn abs(self) -> $type { self.abs() }
fn floor(self) -> $type { self.floor() }
fn ceil(self) -> $type { self.ceil() }
fn trunc(self) -> $type { self.trunc() }
fn round(self) -> $type { self.round() }
fn abs(self) -> $type {
$intermediate::abs(self.into()).into()
}
fn floor(self) -> $type {
$intermediate::floor(self.into()).into()
}
fn ceil(self) -> $type {
$intermediate::ceil(self.into()).into()
}
fn trunc(self) -> $type {
$intermediate::trunc(self.into()).into()
}
fn round(self) -> $type {
$intermediate::round(self.into()).into()
}
fn nearest(self) -> $type {
let round = self.round();
if self.fract().abs() != 0.5 {
@ -583,12 +713,13 @@ macro_rules! impl_float {
round
}
}
fn sqrt(self) -> $type { self.sqrt() }
fn sqrt(self) -> $type {
$intermediate::sqrt(self.into()).into()
}
// This instruction corresponds to what is sometimes called "minNaN" in other languages.
fn min(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() {
use std::$type;
return $type::NAN;
return ::std::$intermediate::NAN.into();
}
self.min(other)
@ -596,8 +727,7 @@ macro_rules! impl_float {
// This instruction corresponds to what is sometimes called "maxNaN" in other languages.
fn max(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() {
use std::$type;
return $type::NAN;
return ::std::$intermediate::NAN.into();
}
self.max(other)
@ -623,8 +753,10 @@ macro_rules! impl_float {
}
}
}
}
};
}
impl_float!(f32, i32);
impl_float!(f64, i64);
impl_float!(F32, f32, i32);
impl_float!(F64, f64, i64);

View File

@ -2,22 +2,20 @@
use std::collections::HashMap;
use wasmi::{
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap,
};
use wasmi::memory_units::Pages;
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
use wasmi::memory_units::Pages;
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap};
fn spec_to_runtime_value(value: Value) -> RuntimeValue {
match value {
Value::I32(v) => RuntimeValue::I32(v),
Value::I64(v) => RuntimeValue::I64(v),
Value::F32(v) => RuntimeValue::F32(v),
Value::F64(v) => RuntimeValue::F64(v),
}
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
match val {
Value::I32(v) => RuntimeValue::I32(v),
Value::I64(v) => RuntimeValue::I64(v),
Value::F32(v) => RuntimeValue::F32(v.into()),
Value::F64(v) => RuntimeValue::F64(v.into()),
}
}
#[derive(Debug)]
@ -49,15 +47,15 @@ struct SpecModule {
}
impl SpecModule {
fn new() -> Self {
SpecModule {
table: TableInstance::alloc(10, Some(20)).unwrap(),
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0), false),
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0), false),
}
}
fn new() -> Self {
SpecModule {
table: TableInstance::alloc(10, Some(20)).unwrap(),
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false),
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false),
}
}
}
const PRINT_FUNC_INDEX: usize = 0;
@ -121,341 +119,365 @@ impl ModuleImportResolver for SpecModule {
_ => Err(InterpreterError::Instantiation(format!(
"Unknown host global import {}",
field_name
)))
))),
}
}
}
fn resolve_memory(
&self,
field_name: &str,
_memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, InterpreterError> {
if field_name == "memory" {
return Ok(self.memory.clone());
}
fn resolve_memory(
&self,
field_name: &str,
_memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, InterpreterError> {
if field_name == "memory" {
return Ok(self.memory.clone());
}
Err(InterpreterError::Instantiation(format!(
"Unknown host memory import {}",
field_name
)))
}
Err(InterpreterError::Instantiation(format!(
"Unknown host memory import {}",
field_name
)))
}
fn resolve_table(
&self,
field_name: &str,
_table_type: &TableDescriptor,
) -> Result<TableRef, InterpreterError> {
if field_name == "table" {
return Ok(self.table.clone());
}
fn resolve_table(
&self,
field_name: &str,
_table_type: &TableDescriptor,
) -> Result<TableRef, InterpreterError> {
if field_name == "table" {
return Ok(self.table.clone());
}
Err(InterpreterError::Instantiation(format!(
"Unknown host table import {}",
field_name
)))
}
Err(InterpreterError::Instantiation(format!(
"Unknown host table import {}",
field_name
)))
}
}
struct SpecDriver {
spec_module: SpecModule,
instances: HashMap<String, ModuleRef>,
last_module: Option<ModuleRef>,
spec_module: SpecModule,
instances: HashMap<String, ModuleRef>,
last_module: Option<ModuleRef>,
}
impl SpecDriver {
fn new() -> SpecDriver {
SpecDriver {
spec_module: SpecModule::new(),
instances: HashMap::new(),
last_module: None,
}
}
fn new() -> SpecDriver {
SpecDriver {
spec_module: SpecModule::new(),
instances: HashMap::new(),
last_module: None,
}
}
fn spec_module(&mut self) -> &mut SpecModule {
&mut self.spec_module
}
fn spec_module(&mut self) -> &mut SpecModule {
&mut self.spec_module
}
fn add_module(&mut self, name: Option<String>, module: ModuleRef) {
self.last_module = Some(module.clone());
if let Some(name) = name {
self.instances.insert(name, module);
}
}
fn add_module(&mut self, name: Option<String>, module: ModuleRef) {
self.last_module = Some(module.clone());
if let Some(name) = name {
self.instances.insert(name, module);
}
}
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
self.instances.get(name).cloned().ok_or_else(|| {
InterpreterError::Instantiation(format!("Module not registered {}", name))
})
}
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
self.instances.get(name).cloned().ok_or_else(|| {
InterpreterError::Instantiation(format!("Module not registered {}", name))
})
}
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
match name {
Some(name) => self.module(name),
None => self.last_module
.clone()
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
}
}
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
match name {
Some(name) => self.module(name),
None => self.last_module
.clone()
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
}
}
}
impl ImportResolver for SpecDriver {
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
func_type: &Signature,
) -> Result<FuncRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_func(field_name, func_type)
} else {
self.module(module_name)?
.resolve_func(field_name, func_type)
}
}
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
func_type: &Signature,
) -> Result<FuncRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_func(field_name, func_type)
} else {
self.module(module_name)?
.resolve_func(field_name, func_type)
}
}
fn resolve_global(
&self,
module_name: &str,
field_name: &str,
global_type: &GlobalDescriptor,
) -> Result<GlobalRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_global(field_name, global_type)
} else {
self.module(module_name)?
.resolve_global(field_name, global_type)
}
}
fn resolve_global(
&self,
module_name: &str,
field_name: &str,
global_type: &GlobalDescriptor,
) -> Result<GlobalRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_global(field_name, global_type)
} else {
self.module(module_name)?
.resolve_global(field_name, global_type)
}
}
fn resolve_memory(
&self,
module_name: &str,
field_name: &str,
memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_memory(field_name, memory_type)
} else {
self.module(module_name)?
.resolve_memory(field_name, memory_type)
}
}
fn resolve_memory(
&self,
module_name: &str,
field_name: &str,
memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_memory(field_name, memory_type)
} else {
self.module(module_name)?
.resolve_memory(field_name, memory_type)
}
}
fn resolve_table(
&self,
module_name: &str,
field_name: &str,
table_type: &TableDescriptor,
) -> Result<TableRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_table(field_name, table_type)
} else {
self.module(module_name)?
.resolve_table(field_name, table_type)
}
}
fn resolve_table(
&self,
module_name: &str,
field_name: &str,
table_type: &TableDescriptor,
) -> Result<TableRef, InterpreterError> {
if module_name == "spectest" {
self.spec_module.resolve_table(field_name, table_type)
} else {
self.module(module_name)?
.resolve_table(field_name, table_type)
}
}
}
fn try_load_module(wasm: &[u8]) -> Result<Module, Error> {
Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string()))
Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string()))
}
fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> {
let module = try_load_module(wasm)?;
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
instance
.run_start(spec_driver.spec_module())
.map_err(|trap| Error::Start(trap))?;
Ok(())
let module = try_load_module(wasm)?;
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
instance
.run_start(spec_driver.spec_module())
.map_err(|trap| Error::Start(trap))?;
Ok(())
}
fn load_module(wasm: &[u8], name: &Option<String>, spec_driver: &mut SpecDriver) -> Result<ModuleRef, Error> {
let module = try_load_module(wasm)?;
let instance = ModuleInstance::new(&module, spec_driver)
.map_err(|e| Error::Load(e.to_string()))?
.run_start(spec_driver.spec_module())
.map_err(|trap| Error::Start(trap))?;
fn load_module(
wasm: &[u8],
name: &Option<String>,
spec_driver: &mut SpecDriver,
) -> Result<ModuleRef, Error> {
let module = try_load_module(wasm)?;
let instance = ModuleInstance::new(&module, spec_driver)
.map_err(|e| Error::Load(e.to_string()))?
.run_start(spec_driver.spec_module())
.map_err(|trap| Error::Start(trap))?;
let module_name = name.clone();
spec_driver.add_module(module_name, instance.clone());
let module_name = name.clone();
spec_driver.add_module(module_name, instance.clone());
Ok(instance)
Ok(instance)
}
fn run_action(
program: &mut SpecDriver,
action: &Action,
program: &mut SpecDriver,
action: &Action<u32, u64>,
) -> Result<Option<RuntimeValue>, InterpreterError> {
match *action {
Action::Invoke {
ref module,
ref field,
ref args,
} => {
let module = program
.module_or_last(module.as_ref().map(|x| x.as_ref()))
.expect(&format!(
"Expected program to have loaded module {:?}",
module
));
module.invoke_export(
field,
&args.iter()
.cloned()
.map(spec_to_runtime_value)
.collect::<Vec<_>>(),
program.spec_module(),
)
}
Action::Get {
ref module,
ref field,
..
} => {
let module = program
.module_or_last(module.as_ref().map(|x| x.as_ref()))
.expect(&format!(
"Expected program to have loaded module {:?}",
module
));
let global = module
.export_by_name(&field)
.ok_or_else(|| {
InterpreterError::Global(format!("Expected to have export with name {}", field))
})?
.as_global()
.cloned()
.ok_or_else(|| {
InterpreterError::Global(format!("Expected export {} to be a global", field))
})?;
Ok(Some(global.get()))
}
}
match *action {
Action::Invoke {
ref module,
ref field,
ref args,
} => {
let module = program
.module_or_last(module.as_ref().map(|x| x.as_ref()))
.expect(&format!(
"Expected program to have loaded module {:?}",
module
));
let vec_args = args.iter()
.cloned()
.map(spec_to_runtime_value)
.collect::<Vec<_>>();
module.invoke_export(field, &vec_args, program.spec_module())
}
Action::Get {
ref module,
ref field,
..
} => {
let module = program
.module_or_last(module.as_ref().map(|x| x.as_ref()))
.expect(&format!(
"Expected program to have loaded module {:?}",
module
));
let global = module
.export_by_name(&field)
.ok_or_else(|| {
InterpreterError::Global(format!("Expected to have export with name {}", field))
})?
.as_global()
.cloned()
.ok_or_else(|| {
InterpreterError::Global(format!("Expected export {} to be a global", field))
})?;
Ok(Some(global.get()))
}
}
}
pub fn spec(name: &str) {
println!("running test: {}", name);
try_spec(name).expect("Failed to run spec");
println!("running test: {}", name);
try_spec(name).expect("Failed to run spec");
}
fn try_spec(name: &str) -> Result<(), Error> {
let mut spec_driver = SpecDriver::new();
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name);
let mut parser = ScriptParser::from_file(spec_script_path).expect("Can't read spec script");
while let Some(Command { kind, line }) = parser.next()? {
println!("Line {}:", line);
match kind {
CommandKind::Module { name, module, .. } => {
load_module(&module.into_vec()?, &name, &mut spec_driver).expect("Failed to load module");
}
CommandKind::AssertReturn { action, expected } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => {
let spec_expected = expected
.iter()
.cloned()
.map(spec_to_runtime_value)
.collect::<Vec<_>>();
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
for (actual_result, spec_expected) in
actual_result.iter().zip(spec_expected.iter())
{
assert_eq!(actual_result.value_type(), spec_expected.value_type());
// f32::NAN != f32::NAN
match spec_expected {
&RuntimeValue::F32(val) if val.is_nan() => match actual_result {
&RuntimeValue::F32(val) => assert!(val.is_nan()),
_ => unreachable!(), // checked above that types are same
},
&RuntimeValue::F64(val) if val.is_nan() => match actual_result {
&RuntimeValue::F64(val) => assert!(val.is_nan()),
_ => unreachable!(), // checked above that types are same
},
spec_expected @ _ => assert_eq!(actual_result, spec_expected),
}
}
println!("assert_return at line {} - success", line);
}
Err(e) => {
panic!("Expected action to return value, got error: {:?}", e);
}
}
}
CommandKind::AssertReturnCanonicalNan { action }
| CommandKind::AssertReturnArithmeticNan { action } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => {
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
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);
}
}
}
CommandKind::AssertExhaustion { action, .. } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
Err(e) => println!("assert_exhaustion at line {} - success ({:?})", line, e),
}
}
CommandKind::AssertTrap { action, .. } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => {
panic!(
"Expected action to result in a trap, got result: {:?}",
result
);
}
Err(e) => {
println!("assert_trap at line {} - success ({:?})", line, e);
}
}
}
CommandKind::AssertInvalid { module, .. }
| CommandKind::AssertMalformed { module, .. }
| CommandKind::AssertUnlinkable { module, .. } => {
let module_load = try_load(&module.into_vec()?, &mut spec_driver);
match module_load {
Ok(_) => panic!("Expected invalid module definition, got some module!"),
Err(e) => println!("assert_invalid at line {} - success ({:?})", line, e),
}
}
CommandKind::AssertUninstantiable { module, .. } => {
match try_load(&module.into_vec()?, &mut spec_driver) {
Ok(_) => panic!("Expected error running start function at line {}", line),
Err(e) => println!("assert_uninstantiable - success ({:?})", e),
}
}
CommandKind::Register { name, as_name, .. } => {
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
Ok(module) => module,
Err(e) => panic!("No such module, at line {} - ({:?})", e, line),
};
spec_driver.add_module(Some(as_name.clone()), module);
}
CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) {
Ok(_) => {}
Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e),
},
}
}
let mut spec_driver = SpecDriver::new();
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name);
let mut parser = ScriptParser::from_file(spec_script_path).expect("Can't read spec script");
let mut errors = vec![];
Ok(())
while let Some(Command { kind, line }) = parser.next()? {
macro_rules! assert_eq {
($a:expr, $b:expr) => {{
let (a, b) = ($a, $b);
if a != b {
errors.push(format!(
r#"ERROR (line {}):
expected: {:?}
got: {:?}
"#,
line, b, a,
));
}
}};
}
match kind {
CommandKind::Module { name, module, .. } => {
load_module(&module.into_vec()?, &name, &mut spec_driver)
.expect("Failed to load module");
}
CommandKind::AssertReturn { action, expected } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => {
let spec_expected = expected
.iter()
.cloned()
.map(spec_to_runtime_value)
.collect::<Vec<_>>();
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
for (actual_result, spec_expected) in
actual_result.iter().zip(spec_expected.iter())
{
assert_eq!(actual_result.value_type(), spec_expected.value_type());
// f32::NAN != f32::NAN
match spec_expected {
&RuntimeValue::F32(val) if val.is_nan() => match actual_result {
&RuntimeValue::F32(val) => assert!(val.is_nan()),
_ => unreachable!(), // checked above that types are same
},
&RuntimeValue::F64(val) if val.is_nan() => match actual_result {
&RuntimeValue::F64(val) => assert!(val.is_nan()),
_ => unreachable!(), // checked above that types are same
},
spec_expected @ _ => assert_eq!(actual_result, spec_expected),
}
}
}
Err(e) => {
panic!("Expected action to return value, got error: {:?}", e);
}
}
}
CommandKind::AssertReturnCanonicalNan { action }
| CommandKind::AssertReturnArithmeticNan { action } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => {
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
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)
}
}
}
}
Err(e) => {
panic!("Expected action to return value, got error: {:?}", e);
}
}
}
CommandKind::AssertExhaustion { action, .. } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
Err(_e) => {},
}
}
CommandKind::AssertTrap { action, .. } => {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => {
panic!(
"Expected action to result in a trap, got result: {:?}",
result
);
}
Err(_e) => {}
}
}
CommandKind::AssertInvalid { module, .. }
| CommandKind::AssertMalformed { module, .. }
| CommandKind::AssertUnlinkable { module, .. } => {
let module_load = try_load(&module.into_vec()?, &mut spec_driver);
match module_load {
Ok(_) => panic!("Expected invalid module definition, got some module!"),
Err(_e) => {},
}
}
CommandKind::AssertUninstantiable { module, .. } => {
match try_load(&module.into_vec()?, &mut spec_driver) {
Ok(_) => panic!("Expected error running start function at line {}", line),
Err(_e) => {},
}
}
CommandKind::Register { name, as_name, .. } => {
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
Ok(module) => module,
Err(e) => panic!("No such module, at line {} - ({:?})", e, line),
};
spec_driver.add_module(Some(as_name.clone()), module);
}
CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) {
Ok(_) => {}
Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e),
},
}
}
if !errors.is_empty() {
use std::fmt::Write;
let mut out = "\n".to_owned();
for err in errors {
write!(out, "{}", err).expect("Error formatting errors");
}
panic!(out);
}
Ok(())
}