diff --git a/crates/it-lilo-utils/src/lib.rs b/crates/it-lilo-utils/src/lib.rs index dd94ae1..170b727 100644 --- a/crates/it-lilo-utils/src/lib.rs +++ b/crates/it-lilo-utils/src/lib.rs @@ -1,4 +1,21 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + pub mod error; +mod macros; pub mod memory_reader; pub mod memory_writer; @@ -45,7 +62,7 @@ pub fn record_size(record_type: &IRecordType) -> usize { } pub fn type_tag_form_itype(itype: &IType) -> u32 { - const POINTER_CODE: u32 = 3; // u32 on the sdk + const POINTER_CODE: u32 = 3; // u32 in the sdk match itype { IType::Boolean => 0, // u8 @@ -64,7 +81,7 @@ pub fn type_tag_form_itype(itype: &IType) -> u32 { } pub fn type_tag_form_ivalue(itype: &IValue) -> u32 { - const POINTER_CODE: u32 = 3; // u32 on the sdk + const POINTER_CODE: u32 = 3; // u32 in the sdk match itype { IValue::Boolean(_) => 0, // u8 diff --git a/crates/it-lilo-utils/src/macros.rs b/crates/it-lilo-utils/src/macros.rs new file mode 100644 index 0000000..dfb0d14 --- /dev/null +++ b/crates/it-lilo-utils/src/macros.rs @@ -0,0 +1,103 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#[macro_export] +macro_rules! value_der { + ($self:expr, $offset:expr, @seq_start $($ids:tt),* @seq_end) => { + [$($self.reader.memory[$offset + $ids].get()),+] + }; + + ($self:expr, $offset:expr, 1) => { + crate::value_der!($self, $offset, @seq_start 0 @seq_end); + }; + + ($self:expr, $offset:expr, 2) => { + crate::value_der!($self, $offset, @seq_start 0, 1 @seq_end); + }; + + ($self:expr, $offset:expr, 4) => { + crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3 @seq_end); + }; + + ($self:expr, $offset:expr, 8) => { + crate::value_der!($self, $offset, @seq_start 0, 1, 2, 3, 4, 5, 6, 7 @seq_end); + }; +} + +#[macro_export] +macro_rules! read_ty { + ($func_name:ident, $ty:ty, 1) => { + pub fn $func_name(&self) -> $ty { + let offset = self.offset.get(); + let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 1)); + + self.offset.set(offset + 1); + result + } + }; + + ($func_name:ident, $ty:ty, 2) => { + pub fn $func_name(&self) -> $ty { + let offset = self.offset.get(); + let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 2)); + + self.offset.set(offset + 2); + result + } + }; + + ($func_name:ident, $ty:ty, 4) => { + pub fn $func_name(&self) -> $ty { + let offset = self.offset.get(); + let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 4)); + + self.offset.set(offset + 4); + result + } + }; + + ($func_name:ident, $ty:ty, 8) => { + pub fn $func_name(&self) -> $ty { + let offset = self.offset.get(); + let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 8)); + + self.offset.set(offset + 8); + result + } + }; +} + +#[macro_export] +macro_rules! read_array_ty { + ($func_name:ident, $ty:ident, $ity:ident) => { + pub fn $func_name( + &self, + offset: usize, + elements_count: usize, + ) -> crate::MResult> { + let reader = + self.sequential_reader(offset, std::mem::size_of::<$ty>() * elements_count)?; + let mut result = Vec::with_capacity(elements_count); + + for _ in 0..elements_count { + let value = paste::paste! { reader.[]()}; + result.push(IValue::$ity(value)); + } + + Ok(result) + } + }; +} diff --git a/crates/it-lilo-utils/src/memory_reader.rs b/crates/it-lilo-utils/src/memory_reader.rs index 70e1956..5065ee2 100644 --- a/crates/it-lilo-utils/src/memory_reader.rs +++ b/crates/it-lilo-utils/src/memory_reader.rs @@ -15,6 +15,8 @@ */ use crate::error::MemoryAccessError; +use crate::read_array_ty; +use crate::read_ty; use crate::IValue; use crate::MResult; @@ -25,98 +27,13 @@ pub struct MemoryReader<'m> { } /// Reads values of basic types sequentially from the provided reader. -/// It don't check memory limits for the optimization purposes, -/// so it could created by the MemoryReader::sequential_reader method. +/// It doesn't check memory limits for the optimization purposes, +/// so it could be created only by the MemoryReader::sequential_reader method. pub struct SequentialReader<'r, 'm> { reader: &'r MemoryReader<'m>, offset: Cell, } -macro_rules! value_der { - ($self:expr, $offset:expr, @seq_start $($ids:tt),* @seq_end) => { - [$($self.reader.memory[$offset + $ids].get()),+] - }; - - ($self:expr, $offset:expr, 1) => { - value_der!($self, $offset, @seq_start 0 @seq_end); - }; - - ($self:expr, $offset:expr, 2) => { - value_der!($self, $offset, @seq_start 0, 1 @seq_end); - }; - - ($self:expr, $offset:expr, 4) => { - value_der!($self, $offset, @seq_start 0, 1, 2, 3 @seq_end); - }; - - ($self:expr, $offset:expr, 8) => { - value_der!($self, $offset, @seq_start 0, 1, 2, 3, 4, 5, 6, 7 @seq_end); - }; -} - -macro_rules! read_ty { - ($func_name:ident, $ty:ty, 1) => { - pub fn $func_name(&self) -> $ty { - let offset = self.offset.get(); - let result = <$ty>::from_le_bytes(value_der!(self, offset, 1)); - - self.offset.set(offset + 1); - result - } - }; - - ($func_name:ident, $ty:ty, 2) => { - pub fn $func_name(&self) -> $ty { - let offset = self.offset.get(); - let result = <$ty>::from_le_bytes(value_der!(self, offset, 2)); - - self.offset.set(offset + 2); - result - } - }; - - ($func_name:ident, $ty:ty, 4) => { - pub fn $func_name(&self) -> $ty { - let offset = self.offset.get(); - let result = <$ty>::from_le_bytes(value_der!(self, offset, 4)); - - self.offset.set(offset + 4); - result - } - }; - - ($func_name:ident, $ty:ty, 8) => { - pub fn $func_name(&self) -> $ty { - let offset = self.offset.get(); - let result = <$ty>::from_le_bytes(value_der!(self, offset, 8)); - - self.offset.set(offset + 8); - result - } - }; -} - -macro_rules! read_array_ty { - ($func_name:ident, $ty:ident, $ity:ident) => { - pub fn $func_name( - &self, - offset: usize, - elements_count: usize, - ) -> crate::MResult> { - let reader = - self.sequential_reader(offset, std::mem::size_of::<$ty>() * elements_count)?; - let mut result = Vec::with_capacity(elements_count); - - for _ in 0..elements_count { - let value = paste::paste! { reader.[]()}; - result.push(IValue::$ity(value)); - } - - Ok(result) - } - }; -} - impl<'m> MemoryReader<'m> { pub fn new(memory: &'m [Cell]) -> Self { Self { memory } @@ -161,6 +78,8 @@ impl<'m> MemoryReader<'m> { pub fn check_access(&self, offset: usize, size: usize) -> MResult<()> { let right = offset + size; + + // the first condition is a check for overflow if right < offset || right >= self.memory.len() { return Err(MemoryAccessError::InvalidAccess { offset, diff --git a/crates/it-lilo-utils/src/memory_writer.rs b/crates/it-lilo-utils/src/memory_writer.rs index f75cd65..a616d1f 100644 --- a/crates/it-lilo-utils/src/memory_writer.rs +++ b/crates/it-lilo-utils/src/memory_writer.rs @@ -25,7 +25,7 @@ pub struct MemoryWriter<'m> { /// Writes values of basic types sequentially to the provided writer. /// It don't check memory limits for the optimization purposes, -/// so it could created by the MemoryReader::sequential_reader method. +/// so it could be created only by the MemoryReader::sequential_reader method. pub struct SequentialWriter<'w, 'm> { writer: &'w MemoryWriter<'m>, offset: Cell, @@ -87,6 +87,8 @@ impl<'m> MemoryWriter<'m> { pub fn check_access(&self, offset: usize, size: usize) -> MResult<()> { let right = offset + size; + + // the first condition is a check for overflow if right < offset || right >= self.memory.len() { return Err(MemoryAccessError::InvalidAccess { offset, diff --git a/wasmer-it/src/decoders/binary.rs b/wasmer-it/src/decoders/binary.rs index ef69dc8..ea24b01 100644 --- a/wasmer-it/src/decoders/binary.rs +++ b/wasmer-it/src/decoders/binary.rs @@ -308,30 +308,6 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( (input, Instruction::ArrayLowerMemory { value_type }) } - /* - 0x39 => (input, Instruction::ArraySize), - - 0x25 => { - consume!((input, argument_0) = uleb(input)?); - - ( - input, - Instruction::RecordLift { - type_index: argument_0 as u32, - }, - ) - } - 0x26 => { - consume!((input, argument_0) = uleb(input)?); - - ( - input, - Instruction::RecordLower { - type_index: argument_0 as u32, - }, - ) - } - */ 0x3A => { consume!((input, record_type_id) = uleb(input)?); @@ -360,6 +336,28 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x3E => (input, Instruction::BoolFromI32), 0x3F => (input, Instruction::I32FromBool), + 0x40 => { + consume!((input, value) = uleb(input)?); + + ( + input, + Instruction::PushI32 { + value: value as i32, + }, + ) + } + + 0x41 => { + consume!((input, value) = uleb(input)?); + + ( + input, + Instruction::PushI64 { + value: value as i64, + }, + ) + } + _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), }) } diff --git a/wasmer-it/src/decoders/wat.rs b/wasmer-it/src/decoders/wat.rs index 9cbb8b1..577ffa4 100644 --- a/wasmer-it/src/decoders/wat.rs +++ b/wasmer-it/src/decoders/wat.rs @@ -53,10 +53,12 @@ mod keyword { custom_keyword!(i32_from_s16 = "i32.from_s16"); custom_keyword!(i32_from_s32 = "i32.from_s32"); custom_keyword!(i32_from_s64 = "i32.from_s64"); + custom_keyword!(i32_push = "i32.push"); custom_keyword!(i64_from_s8 = "i64.from_s8"); custom_keyword!(i64_from_s16 = "i64.from_s16"); custom_keyword!(i64_from_s32 = "i64.from_s32"); custom_keyword!(i64_from_s64 = "i64.from_s64"); + custom_keyword!(i64_push = "i64.push"); custom_keyword!(u8_from_i32 = "u8.from_i32"); custom_keyword!(u8_from_i64 = "u8.from_i64"); custom_keyword!(u16_from_i32 = "u16.from_i32"); @@ -73,6 +75,8 @@ mod keyword { custom_keyword!(i64_from_u16 = "i64.from_u16"); custom_keyword!(i64_from_u32 = "i64.from_u32"); custom_keyword!(i64_from_u64 = "i64.from_u64"); + custom_keyword!(f32_push = "f32.push"); + custom_keyword!(f64_push = "f64.push"); custom_keyword!(string_lift_memory = "string.lift_memory"); custom_keyword!(string_lower_memory = "string.lower_memory"); custom_keyword!(string_size = "string.size"); @@ -153,6 +157,12 @@ impl<'a> Parse<'a> for Instruction { parser.parse::()?; Ok(Instruction::I32FromS64) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::PushI32 { + value: parser.parse()?, + }) } else if lookahead.peek::() { parser.parse::()?; @@ -169,6 +179,12 @@ impl<'a> Parse<'a> for Instruction { parser.parse::()?; Ok(Instruction::I64FromS64) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::PushI64 { + value: parser.parse()?, + }) } else if lookahead.peek::() { parser.parse::()?; diff --git a/wasmer-it/src/encoders/binary.rs b/wasmer-it/src/encoders/binary.rs index 4ca3a83..ae64052 100644 --- a/wasmer-it/src/encoders/binary.rs +++ b/wasmer-it/src/encoders/binary.rs @@ -243,17 +243,6 @@ where 0x38_u8.to_bytes(writer)?; value_type.to_bytes(writer)? } - /* - Instruction::ArraySize => 0x39_u8.to_bytes(writer)?, - Instruction::RecordLift { type_index } => { - 0x25_u8.to_bytes(writer)?; - (*type_index as u64).to_bytes(writer)? - } - Instruction::RecordLower { type_index } => { - 0x26_u8.to_bytes(writer)?; - (*type_index as u64).to_bytes(writer)? - } - */ Instruction::RecordLiftMemory { record_type_id: type_index, } => { @@ -268,6 +257,14 @@ where } Instruction::Dup => 0x34_u8.to_bytes(writer)?, Instruction::Swap2 => 0x35_u8.to_bytes(writer)?, + Instruction::PushI32 { value } => { + 0x40_u8.to_bytes(writer)?; + (*value as u64).to_bytes(writer)? + } + Instruction::PushI64 { value } => { + 0x41_u8.to_bytes(writer)?; + (*value as u64).to_bytes(writer)? + } } Ok(()) diff --git a/wasmer-it/src/encoders/wat.rs b/wasmer-it/src/encoders/wat.rs index 6a0f1eb..a11c6de 100644 --- a/wasmer-it/src/encoders/wat.rs +++ b/wasmer-it/src/encoders/wat.rs @@ -108,11 +108,6 @@ impl ToString for &Instruction { Instruction::ArrayLowerMemory { value_type } => { format!("array.lower_memory {}", value_type.to_string()) } - /* - Instruction::ArraySize => "byte_array.size".into(), - Instruction::RecordLift { type_index } => format!("record.lift {}", type_index), - Instruction::RecordLower { type_index } => format!("record.lower {}", type_index), - */ Instruction::RecordLiftMemory { record_type_id: type_index, } => format!("record.lift_memory {}", type_index), @@ -121,6 +116,8 @@ impl ToString for &Instruction { } => format!("record.lower_memory {}", type_index), Instruction::Dup => "dup".into(), Instruction::Swap2 => "swap2".into(), + Instruction::PushI32 { value } => format!("i32.push {}", value), + Instruction::PushI64 { value } => format!("i64.push {}", value), } } } diff --git a/wasmer-it/src/interpreter/instructions/mod.rs b/wasmer-it/src/interpreter/instructions/mod.rs index b2c31a5..fdf64da 100644 --- a/wasmer-it/src/interpreter/instructions/mod.rs +++ b/wasmer-it/src/interpreter/instructions/mod.rs @@ -4,6 +4,7 @@ mod call_core; mod dup; pub(self) mod lilo; mod numbers; +mod push; mod records; mod strings; mod swap2; @@ -21,6 +22,7 @@ pub(crate) use arrays::*; pub(crate) use call_core::call_core; pub(crate) use dup::dup; pub(crate) use numbers::*; +pub(crate) use push::*; pub(crate) use records::*; pub(crate) use strings::*; pub(crate) use swap2::swap2; @@ -183,6 +185,18 @@ pub enum Instruction { record_type_id: u32, }, + /// The `i32.push` instruction. + PushI32 { + /// The value that should be pushed on the stack. + value: i32, + }, + + /// The `i64.push` instruction. + PushI64 { + /// The value that should be pushed on the stack. + value: i64, + }, + /// The `dup` instructions. Dup, diff --git a/wasmer-it/src/interpreter/instructions/numbers.rs b/wasmer-it/src/interpreter/instructions/numbers.rs index 6eab229..e6edb2b 100644 --- a/wasmer-it/src/interpreter/instructions/numbers.rs +++ b/wasmer-it/src/interpreter/instructions/numbers.rs @@ -68,7 +68,7 @@ lowering_lifting!(s32_from_i32, "s32.from_i32", S32, I32); lowering_lifting!(s32_from_i64, "s32.from_i64", S32, I64); lowering_lifting!(s64_from_i32, "s64.from_i32", S64, I32); lowering_lifting!(s64_from_i64, "s64.from_i64", S64, I64); -//lowering_lifting!(i32_from_bool, "i32.from_bool", I32, Boolean); +lowering_lifting!(i32_from_bool, "i32.from_bool", I32, Boolean); lowering_lifting!(i32_from_s8, "i32.from_s8", I32, S8); lowering_lifting!(i32_from_s16, "i32.from_s16", I32, S16); lowering_lifting!(i32_from_s32, "i32.from_s32", I32, S32); @@ -132,44 +132,6 @@ executable_instruction!( } ); -executable_instruction!( - i32_from_bool(instruction: Instruction) -> _ { - move |runtime| -> _ { - match runtime.stack.pop1() { - Some(IValue::Boolean(value)) => { - runtime - .stack - .push({ - let converted_value = IValue::I32(value as _); - - log::trace!("i32.from_bool: converting {:?} to {:?}" , value, converted_value); - - converted_value - }) - } - Some(wrong_value) => { - return instr_error!( - instruction.clone(), - InstructionErrorKind::InvalidValueOnTheStack { - expected_type: IType::I32, - received_value: wrong_value, - } - ) - }, - - None => { - return instr_error!( - instruction.clone(), - InstructionErrorKind::StackIsTooSmall { needed: 1 } - ) - } - } - - Ok(()) - } - } -); - #[cfg(test)] mod tests { test_executable_instruction!( diff --git a/wasmer-it/src/interpreter/instructions/push.rs b/wasmer-it/src/interpreter/instructions/push.rs new file mode 100644 index 0000000..0281335 --- /dev/null +++ b/wasmer-it/src/interpreter/instructions/push.rs @@ -0,0 +1,25 @@ +use crate::IValue; + +executable_instruction!( + push_i32(value: i32) -> _ { + move |runtime| -> _ { + + log::trace!("push_i32: push {} on the stack", value); + runtime.stack.push(IValue::I32(value)); + + Ok(()) + } + } +); + +executable_instruction!( + push_i64(value: i64) -> _ { + move |runtime| -> _ { + + log::trace!("push_i32: push {} on the stack", value); + runtime.stack.push(IValue::I64(value)); + + Ok(()) + } + } +); diff --git a/wasmer-it/src/interpreter/instructions/records/lift_record.rs b/wasmer-it/src/interpreter/instructions/records/lift_record.rs index 0ecc24b..b52ab43 100644 --- a/wasmer-it/src/interpreter/instructions/records/lift_record.rs +++ b/wasmer-it/src/interpreter/instructions/records/lift_record.rs @@ -9,7 +9,6 @@ use it_lilo_utils::memory_reader::MemoryReader; use it_lilo_utils::memory_reader::SequentialReader; use it_lilo_utils::record_size; -#[rustfmt::skip] pub(crate) fn record_lift_memory_impl( li_helper: &LiHelper<'_>, record_type: &IRecordType, @@ -38,8 +37,10 @@ pub(crate) fn record_lift_memory_impl( IType::F64 => values.push(IValue::F64(seq_reader.read_f64())), IType::String => values.push(IValue::String(read_string(reader, &seq_reader)?)), IType::ByteArray => values.push(read_byte_array(reader, &seq_reader)?), - IType::Array(ty) => values.push(read_array(&li_helper, &seq_reader, &**ty)?), - IType::Record(record_type_id) => values.push(read_record(li_helper, &seq_reader, *record_type_id)?), + IType::Array(ty) => values.push(read_array(&li_helper, &seq_reader, &**ty)?), + IType::Record(record_type_id) => { + values.push(read_record(li_helper, &seq_reader, *record_type_id)?) + } } } diff --git a/wasmer-it/src/interpreter/mod.rs b/wasmer-it/src/interpreter/mod.rs index 868f76f..659c71a 100644 --- a/wasmer-it/src/interpreter/mod.rs +++ b/wasmer-it/src/interpreter/mod.rs @@ -233,6 +233,8 @@ where Instruction::I64FromU16 => instructions::i64_from_u16(instruction), Instruction::I64FromU32 => instructions::i64_from_u32(instruction), Instruction::I64FromU64 => instructions::i64_from_u64(instruction), + Instruction::PushI32 { value } => instructions::push_i32(value), + Instruction::PushI64 { value } => instructions::push_i64(value), Instruction::StringLiftMemory => instructions::string_lift_memory(instruction), Instruction::StringLowerMemory => instructions::string_lower_memory(instruction),