From 30e72eb796984200298dac5070f3c66c518bc81a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 16:57:05 +0100 Subject: [PATCH 1/4] feat(interface-types) Implement lifting and lowering instructions. --- .../instructions/lowering_lifting.rs | 342 ++++++++++++++++++ .../src/interpreter/instructions/mod.rs | 2 + lib/interface-types/src/interpreter/mod.rs | 39 ++ lib/interface-types/src/macros.rs | 17 +- 4 files changed, 392 insertions(+), 8 deletions(-) create mode 100644 lib/interface-types/src/interpreter/instructions/lowering_lifting.rs diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs new file mode 100644 index 00000000000..cd43104d1db --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -0,0 +1,342 @@ +use crate::interpreter::wasm::values::InterfaceValue; +use std::convert::TryInto; + +macro_rules! lowering_lifting { + ($instruction_function_name:ident, $instruction_name:expr, $from_variant:ident, $to_variant:ident) => { + executable_instruction!( + $instruction_function_name() -> _ { + move |runtime| -> _ { + match runtime.stack.pop1() { + Some(InterfaceValue::$from_variant(value)) => { + runtime + .stack + .push(InterfaceValue::$to_variant(value.try_into().map_err( + |_| { + concat!( + "Failed to cast ", + stringify!($from_variant), + " to ", + stringify!($to_variant), + "." + ).to_string() + }, + )?)) + } + + Some(_) => { + return Err(concat!( + "Instruction `", + $instruction_name, + "` expects a `i32` value on the stack." + ) + .to_string()) + }, + + None => { + return Err(concat!( + "Instruction `", + $instruction_name, + "` needs one value on the stack." + ) + .to_string()) + } + } + + Ok(()) + } + } + ); + }; +} + +lowering_lifting!(i32_to_s8, "i32-to-s8", I32, S8); +lowering_lifting!(i32_to_u8, "i32-to-u8", I32, U8); +lowering_lifting!(i32_to_s16, "i32-to-s16", I32, S16); +lowering_lifting!(i32_to_u16, "i32-to-u16", I32, U16); +lowering_lifting!(i32_to_s32, "i32-to-s32", I32, S32); +lowering_lifting!(i32_to_u32, "i32-to-u32", I32, U32); +lowering_lifting!(i32_to_s64, "i32-to-s64", I32, S64); +lowering_lifting!(i32_to_u64, "i32-to-u64", I32, U64); +lowering_lifting!(i64_to_s8, "i64-to-s8", I64, S8); +lowering_lifting!(i64_to_u8, "i64-to-u8", I64, U8); +lowering_lifting!(i64_to_s16, "i64-to-s16", I64, S16); +lowering_lifting!(i64_to_u16, "i64-to-u16", I64, U16); +lowering_lifting!(i64_to_s32, "i64-to-s32", I64, S32); +lowering_lifting!(i64_to_u32, "i64-to-u32", I64, U32); +lowering_lifting!(i64_to_s64, "i64-to-s64", I64, S64); +lowering_lifting!(i64_to_u64, "i64-to-u64", I64, U64); +lowering_lifting!(s8_to_i32, "s8-to-i32", S8, I32); +lowering_lifting!(u8_to_i32, "u8-to-i32", U8, I32); +lowering_lifting!(s16_to_i32, "s16-to-i32", S16, I32); +lowering_lifting!(u16_to_i32, "u16-to-i32", U16, I32); +lowering_lifting!(s32_to_i32, "s32-to-i32", S32, I32); +lowering_lifting!(u32_to_i32, "u32-to-i32", U32, I32); +lowering_lifting!(s64_to_i32, "s64-to-i32", S64, I32); +lowering_lifting!(u64_to_i32, "u64-to-i32", U64, I32); +lowering_lifting!(s8_to_i64, "s8-to-i64", S8, I64); +lowering_lifting!(u8_to_i64, "u8-to-i64", U8, I64); +lowering_lifting!(s16_to_i64, "s16-to-i64", S16, I64); +lowering_lifting!(u16_to_i64, "u16-to-i64", U16, I64); +lowering_lifting!(s32_to_i64, "s32-to-i64", S32, I64); +lowering_lifting!(u32_to_i64, "u32-to-i64", U32, I64); +lowering_lifting!(s64_to_i64, "s64-to-i64", S64, I64); +lowering_lifting!(u64_to_i64, "u64-to-i64", U64, I64); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_i32_to_s8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S8(42)], + ); + + test_executable_instruction!( + test_i32_to_u8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU8], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U8(42)], + ); + + test_executable_instruction!( + test_i32_to_s16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS16], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S16(42)], + ); + + test_executable_instruction!( + test_i32_to_u16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU16], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U16(42)], + ); + + test_executable_instruction!( + test_i32_to_s32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS32], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S32(42)], + ); + + test_executable_instruction!( + test_i32_to_u32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU32], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U32(42)], + ); + + test_executable_instruction!( + test_i32_to_s64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToS64], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::S64(42)], + ); + + test_executable_instruction!( + test_i32_to_u64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU64], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::U64(42)], + ); + + test_executable_instruction!( + test_i64_to_s8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS8], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S8(42)], + ); + + test_executable_instruction!( + test_i64_to_u8 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU8], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U8(42)], + ); + + test_executable_instruction!( + test_i64_to_s16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS16], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S16(42)], + ); + + test_executable_instruction!( + test_i64_to_u16 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU16], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U16(42)], + ); + + test_executable_instruction!( + test_i64_to_s32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS32], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S32(42)], + ); + + test_executable_instruction!( + test_i64_to_u32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU32], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U32(42)], + ); + + test_executable_instruction!( + test_i64_to_s64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToS64], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::S64(42)], + ); + + test_executable_instruction!( + test_i64_to_u64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64ToU64], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + stack: [InterfaceValue::U64(42)], + ); + + test_executable_instruction!( + test_s8_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8ToI32], + invocation_inputs: [InterfaceValue::S8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u8_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8ToI32], + invocation_inputs: [InterfaceValue::U8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s16_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16ToI32], + invocation_inputs: [InterfaceValue::S16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u16_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16ToI32], + invocation_inputs: [InterfaceValue::U16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s32_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32ToI32], + invocation_inputs: [InterfaceValue::S32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u32_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32ToI32], + invocation_inputs: [InterfaceValue::U32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s64_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64ToI32], + invocation_inputs: [InterfaceValue::S64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_u64_to_i32 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64ToI32], + invocation_inputs: [InterfaceValue::U64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_s8_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8ToI64], + invocation_inputs: [InterfaceValue::S8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u8_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8ToI64], + invocation_inputs: [InterfaceValue::U8(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_s16_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16ToI64], + invocation_inputs: [InterfaceValue::S16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u16_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16ToI64], + invocation_inputs: [InterfaceValue::U16(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_s32_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32ToI64], + invocation_inputs: [InterfaceValue::S32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u32_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32ToI64], + invocation_inputs: [InterfaceValue::U32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_s64_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64ToI64], + invocation_inputs: [InterfaceValue::S64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); + + test_executable_instruction!( + test_u64_to_i64 = + instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64ToI64], + invocation_inputs: [InterfaceValue::U64(42)], + instance: Instance::new(), + stack: [InterfaceValue::I64(42)], + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index d84d2b47e82..28aa5209bb8 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -1,12 +1,14 @@ mod argument_get; mod call; mod call_export; +mod lowering_lifting; mod read_utf8; mod write_utf8; pub(crate) use argument_get::argument_get; pub(crate) use call::call; pub(crate) use call_export::call_export; +pub(crate) use lowering_lifting::*; pub(crate) use read_utf8::read_utf8; pub(crate) use write_utf8::write_utf8; diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 5cba4613c7d..2fbb8468516 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -206,6 +206,45 @@ where Instruction::WriteUtf8 { allocator_name } => { instructions::write_utf8((*allocator_name).to_owned(), instruction_name) } + + Instruction::I32ToS8 => instructions::i32_to_s8(), + //Instruction::I32ToS8X + Instruction::I32ToU8 => instructions::i32_to_u8(), + Instruction::I32ToS16 => instructions::i32_to_s16(), + //Instruction::I32ToS16X + Instruction::I32ToU16 => instructions::i32_to_u16(), + Instruction::I32ToS32 => instructions::i32_to_s32(), + Instruction::I32ToU32 => instructions::i32_to_u32(), + Instruction::I32ToS64 => instructions::i32_to_s64(), + Instruction::I32ToU64 => instructions::i32_to_u64(), + Instruction::I64ToS8 => instructions::i64_to_s8(), + //Instruction::I64ToS8X + Instruction::I64ToU8 => instructions::i64_to_u8(), + Instruction::I64ToS16 => instructions::i64_to_s16(), + //Instruction::I64ToS16X + Instruction::I64ToU16 => instructions::i64_to_u16(), + Instruction::I64ToS32 => instructions::i64_to_s32(), + Instruction::I64ToU32 => instructions::i64_to_u32(), + Instruction::I64ToS64 => instructions::i64_to_s64(), + Instruction::I64ToU64 => instructions::i64_to_u64(), + Instruction::S8ToI32 => instructions::s8_to_i32(), + Instruction::U8ToI32 => instructions::u8_to_i32(), + Instruction::S16ToI32 => instructions::s16_to_i32(), + Instruction::U16ToI32 => instructions::u16_to_i32(), + Instruction::S32ToI32 => instructions::s32_to_i32(), + Instruction::U32ToI32 => instructions::u32_to_i32(), + Instruction::S64ToI32 => instructions::s64_to_i32(), + //Instruction::S64ToI32X + Instruction::U64ToI32 => instructions::u64_to_i32(), + //Instruction::U64ToI32X + Instruction::S8ToI64 => instructions::s8_to_i64(), + Instruction::U8ToI64 => instructions::u8_to_i64(), + Instruction::S16ToI64 => instructions::s16_to_i64(), + Instruction::U16ToI64 => instructions::u16_to_i64(), + Instruction::S32ToI64 => instructions::s32_to_i64(), + Instruction::U32ToI64 => instructions::u32_to_i64(), + Instruction::S64ToI64 => instructions::s64_to_i64(), + Instruction::U64ToI64 => instructions::u64_to_i64(), _ => unimplemented!(), } }) diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index 7ddfb690fc7..5e2217e29e0 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -40,18 +40,19 @@ macro_rules! consume { /// Check the existing executable instruction to get more examples. macro_rules! executable_instruction { ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => { - use crate::interpreter::{ExecutableInstruction, wasm, stack::Stackable}; - pub(crate) fn $name( $($argument_name: $argument_type),* - ) -> ExecutableInstruction + ) -> crate::interpreter::ExecutableInstruction where - Export: wasm::structures::Export, - LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance, { + #[allow(unused_imports)] + use crate::interpreter::{stack::Stackable}; + Box::new($implementation) } }; From 6576dfd64b1aed61bc6f661645ec7cc13edfda53 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 17:04:26 +0100 Subject: [PATCH 2/4] test(interface-types) Add test cases for errors. --- .../instructions/lowering_lifting.rs | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index cd43104d1db..1ba8a386ea8 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -23,11 +23,17 @@ macro_rules! lowering_lifting { )?)) } - Some(_) => { - return Err(concat!( - "Instruction `", - $instruction_name, - "` expects a `i32` value on the stack." + Some(wrong_value) => { + return Err(format!( + concat!( + "Instruction `", + $instruction_name, + "` expects a `", + stringify!($from_variant), + "` value on the stack, got `{:?}`.", + ), + wrong_value + ) .to_string()) }, @@ -92,6 +98,22 @@ mod tests { stack: [InterfaceValue::S8(42)], ); + test_executable_instruction!( + test_type_mismatch = + instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I64(42)], + instance: Instance::new(), + error: "Instruction `i32-to-s8` expects a `I32` value on the stack, got `I64(42)`." + ); + + test_executable_instruction!( + test_no_value_on_the_stack = + instructions: [Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + error: "Instruction `i32-to-s8` needs one value on the stack." + ); + test_executable_instruction!( test_i32_to_u8 = instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32ToU8], From c157bdaf637848d294e71cd5e5a371ff1217ee3a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 17:10:10 +0100 Subject: [PATCH 3/4] test(interface-types) Test when lowering or lifting fails because of the value. --- .../interpreter/instructions/lowering_lifting.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index 1ba8a386ea8..c24fd992bb7 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -13,11 +13,11 @@ macro_rules! lowering_lifting { .push(InterfaceValue::$to_variant(value.try_into().map_err( |_| { concat!( - "Failed to cast ", + "Failed to cast `", stringify!($from_variant), - " to ", + "` to `", stringify!($to_variant), - "." + "`." ).to_string() }, )?)) @@ -98,6 +98,14 @@ mod tests { stack: [InterfaceValue::S8(42)], ); + test_executable_instruction!( + test_convert_fails = + instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], + invocation_inputs: [InterfaceValue::I32(128)], + instance: Instance::new(), + error: "Failed to cast `I32` to `S8`." + ); + test_executable_instruction!( test_type_mismatch = instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], From de53659ee7425d4b1eafaed97dab1869541dc85a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 3 Mar 2020 17:17:26 +0100 Subject: [PATCH 4/4] feat(interface-types) Implement `s64-to-i32x` and `u64-to-i32x` Actually, `s64-to-i32` and `u64-to-i32` already error when overflow happens. --- lib/interface-types/src/interpreter/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 2fbb8468516..1cfe66531b6 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -233,10 +233,8 @@ where Instruction::U16ToI32 => instructions::u16_to_i32(), Instruction::S32ToI32 => instructions::s32_to_i32(), Instruction::U32ToI32 => instructions::u32_to_i32(), - Instruction::S64ToI32 => instructions::s64_to_i32(), - //Instruction::S64ToI32X - Instruction::U64ToI32 => instructions::u64_to_i32(), - //Instruction::U64ToI32X + Instruction::S64ToI32 | Instruction::S64ToI32X => instructions::s64_to_i32(), + Instruction::U64ToI32 | Instruction::U64ToI32X => instructions::u64_to_i32(), Instruction::S8ToI64 => instructions::s8_to_i64(), Instruction::U8ToI64 => instructions::u8_to_i64(), Instruction::S16ToI64 => instructions::s16_to_i64(),