diff --git a/crates/wasmi/src/engine/translator/mod.rs b/crates/wasmi/src/engine/translator/mod.rs index 50aea0b8d2..d8c42995bc 100644 --- a/crates/wasmi/src/engine/translator/mod.rs +++ b/crates/wasmi/src/engine/translator/mod.rs @@ -60,6 +60,7 @@ use crate::{ }; use core::fmt; use std::vec::Vec; +use utils::Wrap; use wasmparser::{ BinaryReaderError, FuncToValidate, @@ -1918,6 +1919,37 @@ impl FuncTranslator { Ok(()) } + /// Translates non-wrapping Wasm integer `store` to Wasmi bytecode. + /// + /// # Note + /// + /// Convenience method that simply forwards to [`Self::translate_istore_wrap`]. + #[allow(clippy::too_many_arguments)] + fn translate_istore( + &mut self, + memarg: MemArg, + make_instr: fn(ptr: Reg, memory: index::Memory) -> Instruction, + make_instr_imm: fn(ptr: Reg, memory: index::Memory) -> Instruction, + make_instr_offset16: fn(ptr: Reg, offset: u16, value: Reg) -> Instruction, + make_instr_offset16_imm: fn(ptr: Reg, offset: u16, value: Field) -> Instruction, + make_instr_at: fn(value: Reg, address: u32) -> Instruction, + make_instr_at_imm: fn(value: Field, address: u32) -> Instruction, + ) -> Result<(), Error> + where + Src: Copy + From, + Field: TryFrom + Into, + { + self.translate_istore_wrap::( + memarg, + make_instr, + make_instr_imm, + make_instr_offset16, + make_instr_offset16_imm, + make_instr_at, + make_instr_at_imm, + ) + } + /// Translates Wasm integer `store` and `storeN` instructions to Wasmi bytecode. /// /// # Note @@ -1931,19 +1963,19 @@ impl FuncTranslator { /// /// - `{i32, i64}.{store, store8, store16, store32}` #[allow(clippy::too_many_arguments)] - fn translate_istore( + fn translate_istore_wrap( &mut self, memarg: MemArg, make_instr: fn(ptr: Reg, memory: index::Memory) -> Instruction, make_instr_imm: fn(ptr: Reg, memory: index::Memory) -> Instruction, make_instr_offset16: fn(ptr: Reg, offset: u16, value: Reg) -> Instruction, - make_instr_offset16_imm: fn(ptr: Reg, offset: u16, value: U) -> Instruction, + make_instr_offset16_imm: fn(ptr: Reg, offset: u16, value: Field) -> Instruction, make_instr_at: fn(value: Reg, address: u32) -> Instruction, - make_instr_at_imm: fn(value: U, address: u32) -> Instruction, + make_instr_at_imm: fn(value: Field, address: u32) -> Instruction, ) -> Result<(), Error> where - T: Copy + From, - U: TryFrom + Into, + Src: Copy + Wrap + From, + Field: TryFrom + Into, { bail_unreachable!(self); let (memory, offset) = Self::decode_memarg(memarg); @@ -1951,7 +1983,7 @@ impl FuncTranslator { let ptr = match ptr { Provider::Register(ptr) => ptr, Provider::Const(ptr) => { - return self.translate_istore_at::( + return self.translate_istore_wrap_at::( memory, u32::from(ptr), offset, @@ -1962,7 +1994,7 @@ impl FuncTranslator { } }; if memory.is_default() { - if let Some(_instr) = self.translate_istore_mem0::( + if let Some(_instr) = self.translate_istore_wrap_mem0::( ptr, offset, value, @@ -1977,7 +2009,7 @@ impl FuncTranslator { make_instr(ptr, memory), Instruction::register_and_imm32(value, offset), ), - TypedProvider::Const(value) => match U::try_from(T::from(value)).ok() { + TypedProvider::Const(value) => match Field::try_from(Src::from(value).wrap()).ok() { Some(value) => ( make_instr_imm(ptr, memory), Instruction::imm16_and_imm32(value, offset), @@ -1998,18 +2030,18 @@ impl FuncTranslator { /// # Note /// /// This is used in cases where the `ptr` is a known constant value. - fn translate_istore_at( + fn translate_istore_wrap_at( &mut self, memory: index::Memory, ptr: u32, offset: u32, value: TypedProvider, make_instr_at: fn(value: Reg, address: u32) -> Instruction, - make_instr_at_imm: fn(value: U, address: u32) -> Instruction, + make_instr_at_imm: fn(value: Field, address: u32) -> Instruction, ) -> Result<(), Error> where - T: Copy + From, - U: TryFrom, + Src: Copy + From + Wrap, + Field: TryFrom, { let Some(address) = Self::effective_address(ptr, offset) else { return self.translate_trap(TrapCode::MemoryOutOfBounds); @@ -2019,7 +2051,7 @@ impl FuncTranslator { self.push_fueled_instr(make_instr_at(value, address), FuelCosts::store)?; } Provider::Const(value) => { - if let Ok(value) = U::try_from(T::from(value)) { + if let Ok(value) = Field::try_from(Src::from(value).wrap()) { self.push_fueled_instr(make_instr_at_imm(value, address), FuelCosts::store)?; } else { let value = self.alloc.stack.alloc_const(value)?; @@ -2042,17 +2074,17 @@ impl FuncTranslator { /// This optimizes for cases where the Wasm linear memory that is operated on is known /// to be the default memory. /// Returns `Some` in case the optimized instructions have been encoded. - fn translate_istore_mem0( + fn translate_istore_wrap_mem0( &mut self, ptr: Reg, offset: u32, value: TypedProvider, make_instr_offset16: fn(Reg, u16, Reg) -> Instruction, - make_instr_offset16_imm: fn(Reg, u16, U) -> Instruction, + make_instr_offset16_imm: fn(Reg, u16, Field) -> Instruction, ) -> Result, Error> where - T: Copy + From, - U: TryFrom, + Src: Copy + From + Wrap, + Field: TryFrom, { let Ok(offset16) = u16::try_from(offset) else { return Ok(None); @@ -2061,7 +2093,7 @@ impl FuncTranslator { Provider::Register(value) => { self.push_fueled_instr(make_instr_offset16(ptr, offset16, value), FuelCosts::store)? } - Provider::Const(value) => match U::try_from(T::from(value)) { + Provider::Const(value) => match Field::try_from(Src::from(value).wrap()) { Ok(value) => self.push_fueled_instr( make_instr_offset16_imm(ptr, offset16, value), FuelCosts::store, diff --git a/crates/wasmi/src/engine/translator/tests/op/store/i32_store16.rs b/crates/wasmi/src/engine/translator/tests/op/store/i32_store16.rs index 28786d557f..15482f9a11 100644 --- a/crates/wasmi/src/engine/translator/tests/op/store/i32_store16.rs +++ b/crates/wasmi/src/engine/translator/tests/op/store/i32_store16.rs @@ -12,7 +12,15 @@ fn reg() { #[cfg_attr(miri, ignore)] fn imm() { let values = [ + 0, + 1, + -1, + 42, i32::from(i16::MIN) - 1, + i32::from(i16::MIN), + i32::from(i16::MIN + 1), + i32::from(i16::MAX - 1), + i32::from(i16::MAX), i32::from(i16::MAX) + 1, i32::MIN, i32::MIN + 1, @@ -20,16 +28,7 @@ fn imm() { i32::MAX - 1, ]; for value in values { - test_store_imm::(WASM_OP, value, Instruction::i32_store16); - } -} - -#[test] -#[cfg_attr(miri, ignore)] -fn imm16() { - let values = [0, 1, -1, 42, i16::MIN + 1, i16::MIN, i16::MAX - 1, i16::MAX]; - for value in values { - test_store_imm16::(WASM_OP, Instruction::i32_store16_imm, value); + test_store_wrap_imm::(WASM_OP, value, Instruction::i32_store16_imm); } } @@ -42,32 +41,36 @@ fn offset16() { #[test] #[cfg_attr(miri, ignore)] fn offset16_imm() { - test_store_offset16_imm::( - WASM_OP, + let values = [ + 0, + 1, + -1, + -1000, + 1000, + i32::from(i8::MIN) - 1, + i32::from(i8::MIN), + i32::from(i8::MIN) + 1, + i32::from(i8::MAX) - 1, + i32::from(i8::MAX), + i32::from(i8::MAX) + 1, i32::from(i16::MIN) - 1, - Instruction::i32_store16_offset16, - ); - test_store_offset16_imm::( - WASM_OP, + i32::from(i16::MIN), + i32::from(i16::MIN) + 1, + i32::from(i16::MAX) - 1, + i32::from(i16::MAX), i32::from(i16::MAX) + 1, - Instruction::i32_store16_offset16, - ); - test_store_offset16_imm::(WASM_OP, i32::MIN + 1, Instruction::i32_store16_offset16); - test_store_offset16_imm::(WASM_OP, i32::MAX - 1, Instruction::i32_store16_offset16); - test_store_offset16_imm::(WASM_OP, i32::MIN, Instruction::i32_store16_offset16); - test_store_offset16_imm::(WASM_OP, i32::MAX, Instruction::i32_store16_offset16); -} - -#[test] -#[cfg_attr(miri, ignore)] -fn offset16_imm16() { - test_store_offset16_imm16::(WASM_OP, 0, Instruction::i32_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, 1, Instruction::i32_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, -1, Instruction::i32_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MIN + 1, Instruction::i32_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MAX - 1, Instruction::i32_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MIN, Instruction::i32_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MAX, Instruction::i32_store16_offset16_imm); + i32::MIN, + i32::MIN + 1, + i32::MAX - 1, + i32::MAX, + ]; + for value in values { + test_store_wrap_offset16_imm::( + WASM_OP, + value, + Instruction::i32_store16_offset16_imm, + ); + } } #[test] @@ -85,22 +88,30 @@ fn at_overflow() { #[test] #[cfg_attr(miri, ignore)] fn at_imm() { - test_store_at_imm::( - WASM_OP, - i32::from(i16::MAX) + 1, - Instruction::i32_store16_at, - ); - test_store_at_imm::(WASM_OP, i32::MAX - 1, Instruction::i32_store16_at); - test_store_at_imm::(WASM_OP, i32::MAX, Instruction::i32_store16_at); + let values = [ + 0, + 1, + -1000, + 1000, + i32::from(i16::MIN), + i32::from(i16::MIN) + 1, + i32::from(i16::MAX) - 1, + i32::from(i16::MAX), + i32::MIN, + i32::MIN + 1, + i32::MAX - 1, + i32::MAX, + ]; + for value in values { + test_store_wrap_at_imm::(WASM_OP, value, Instruction::i32_store16_at_imm); + } } #[test] #[cfg_attr(miri, ignore)] fn imm_at_overflow() { - test_store_at_imm_overflow(WASM_OP, 0); - test_store_at_imm_overflow(WASM_OP, 1); - test_store_at_imm_overflow(WASM_OP, -1); - test_store_at_imm_overflow(WASM_OP, 42); - test_store_at_imm_overflow(WASM_OP, i32::MIN); - test_store_at_imm_overflow(WASM_OP, i32::MAX); + let values = [0, 1, -1, i32::MIN, i32::MAX]; + for value in values { + test_store_at_imm_overflow(WASM_OP, value); + } } diff --git a/crates/wasmi/src/engine/translator/tests/op/store/i32_store8.rs b/crates/wasmi/src/engine/translator/tests/op/store/i32_store8.rs index d85aec2f27..3f4f71c3dc 100644 --- a/crates/wasmi/src/engine/translator/tests/op/store/i32_store8.rs +++ b/crates/wasmi/src/engine/translator/tests/op/store/i32_store8.rs @@ -12,7 +12,15 @@ fn reg() { #[cfg_attr(miri, ignore)] fn imm() { let values = [ + 0, + 1, + -1, + 42, i32::from(i16::MIN) - 1, + i32::from(i16::MIN), + i32::from(i16::MIN + 1), + i32::from(i16::MAX - 1), + i32::from(i16::MAX), i32::from(i16::MAX) + 1, i32::MIN, i32::MIN + 1, @@ -20,25 +28,7 @@ fn imm() { i32::MAX - 1, ]; for value in values { - test_store_imm::(WASM_OP, value, Instruction::i32_store8); - } -} - -#[test] -#[cfg_attr(miri, ignore)] -fn imm16() { - let values = [ - 0, - 1, - -1, - 42, - (i16::MIN + 1) as i8, - (i16::MIN) as i8, - (i16::MAX - 1) as i8, - (i16::MAX) as i8, - ]; - for value in values { - test_store_imm16::(WASM_OP, Instruction::i32_store8_imm, value); + test_store_wrap_imm::(WASM_OP, value, Instruction::i32_store8_imm); } } @@ -51,32 +41,36 @@ fn offset16() { #[test] #[cfg_attr(miri, ignore)] fn offset16_imm() { - test_store_offset16_imm::( - WASM_OP, + let values = [ + 0, + 1, + -1, + -1000, + 1000, + i32::from(i8::MIN) - 1, + i32::from(i8::MIN), + i32::from(i8::MIN) + 1, + i32::from(i8::MAX) - 1, + i32::from(i8::MAX), + i32::from(i8::MAX) + 1, i32::from(i16::MIN) - 1, - Instruction::i32_store8_offset16, - ); - test_store_offset16_imm::( - WASM_OP, + i32::from(i16::MIN), + i32::from(i16::MIN) + 1, + i32::from(i16::MAX) - 1, + i32::from(i16::MAX), i32::from(i16::MAX) + 1, - Instruction::i32_store8_offset16, - ); - test_store_offset16_imm::(WASM_OP, i32::MIN + 1, Instruction::i32_store8_offset16); - test_store_offset16_imm::(WASM_OP, i32::MAX - 1, Instruction::i32_store8_offset16); - test_store_offset16_imm::(WASM_OP, i32::MIN, Instruction::i32_store8_offset16); - test_store_offset16_imm::(WASM_OP, i32::MAX, Instruction::i32_store8_offset16); -} - -#[test] -#[cfg_attr(miri, ignore)] -fn offset16_imm16() { - test_store_offset16_imm16::(WASM_OP, 0, Instruction::i32_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, 1, Instruction::i32_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, -1, Instruction::i32_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MIN + 1, Instruction::i32_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MAX - 1, Instruction::i32_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MIN, Instruction::i32_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MAX, Instruction::i32_store8_offset16_imm); + i32::MIN, + i32::MIN + 1, + i32::MAX - 1, + i32::MAX, + ]; + for value in values { + test_store_wrap_offset16_imm::( + WASM_OP, + value, + Instruction::i32_store8_offset16_imm, + ); + } } #[test] @@ -94,18 +88,30 @@ fn at_overflow() { #[test] #[cfg_attr(miri, ignore)] fn at_imm() { - test_store_at_imm::(WASM_OP, i32::from(i16::MAX) + 1, Instruction::i32_store8_at); - test_store_at_imm::(WASM_OP, i32::MAX - 1, Instruction::i32_store8_at); - test_store_at_imm::(WASM_OP, i32::MAX, Instruction::i32_store8_at); + let values = [ + 0, + 1, + -1000, + 1000, + i32::from(i16::MIN), + i32::from(i16::MIN) + 1, + i32::from(i16::MAX) - 1, + i32::from(i16::MAX), + i32::MIN, + i32::MIN + 1, + i32::MAX - 1, + i32::MAX, + ]; + for value in values { + test_store_wrap_at_imm::(WASM_OP, value, Instruction::i32_store8_at_imm); + } } #[test] #[cfg_attr(miri, ignore)] fn imm_at_overflow() { - test_store_at_imm_overflow(WASM_OP, 0); - test_store_at_imm_overflow(WASM_OP, 1); - test_store_at_imm_overflow(WASM_OP, -1); - test_store_at_imm_overflow(WASM_OP, 42); - test_store_at_imm_overflow(WASM_OP, i32::MIN); - test_store_at_imm_overflow(WASM_OP, i32::MAX); + let values = [0, 1, -1, i32::MIN, i32::MAX]; + for value in values { + test_store_at_imm_overflow(WASM_OP, value); + } } diff --git a/crates/wasmi/src/engine/translator/tests/op/store/i64_store16.rs b/crates/wasmi/src/engine/translator/tests/op/store/i64_store16.rs index a056acff96..127761daae 100644 --- a/crates/wasmi/src/engine/translator/tests/op/store/i64_store16.rs +++ b/crates/wasmi/src/engine/translator/tests/op/store/i64_store16.rs @@ -12,7 +12,15 @@ fn reg() { #[cfg_attr(miri, ignore)] fn imm() { let values = [ + 0, + 1, + -1, + 42, i64::from(i16::MIN) - 1, + i64::from(i16::MIN), + i64::from(i16::MIN + 1), + i64::from(i16::MAX - 1), + i64::from(i16::MAX), i64::from(i16::MAX) + 1, i64::MIN, i64::MIN + 1, @@ -20,16 +28,7 @@ fn imm() { i64::MAX - 1, ]; for value in values { - test_store_imm::(WASM_OP, value, Instruction::i64_store16); - } -} - -#[test] -#[cfg_attr(miri, ignore)] -fn imm16() { - let values = [0, 1, -1, 42, i16::MIN + 1, i16::MIN, i16::MAX - 1, i16::MAX]; - for value in values { - test_store_imm16::(WASM_OP, Instruction::i64_store16_imm, value); + test_store_wrap_imm::(WASM_OP, value, Instruction::i64_store16_imm); } } @@ -42,32 +41,36 @@ fn offset16() { #[test] #[cfg_attr(miri, ignore)] fn offset16_imm() { - test_store_offset16_imm::( - WASM_OP, + let values = [ + 0, + 1, + -1, + -1000, + 1000, + i64::from(i8::MIN) - 1, + i64::from(i8::MIN), + i64::from(i8::MIN) + 1, + i64::from(i8::MAX) - 1, + i64::from(i8::MAX), + i64::from(i8::MAX) + 1, i64::from(i16::MIN) - 1, - Instruction::i64_store16_offset16, - ); - test_store_offset16_imm::( - WASM_OP, + i64::from(i16::MIN), + i64::from(i16::MIN) + 1, + i64::from(i16::MAX) - 1, + i64::from(i16::MAX), i64::from(i16::MAX) + 1, - Instruction::i64_store16_offset16, - ); - test_store_offset16_imm::(WASM_OP, i64::MIN + 1, Instruction::i64_store16_offset16); - test_store_offset16_imm::(WASM_OP, i64::MAX - 1, Instruction::i64_store16_offset16); - test_store_offset16_imm::(WASM_OP, i64::MIN, Instruction::i64_store16_offset16); - test_store_offset16_imm::(WASM_OP, i64::MAX, Instruction::i64_store16_offset16); -} - -#[test] -#[cfg_attr(miri, ignore)] -fn offset16_imm16() { - test_store_offset16_imm16::(WASM_OP, 0, Instruction::i64_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, 1, Instruction::i64_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, -1, Instruction::i64_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MIN + 1, Instruction::i64_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MAX - 1, Instruction::i64_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MIN, Instruction::i64_store16_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i16::MAX, Instruction::i64_store16_offset16_imm); + i64::MIN, + i64::MIN + 1, + i64::MAX - 1, + i64::MAX, + ]; + for value in values { + test_store_wrap_offset16_imm::( + WASM_OP, + value, + Instruction::i64_store16_offset16_imm, + ); + } } #[test] @@ -85,22 +88,30 @@ fn at_overflow() { #[test] #[cfg_attr(miri, ignore)] fn at_imm() { - test_store_at_imm::( - WASM_OP, - i64::from(i16::MAX) + 1, - Instruction::i64_store16_at, - ); - test_store_at_imm::(WASM_OP, i64::MAX - 1, Instruction::i64_store16_at); - test_store_at_imm::(WASM_OP, i64::MAX, Instruction::i64_store16_at); + let values = [ + 0, + 1, + -1000, + 1000, + i64::from(i16::MIN), + i64::from(i16::MIN) + 1, + i64::from(i16::MAX) - 1, + i64::from(i16::MAX), + i64::MIN, + i64::MIN + 1, + i64::MAX - 1, + i64::MAX, + ]; + for value in values { + test_store_wrap_at_imm::(WASM_OP, value, Instruction::i64_store16_at_imm); + } } #[test] #[cfg_attr(miri, ignore)] fn imm_at_overflow() { - test_store_at_imm_overflow(WASM_OP, 0); - test_store_at_imm_overflow(WASM_OP, 1); - test_store_at_imm_overflow(WASM_OP, -1); - test_store_at_imm_overflow(WASM_OP, 42); - test_store_at_imm_overflow(WASM_OP, i64::MIN); - test_store_at_imm_overflow(WASM_OP, i64::MAX); + let values = [0, 1, -1, i64::MIN, i64::MAX]; + for value in values { + test_store_at_imm_overflow(WASM_OP, value); + } } diff --git a/crates/wasmi/src/engine/translator/tests/op/store/i64_store32.rs b/crates/wasmi/src/engine/translator/tests/op/store/i64_store32.rs index dcb594f06f..c61dcc7079 100644 --- a/crates/wasmi/src/engine/translator/tests/op/store/i64_store32.rs +++ b/crates/wasmi/src/engine/translator/tests/op/store/i64_store32.rs @@ -14,10 +14,8 @@ fn imm() { let values = [ i64::from(i16::MIN) - 1, i64::from(i16::MAX) + 1, - i64::MIN, - i64::MIN + 1, - i64::MAX, - i64::MAX - 1, + i64::from(i32::MIN) + i64::from(i16::MAX), + i64::from(i32::MIN) + i64::from(i16::MAX) - 1, ]; for value in values { test_store_imm::(WASM_OP, value, Instruction::i64_store32); @@ -27,9 +25,22 @@ fn imm() { #[test] #[cfg_attr(miri, ignore)] fn imm16() { - let values = [0, 1, -1, 42, i16::MIN + 1, i16::MIN, i16::MAX - 1, i16::MAX].map(i32::from); + let values = [ + 0, + 1, + -1, + 42, + i64::from(i16::MIN) + 1, + i64::from(i16::MIN), + i64::from(i16::MAX) - 1, + i64::from(i16::MAX), + i64::MIN, + i64::MIN + 1, + i64::MAX, + i64::MAX - 1, + ]; for value in values { - test_store_imm16::(WASM_OP, Instruction::i64_store32_imm16, value); + test_store_wrap_imm::(WASM_OP, value, Instruction::i64_store32_imm16); } } @@ -42,40 +53,42 @@ fn offset16() { #[test] #[cfg_attr(miri, ignore)] fn offset16_imm() { - test_store_offset16_imm::( - WASM_OP, + let values = [ i64::from(i16::MIN) - 1, - Instruction::i64_store32_offset16, - ); - test_store_offset16_imm::( - WASM_OP, i64::from(i16::MAX) + 1, - Instruction::i64_store32_offset16, - ); - test_store_offset16_imm::(WASM_OP, i64::MAX - 1, Instruction::i64_store32_offset16); - test_store_offset16_imm::(WASM_OP, i64::MIN + 1, Instruction::i64_store32_offset16); - test_store_offset16_imm::(WASM_OP, i64::MIN, Instruction::i64_store32_offset16); - test_store_offset16_imm::(WASM_OP, i64::MAX, Instruction::i64_store32_offset16); + i64::from(i32::MIN) + i64::from(i16::MAX), + i64::from(i32::MIN) + i64::from(i16::MAX) - 1, + ]; + for value in values { + test_store_offset16_imm::(WASM_OP, value, Instruction::i64_store32_offset16); + } } #[test] #[cfg_attr(miri, ignore)] fn offset16_imm16() { - test_store_offset16_imm16::(WASM_OP, 0, Instruction::i64_store32_offset16_imm16); - test_store_offset16_imm16::(WASM_OP, 1, Instruction::i64_store32_offset16_imm16); - test_store_offset16_imm16::(WASM_OP, -1, Instruction::i64_store32_offset16_imm16); - test_store_offset16_imm16::( - WASM_OP, - i16::MIN + 1, - Instruction::i64_store32_offset16_imm16, - ); - test_store_offset16_imm16::( - WASM_OP, - i16::MAX - 1, - Instruction::i64_store32_offset16_imm16, - ); - test_store_offset16_imm16::(WASM_OP, i16::MIN, Instruction::i64_store32_offset16_imm16); - test_store_offset16_imm16::(WASM_OP, i16::MAX, Instruction::i64_store32_offset16_imm16); + let values = [ + 0, + 1, + -1, + 1000, + -1000, + i64::from(i16::MIN) + 1, + i64::from(i16::MAX) - 1, + i64::from(i16::MIN), + i64::from(i16::MAX), + i64::MAX - 1, + i64::MIN + 1, + i64::MIN, + i64::MAX, + ]; + for value in values { + test_store_wrap_offset16_imm::( + WASM_OP, + value, + Instruction::i64_store32_offset16_imm16, + ); + } } #[test] @@ -93,22 +106,44 @@ fn at_overflow() { #[test] #[cfg_attr(miri, ignore)] fn at_imm() { - test_store_at_imm::( - WASM_OP, + let values = [ + i64::from(i16::MIN) - 1, i64::from(i16::MAX) + 1, - Instruction::i64_store32_at, - ); - test_store_at_imm::(WASM_OP, i64::MAX - 1, Instruction::i64_store32_at); - test_store_at_imm::(WASM_OP, i64::MAX, Instruction::i64_store32_at); + i64::from(i32::MIN) + i64::from(i16::MAX), + i64::from(i32::MIN) + i64::from(i16::MAX) - 1, + ]; + for value in values { + test_store_at_imm::(WASM_OP, value, Instruction::i64_store32_at); + } +} + +#[test] +#[cfg_attr(miri, ignore)] +fn at_imm16() { + let values = [ + 0, + 1, + -1000, + 1000, + i64::from(i16::MIN), + i64::from(i16::MIN) + 1, + i64::from(i16::MAX) - 1, + i64::from(i16::MAX), + i64::MIN, + i64::MIN + 1, + i64::MAX - 1, + i64::MAX, + ]; + for value in values { + test_store_wrap_at_imm::(WASM_OP, value, Instruction::i64_store32_at_imm16); + } } #[test] #[cfg_attr(miri, ignore)] fn imm_at_overflow() { - test_store_at_imm_overflow(WASM_OP, 0); - test_store_at_imm_overflow(WASM_OP, 1); - test_store_at_imm_overflow(WASM_OP, -1); - test_store_at_imm_overflow(WASM_OP, 42); - test_store_at_imm_overflow(WASM_OP, i64::MIN); - test_store_at_imm_overflow(WASM_OP, i64::MAX); + let values = [0, 1, -1, 1000, -1000, i64::MIN, i64::MAX]; + for value in values { + test_store_at_imm_overflow(WASM_OP, value); + } } diff --git a/crates/wasmi/src/engine/translator/tests/op/store/i64_store8.rs b/crates/wasmi/src/engine/translator/tests/op/store/i64_store8.rs index 16efbbf963..d78519deda 100644 --- a/crates/wasmi/src/engine/translator/tests/op/store/i64_store8.rs +++ b/crates/wasmi/src/engine/translator/tests/op/store/i64_store8.rs @@ -12,7 +12,15 @@ fn reg() { #[cfg_attr(miri, ignore)] fn imm() { let values = [ + 0, + 1, + -1, + 42, i64::from(i16::MIN) - 1, + i64::from(i16::MIN), + i64::from(i16::MIN + 1), + i64::from(i16::MAX - 1), + i64::from(i16::MAX), i64::from(i16::MAX) + 1, i64::MIN, i64::MIN + 1, @@ -20,25 +28,7 @@ fn imm() { i64::MAX - 1, ]; for value in values { - test_store_imm::(WASM_OP, value, Instruction::i64_store8); - } -} - -#[test] -#[cfg_attr(miri, ignore)] -fn imm16() { - let values = [ - 0, - 1, - -1, - 42, - (i16::MIN + 1) as i8, - (i16::MIN) as i8, - (i16::MAX - 1) as i8, - (i16::MAX) as i8, - ]; - for value in values { - test_store_imm16::(WASM_OP, Instruction::i64_store8_imm, value); + test_store_wrap_imm::(WASM_OP, value, Instruction::i64_store8_imm); } } @@ -51,32 +41,36 @@ fn offset16() { #[test] #[cfg_attr(miri, ignore)] fn offset16_imm() { - test_store_offset16_imm::( - WASM_OP, + let values = [ + 0, + 1, + -1, + -1000, + 1000, + i64::from(i8::MIN) - 1, + i64::from(i8::MIN), + i64::from(i8::MIN) + 1, + i64::from(i8::MAX) - 1, + i64::from(i8::MAX), + i64::from(i8::MAX) + 1, i64::from(i16::MIN) - 1, - Instruction::i64_store8_offset16, - ); - test_store_offset16_imm::( - WASM_OP, + i64::from(i16::MIN), + i64::from(i16::MIN) + 1, + i64::from(i16::MAX) - 1, + i64::from(i16::MAX), i64::from(i16::MAX) + 1, - Instruction::i64_store8_offset16, - ); - test_store_offset16_imm::(WASM_OP, i64::MIN + 1, Instruction::i64_store8_offset16); - test_store_offset16_imm::(WASM_OP, i64::MAX - 1, Instruction::i64_store8_offset16); - test_store_offset16_imm::(WASM_OP, i64::MIN, Instruction::i64_store8_offset16); - test_store_offset16_imm::(WASM_OP, i64::MAX, Instruction::i64_store8_offset16); -} - -#[test] -#[cfg_attr(miri, ignore)] -fn offset16_imm16() { - test_store_offset16_imm16::(WASM_OP, 0, Instruction::i64_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, 1, Instruction::i64_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, -1, Instruction::i64_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MIN + 1, Instruction::i64_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MAX - 1, Instruction::i64_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MIN, Instruction::i64_store8_offset16_imm); - test_store_offset16_imm16::(WASM_OP, i8::MAX, Instruction::i64_store8_offset16_imm); + i64::MIN, + i64::MIN + 1, + i64::MAX - 1, + i64::MAX, + ]; + for value in values { + test_store_wrap_offset16_imm::( + WASM_OP, + value, + Instruction::i64_store8_offset16_imm, + ); + } } #[test] @@ -94,18 +88,30 @@ fn at_overflow() { #[test] #[cfg_attr(miri, ignore)] fn at_imm() { - test_store_at_imm::(WASM_OP, i64::from(i16::MAX) + 1, Instruction::i64_store8_at); - test_store_at_imm::(WASM_OP, i64::MAX - 1, Instruction::i64_store8_at); - test_store_at_imm::(WASM_OP, i64::MAX, Instruction::i64_store8_at); + let values = [ + 0, + 1, + -1000, + 1000, + i64::from(i16::MIN), + i64::from(i16::MIN) + 1, + i64::from(i16::MAX) - 1, + i64::from(i16::MAX), + i64::MIN, + i64::MIN + 1, + i64::MAX - 1, + i64::MAX, + ]; + for value in values { + test_store_wrap_at_imm::(WASM_OP, value, Instruction::i64_store8_at_imm); + } } #[test] #[cfg_attr(miri, ignore)] fn imm_at_overflow() { - test_store_at_imm_overflow(WASM_OP, 0); - test_store_at_imm_overflow(WASM_OP, 1); - test_store_at_imm_overflow(WASM_OP, -1); - test_store_at_imm_overflow(WASM_OP, 42); - test_store_at_imm_overflow(WASM_OP, i64::MIN); - test_store_at_imm_overflow(WASM_OP, i64::MAX); + let values = [0, 1, -1, i64::MIN, i64::MAX]; + for value in values { + test_store_at_imm_overflow(WASM_OP, value); + } } diff --git a/crates/wasmi/src/engine/translator/tests/op/store/mod.rs b/crates/wasmi/src/engine/translator/tests/op/store/mod.rs index 7752843845..296ffaa479 100644 --- a/crates/wasmi/src/engine/translator/tests/op/store/mod.rs +++ b/crates/wasmi/src/engine/translator/tests/op/store/mod.rs @@ -3,8 +3,10 @@ use super::*; use crate::{ core::UntypedVal, + engine::translator::utils::Wrap, ir::{index::Memory, AnyConst16}, }; +use std::vec; mod f32_store; mod f64_store; @@ -179,6 +181,104 @@ fn test_store_offset16_imm16( test_store_offset16_imm16_for(wasm_op, u16::MAX, value, make_instr); } +fn test_store_wrap_offset16_imm_for( + wasm_op: WasmOp, + offset: u16, + value: Src, + make_instr: fn(ptr: Reg, offset: u16, value: Field) -> Instruction, +) where + Src: Copy + Wrap, + Field: TryFrom, + DisplayWasm: Display, +{ + let param_ty = wasm_op.param_ty(); + let display_value = DisplayWasm::from(value); + let wasm = format!( + r#" + (module + (memory 1) + (func (param $ptr i32) + local.get $ptr + {param_ty}.const {display_value} + {wasm_op} offset={offset} + ) + ) + "# + ); + let value = Field::try_from(value.wrap()).ok().unwrap(); + TranslationTest::from_wat(&wasm) + .expect_func_instrs([make_instr(Reg::from(0), offset, value), Instruction::Return]) + .run(); +} + +fn test_store_wrap_offset16_imm( + wasm_op: WasmOp, + value: Src, + make_instr: fn(ptr: Reg, offset: u16, value: Field) -> Instruction, +) where + Src: Copy + Wrap, + Field: TryFrom, + DisplayWasm: Display, +{ + let offsets = [0, u16::MAX - 1, u16::MAX]; + for offset in offsets { + test_store_wrap_offset16_imm_for(wasm_op, offset, value, make_instr); + } +} + +fn test_store_wrap_imm_for( + wasm_op: WasmOp, + offset: u32, + value: Src, + make_instr: fn(ptr: Reg, memory: Memory) -> Instruction, +) where + Src: Copy + Into + Wrap, + Field: TryFrom + Into, + DisplayWasm: Display, +{ + assert!( + u16::try_from(offset).is_err(), + "this test requires non-16 bit offsets but found {offset}" + ); + let param_ty = wasm_op.param_ty(); + let display_value = DisplayWasm::from(value); + let wasm = format!( + r#" + (module + (memory 1) + (func (param $ptr i32) + local.get $ptr + {param_ty}.const {display_value} + {wasm_op} offset={offset} + ) + ) + "# + ); + let value = Field::try_from(value.wrap()).ok().unwrap(); + TranslationTest::from_wat(&wasm) + .expect_func_instrs([ + make_instr(Reg::from(0), Memory::from(0)), + Instruction::imm16_and_imm32(value, offset), + Instruction::Return, + ]) + .run(); +} + +fn test_store_wrap_imm( + wasm_op: WasmOp, + value: Src, + make_instr: fn(ptr: Reg, memory: Memory) -> Instruction, +) where + Src: Copy + Into + Wrap, + Field: TryFrom + Into, + DisplayWasm: Display, +{ + let offsets = [u32::from(u16::MAX) + 1, u32::MAX - 1, u32::MAX]; + for offset in offsets { + test_store_wrap_imm_for::(wasm_op, offset, value, make_instr); + } +} + fn test_store_imm_for( wasm_op: WasmOp, offset: u32, @@ -226,9 +326,10 @@ fn test_store_imm( T: Copy + Into, DisplayWasm: Display, { - test_store_imm_for(wasm_op, u32::from(u16::MAX) + 1, value, make_instr); - test_store_imm_for(wasm_op, u32::MAX - 1, value, make_instr); - test_store_imm_for(wasm_op, u32::MAX, value, make_instr); + let offsets = [u32::from(u16::MAX) + 1, u32::MAX - 1, u32::MAX]; + for offset in offsets { + test_store_imm_for::(wasm_op, offset, value, make_instr); + } } fn test_store_imm16_for( @@ -320,7 +421,7 @@ fn test_store_at(wasm_op: WasmOp, make_instr: fn(value: Reg, address: u32) -> In test_store_at_for(wasm_op, u32::MAX, 0, make_instr); } -fn test_store_at_overflow_for(wasm_op: WasmOp, ptr: u32, offset: u32) { +fn test_store_at_overflow_for(wasm_op: WasmOp, mem_idx: u32, ptr: u32, offset: u32) { assert!( ptr.checked_add(offset).is_none(), "testcase expects overflowing ptr+offset address" @@ -329,11 +430,12 @@ fn test_store_at_overflow_for(wasm_op: WasmOp, ptr: u32, offset: u32) { let wasm = format!( r#" (module - (memory 1) + (memory $mem0 1) + (memory $mem1 1) (func (param $value {param_ty}) i32.const {ptr} local.get $value - {wasm_op} offset={offset} + {wasm_op} $mem{mem_idx} offset={offset} ) ) "# @@ -344,9 +446,11 @@ fn test_store_at_overflow_for(wasm_op: WasmOp, ptr: u32, offset: u32) { } fn test_store_at_overflow(wasm_op: WasmOp) { - test_store_at_overflow_for(wasm_op, 1, u32::MAX); - test_store_at_overflow_for(wasm_op, u32::MAX, 1); - test_store_at_overflow_for(wasm_op, u32::MAX, u32::MAX); + let ptrs_and_offsets = [(1, u32::MAX), (u32::MAX, 1), (u32::MAX, u32::MAX)]; + for (ptr, offset) in ptrs_and_offsets { + test_store_at_overflow_for(wasm_op, 0, ptr, offset); + test_store_at_overflow_for(wasm_op, 1, ptr, offset); + } } fn test_store_at_imm_for( @@ -403,7 +507,78 @@ fn test_store_at_imm( test_store_at_imm_for(wasm_op, u32::MAX, 0, value, make_instr); } -fn test_store_at_imm_overflow_for(wasm_op: WasmOp, ptr: u32, offset: u32, value: T) +fn test_store_wrap_at_imm_for( + wasm_op: WasmOp, + mem_idx: u32, + ptr: u32, + offset: u32, + value: Src, + make_instr: fn(value: Field, address: u32) -> Instruction, +) where + Src: Copy + Into + Wrap, + Field: TryFrom + Into, + DisplayWasm: Display, +{ + let address = ptr + .checked_add(offset) + .expect("testcase requires valid ptr+offset address"); + let display_value = DisplayWasm::from(value); + let param_ty = wasm_op.param_ty(); + let wasm = format!( + r#" + (module + (memory $mem0 1) + (memory $mem1 1) + (func + i32.const {ptr} + {param_ty}.const {display_value} + {wasm_op} $mem{mem_idx} offset={offset} + ) + ) + "# + ); + let value = Field::try_from(value.wrap()).ok().unwrap(); + let mut instrs = vec![make_instr(value, address)]; + if mem_idx != 0 { + instrs.push(Instruction::memory_index(mem_idx)); + } + instrs.push(Instruction::Return); + TranslationTest::from_wat(&wasm) + .expect_func_instrs(instrs) + .run(); +} + +fn test_store_wrap_at_imm( + wasm_op: WasmOp, + value: Src, + make_instr: fn(value: Field, address: u32) -> Instruction, +) where + Src: Copy + Into + Wrap, + Field: TryFrom + Into, + DisplayWasm: Display, +{ + let ptrs_and_offsets = [ + (0, 0), + (0, 1), + (1, 0), + (1, 1), + (1000, 1000), + (1, u32::MAX - 1), + (u32::MAX - 1, 1), + (0, u32::MAX), + (u32::MAX, 0), + ]; + for (ptr, offset) in ptrs_and_offsets { + test_store_wrap_at_imm_for::( + wasm_op, 0, ptr, offset, value, make_instr, + ); + test_store_wrap_at_imm_for::( + wasm_op, 1, ptr, offset, value, make_instr, + ); + } +} + +fn test_store_at_imm_overflow_for(wasm_op: WasmOp, mem_idx: u8, ptr: u32, offset: u32, value: T) where T: Copy, DisplayWasm: Display, @@ -417,11 +592,12 @@ where let wasm = format!( r#" (module - (memory 1) + (memory $mem0 1) + (memory $mem1 1) (func i32.const {ptr} {param_ty}.const {display_value} - {wasm_op} offset={offset} + {wasm_op} $mem{mem_idx} offset={offset} ) ) "# @@ -436,7 +612,9 @@ where T: Copy, DisplayWasm: Display, { - test_store_at_imm_overflow_for(wasm_op, 1, u32::MAX, value); - test_store_at_imm_overflow_for(wasm_op, u32::MAX, 1, value); - test_store_at_imm_overflow_for(wasm_op, u32::MAX, u32::MAX, value); + let ptrs_and_offsets = [(1, u32::MAX), (u32::MAX, 1), (u32::MAX, u32::MAX)]; + for (ptr, offset) in ptrs_and_offsets { + test_store_at_imm_overflow_for(wasm_op, 0, ptr, offset, value); + test_store_at_imm_overflow_for(wasm_op, 1, ptr, offset, value); + } } diff --git a/crates/wasmi/src/engine/translator/utils.rs b/crates/wasmi/src/engine/translator/utils.rs index 8e6fa3dc80..9a4e6ed431 100644 --- a/crates/wasmi/src/engine/translator/utils.rs +++ b/crates/wasmi/src/engine/translator/utils.rs @@ -100,16 +100,6 @@ impl WasmFloat for f64 { } } -impl Provider { - /// Creates a new `memory` value [`Provider`] from the general [`TypedProvider`]. - pub fn new(provider: TypedProvider) -> Self { - match provider { - TypedProvider::Const(value) => Self::Const(u32::from(value) as u8), - TypedProvider::Register(register) => Self::Register(register), - } - } -} - impl Provider> { /// Creates a new `table` or `memory` index [`Provider`] from the general [`TypedProvider`]. /// @@ -169,3 +159,43 @@ impl FromProviders for BoundedRegSpan { Some(Self::new(RegSpan::new(Reg::from(first_index)), len)) } } + +/// Implemented by integer types to wrap them to another (smaller) integer type. +pub trait Wrap { + /// Wraps `self` into a value of type `T`. + fn wrap(self) -> T; +} + +impl Wrap for T { + #[inline] + fn wrap(self) -> T { + self + } +} + +macro_rules! impl_wrap_for { + ( $($from_ty:ty => $to_ty:ty),* $(,)? ) => { + $( + impl Wrap<$to_ty> for $from_ty { + #[inline] + fn wrap(self) -> $to_ty { self as _ } + } + )* + }; +} +impl_wrap_for! { + // signed + i16 => i8, + i32 => i8, + i32 => i16, + i64 => i8, + i64 => i16, + i64 => i32, + // unsigned + u16 => u8, + u32 => u8, + u32 => u16, + u64 => u8, + u64 => u16, + u64 => u32, +} diff --git a/crates/wasmi/src/engine/translator/visit.rs b/crates/wasmi/src/engine/translator/visit.rs index aad0240256..1190e7b739 100644 --- a/crates/wasmi/src/engine/translator/visit.rs +++ b/crates/wasmi/src/engine/translator/visit.rs @@ -929,7 +929,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_i32_store8(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - self.translate_istore::( + self.translate_istore_wrap::( memarg, Instruction::i32_store8, Instruction::i32_store8_imm, @@ -941,7 +941,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_i32_store16(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - self.translate_istore::( + self.translate_istore_wrap::( memarg, Instruction::i32_store16, Instruction::i32_store16_imm, @@ -953,7 +953,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_i64_store8(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - self.translate_istore::( + self.translate_istore_wrap::( memarg, Instruction::i64_store8, Instruction::i64_store8_imm, @@ -965,7 +965,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_i64_store16(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - self.translate_istore::( + self.translate_istore_wrap::( memarg, Instruction::i64_store16, Instruction::i64_store16_imm, @@ -977,7 +977,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_i64_store32(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - self.translate_istore::( + self.translate_istore_wrap::( memarg, Instruction::i64_store32, Instruction::i64_store32_imm16, @@ -3156,7 +3156,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { let memory = index::Memory::from(mem); let (dst, value, len) = self.alloc.stack.pop3(); let dst = >>::new(dst, &mut self.alloc.stack)?; - let value = >::new(value); + let value = value.map_const(|value| u32::from(value) as u8); let len = >>::new(len, &mut self.alloc.stack)?; let instr = match (dst, value, len) { (Provider::Register(dst), Provider::Register(value), Provider::Register(len)) => {