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" parity-wasm = "0.27"
byteorder = "1.0" byteorder = "1.0"
memory_units = "0.3.0" memory_units = "0.3.0"
nan-preserving-float = "0.1.0"
[dev-dependencies] [dev-dependencies]
wabt = "~0.2.2"
assert_matches = "1.1" assert_matches = "1.1"
wabt = "^0.2.3"
[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 = []

View File

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

View File

@ -105,13 +105,7 @@ extern crate assert_matches;
extern crate parity_wasm; extern crate parity_wasm;
extern crate byteorder; extern crate byteorder;
extern crate memory_units as memory_units_crate; extern crate memory_units as memory_units_crate;
extern crate nan_preserving_float;
#[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"
}
use std::fmt; use std::fmt;
use std::error; use std::error;

View File

@ -369,6 +369,7 @@ mod tests {
#[test] #[test]
fn alloc() { fn alloc() {
#[cfg(target_pointer_width = "64")]
let fixtures = &[ let fixtures = &[
(0, None, true), (0, None, true),
(0, Some(0), true), (0, Some(0), true),
@ -381,6 +382,17 @@ mod tests {
(65536, Some(0), false), (65536, Some(0), false),
(65536, None, true), (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() { for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() {
let initial: Pages = Pages(initial); let initial: Pages = Pages(initial);
let maximum: Option<Pages> = maybe_max.map(|m| Pages(m)); 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 // This check is not only for bailing out early, but also to check the case when
// segment consist of 0 members. // 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( return Err(
Error::Instantiation("elements segment does not fit".to_string()) 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::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType};
use common::stack::StackWithLimit; use common::stack::StackWithLimit;
use memory_units::Pages; use memory_units::Pages;
use nan_preserving_float::{F32, F64};
/// Maximum number of entries in value stack. /// Maximum number of entries in value stack.
pub const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; 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::I32Load(align, offset) => self.run_load::<i32>(context, align, offset),
&Opcode::I64Load(align, offset) => self.run_load::<i64>(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::F32Load(align, offset) => self.run_load::<F32>(context, align, offset),
&Opcode::F64Load(align, offset) => self.run_load::<f64>(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::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::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), &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::I32Store(align, offset) => self.run_store::<i32>(context, align, offset),
&Opcode::I64Store(align, offset) => self.run_store::<i64>(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::F32Store(align, offset) => self.run_store::<F32>(context, align, offset),
&Opcode::F64Store(align, offset) => self.run_store::<f64>(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::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::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), &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::I64GeS => self.run_gte::<i64>(context),
&Opcode::I64GeU => self.run_gte::<u64>(context), &Opcode::I64GeU => self.run_gte::<u64>(context),
&Opcode::F32Eq => self.run_eq::<f32>(context), &Opcode::F32Eq => self.run_eq::<F32>(context),
&Opcode::F32Ne => self.run_ne::<f32>(context), &Opcode::F32Ne => self.run_ne::<F32>(context),
&Opcode::F32Lt => self.run_lt::<f32>(context), &Opcode::F32Lt => self.run_lt::<F32>(context),
&Opcode::F32Gt => self.run_gt::<f32>(context), &Opcode::F32Gt => self.run_gt::<F32>(context),
&Opcode::F32Le => self.run_lte::<f32>(context), &Opcode::F32Le => self.run_lte::<F32>(context),
&Opcode::F32Ge => self.run_gte::<f32>(context), &Opcode::F32Ge => self.run_gte::<F32>(context),
&Opcode::F64Eq => self.run_eq::<f64>(context), &Opcode::F64Eq => self.run_eq::<F64>(context),
&Opcode::F64Ne => self.run_ne::<f64>(context), &Opcode::F64Ne => self.run_ne::<F64>(context),
&Opcode::F64Lt => self.run_lt::<f64>(context), &Opcode::F64Lt => self.run_lt::<F64>(context),
&Opcode::F64Gt => self.run_gt::<f64>(context), &Opcode::F64Gt => self.run_gt::<F64>(context),
&Opcode::F64Le => self.run_lte::<f64>(context), &Opcode::F64Le => self.run_lte::<F64>(context),
&Opcode::F64Ge => self.run_gte::<f64>(context), &Opcode::F64Ge => self.run_gte::<F64>(context),
&Opcode::I32Clz => self.run_clz::<i32>(context), &Opcode::I32Clz => self.run_clz::<i32>(context),
&Opcode::I32Ctz => self.run_ctz::<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::I64Rotl => self.run_rotl::<i64>(context),
&Opcode::I64Rotr => self.run_rotr::<i64>(context), &Opcode::I64Rotr => self.run_rotr::<i64>(context),
&Opcode::F32Abs => self.run_abs::<f32>(context), &Opcode::F32Abs => self.run_abs::<F32>(context),
&Opcode::F32Neg => self.run_neg::<f32>(context), &Opcode::F32Neg => self.run_neg::<F32>(context),
&Opcode::F32Ceil => self.run_ceil::<f32>(context), &Opcode::F32Ceil => self.run_ceil::<F32>(context),
&Opcode::F32Floor => self.run_floor::<f32>(context), &Opcode::F32Floor => self.run_floor::<F32>(context),
&Opcode::F32Trunc => self.run_trunc::<f32>(context), &Opcode::F32Trunc => self.run_trunc::<F32>(context),
&Opcode::F32Nearest => self.run_nearest::<f32>(context), &Opcode::F32Nearest => self.run_nearest::<F32>(context),
&Opcode::F32Sqrt => self.run_sqrt::<f32>(context), &Opcode::F32Sqrt => self.run_sqrt::<F32>(context),
&Opcode::F32Add => self.run_add::<f32>(context), &Opcode::F32Add => self.run_add::<F32>(context),
&Opcode::F32Sub => self.run_sub::<f32>(context), &Opcode::F32Sub => self.run_sub::<F32>(context),
&Opcode::F32Mul => self.run_mul::<f32>(context), &Opcode::F32Mul => self.run_mul::<F32>(context),
&Opcode::F32Div => self.run_div::<f32, f32>(context), &Opcode::F32Div => self.run_div::<F32, F32>(context),
&Opcode::F32Min => self.run_min::<f32>(context), &Opcode::F32Min => self.run_min::<F32>(context),
&Opcode::F32Max => self.run_max::<f32>(context), &Opcode::F32Max => self.run_max::<F32>(context),
&Opcode::F32Copysign => self.run_copysign::<f32>(context), &Opcode::F32Copysign => self.run_copysign::<F32>(context),
&Opcode::F64Abs => self.run_abs::<f64>(context), &Opcode::F64Abs => self.run_abs::<F64>(context),
&Opcode::F64Neg => self.run_neg::<f64>(context), &Opcode::F64Neg => self.run_neg::<F64>(context),
&Opcode::F64Ceil => self.run_ceil::<f64>(context), &Opcode::F64Ceil => self.run_ceil::<F64>(context),
&Opcode::F64Floor => self.run_floor::<f64>(context), &Opcode::F64Floor => self.run_floor::<F64>(context),
&Opcode::F64Trunc => self.run_trunc::<f64>(context), &Opcode::F64Trunc => self.run_trunc::<F64>(context),
&Opcode::F64Nearest => self.run_nearest::<f64>(context), &Opcode::F64Nearest => self.run_nearest::<F64>(context),
&Opcode::F64Sqrt => self.run_sqrt::<f64>(context), &Opcode::F64Sqrt => self.run_sqrt::<F64>(context),
&Opcode::F64Add => self.run_add::<f64>(context), &Opcode::F64Add => self.run_add::<F64>(context),
&Opcode::F64Sub => self.run_sub::<f64>(context), &Opcode::F64Sub => self.run_sub::<F64>(context),
&Opcode::F64Mul => self.run_mul::<f64>(context), &Opcode::F64Mul => self.run_mul::<F64>(context),
&Opcode::F64Div => self.run_div::<f64, f64>(context), &Opcode::F64Div => self.run_div::<F64, F64>(context),
&Opcode::F64Min => self.run_min::<f64>(context), &Opcode::F64Min => self.run_min::<F64>(context),
&Opcode::F64Max => self.run_max::<f64>(context), &Opcode::F64Max => self.run_max::<F64>(context),
&Opcode::F64Copysign => self.run_copysign::<f64>(context), &Opcode::F64Copysign => self.run_copysign::<F64>(context),
&Opcode::I32WrapI64 => self.run_wrap::<i64, i32>(context), &Opcode::I32WrapI64 => self.run_wrap::<i64, i32>(context),
&Opcode::I32TruncSF32 => self.run_trunc_to_int::<f32, i32, 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::I32TruncUF32 => self.run_trunc_to_int::<F32, u32, i32>(context),
&Opcode::I32TruncSF64 => self.run_trunc_to_int::<f64, i32, 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::I32TruncUF64 => self.run_trunc_to_int::<F64, u32, i32>(context),
&Opcode::I64ExtendSI32 => self.run_extend::<i32, i64, i64>(context), &Opcode::I64ExtendSI32 => self.run_extend::<i32, i64, i64>(context),
&Opcode::I64ExtendUI32 => self.run_extend::<u32, u64, i64>(context), &Opcode::I64ExtendUI32 => self.run_extend::<u32, u64, i64>(context),
&Opcode::I64TruncSF32 => self.run_trunc_to_int::<f32, i64, 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::I64TruncUF32 => self.run_trunc_to_int::<F32, u64, i64>(context),
&Opcode::I64TruncSF64 => self.run_trunc_to_int::<f64, i64, 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::I64TruncUF64 => self.run_trunc_to_int::<F64, u64, i64>(context),
&Opcode::F32ConvertSI32 => self.run_extend::<i32, f32, f32>(context), &Opcode::F32ConvertSI32 => self.run_extend::<i32, F32, F32>(context),
&Opcode::F32ConvertUI32 => self.run_extend::<u32, f32, f32>(context), &Opcode::F32ConvertUI32 => self.run_extend::<u32, F32, F32>(context),
&Opcode::F32ConvertSI64 => self.run_wrap::<i64, f32>(context), &Opcode::F32ConvertSI64 => self.run_wrap::<i64, F32>(context),
&Opcode::F32ConvertUI64 => self.run_wrap::<u64, f32>(context), &Opcode::F32ConvertUI64 => self.run_wrap::<u64, F32>(context),
&Opcode::F32DemoteF64 => self.run_wrap::<f64, f32>(context), &Opcode::F32DemoteF64 => self.run_wrap::<F64, F32>(context),
&Opcode::F64ConvertSI32 => self.run_extend::<i32, f64, f64>(context), &Opcode::F64ConvertSI32 => self.run_extend::<i32, F64, F64>(context),
&Opcode::F64ConvertUI32 => self.run_extend::<u32, f64, f64>(context), &Opcode::F64ConvertUI32 => self.run_extend::<u32, F64, F64>(context),
&Opcode::F64ConvertSI64 => self.run_extend::<i64, f64, f64>(context), &Opcode::F64ConvertSI64 => self.run_extend::<i64, F64, F64>(context),
&Opcode::F64ConvertUI64 => self.run_extend::<u64, f64, f64>(context), &Opcode::F64ConvertUI64 => self.run_extend::<u64, F64, F64>(context),
&Opcode::F64PromoteF32 => self.run_extend::<f32, f64, f64>(context), &Opcode::F64PromoteF32 => self.run_extend::<F32, F64, F64>(context),
&Opcode::I32ReinterpretF32 => self.run_reinterpret::<f32, i32>(context), &Opcode::I32ReinterpretF32 => self.run_reinterpret::<F32, i32>(context),
&Opcode::I64ReinterpretF64 => self.run_reinterpret::<f64, i64>(context), &Opcode::I64ReinterpretF64 => self.run_reinterpret::<F64, i64>(context),
&Opcode::F32ReinterpretI32 => self.run_reinterpret::<i32, f32>(context), &Opcode::F32ReinterpretI32 => self.run_reinterpret::<i32, F32>(context),
&Opcode::F64ReinterpretI64 => self.run_reinterpret::<i64, f64>(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 byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use nan_preserving_float::{F32, F64};
use std::io;
use std::{f32, i32, i64, u32, u64};
use TrapKind; use TrapKind;
#[derive(Debug)] #[derive(Debug)]
@ -22,9 +23,9 @@ pub enum RuntimeValue {
/// Value of 64-bit signed or unsigned integer. /// Value of 64-bit signed or unsigned integer.
I64(i64), I64(i64),
/// Value of 32-bit IEEE 754-2008 floating point number. /// Value of 32-bit IEEE 754-2008 floating point number.
F32(f32), F32(F32),
/// Value of 64-bit IEEE 754-2008 floating point number. /// Value of 64-bit IEEE 754-2008 floating point number.
F64(f64), F64(F64),
} }
/// Trait for creating value from a [`RuntimeValue`]. /// Trait for creating value from a [`RuntimeValue`].
@ -136,19 +137,19 @@ impl RuntimeValue {
match value_type { match value_type {
::types::ValueType::I32 => RuntimeValue::I32(0), ::types::ValueType::I32 => RuntimeValue::I32(0),
::types::ValueType::I64 => RuntimeValue::I64(0), ::types::ValueType::I64 => RuntimeValue::I64(0),
::types::ValueType::F32 => RuntimeValue::F32(0f32), ::types::ValueType::F32 => RuntimeValue::F32(0f32.into()),
::types::ValueType::F64 => RuntimeValue::F64(0f64), ::types::ValueType::F64 => RuntimeValue::F64(0f64.into()),
} }
} }
/// Creates new value by interpreting passed u32 as f32. /// Creates new value by interpreting passed u32 as f32.
pub fn decode_f32(val: u32) -> Self { 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. /// Creates new value by interpreting passed u64 as f64.
pub fn decode_f64(val: u64) -> Self { 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. /// Get variable type for this value.
@ -197,14 +198,14 @@ impl From<u64> for RuntimeValue {
} }
} }
impl From<f32> for RuntimeValue { impl From<F32> for RuntimeValue {
fn from(val: f32) -> Self { fn from(val: F32) -> Self {
RuntimeValue::F32(val) RuntimeValue::F32(val)
} }
} }
impl From<f64> for RuntimeValue { impl From<F64> for RuntimeValue {
fn from(val: f64) -> Self { fn from(val: F64) -> Self {
RuntimeValue::F64(val) RuntimeValue::F64(val)
} }
} }
@ -214,7 +215,7 @@ macro_rules! impl_from_runtime_value {
impl FromRuntimeValue for $into { impl FromRuntimeValue for $into {
fn from_runtime_value(val: RuntimeValue) -> Option<Self> { fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
match val { match val {
RuntimeValue::$expected_rt_ty(val) => Some(val as $into), RuntimeValue::$expected_rt_ty(val) => Some(val.transmute_into()),
_ => None, _ => None,
} }
} }
@ -237,8 +238,8 @@ impl FromRuntimeValue for bool {
impl_from_runtime_value!(I32, i32); impl_from_runtime_value!(I32, i32);
impl_from_runtime_value!(I64, i64); impl_from_runtime_value!(I64, i64);
impl_from_runtime_value!(F32, f32); impl_from_runtime_value!(F32, F32);
impl_from_runtime_value!(F64, f64); impl_from_runtime_value!(F64, F64);
impl_from_runtime_value!(I32, u32); impl_from_runtime_value!(I32, u32);
impl_from_runtime_value!(I64, u64); impl_from_runtime_value!(I64, u64);
@ -249,21 +250,34 @@ macro_rules! impl_wrap_into {
self as $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); impl_wrap_into!(i32, i8);
impl_wrap_into!(i32, i16); impl_wrap_into!(i32, i16);
impl_wrap_into!(i64, i8); impl_wrap_into!(i64, i8);
impl_wrap_into!(i64, i16); impl_wrap_into!(i64, i16);
impl_wrap_into!(i64, i32); impl_wrap_into!(i64, i32);
impl_wrap_into!(i64, f32); impl_wrap_into!(i64, f32, F32);
impl_wrap_into!(u64, f32); impl_wrap_into!(u64, f32, F32);
// Casting from an f64 to an f32 will produce the closest possible value (rounding strategy unspecified) // 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 // 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. // largest or smallest finite value representable by f32. This is a bug and will be fixed.
impl_wrap_into!(f64, f32); 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 { macro_rules! impl_try_truncate_into {
($from: ident, $into: ident) => { ($from: ident, $into: ident) => {
impl TryTruncateInto<$into, TrapKind> for $from { impl TryTruncateInto<$into, TrapKind> for $from {
@ -284,8 +298,15 @@ macro_rules! impl_try_truncate_into {
Ok(self as $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); impl_try_truncate_into!(f32, i32);
impl_try_truncate_into!(f32, i64); impl_try_truncate_into!(f32, i64);
@ -295,6 +316,14 @@ impl_try_truncate_into!(f32, u32);
impl_try_truncate_into!(f32, u64); impl_try_truncate_into!(f32, u64);
impl_try_truncate_into!(f64, u32); impl_try_truncate_into!(f64, u32);
impl_try_truncate_into!(f64, u64); 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 { macro_rules! impl_extend_into {
($from:ident, $into:ident) => { ($from:ident, $into:ident) => {
@ -303,8 +332,15 @@ macro_rules! impl_extend_into {
self as $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); impl_extend_into!(i8, i32);
impl_extend_into!(u8, i32); impl_extend_into!(u8, i32);
@ -325,6 +361,20 @@ impl_extend_into!(i64, f64);
impl_extend_into!(u64, f64); impl_extend_into!(u64, f64);
impl_extend_into!(f32, 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 { macro_rules! impl_transmute_into_self {
($type: ident) => { ($type: ident) => {
impl TransmuteInto<$type> for $type { impl TransmuteInto<$type> for $type {
@ -339,6 +389,8 @@ impl_transmute_into_self!(i32);
impl_transmute_into_self!(i64); impl_transmute_into_self!(i64);
impl_transmute_into_self!(f32); impl_transmute_into_self!(f32);
impl_transmute_into_self!(f64); impl_transmute_into_self!(f64);
impl_transmute_into_self!(F32);
impl_transmute_into_self!(F64);
macro_rules! impl_transmute_into_as { macro_rules! impl_transmute_into_as {
($from: ident, $into: ident) => { ($from: ident, $into: ident) => {
@ -357,6 +409,49 @@ impl_transmute_into_as!(u32, i32);
impl_transmute_into_as!(i64, u64); impl_transmute_into_as!(i64, u64);
impl_transmute_into_as!(u64, i64); 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 { impl TransmuteInto<i32> for f32 {
fn transmute_into(self) -> i32 { self.to_bits() as i32 } 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 { macro_rules! impl_integer_arithmetic_ops {
($type: ident) => { ($type: ident) => {
impl ArithmeticOps<$type> for $type { impl ArithmeticOps<$type> for $type {
@ -538,6 +653,8 @@ macro_rules! impl_float_arithmetic_ops {
impl_float_arithmetic_ops!(f32); impl_float_arithmetic_ops!(f32);
impl_float_arithmetic_ops!(f64); impl_float_arithmetic_ops!(f64);
impl_float_arithmetic_ops!(F32);
impl_float_arithmetic_ops!(F64);
macro_rules! impl_integer { macro_rules! impl_integer {
($type: ident) => { ($type: ident) => {
@ -562,12 +679,25 @@ impl_integer!(u64);
macro_rules! impl_float { 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 { impl Float<$type> for $type {
fn abs(self) -> $type { self.abs() } fn abs(self) -> $type {
fn floor(self) -> $type { self.floor() } $intermediate::abs(self.into()).into()
fn ceil(self) -> $type { self.ceil() } }
fn trunc(self) -> $type { self.trunc() } fn floor(self) -> $type {
fn round(self) -> $type { self.round() } $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 { fn nearest(self) -> $type {
let round = self.round(); let round = self.round();
if self.fract().abs() != 0.5 { if self.fract().abs() != 0.5 {
@ -583,12 +713,13 @@ macro_rules! impl_float {
round 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. // This instruction corresponds to what is sometimes called "minNaN" in other languages.
fn min(self, other: $type) -> $type { fn min(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() { if self.is_nan() || other.is_nan() {
use std::$type; return ::std::$intermediate::NAN.into();
return $type::NAN;
} }
self.min(other) self.min(other)
@ -596,8 +727,7 @@ macro_rules! impl_float {
// This instruction corresponds to what is sometimes called "maxNaN" in other languages. // This instruction corresponds to what is sometimes called "maxNaN" in other languages.
fn max(self, other: $type) -> $type { fn max(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() { if self.is_nan() || other.is_nan() {
use std::$type; return ::std::$intermediate::NAN.into();
return $type::NAN;
} }
self.max(other) self.max(other)
@ -623,8 +753,10 @@ macro_rules! impl_float {
} }
} }
} }
} };
} }
impl_float!(f32, i32); impl_float!(f32, i32);
impl_float!(f64, i64); impl_float!(f64, i64);
impl_float!(F32, f32, i32);
impl_float!(F64, f64, i64);

View File

@ -2,21 +2,19 @@
use std::collections::HashMap; use std::collections::HashMap;
use wasmi::{ use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, use wasmi::memory_units::Pages;
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap, RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap};
};
use wasmi::memory_units::Pages;
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
fn spec_to_runtime_value(value: Value) -> RuntimeValue { fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
match value { match val {
Value::I32(v) => RuntimeValue::I32(v), Value::I32(v) => RuntimeValue::I32(v),
Value::I64(v) => RuntimeValue::I64(v), Value::I64(v) => RuntimeValue::I64(v),
Value::F32(v) => RuntimeValue::F32(v), Value::F32(v) => RuntimeValue::F32(v.into()),
Value::F64(v) => RuntimeValue::F64(v), Value::F64(v) => RuntimeValue::F64(v.into()),
} }
} }
@ -54,8 +52,8 @@ impl SpecModule {
table: TableInstance::alloc(10, Some(20)).unwrap(), table: TableInstance::alloc(10, Some(20)).unwrap(),
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(), memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false), global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0), false), global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false),
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0), false), global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false),
} }
} }
} }
@ -121,7 +119,7 @@ impl ModuleImportResolver for SpecModule {
_ => Err(InterpreterError::Instantiation(format!( _ => Err(InterpreterError::Instantiation(format!(
"Unknown host global import {}", "Unknown host global import {}",
field_name field_name
))) ))),
} }
} }
@ -269,7 +267,11 @@ fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn load_module(wasm: &[u8], name: &Option<String>, spec_driver: &mut SpecDriver) -> Result<ModuleRef, Error> { fn load_module(
wasm: &[u8],
name: &Option<String>,
spec_driver: &mut SpecDriver,
) -> Result<ModuleRef, Error> {
let module = try_load_module(wasm)?; let module = try_load_module(wasm)?;
let instance = ModuleInstance::new(&module, spec_driver) let instance = ModuleInstance::new(&module, spec_driver)
.map_err(|e| Error::Load(e.to_string()))? .map_err(|e| Error::Load(e.to_string()))?
@ -284,7 +286,7 @@ fn load_module(wasm: &[u8], name: &Option<String>, spec_driver: &mut SpecDriver)
fn run_action( fn run_action(
program: &mut SpecDriver, program: &mut SpecDriver,
action: &Action, action: &Action<u32, u64>,
) -> Result<Option<RuntimeValue>, InterpreterError> { ) -> Result<Option<RuntimeValue>, InterpreterError> {
match *action { match *action {
Action::Invoke { Action::Invoke {
@ -298,14 +300,11 @@ fn run_action(
"Expected program to have loaded module {:?}", "Expected program to have loaded module {:?}",
module module
)); ));
module.invoke_export( let vec_args = args.iter()
field,
&args.iter()
.cloned() .cloned()
.map(spec_to_runtime_value) .map(spec_to_runtime_value)
.collect::<Vec<_>>(), .collect::<Vec<_>>();
program.spec_module(), module.invoke_export(field, &vec_args, program.spec_module())
)
} }
Action::Get { Action::Get {
ref module, ref module,
@ -342,11 +341,29 @@ fn try_spec(name: &str) -> Result<(), Error> {
let mut spec_driver = SpecDriver::new(); let mut spec_driver = SpecDriver::new();
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name); 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 parser = ScriptParser::from_file(spec_script_path).expect("Can't read spec script");
let mut errors = vec![];
while let Some(Command { kind, line }) = parser.next()? { while let Some(Command { kind, line }) = parser.next()? {
println!("Line {}:", line); 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 { match kind {
CommandKind::Module { name, module, .. } => { CommandKind::Module { name, module, .. } => {
load_module(&module.into_vec()?, &name, &mut spec_driver).expect("Failed to load module"); load_module(&module.into_vec()?, &name, &mut spec_driver)
.expect("Failed to load module");
} }
CommandKind::AssertReturn { action, expected } => { CommandKind::AssertReturn { action, expected } => {
let result = run_action(&mut spec_driver, &action); let result = run_action(&mut spec_driver, &action);
@ -375,7 +392,6 @@ fn try_spec(name: &str) -> Result<(), Error> {
spec_expected @ _ => assert_eq!(actual_result, spec_expected), spec_expected @ _ => assert_eq!(actual_result, spec_expected),
} }
} }
println!("assert_return at line {} - success", line);
} }
Err(e) => { Err(e) => {
panic!("Expected action to return value, got error: {:?}", e); panic!("Expected action to return value, got error: {:?}", e);
@ -400,7 +416,6 @@ fn try_spec(name: &str) -> Result<(), Error> {
} }
} }
} }
println!("assert_return_nan at line {} - success", line);
} }
Err(e) => { Err(e) => {
panic!("Expected action to return value, got error: {:?}", e); panic!("Expected action to return value, got error: {:?}", e);
@ -411,7 +426,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
let result = run_action(&mut spec_driver, &action); let result = run_action(&mut spec_driver, &action);
match result { match result {
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result), Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
Err(e) => println!("assert_exhaustion at line {} - success ({:?})", line, e), Err(_e) => {},
} }
} }
CommandKind::AssertTrap { action, .. } => { CommandKind::AssertTrap { action, .. } => {
@ -423,9 +438,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
result result
); );
} }
Err(e) => { Err(_e) => {}
println!("assert_trap at line {} - success ({:?})", line, e);
}
} }
} }
CommandKind::AssertInvalid { module, .. } CommandKind::AssertInvalid { module, .. }
@ -434,13 +447,13 @@ fn try_spec(name: &str) -> Result<(), Error> {
let module_load = try_load(&module.into_vec()?, &mut spec_driver); let module_load = try_load(&module.into_vec()?, &mut spec_driver);
match module_load { match module_load {
Ok(_) => panic!("Expected invalid module definition, got some module!"), Ok(_) => panic!("Expected invalid module definition, got some module!"),
Err(e) => println!("assert_invalid at line {} - success ({:?})", line, e), Err(_e) => {},
} }
} }
CommandKind::AssertUninstantiable { module, .. } => { CommandKind::AssertUninstantiable { module, .. } => {
match try_load(&module.into_vec()?, &mut spec_driver) { match try_load(&module.into_vec()?, &mut spec_driver) {
Ok(_) => panic!("Expected error running start function at line {}", line), Ok(_) => panic!("Expected error running start function at line {}", line),
Err(e) => println!("assert_uninstantiable - success ({:?})", e), Err(_e) => {},
} }
} }
CommandKind::Register { name, as_name, .. } => { CommandKind::Register { name, as_name, .. } => {
@ -457,5 +470,14 @@ fn try_spec(name: &str) -> Result<(), Error> {
} }
} }
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(()) Ok(())
} }