From 5e573f0765570e0aaf6c9415b652b25d5bddebd9 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 00:17:09 +0100 Subject: no more simd Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/fibonacci.rs | 6 +- crates/parser/src/visit.rs | 69 +++++++++++- crates/tinywasm/src/runtime/interpreter/macros.rs | 2 +- crates/tinywasm/src/runtime/interpreter/mod.rs | 129 +++++++++++++--------- crates/tinywasm/src/runtime/stack/call_stack.rs | 12 +- crates/tinywasm/src/runtime/stack/value_stack.rs | 10 +- crates/tinywasm/src/runtime/value.rs | 42 +++---- crates/types/src/instructions.rs | 26 +++++ crates/types/src/value.rs | 15 ++- examples/rust/analyze.py | 36 ++++++ 10 files changed, 250 insertions(+), 97 deletions(-) create mode 100644 examples/rust/analyze.py diff --git a/crates/benchmarks/benches/fibonacci.rs b/crates/benchmarks/benches/fibonacci.rs index 38bbde9..8a4dab2 100644 --- a/crates/benchmarks/benches/fibonacci.rs +++ b/crates/benchmarks/benches/fibonacci.rs @@ -17,10 +17,10 @@ fn run_wasmi(wasm: &[u8], iterations: i32, name: &str) { fn run_wasmer(wasm: &[u8], iterations: i32, name: &str) { use wasmer::*; - let engine: Engine = wasmer::Singlepass::default().into(); - let mut store = Store::default(); + let compiler = wasmer::Singlepass::default(); + let mut store = Store::new(compiler); let import_object = imports! {}; - let module = wasmer::Module::from_binary(&engine, wasm).expect("wasmer::Module::from_binary"); + let module = wasmer::Module::from_binary(&store, wasm).expect("wasmer::Module::from_binary"); let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); let fib = instance.exports.get_typed_function::(&store, name).expect("get_function"); fib.call(&mut store, iterations).expect("call"); diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 15024f1..9038567 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -142,9 +142,6 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { define_primitive_operands! { visit_br, Instruction::Br, u32, visit_br_if, Instruction::BrIf, u32, - visit_local_get, Instruction::LocalGet, u32, - visit_local_set, Instruction::LocalSet, u32, - visit_local_tee, Instruction::LocalTee, u32, visit_global_get, Instruction::GlobalGet, u32, visit_global_set, Instruction::GlobalSet, u32, visit_i32_const, Instruction::I32Const, i32, @@ -220,7 +217,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_i32_clz, Instruction::I32Clz, visit_i32_ctz, Instruction::I32Ctz, visit_i32_popcnt, Instruction::I32Popcnt, - visit_i32_add, Instruction::I32Add, + // visit_i32_add, Instruction::I32Add, custom implementation visit_i32_sub, Instruction::I32Sub, visit_i32_mul, Instruction::I32Mul, visit_i32_div_s, Instruction::I32DivS, @@ -251,7 +248,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_i64_shl, Instruction::I64Shl, visit_i64_shr_s, Instruction::I64ShrS, visit_i64_shr_u, Instruction::I64ShrU, - visit_i64_rotl, Instruction::I64Rotl, + // visit_i64_rotl, Instruction::I64Rotl, custom implementation visit_i64_rotr, Instruction::I64Rotr, visit_f32_abs, Instruction::F32Abs, visit_f32_neg, Instruction::F32Neg, @@ -325,6 +322,68 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U } + fn visit_local_get(&mut self, idx: u32) -> Self::Output { + if let Some(instruction) = self.instructions.last_mut() { + 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::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), + _ => return self.visit(Instruction::LocalGet(idx)), + }; + Ok(()) + } else { + self.visit(Instruction::LocalGet(idx)) + } + } + + fn visit_local_set(&mut self, idx: u32) -> Self::Output { + // LocalGetSet + if let Some(instruction) = self.instructions.last_mut() { + match instruction { + // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), + _ => return self.visit(Instruction::LocalSet(idx)), + }; + Ok(()) + } else { + self.visit(Instruction::LocalSet(idx)) + } + } + + fn visit_local_tee(&mut self, idx: u32) -> Self::Output { + self.visit(Instruction::LocalTee(idx)) + } + + fn visit_i64_rotl(&mut self) -> Self::Output { + if self.instructions.len() < 2 { + return self.visit(Instruction::I64Rotl); + } + + match self.instructions[self.instructions.len() - 2..] { + // [Instruction::I64Xor, Instruction::I64Const(a)] => { + // self.instructions.pop(); + // self.instructions.pop(); + // self.visit(Instruction::I64XorConstRotl(a)) + // } + _ => self.visit(Instruction::I64Rotl), + } + } + + fn visit_i32_add(&mut self) -> Self::Output { + if self.instructions.len() < 2 { + return self.visit(Instruction::I32Add); + } + + match self.instructions[self.instructions.len() - 2..] { + // [Instruction::LocalGet(a), Instruction::I32Const(b)] => { + // self.instructions.pop(); + // self.instructions.pop(); + // self.visit(Instruction::I32LocalGetConstAdd(a, b)) + // } + _ => self.visit(Instruction::I32Add), + } + } + fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output { self.label_ptrs.push(self.instructions.len()); self.visit(Instruction::Block(convert_blocktype(blockty), 0)) diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index 18330a4..1da8758 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 { diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index b06152d..57e5f34 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -89,7 +89,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // unreasonable complexity // See https://pliniker.github.io/post/dispatchers/ use tinywasm_types::Instruction::*; - match &instrs[cf.instr_ptr] { + match cf.current_instruction() { Nop => { /* do nothing */ } Unreachable => { cold(); @@ -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::()?; // 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, + cf.instr_ptr + end_offset, stack.values.len(), BlockType::If, - args, + &args, module, ), &mut stack.values, @@ -217,17 +217,17 @@ 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 { let label = BlockFrame::new( - cf.instr_ptr + *else_offset, - cf.instr_ptr + *end_offset, + cf.instr_ptr + else_offset, + cf.instr_ptr + end_offset, stack.values.len(), BlockType::Else, - args, + &args, module, ); - cf.instr_ptr += *else_offset; + cf.instr_ptr += else_offset; cf.enter_block(label, &mut stack.values, &mut stack.blocks); } else { - cf.instr_ptr += *end_offset; + cf.instr_ptr += end_offset; } } @@ -235,10 +235,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, + cf.instr_ptr + end_offset, stack.values.len(), BlockType::Loop, - args, + &args, module, ), &mut stack.values, @@ -250,10 +250,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, + cf.instr_ptr + end_offset, stack.values.len(), // - params, BlockType::Block, - args, + &args, module, ), &mut stack.values, @@ -262,7 +262,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } BrTable(default, len) => { - let instr = instrs[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len] + let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + len] .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::>>()?; - if unlikely(instr.len() != *len) { + if unlikely(instr.len() != len) { 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::()? 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; + cf.instr_ptr += end_offset; } EndBlockFrame => { @@ -332,55 +332,53 @@ 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) => { - let last_val = match stack.values.last() { - Ok(val) => val, - Err(_) => { - log::error!("index: {}", local_index); - log::error!("stack: {:?}", stack.values); - - panic!(); - } - }; - cf.set_local(*local_index as usize, *last_val) + cf.set_local( + local_index as usize, + stack + .values + .last() + .expect("localtee: stack is empty. this should have been validated by the parser") + .clone(), + ); } 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) = { @@ -401,7 +399,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let src = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - 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 { @@ -409,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, 0, size as usize)?)?; } @@ -420,7 +418,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let val = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - 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)?; } @@ -430,13 +428,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let offset = stack.values.pop_t::()? as usize; let dst = stack.values.pop_t::()? 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(); @@ -453,7 +451,7 @@ 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(); } @@ -632,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::()? as usize; let v = table.borrow().get_wasm_val(idx)?; @@ -640,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::()?; let idx = stack.values.pop_t::()? as usize; @@ -648,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,6 +678,33 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M I64TruncSatF64S => arithmetic_single!(trunc, f64, i64, stack), I64TruncSatF64U => arithmetic_single!(trunc, f64, u64, stack), + // custom instructions + LocalGet2(a, b) => { + stack.values.push(cf.get_local(a as usize)); + stack.values.push(cf.get_local(b as usize)); + } + LocalGet3(a, b, c) => { + stack.values.push(cf.get_local(a as usize)); + stack.values.push(cf.get_local(b as usize)); + stack.values.push(cf.get_local(c as usize)); + } + LocalGet4(a, b, c, d) => { + stack.values.push(cf.get_local(a as usize)); + stack.values.push(cf.get_local(b as usize)); + stack.values.push(cf.get_local(c as usize)); + stack.values.push(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)); + } + + // LocalTeeGet + // LocalGetSet + // I64XorConstRotl + // I32LocalGetConstAdd 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 1c441a8..dd80bb7 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -1,5 +1,5 @@ use alloc::{boxed::Box, rc::Rc, vec::Vec}; -use tinywasm_types::{ModuleInstanceAddr, WasmFunction}; +use tinywasm_types::{Instruction, ModuleInstanceAddr, WasmFunction}; use crate::runtime::{BlockType, RawWasmValue}; use crate::unlikely; @@ -142,4 +142,14 @@ impl CallFrame { pub(crate) fn get_local(&self, local_index: usize) -> RawWasmValue { self.locals[local_index] } + + #[inline] + pub(crate) fn instructions(&self) -> &[Instruction] { + &self.func_instance.0.instructions + } + + #[inline(always)] + pub(crate) fn current_instruction(&self) -> Instruction { + self.func_instance.0.instructions[self.instr_ptr] + } } diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index 9649177..be5b53b 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -58,14 +58,8 @@ impl ValueStack { } #[inline] - pub(crate) fn last(&self) -> Result<&RawWasmValue> { - match self.stack.last() { - Some(v) => Ok(v), - None => { - cold(); - Err(Error::ValueStackUnderflow) - } - } + pub(crate) fn last(&self) -> Option<&RawWasmValue> { + self.stack.last() } #[inline] diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs index 4e1b746..56fdf60 100644 --- a/crates/tinywasm/src/runtime/value.rs +++ b/crates/tinywasm/src/runtime/value.rs @@ -8,7 +8,8 @@ use tinywasm_types::{ValType, WasmValue}; /// See [`WasmValue`] for the public representation. #[derive(Clone, Copy, Default, PartialEq, Eq)] #[repr(transparent)] -pub struct RawWasmValue([u8; 16]); +// pub struct RawWasmValue([u8; 16]); +pub struct RawWasmValue([u8; 8]); impl Debug for RawWasmValue { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -18,7 +19,7 @@ impl Debug for RawWasmValue { impl RawWasmValue { #[inline(always)] - pub fn raw_value(&self) -> [u8; 16] { + pub fn raw_value(&self) -> [u8; 8] { self.0 } @@ -29,7 +30,7 @@ impl RawWasmValue { ValType::I64 => WasmValue::I64(self.into()), ValType::F32 => WasmValue::F32(f32::from_bits(self.into())), ValType::F64 => WasmValue::F64(f64::from_bits(self.into())), - ValType::V128 => WasmValue::V128(self.into()), + // ValType::V128 => WasmValue::V128(self.into()), ValType::RefExtern => { let val: i64 = self.into(); if val < 0 { @@ -58,7 +59,7 @@ impl From for RawWasmValue { WasmValue::I64(i) => Self::from(i), WasmValue::F32(i) => Self::from(i), WasmValue::F64(i) => Self::from(i), - WasmValue::V128(i) => Self::from(i), + // WasmValue::V128(i) => Self::from(i), WasmValue::RefExtern(v) => Self::from(v as i64), WasmValue::RefFunc(v) => Self::from(v as i64), WasmValue::RefNull(_) => Self::from(-1i64), @@ -72,8 +73,8 @@ macro_rules! impl_from_raw_wasm_value { impl From<$type> for RawWasmValue { #[inline] fn from(value: $type) -> Self { - #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :) - Self(u128::to_ne_bytes($to_raw(value))) + #[allow(clippy::redundant_closure_call)] + Self(u64::to_ne_bytes($to_raw(value))) } } @@ -81,29 +82,32 @@ macro_rules! impl_from_raw_wasm_value { impl From for $type { #[inline] fn from(value: RawWasmValue) -> Self { - #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :) + #[allow(clippy::redundant_closure_call)] $from_raw(value.0) } } }; } +type RawValue = u64; +type RawValueRep = [u8; 8]; + // This all looks like a lot of extra steps, but the compiler will optimize it all away. -// The u128 just makes it a bit easier to write. -impl_from_raw_wasm_value!(i32, |x| x as u128, |x: [u8; 16]| i32::from_ne_bytes(x[0..4].try_into().unwrap())); -impl_from_raw_wasm_value!(i64, |x| x as u128, |x: [u8; 16]| i64::from_ne_bytes(x[0..8].try_into().unwrap())); -impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as u128, |x: [u8; 16]| f32::from_bits(u32::from_ne_bytes( +// The `u128` is used to make the conversion easier to write. +impl_from_raw_wasm_value!(i32, |x| x as RawValue, |x: RawValueRep| i32::from_ne_bytes(x[0..4].try_into().unwrap())); +impl_from_raw_wasm_value!(i64, |x| x as RawValue, |x: RawValueRep| i64::from_ne_bytes(x[0..8].try_into().unwrap())); +impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as RawValue, |x: RawValueRep| f32::from_bits(u32::from_ne_bytes( x[0..4].try_into().unwrap() ))); -impl_from_raw_wasm_value!(f64, |x| f64::to_bits(x) as u128, |x: [u8; 16]| f64::from_bits(u64::from_ne_bytes( +impl_from_raw_wasm_value!(f64, |x| f64::to_bits(x) as RawValue, |x: RawValueRep| f64::from_bits(u64::from_ne_bytes( x[0..8].try_into().unwrap() ))); -impl_from_raw_wasm_value!(u8, |x| x as u128, |x: [u8; 16]| u8::from_ne_bytes(x[0..1].try_into().unwrap())); -impl_from_raw_wasm_value!(u16, |x| x as u128, |x: [u8; 16]| u16::from_ne_bytes(x[0..2].try_into().unwrap())); -impl_from_raw_wasm_value!(u32, |x| x as u128, |x: [u8; 16]| u32::from_ne_bytes(x[0..4].try_into().unwrap())); -impl_from_raw_wasm_value!(u64, |x| x as u128, |x: [u8; 16]| u64::from_ne_bytes(x[0..8].try_into().unwrap())); -impl_from_raw_wasm_value!(u128, |x| x, |x: [u8; 16]| u128::from_ne_bytes(x.try_into().unwrap())); +impl_from_raw_wasm_value!(u8, |x| x as RawValue, |x: RawValueRep| u8::from_ne_bytes(x[0..1].try_into().unwrap())); +impl_from_raw_wasm_value!(u16, |x| x as RawValue, |x: RawValueRep| u16::from_ne_bytes(x[0..2].try_into().unwrap())); +impl_from_raw_wasm_value!(u32, |x| x as RawValue, |x: RawValueRep| u32::from_ne_bytes(x[0..4].try_into().unwrap())); +impl_from_raw_wasm_value!(u64, |x| x as RawValue, |x: RawValueRep| u64::from_ne_bytes(x[0..8].try_into().unwrap())); +// impl_from_raw_wasm_value!(u128, |x| x, |x: RawValueRep| RawValue::from_ne_bytes(x)); -impl_from_raw_wasm_value!(i8, |x| x as u128, |x: [u8; 16]| i8::from_ne_bytes(x[0..1].try_into().unwrap())); -impl_from_raw_wasm_value!(i16, |x| x as u128, |x: [u8; 16]| i16::from_ne_bytes(x[0..2].try_into().unwrap())); +impl_from_raw_wasm_value!(i8, |x| x as RawValue, |x: RawValueRep| i8::from_ne_bytes(x[0..1].try_into().unwrap())); +impl_from_raw_wasm_value!(i16, |x| x as RawValue, |x: RawValueRep| i16::from_ne_bytes(x[0..2].try_into().unwrap())); diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index fc12b54..dd941b3 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -54,6 +54,32 @@ pub enum Instruction { // Custom Instructions BrLabel(LabelAddr), + //== Not implemented yet, to be determined + + // LocalGet + I32Const + I32Add + // One of the most common patterns in the Rust compiler output + I32LocalGetConstAdd(LocalAddr, i32), + + // LocalGet + I32Const + I32Store + // Also common, helps us skip the stack entirely + I32LocalGetConstStore(LocalAddr, i32, MemoryArg), // I32Store + LocalGet + I32Const + + // I64Xor + I64Const + I64RotL + // Commonly used by a few crypto libraries + I64XorConstRotl(i64), + + // LocalTee + LocalGet + 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), + // Control Instructions // See Unreachable, diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 3c02e55..8fd72fb 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -16,9 +16,8 @@ pub enum WasmValue { F32(f32), /// A 64-bit float. F64(f64), - /// A half of a 128-bit vector. Allways used in pairs. - V128(u128), - + // /// A 128-bit vector + // V128(u128), RefExtern(ExternAddr), RefFunc(FuncAddr), RefNull(ValType), @@ -49,7 +48,7 @@ impl WasmValue { ValType::I64 => Self::I64(0), ValType::F32 => Self::F32(0.0), ValType::F64 => Self::F64(0.0), - ValType::V128 => Self::V128(0), + // ValType::V128 => Self::V128(0), ValType::RefFunc => Self::RefNull(ValType::RefFunc), ValType::RefExtern => Self::RefNull(ValType::RefExtern), } @@ -92,7 +91,7 @@ impl Debug for WasmValue { WasmValue::I64(i) => write!(f, "i64({})", i), WasmValue::F32(i) => write!(f, "f32({})", i), WasmValue::F64(i) => write!(f, "f64({})", i), - WasmValue::V128(i) => write!(f, "v128.half({:?})", i), + // WasmValue::V128(i) => write!(f, "v128.half({:?})", i), WasmValue::RefExtern(addr) => write!(f, "ref.extern({:?})", addr), WasmValue::RefFunc(addr) => write!(f, "ref.func({:?})", addr), WasmValue::RefNull(ty) => write!(f, "ref.null({:?})", ty), @@ -109,7 +108,7 @@ impl WasmValue { Self::I64(_) => ValType::I64, Self::F32(_) => ValType::F32, Self::F64(_) => ValType::F64, - Self::V128(_) => ValType::V128, + // Self::V128(_) => ValType::V128, Self::RefExtern(_) => ValType::RefExtern, Self::RefFunc(_) => ValType::RefFunc, Self::RefNull(ty) => *ty, @@ -129,8 +128,8 @@ pub enum ValType { F32, /// A 64-bit float. F64, - /// A half of a 128-bit vector. Allways used in pairs. - V128, + /// A 128-bit vector + // V128, /// A reference to a function. RefFunc, /// A reference to an external value. diff --git a/examples/rust/analyze.py b/examples/rust/analyze.py new file mode 100644 index 0000000..a450a1a --- /dev/null +++ b/examples/rust/analyze.py @@ -0,0 +1,36 @@ +import re +import sys +from collections import Counter + +seq_len = 5 + +# Check if a file path was provided +if len(sys.argv) < 2: + print("Usage: python script.py path/to/yourfile.wat") + sys.exit(1) + +# The first command line argument is the file path +file_path = sys.argv[1] + +# Regex to match WASM operators, adjust as necessary +operator_pattern = re.compile(r'\b[a-z0-9_]+\.[a-z0-9_]+\b') + +# Read the file +with open(file_path, 'r') as file: + content = file.read() + +# Find all operators +operators = operator_pattern.findall(content) + +# Generate sequences of three consecutive operators +sequences = [' '.join(operators[i:i+seq_len]) for i in range(len(operators) - 2)] + +# Count occurrences of each sequence +sequence_counts = Counter(sequences) + +# Sort sequences by their count, this time in ascending order for reverse display +sorted_sequences = sorted(sequence_counts.items(), key=lambda x: x[1]) + +# Print the sequences, now from least common to most common +for sequence, count in sorted_sequences: + print(f"{sequence}: {count}") -- cgit v1.3.1