diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/benchmarks/benches/selfhosted.rs | 6 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 79 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/macros.rs | 27 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/mod.rs | 177 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/call_stack.rs | 4 | ||||
| -rw-r--r-- | crates/types/src/archive.rs | 2 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 156 | ||||
| -rw-r--r-- | crates/types/src/value.rs | 23 |
8 files changed, 295 insertions, 179 deletions
diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 1396fd1..94dfdce 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -61,10 +61,10 @@ fn criterion_benchmark(c: &mut Criterion) { { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); - // group.bench_function("native", |b| b.iter(run_native)); + group.bench_function("native", |b| b.iter(run_native)); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); - // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); - // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 3a10a93..edebc97 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -3,7 +3,7 @@ use crate::{conversion::convert_blocktype, Result}; use crate::conversion::{convert_heaptype, convert_memarg, convert_valtype}; use alloc::string::ToString; use alloc::{boxed::Box, format, vec::Vec}; -use tinywasm_types::Instruction; +use tinywasm_types::{BlockArgsPacked, Instruction}; use wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources}; struct ValidateThenVisit<'a, T, U>(T, &'a mut U); @@ -74,12 +74,14 @@ macro_rules! define_primitive_operands { } macro_rules! define_mem_operands { - ($($name:ident, $instr:expr),*) => { + ($($name:ident, $instr:ident),*) => { $( fn $name(&mut self, mem_arg: wasmparser::MemArg) -> Self::Output { - self.instructions.push($instr( - convert_memarg(mem_arg) - )); + let arg = convert_memarg(mem_arg); + self.instructions.push(Instruction::$instr { + offset: arg.offset, + mem_addr: arg.mem_addr, + }); Ok(()) } )* @@ -149,29 +151,29 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } define_mem_operands! { - visit_i32_load, Instruction::I32Load, - visit_i64_load, Instruction::I64Load, - visit_f32_load, Instruction::F32Load, - visit_f64_load, Instruction::F64Load, - visit_i32_load8_s, Instruction::I32Load8S, - visit_i32_load8_u, Instruction::I32Load8U, - visit_i32_load16_s, Instruction::I32Load16S, - visit_i32_load16_u, Instruction::I32Load16U, - visit_i64_load8_s, Instruction::I64Load8S, - visit_i64_load8_u, Instruction::I64Load8U, - visit_i64_load16_s, Instruction::I64Load16S, - visit_i64_load16_u, Instruction::I64Load16U, - visit_i64_load32_s, Instruction::I64Load32S, - visit_i64_load32_u, Instruction::I64Load32U, - visit_i32_store, Instruction::I32Store, - visit_i64_store, Instruction::I64Store, - visit_f32_store, Instruction::F32Store, - visit_f64_store, Instruction::F64Store, - visit_i32_store8, Instruction::I32Store8, - visit_i32_store16, Instruction::I32Store16, - visit_i64_store8, Instruction::I64Store8, - visit_i64_store16, Instruction::I64Store16, - visit_i64_store32, Instruction::I64Store32 + visit_i32_load, I32Load, + visit_i64_load, I64Load, + visit_f32_load, F32Load, + visit_f64_load, F64Load, + visit_i32_load8_s, I32Load8S, + visit_i32_load8_u, I32Load8U, + visit_i32_load16_s, I32Load16S, + visit_i32_load16_u, I32Load16U, + visit_i64_load8_s, I64Load8S, + visit_i64_load8_u, I64Load8U, + visit_i64_load16_s, I64Load16S, + visit_i64_load16_u, I64Load16U, + visit_i64_load32_s, I64Load32S, + visit_i64_load32_u, I64Load32U, + visit_i32_store, I32Store, + visit_i64_store, I64Store, + visit_f32_store, F32Store, + visit_f64_store, F64Store, + visit_i32_store8, I32Store8, + visit_i32_store16, I32Store16, + visit_i64_store8, I64Store8, + visit_i64_store16, I64Store16, + visit_i64_store32, I64Store32 } define_operands! { @@ -327,7 +329,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { match instruction { Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), - Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), + // Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), _ => return self.visit(Instruction::LocalGet(idx)), }; @@ -396,7 +398,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { fn visit_if(&mut self, ty: wasmparser::BlockType) -> Self::Output { self.label_ptrs.push(self.instructions.len()); - self.visit(Instruction::If(convert_blocktype(ty), None, 0)) + self.visit(Instruction::If(BlockArgsPacked::new(convert_blocktype(ty)), 0, 0)) } fn visit_else(&mut self) -> Self::Output { @@ -414,7 +416,9 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { match self.instructions[label_pointer] { Instruction::Else(ref mut else_instr_end_offset) => { - *else_instr_end_offset = (current_instr_ptr - label_pointer as usize) as u32; + *else_instr_end_offset = (current_instr_ptr - label_pointer as usize) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support if blocks that large"); #[cold] fn error() -> crate::ParseError { @@ -431,13 +435,20 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { return Err(error()); }; - *else_offset = Some((label_pointer - if_label_pointer) as u32); - *end_offset = (current_instr_ptr - if_label_pointer) as u32; + *else_offset = (label_pointer - if_label_pointer) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); + + *end_offset = (current_instr_ptr - if_label_pointer) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); } Instruction::Block(_, ref mut end_offset) | Instruction::Loop(_, ref mut end_offset) | Instruction::If(_, _, ref mut end_offset) => { - *end_offset = (current_instr_ptr - label_pointer) as u32; + *end_offset = (current_instr_ptr - label_pointer) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); } _ => { return Err(crate::ParseError::UnsupportedOperator( diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index a13531b..9227ceb 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -11,7 +11,7 @@ // from a function, so we need to check if the label stack is empty macro_rules! break_to { ($cf:ident, $stack:ident, $break_to_relative:ident) => {{ - if $cf.break_to($break_to_relative, &mut $stack.values, &mut $stack.blocks).is_none() { + if $cf.break_to(*$break_to_relative, &mut $stack.values, &mut $stack.blocks).is_none() { if $stack.call_stack.is_empty() { return Ok(ExecResult::Return); } else { @@ -23,20 +23,22 @@ macro_rules! break_to { /// Load a value from memory macro_rules! mem_load { - ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ + ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ mem_load!($type, $type, $arg, $stack, $store, $module) }}; - ($load_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ - let mem_idx = $module.resolve_mem_addr($arg.mem_addr); + ($load_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ + let (mem_addr, offset) = $arg; + + let mem_idx = $module.resolve_mem_addr(*mem_addr); let mem = $store.get_mem(mem_idx as usize)?; let mem_ref = mem.borrow_mut(); let addr: u64 = $stack.values.pop()?.into(); - let addr = $arg.offset.checked_add(addr).ok_or_else(|| { + let addr = offset.checked_add(addr).ok_or_else(|| { cold(); Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: $arg.offset as usize, + offset: *offset as usize, len: core::mem::size_of::<$load_type>(), max: mem_ref.max_pages(), }) @@ -45,7 +47,7 @@ macro_rules! mem_load { let addr: usize = addr.try_into().ok().ok_or_else(|| { cold(); Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: $arg.offset as usize, + offset: *offset as usize, len: core::mem::size_of::<$load_type>(), max: mem_ref.max_pages(), }) @@ -59,15 +61,14 @@ macro_rules! mem_load { /// Store a value to memory macro_rules! mem_store { - ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ + ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ log::debug!("mem_store!({}, {:?})", stringify!($type), $arg); - mem_store!($type, $type, $arg, $stack, $store, $module) }}; - ($store_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ - // likewise, there could be a lot of performance improvements here - let mem_idx = $module.resolve_mem_addr($arg.mem_addr); + ($store_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ + let (mem_addr, offset) = $arg; + let mem_idx = $module.resolve_mem_addr(*mem_addr); let mem = $store.get_mem(mem_idx as usize)?; let val = $stack.values.pop_t::<$store_type>()?; @@ -76,7 +77,7 @@ macro_rules! mem_store { let val = val as $store_type; let val = val.to_le_bytes(); - mem.borrow_mut().store(($arg.offset + addr) as usize, val.len(), &val)?; + mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?; }}; } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 45e10b7..bd07051 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -113,7 +113,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M Call(v) => { // prepare the call frame - let func_idx = module.resolve_func_addr(v); + let func_idx = module.resolve_func_addr(*v); let func_inst = store.get_func(func_idx as usize)?.clone(); let wasm_func = match &func_inst.func { @@ -140,7 +140,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } CallIndirect(type_addr, table_addr) => { - let table = store.get_table(module.resolve_table_addr(table_addr) as usize)?; + let table = store.get_table(module.resolve_table_addr(*table_addr) as usize)?; let table_idx = stack.values.pop_t::<u32>()?; // verify that the table is of the right type, this should be validated by the parser already @@ -155,7 +155,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }; let func_inst = store.get_func(func_ref as usize)?.clone(); - let call_ty = module.func_ty(type_addr); + let call_ty = module.func_ty(*type_addr); let wasm_func = match func_inst.func { crate::Function::Wasm(ref f) => f.clone(), @@ -202,10 +202,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.enter_block( BlockFrame::new( cf.instr_ptr, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::If, - &args, + &args.unpack(), module, ), &mut stack.values, @@ -215,19 +215,19 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } // falsy value is on the top of the stack - if let Some(else_offset) = else_offset { + if *else_offset != 0 { let label = BlockFrame::new( - cf.instr_ptr + else_offset as usize, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *else_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::Else, - &args, + &args.unpack(), module, ); - cf.instr_ptr += else_offset as usize; + cf.instr_ptr += *else_offset as usize; cf.enter_block(label, &mut stack.values, &mut stack.blocks); } else { - cf.instr_ptr += end_offset as usize; + cf.instr_ptr += *end_offset as usize; } } @@ -235,7 +235,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.enter_block( BlockFrame::new( cf.instr_ptr, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::Loop, &args, @@ -250,7 +250,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.enter_block( BlockFrame::new( cf.instr_ptr, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), // - params, BlockType::Block, &args, @@ -262,7 +262,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } BrTable(default, len) => { - let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + len as usize] + let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len as usize] .iter() .map(|i| match i { BrLabel(l) => Ok(*l), @@ -273,7 +273,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }) .collect::<Result<Vec<_>>>()?; - if unlikely(instr.len() != len as usize) { + if unlikely(instr.len() != *len as usize) { panic!( "Expected {} BrLabel instructions, got {}, this should have been validated by the parser", len, @@ -282,7 +282,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } let idx = stack.values.pop_t::<i32>()? as usize; - let to = *instr.get(idx).unwrap_or(&default); + let to = instr.get(idx).unwrap_or(&default); break_to!(cf, stack, to); } @@ -319,7 +319,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let res_count = block.results; stack.values.truncate_keep(block.stack_ptr, res_count); - cf.instr_ptr += end_offset as usize; + cf.instr_ptr += *end_offset as usize; } EndBlockFrame => { @@ -332,11 +332,11 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M stack.values.truncate_keep(block.stack_ptr, block.results); } - LocalGet(local_index) => stack.values.push(cf.get_local(local_index as usize)), - LocalSet(local_index) => cf.set_local(local_index as usize, stack.values.pop()?), + LocalGet(local_index) => stack.values.push(cf.get_local(*local_index as usize)), + LocalSet(local_index) => cf.set_local(*local_index as usize, stack.values.pop()?), LocalTee(local_index) => { cf.set_local( - local_index as usize, + *local_index as usize, stack .values .last() @@ -346,39 +346,39 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } GlobalGet(global_index) => { - let idx = module.resolve_global_addr(global_index); + let idx = module.resolve_global_addr(*global_index); let global = store.get_global_val(idx as usize)?; stack.values.push(global); } GlobalSet(global_index) => { - let idx = module.resolve_global_addr(global_index); + let idx = module.resolve_global_addr(*global_index); store.set_global_val(idx as usize, stack.values.pop()?)?; } - I32Const(val) => stack.values.push((val).into()), - I64Const(val) => stack.values.push((val).into()), - F32Const(val) => stack.values.push((val).into()), - F64Const(val) => stack.values.push((val).into()), + I32Const(val) => stack.values.push((*val).into()), + I64Const(val) => stack.values.push((*val).into()), + F32Const(val) => stack.values.push((*val).into()), + F64Const(val) => stack.values.push((*val).into()), MemorySize(addr, byte) => { - if byte != 0 { + if *byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(addr); + let mem_idx = module.resolve_mem_addr(*addr); let mem = store.get_mem(mem_idx as usize)?; stack.values.push((mem.borrow().page_count() as i32).into()); } MemoryGrow(addr, byte) => { - if byte != 0 { + if *byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(addr); + let mem_idx = module.resolve_mem_addr(*addr); let mem = store.get_mem(mem_idx as usize)?; let (res, prev_size) = { @@ -399,7 +399,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let src = stack.values.pop_t::<i32>()?; let dst = stack.values.pop_t::<i32>()?; - let mem = store.get_mem(module.resolve_mem_addr(from) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(*from) as usize)?; let mut mem = mem.borrow_mut(); if from == to { @@ -407,7 +407,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M mem.copy_within(dst as usize, src as usize, size as usize)?; } else { // copy between two memories - let mem2 = store.get_mem(module.resolve_mem_addr(to) as usize)?; + let mem2 = store.get_mem(module.resolve_mem_addr(*to) as usize)?; let mut mem2 = mem2.borrow_mut(); mem2.copy_from_slice(dst as usize, mem.load(src as usize, size as usize)?)?; } @@ -418,7 +418,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let val = stack.values.pop_t::<i32>()?; let dst = stack.values.pop_t::<i32>()?; - let mem = store.get_mem(module.resolve_mem_addr(addr) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(*addr) as usize)?; let mut mem = mem.borrow_mut(); mem.fill(dst as usize, size as usize, val as u8)?; } @@ -428,13 +428,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let offset = stack.values.pop_t::<i32>()? as usize; let dst = stack.values.pop_t::<i32>()? as usize; - let data_idx = module.resolve_data_addr(data_index); + let data_idx = module.resolve_data_addr(*data_index); let Some(ref data) = store.get_data(data_idx as usize)?.data else { cold(); return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()); }; - let mem_idx = module.resolve_mem_addr(mem_index); + let mem_idx = module.resolve_mem_addr(*mem_index); let mem = store.get_mem(mem_idx as usize)?; let data_len = data.len(); @@ -451,35 +451,35 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } DataDrop(data_index) => { - let data_idx = module.resolve_data_addr(data_index); + let data_idx = module.resolve_data_addr(*data_index); let data = store.get_data_mut(data_idx as usize)?; data.drop(); } - I32Store(arg) => mem_store!(i32, arg, stack, store, module), - I64Store(arg) => mem_store!(i64, arg, stack, store, module), - F32Store(arg) => mem_store!(f32, arg, stack, store, module), - F64Store(arg) => mem_store!(f64, arg, stack, store, module), - I32Store8(arg) => mem_store!(i8, i32, arg, stack, store, module), - I32Store16(arg) => mem_store!(i16, i32, arg, stack, store, module), - I64Store8(arg) => mem_store!(i8, i64, arg, stack, store, module), - I64Store16(arg) => mem_store!(i16, i64, arg, stack, store, module), - I64Store32(arg) => mem_store!(i32, i64, arg, stack, store, module), + I32Store { mem_addr, offset } => mem_store!(i32, (mem_addr, offset), stack, store, module), + I64Store { mem_addr, offset } => mem_store!(i64, (mem_addr, offset), stack, store, module), + F32Store { mem_addr, offset } => mem_store!(f32, (mem_addr, offset), stack, store, module), + F64Store { mem_addr, offset } => mem_store!(f64, (mem_addr, offset), stack, store, module), + I32Store8 { mem_addr, offset } => mem_store!(i8, i32, (mem_addr, offset), stack, store, module), + I32Store16 { mem_addr, offset } => mem_store!(i16, i32, (mem_addr, offset), stack, store, module), + I64Store8 { mem_addr, offset } => mem_store!(i8, i64, (mem_addr, offset), stack, store, module), + I64Store16 { mem_addr, offset } => mem_store!(i16, i64, (mem_addr, offset), stack, store, module), + I64Store32 { mem_addr, offset } => mem_store!(i32, i64, (mem_addr, offset), stack, store, module), - I32Load(arg) => mem_load!(i32, arg, stack, store, module), - I64Load(arg) => mem_load!(i64, arg, stack, store, module), - F32Load(arg) => mem_load!(f32, arg, stack, store, module), - F64Load(arg) => mem_load!(f64, arg, stack, store, module), - I32Load8S(arg) => mem_load!(i8, i32, arg, stack, store, module), - I32Load8U(arg) => mem_load!(u8, i32, arg, stack, store, module), - I32Load16S(arg) => mem_load!(i16, i32, arg, stack, store, module), - I32Load16U(arg) => mem_load!(u16, i32, arg, stack, store, module), - I64Load8S(arg) => mem_load!(i8, i64, arg, stack, store, module), - I64Load8U(arg) => mem_load!(u8, i64, arg, stack, store, module), - I64Load16S(arg) => mem_load!(i16, i64, arg, stack, store, module), - I64Load16U(arg) => mem_load!(u16, i64, arg, stack, store, module), - I64Load32S(arg) => mem_load!(i32, i64, arg, stack, store, module), - I64Load32U(arg) => mem_load!(u32, i64, arg, stack, store, module), + I32Load { mem_addr, offset } => mem_load!(i32, (mem_addr, offset), stack, store, module), + I64Load { mem_addr, offset } => mem_load!(i64, (mem_addr, offset), stack, store, module), + F32Load { mem_addr, offset } => mem_load!(f32, (mem_addr, offset), stack, store, module), + F64Load { mem_addr, offset } => mem_load!(f64, (mem_addr, offset), stack, store, module), + I32Load8S { mem_addr, offset } => mem_load!(i8, i32, (mem_addr, offset), stack, store, module), + I32Load8U { mem_addr, offset } => mem_load!(u8, i32, (mem_addr, offset), stack, store, module), + I32Load16S { mem_addr, offset } => mem_load!(i16, i32, (mem_addr, offset), stack, store, module), + I32Load16U { mem_addr, offset } => mem_load!(u16, i32, (mem_addr, offset), stack, store, module), + I64Load8S { mem_addr, offset } => mem_load!(i8, i64, (mem_addr, offset), stack, store, module), + I64Load8U { mem_addr, offset } => mem_load!(u8, i64, (mem_addr, offset), stack, store, module), + I64Load16S { mem_addr, offset } => mem_load!(i16, i64, (mem_addr, offset), stack, store, module), + I64Load16U { mem_addr, offset } => mem_load!(u16, i64, (mem_addr, offset), stack, store, module), + I64Load32S { mem_addr, offset } => mem_load!(i32, i64, (mem_addr, offset), stack, store, module), + I64Load32U { mem_addr, offset } => mem_load!(u32, i64, (mem_addr, offset), stack, store, module), I64Eqz => comp_zero!(==, i64, stack), I32Eqz => comp_zero!(==, i32, stack), @@ -630,7 +630,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M I64TruncF64U => checked_conv_float!(f64, u64, i64, stack), TableGet(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; let idx = stack.values.pop_t::<i32>()? as usize; let v = table.borrow().get_wasm_val(idx)?; @@ -638,7 +638,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSet(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; let val = stack.values.pop_t::<u32>()?; let idx = stack.values.pop_t::<u32>()? as usize; @@ -646,16 +646,16 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSize(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; stack.values.push(table.borrow().size().into()); } TableInit(table_index, elem_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; - let elem_idx = module.resolve_elem_addr(elem_index); + let elem_idx = module.resolve_elem_addr(*elem_index); let elem = store.get_elem(elem_idx as usize)?; if let ElementKind::Passive = elem.kind { @@ -680,43 +680,46 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // custom instructions LocalGet2(a, b) => { - stack.values.extend_from_slice(&[cf.get_local(a as usize), cf.get_local(b as usize)]); + stack.values.extend_from_slice(&[cf.get_local(*a as usize), cf.get_local(*b as usize)]); } LocalGet3(a, b, c) => { stack.values.extend_from_slice(&[ - cf.get_local(a as usize), - cf.get_local(b as usize), - cf.get_local(c as usize), - ]); - } - LocalGet4(a, b, c, d) => { - stack.values.extend_from_slice(&[ - cf.get_local(a as usize), - cf.get_local(b as usize), - cf.get_local(c as usize), - cf.get_local(d as usize), + cf.get_local(*a as usize), + cf.get_local(*b as usize), + cf.get_local(*c as usize), ]); } + // LocalGet4(a, b, c, d) => { + // stack.values.extend_from_slice(&[ + // cf.get_local(*a as usize), + // cf.get_local(*b as usize), + // cf.get_local(*c as usize), + // cf.get_local(*d as usize), + // ]); + // } LocalTeeGet(a, b) => { - let last = - *stack.values.last().expect("localtee: stack is empty. this should have been validated by the parser"); - cf.set_local(a as usize, last); - stack.values.push(cf.get_local(b as usize)); + #[inline] + fn local_tee_get(cf: &mut CallFrame, stack: &mut Stack, a: u32, b: u32) -> Result<()> { + let last = *stack + .values + .last() + .expect("localtee: stack is empty. this should have been validated by the parser"); + cf.set_local(a as usize, last); + stack.values.push(cf.get_local(b as usize)); + Ok(()) + } + local_tee_get(cf, stack, *a, *b)?; } - LocalGetSet(a, b) => { - let a = cf.get_local(a as usize); - cf.set_local(b as usize, a); + let a = cf.get_local(*a as usize); + cf.set_local(*b as usize, a); } - - // I64Xor + I64Const + I64RotL I64XorConstRotl(rotate_by) => { let val = stack.values.pop_t::<i64>()?; let mask = stack.values.pop_t::<i64>()?; let res = val ^ mask; - stack.values.push(res.rotate_left(rotate_by as u32).into()); + stack.values.push(res.rotate_left(*rotate_by as u32).into()); } - i => { cold(); log::error!("unimplemented instruction: {:?}", i); diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index dd80bb7..14ad050 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -149,7 +149,7 @@ impl CallFrame { } #[inline(always)] - pub(crate) fn current_instruction(&self) -> Instruction { - self.func_instance.0.instructions[self.instr_ptr] + pub(crate) fn current_instruction(&self) -> &Instruction { + &self.func_instance.0.instructions[self.instr_ptr] } } diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index bbd2206..273d6ed 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -7,10 +7,8 @@ use rkyv::{ Deserialize, }; -// 16 bytes const TWASM_MAGIC_PREFIX: &[u8; 4] = b"TWAS"; const TWASM_VERSION: &[u8; 2] = b"01"; - #[rustfmt::skip] const TWASM_MAGIC: [u8; 16] = [ TWASM_MAGIC_PREFIX[0], TWASM_MAGIC_PREFIX[1], TWASM_MAGIC_PREFIX[2], TWASM_MAGIC_PREFIX[3], TWASM_VERSION[0], TWASM_VERSION[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index a19b4d2..537b8d7 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -9,14 +9,44 @@ pub enum BlockArgs { FuncType(u32), } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] +/// A packed representation of BlockArgs +/// This is needed to keep the size of the Instruction enum small. +/// Sadly, using #[repr(u8)] on BlockArgs itself is not possible because of the FuncType variant. +pub struct BlockArgsPacked([u8; 5]); // Modifying this directly can cause runtime errors, but no UB +impl BlockArgsPacked { + pub fn new(args: BlockArgs) -> Self { + let mut packed = [0; 5]; + match args { + BlockArgs::Empty => packed[0] = 0, + BlockArgs::Type(t) => { + packed[0] = 1; + packed[1] = t.to_byte(); + } + BlockArgs::FuncType(t) => { + packed[0] = 2; + packed[1..].copy_from_slice(&t.to_le_bytes()); + } + } + Self(packed) + } + pub fn unpack(&self) -> BlockArgs { + match self.0[0] { + 0 => BlockArgs::Empty, + 1 => BlockArgs::Type(ValType::from_byte(self.0[1]).unwrap()), + 2 => BlockArgs::FuncType(u32::from_le_bytes(self.0[1..].try_into().unwrap())), + _ => unreachable!(), + } + } +} + /// Represents a memory immediate in a WebAssembly memory instruction. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct MemoryArg { pub offset: u64, pub mem_addr: MemAddr, - // pub align: u8, - // pub align_max: u8, } type BrTableDefault = u32; @@ -48,21 +78,23 @@ pub enum ConstInstruction { /// This makes it easier to implement the label stack iteratively. /// /// See <https://webassembly.github.io/spec/core/binary/instructions.html> -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] +// should be kept as small as possible (16 bytes max) pub enum Instruction { // Custom Instructions BrLabel(LabelAddr), - //== Not implemented yet, to be determined - + // Not implemented yet // LocalGet + I32Const + I32Add // One of the most common patterns in the Rust compiler output - I32LocalGetConstAdd(LocalAddr, i32), + // I32LocalGetConstAdd(LocalAddr, i32), - // LocalGet + I32Const + I32Store - // Also common, helps us skip the stack entirely - // I32LocalGetConstStore(LocalAddr, i32, MemoryArg), // I32Store + LocalGet + I32Const + // Not implemented yet + // LocalGet + I32Const + I32Store => I32LocalGetConstStore + I32Const + // Also common, helps us skip the stack entirely. + // Has to be followed by an I32Const instruction + // I32LocalGetConstStore { local: LocalAddr, offset: i32, mem_addr: MemAddr }, // I32Store + LocalGet + I32Const // I64Xor + I64Const + I64RotL // Commonly used by a few crypto libraries @@ -72,13 +104,13 @@ pub enum Instruction { LocalTeeGet(LocalAddr, LocalAddr), LocalGet2(LocalAddr, LocalAddr), LocalGet3(LocalAddr, LocalAddr, LocalAddr), - LocalGet4(LocalAddr, LocalAddr, LocalAddr, LocalAddr), LocalGetSet(LocalAddr, LocalAddr), - I32AddConst(i32), - I32SubConst(i32), - I64AddConst(i64), - I64SubConst(i64), + // Not implemented yet + // I32AddConst(i32), + // I32SubConst(i32), + // I64AddConst(i64), + // I64SubConst(i64), // Control Instructions // See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions> @@ -86,7 +118,7 @@ pub enum Instruction { Nop, Block(BlockArgs, EndOffset), Loop(BlockArgs, EndOffset), - If(BlockArgs, Option<ElseOffset>, EndOffset), + If(BlockArgsPacked, ElseOffset, EndOffset), // If else offset is 0 if there is no else block Else(EndOffset), EndBlockFrame, EndFunc, @@ -111,29 +143,29 @@ pub enum Instruction { GlobalSet(GlobalAddr), // Memory Instructions - I32Load(MemoryArg), - I64Load(MemoryArg), - F32Load(MemoryArg), - F64Load(MemoryArg), - I32Load8S(MemoryArg), - I32Load8U(MemoryArg), - I32Load16S(MemoryArg), - I32Load16U(MemoryArg), - I64Load8S(MemoryArg), - I64Load8U(MemoryArg), - I64Load16S(MemoryArg), - I64Load16U(MemoryArg), - I64Load32S(MemoryArg), - I64Load32U(MemoryArg), - I32Store(MemoryArg), - I64Store(MemoryArg), - F32Store(MemoryArg), - F64Store(MemoryArg), - I32Store8(MemoryArg), - I32Store16(MemoryArg), - I64Store8(MemoryArg), - I64Store16(MemoryArg), - I64Store32(MemoryArg), + I32Load { offset: u64, mem_addr: MemAddr }, + I64Load { offset: u64, mem_addr: MemAddr }, + F32Load { offset: u64, mem_addr: MemAddr }, + F64Load { offset: u64, mem_addr: MemAddr }, + I32Load8S { offset: u64, mem_addr: MemAddr }, + I32Load8U { offset: u64, mem_addr: MemAddr }, + I32Load16S { offset: u64, mem_addr: MemAddr }, + I32Load16U { offset: u64, mem_addr: MemAddr }, + I64Load8S { offset: u64, mem_addr: MemAddr }, + I64Load8U { offset: u64, mem_addr: MemAddr }, + I64Load16S { offset: u64, mem_addr: MemAddr }, + I64Load16U { offset: u64, mem_addr: MemAddr }, + I64Load32S { offset: u64, mem_addr: MemAddr }, + I64Load32U { offset: u64, mem_addr: MemAddr }, + I32Store { offset: u64, mem_addr: MemAddr }, + I64Store { offset: u64, mem_addr: MemAddr }, + F32Store { offset: u64, mem_addr: MemAddr }, + F64Store { offset: u64, mem_addr: MemAddr }, + I32Store8 { offset: u64, mem_addr: MemAddr }, + I32Store16 { offset: u64, mem_addr: MemAddr }, + I64Store8 { offset: u64, mem_addr: MemAddr }, + I64Store16 { offset: u64, mem_addr: MemAddr }, + I64Store32 { offset: u64, mem_addr: MemAddr }, MemorySize(MemAddr, u8), MemoryGrow(MemAddr, u8), @@ -302,3 +334,51 @@ pub enum Instruction { MemoryFill(MemAddr), DataDrop(DataAddr), } + +#[cfg(test)] +mod test_blockargs_packed { + use super::*; + + #[test] + fn test_empty() { + let args = BlockArgs::Empty; + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Empty); + } + + #[test] + fn test_val_type_i32() { + let args = BlockArgs::Type(ValType::I32); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::I32)); + } + + #[test] + fn test_val_type_i64() { + let args = BlockArgs::Type(ValType::I64); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::I64)); + } + + #[test] + fn test_val_type_f32() { + let args = BlockArgs::Type(ValType::F32); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::F32)); + } + + #[test] + fn test_val_type_f64() { + let args = BlockArgs::Type(ValType::F64); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::F64)); + } + + #[test] + fn test_func_type() { + let func_type = 123; // Use an arbitrary u32 value + let args = BlockArgs::FuncType(func_type); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::FuncType(func_type)); + } +} diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 8fd72fb..df062ce 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -141,6 +141,29 @@ impl ValType { pub fn default_value(&self) -> WasmValue { WasmValue::default_for(*self) } + + pub(crate) fn to_byte(&self) -> u8 { + match self { + ValType::I32 => 0x7F, + ValType::I64 => 0x7E, + ValType::F32 => 0x7D, + ValType::F64 => 0x7C, + ValType::RefFunc => 0x70, + ValType::RefExtern => 0x6F, + } + } + + pub(crate) fn from_byte(byte: u8) -> Option<Self> { + match byte { + 0x7F => Some(ValType::I32), + 0x7E => Some(ValType::I64), + 0x7D => Some(ValType::F32), + 0x7C => Some(ValType::F64), + 0x70 => Some(ValType::RefFunc), + 0x6F => Some(ValType::RefExtern), + _ => None, + } + } } macro_rules! impl_conversion_for_wasmvalue { |
