diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-02-25 17:51:41 +0100 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-02-25 17:51:41 +0100 |
| commit | 062515c39154b29bb0406b207e3c67bae399a449 (patch) | |
| tree | 722520f15d62714c003f8dad36651d30ffba4e47 /crates | |
| parent | a0368bc39ac620b867a105ca0c9bc12824abcd9d (diff) | |
feat: new parser architecture
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/benchmarks/benches/selfhosted.rs | 2 | ||||
| -rw-r--r-- | crates/benchmarks/benches/util/mod.rs | 5 | ||||
| -rw-r--r-- | crates/parser/src/conversion.rs | 336 | ||||
| -rw-r--r-- | crates/parser/src/lib.rs | 8 | ||||
| -rw-r--r-- | crates/parser/src/module.rs | 27 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 487 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 2 |
7 files changed, 518 insertions, 349 deletions
diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 542343e..94dfdce 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -55,7 +55,7 @@ const TINYWASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm fn criterion_benchmark(c: &mut Criterion) { { let mut group = c.benchmark_group("selfhosted-parse"); - group.bench_function("tinywasm", |b| b.iter(|| util::wasm_to_twasm(TINYWASM))); + group.bench_function("tinywasm", |b| b.iter(|| util::parse_wasm(TINYWASM))); } { diff --git a/crates/benchmarks/benches/util/mod.rs b/crates/benchmarks/benches/util/mod.rs index d6594b9..0df2a52 100644 --- a/crates/benchmarks/benches/util/mod.rs +++ b/crates/benchmarks/benches/util/mod.rs @@ -2,6 +2,11 @@ use tinywasm::{self, parser::Parser, types::TinyWasmModule}; +pub fn parse_wasm(wasm: &[u8]) -> TinyWasmModule { + let parser = Parser::new(); + parser.parse_module_bytes(wasm).expect("parse_module_bytes") +} + pub fn wasm_to_twasm(wasm: &[u8]) -> Vec<u8> { let parser = Parser::new(); let res = parser.parse_module_bytes(wasm).expect("parse_module_bytes"); diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index f81b9e7..03b5f82 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -1,10 +1,9 @@ -use crate::log; +use crate::visit::process_operators; +use crate::Result; use alloc::{boxed::Box, format, string::ToString, vec::Vec}; use tinywasm_types::*; use wasmparser::{FuncValidator, OperatorsReader, ValidatorResources}; -use crate::{module::CodeSection, Result}; - pub(crate) fn convert_module_elements<'a, T: IntoIterator<Item = wasmparser::Result<wasmparser::Element<'a>>>>( elements: T, ) -> Result<Vec<tinywasm_types::Element>> { @@ -160,12 +159,12 @@ pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result<Ex pub(crate) fn convert_module_code( func: wasmparser::FunctionBody<'_>, mut validator: FuncValidator<ValidatorResources>, -) -> Result<CodeSection> { +) -> Result<(Box<[Instruction]>, Box<[ValType]>)> { let locals_reader = func.get_locals_reader()?; let count = locals_reader.get_count(); let pos = locals_reader.original_position(); - let mut locals = Vec::with_capacity(count as usize); + let mut locals = Vec::with_capacity(count as usize); for (i, local) in locals_reader.into_iter().enumerate() { let local = local?; validator.define_locals(pos + i, local.0, local.1)?; @@ -174,10 +173,9 @@ pub(crate) fn convert_module_code( } } - let body_reader = func.get_operators_reader()?; - let body = process_operators(body_reader, validator)?; - - Ok(CodeSection { locals: locals.into_boxed_slice(), body }) + let body = process_operators(&mut validator, &func)?; + let locals = locals.into_boxed_slice(); + Ok((body, locals)) } pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType> { @@ -254,328 +252,10 @@ pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result<Con } } -fn convert_heaptype(heap: wasmparser::HeapType) -> ValType { +pub(crate) fn convert_heaptype(heap: wasmparser::HeapType) -> ValType { match heap { wasmparser::HeapType::Func => ValType::RefFunc, wasmparser::HeapType::Extern => ValType::RefExtern, _ => unimplemented!("Unsupported heap type: {:?}", heap), } } - -pub(crate) fn process_operators( - ops: OperatorsReader<'_>, - mut validator: FuncValidator<ValidatorResources>, -) -> Result<Box<[Instruction]>> { - let mut instructions = Vec::with_capacity(1024); - let mut labels_ptrs = Vec::with_capacity(32); - // indexes into the instructions array - let mut offset = ops.original_position(); - - for op in ops { - let op = match op { - Ok(op) => op, - Err(e) => { - cold(); - log::error!("Error while processing operators: {:?}", e); - return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string())); - } - }; - - match validator.op(offset, &op) { - Ok(_) => (), - Err(e) => { - cold(); - log::error!("Error while processing operators: {:?}", e); - return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string())); - } - } - offset += 1; - - use wasmparser::Operator::*; - let res = match op { - BrTable { targets } => { - let def = targets.default(); - - let instrs = targets - .targets() - .map(|t| t.map(Instruction::BrLabel)) - .collect::<Result<Vec<Instruction>, wasmparser::BinaryReaderError>>()?; - - instructions.push(Instruction::BrTable(def, instrs.len())); - instructions.extend(instrs); - continue; - } - Unreachable => Instruction::Unreachable, - Nop => Instruction::Nop, - Block { blockty } => { - labels_ptrs.push(instructions.len()); - Instruction::Block(convert_blocktype(blockty), 0) - } - Loop { blockty } => { - labels_ptrs.push(instructions.len()); - Instruction::Loop(convert_blocktype(blockty), 0) - } - If { blockty } => { - labels_ptrs.push(instructions.len()); - Instruction::If(convert_blocktype(blockty), None, 0) - } - Else => { - labels_ptrs.push(instructions.len()); - Instruction::Else(0) - } - End => { - if let Some(label_pointer) = labels_ptrs.pop() { - let current_instr_ptr = instructions.len(); - - // last_label_pointer is Some if we're ending a block - match instructions[label_pointer] { - Instruction::Else(ref mut else_instr_end_offset) => { - *else_instr_end_offset = current_instr_ptr - label_pointer; - - // since we're ending an else block, we need to end the if block as well - let if_label_pointer = labels_ptrs.pop().ok_or(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - ))?; - - let if_instruction = &mut instructions[if_label_pointer]; - let Instruction::If(_, ref mut else_offset, ref mut end_offset) = if_instruction else { - return Err(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - )); - }; - - *else_offset = Some(label_pointer - if_label_pointer); - *end_offset = current_instr_ptr - if_label_pointer; - } - 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; - } - _ => { - return Err(crate::ParseError::UnsupportedOperator( - "Expected to end a block, but the last label was not a block".to_string(), - )) - } - } - - Instruction::EndBlockFrame - } else { - // last_label_pointer is None if we're ending the function - Instruction::EndFunc - } - } - - Br { relative_depth } => Instruction::Br(relative_depth), - BrIf { relative_depth } => Instruction::BrIf(relative_depth), - Return => Instruction::Return, - Call { function_index } => Instruction::Call(function_index), - CallIndirect { type_index, table_index, .. } => Instruction::CallIndirect(type_index, table_index), - Drop => Instruction::Drop, - Select => Instruction::Select(None), - TypedSelect { ty } => Instruction::Select(Some(convert_valtype(&ty))), - LocalGet { local_index } => Instruction::LocalGet(local_index), - LocalSet { local_index } => Instruction::LocalSet(local_index), - LocalTee { local_index } => Instruction::LocalTee(local_index), - GlobalGet { global_index } => Instruction::GlobalGet(global_index), - GlobalSet { global_index } => Instruction::GlobalSet(global_index), - MemorySize { mem, mem_byte } => Instruction::MemorySize(mem, mem_byte), - MemoryGrow { mem, mem_byte } => Instruction::MemoryGrow(mem, mem_byte), - - MemoryCopy { dst_mem, src_mem } => Instruction::MemoryCopy(src_mem, dst_mem), - MemoryFill { mem } => Instruction::MemoryFill(mem), - MemoryInit { data_index, mem } => Instruction::MemoryInit(data_index, mem), - DataDrop { data_index } => Instruction::DataDrop(data_index), - - I32Const { value } => Instruction::I32Const(value), - I64Const { value } => Instruction::I64Const(value), - F32Const { value } => Instruction::F32Const(f32::from_bits(value.bits())), - F64Const { value } => Instruction::F64Const(f64::from_bits(value.bits())), - RefNull { hty } => Instruction::RefNull(convert_heaptype(hty)), - RefIsNull => Instruction::RefIsNull, - RefFunc { function_index } => Instruction::RefFunc(function_index), - I32Load { memarg } => Instruction::I32Load(convert_memarg(memarg)), - I64Load { memarg } => Instruction::I64Load(convert_memarg(memarg)), - F32Load { memarg } => Instruction::F32Load(convert_memarg(memarg)), - F64Load { memarg } => Instruction::F64Load(convert_memarg(memarg)), - I32Load8S { memarg } => Instruction::I32Load8S(convert_memarg(memarg)), - I32Load8U { memarg } => Instruction::I32Load8U(convert_memarg(memarg)), - I32Load16S { memarg } => Instruction::I32Load16S(convert_memarg(memarg)), - I32Load16U { memarg } => Instruction::I32Load16U(convert_memarg(memarg)), - I64Load8S { memarg } => Instruction::I64Load8S(convert_memarg(memarg)), - I64Load8U { memarg } => Instruction::I64Load8U(convert_memarg(memarg)), - I64Load16S { memarg } => Instruction::I64Load16S(convert_memarg(memarg)), - I64Load16U { memarg } => Instruction::I64Load16U(convert_memarg(memarg)), - I64Load32S { memarg } => Instruction::I64Load32S(convert_memarg(memarg)), - I64Load32U { memarg } => Instruction::I64Load32U(convert_memarg(memarg)), - I32Store { memarg } => Instruction::I32Store(convert_memarg(memarg)), - I64Store { memarg } => Instruction::I64Store(convert_memarg(memarg)), - F32Store { memarg } => Instruction::F32Store(convert_memarg(memarg)), - F64Store { memarg } => Instruction::F64Store(convert_memarg(memarg)), - I32Store8 { memarg } => Instruction::I32Store8(convert_memarg(memarg)), - I32Store16 { memarg } => Instruction::I32Store16(convert_memarg(memarg)), - I64Store8 { memarg } => Instruction::I64Store8(convert_memarg(memarg)), - I64Store16 { memarg } => Instruction::I64Store16(convert_memarg(memarg)), - I64Store32 { memarg } => Instruction::I64Store32(convert_memarg(memarg)), - I32Eqz => Instruction::I32Eqz, - I32Eq => Instruction::I32Eq, - I32Ne => Instruction::I32Ne, - I32LtS => Instruction::I32LtS, - I32LtU => Instruction::I32LtU, - I32GtS => Instruction::I32GtS, - I32GtU => Instruction::I32GtU, - I32LeS => Instruction::I32LeS, - I32LeU => Instruction::I32LeU, - I32GeS => Instruction::I32GeS, - I32GeU => Instruction::I32GeU, - I64Eqz => Instruction::I64Eqz, - I64Eq => Instruction::I64Eq, - I64Ne => Instruction::I64Ne, - I64LtS => Instruction::I64LtS, - I64LtU => Instruction::I64LtU, - I64GtS => Instruction::I64GtS, - I64GtU => Instruction::I64GtU, - I64LeS => Instruction::I64LeS, - I64LeU => Instruction::I64LeU, - I64GeS => Instruction::I64GeS, - I64GeU => Instruction::I64GeU, - F32Eq => Instruction::F32Eq, - F32Ne => Instruction::F32Ne, - F32Lt => Instruction::F32Lt, - F32Gt => Instruction::F32Gt, - F32Le => Instruction::F32Le, - F32Ge => Instruction::F32Ge, - F64Eq => Instruction::F64Eq, - F64Ne => Instruction::F64Ne, - F64Lt => Instruction::F64Lt, - F64Gt => Instruction::F64Gt, - F64Le => Instruction::F64Le, - F64Ge => Instruction::F64Ge, - I32Clz => Instruction::I32Clz, - I32Ctz => Instruction::I32Ctz, - I32Popcnt => Instruction::I32Popcnt, - I32Add => Instruction::I32Add, - I32Sub => Instruction::I32Sub, - I32Mul => Instruction::I32Mul, - I32DivS => Instruction::I32DivS, - I32DivU => Instruction::I32DivU, - I32RemS => Instruction::I32RemS, - I32RemU => Instruction::I32RemU, - I32And => Instruction::I32And, - I32Or => Instruction::I32Or, - I32Xor => Instruction::I32Xor, - I32Shl => Instruction::I32Shl, - I32ShrS => Instruction::I32ShrS, - I32ShrU => Instruction::I32ShrU, - I32Rotl => Instruction::I32Rotl, - I32Rotr => Instruction::I32Rotr, - I64Clz => Instruction::I64Clz, - I64Ctz => Instruction::I64Ctz, - I64Popcnt => Instruction::I64Popcnt, - I64Add => Instruction::I64Add, - I64Sub => Instruction::I64Sub, - I64Mul => Instruction::I64Mul, - I64DivS => Instruction::I64DivS, - I64DivU => Instruction::I64DivU, - I64RemS => Instruction::I64RemS, - I64RemU => Instruction::I64RemU, - I64And => Instruction::I64And, - I64Or => Instruction::I64Or, - I64Xor => Instruction::I64Xor, - I64Shl => Instruction::I64Shl, - I64ShrS => Instruction::I64ShrS, - I64ShrU => Instruction::I64ShrU, - I64Rotl => Instruction::I64Rotl, - I64Rotr => Instruction::I64Rotr, - F32Abs => Instruction::F32Abs, - F32Neg => Instruction::F32Neg, - F32Ceil => Instruction::F32Ceil, - F32Floor => Instruction::F32Floor, - F32Trunc => Instruction::F32Trunc, - F32Nearest => Instruction::F32Nearest, - F32Sqrt => Instruction::F32Sqrt, - F32Add => Instruction::F32Add, - F32Sub => Instruction::F32Sub, - F32Mul => Instruction::F32Mul, - F32Div => Instruction::F32Div, - F32Min => Instruction::F32Min, - F32Max => Instruction::F32Max, - F32Copysign => Instruction::F32Copysign, - F64Abs => Instruction::F64Abs, - F64Neg => Instruction::F64Neg, - F64Ceil => Instruction::F64Ceil, - F64Floor => Instruction::F64Floor, - F64Trunc => Instruction::F64Trunc, - F64Nearest => Instruction::F64Nearest, - F64Sqrt => Instruction::F64Sqrt, - F64Add => Instruction::F64Add, - F64Sub => Instruction::F64Sub, - F64Mul => Instruction::F64Mul, - F64Div => Instruction::F64Div, - F64Min => Instruction::F64Min, - F64Max => Instruction::F64Max, - F64Copysign => Instruction::F64Copysign, - I32WrapI64 => Instruction::I32WrapI64, - I32TruncF32S => Instruction::I32TruncF32S, - I32TruncF32U => Instruction::I32TruncF32U, - I32TruncF64S => Instruction::I32TruncF64S, - I32TruncF64U => Instruction::I32TruncF64U, - I64Extend8S => Instruction::I64Extend8S, - I64Extend16S => Instruction::I64Extend16S, - I64Extend32S => Instruction::I64Extend32S, - I64ExtendI32S => Instruction::I64ExtendI32S, - I64ExtendI32U => Instruction::I64ExtendI32U, - I32Extend8S => Instruction::I32Extend8S, - I32Extend16S => Instruction::I32Extend16S, - I64TruncF32S => Instruction::I64TruncF32S, - I64TruncF32U => Instruction::I64TruncF32U, - I64TruncF64S => Instruction::I64TruncF64S, - I64TruncF64U => Instruction::I64TruncF64U, - F32ConvertI32S => Instruction::F32ConvertI32S, - F32ConvertI32U => Instruction::F32ConvertI32U, - F32ConvertI64S => Instruction::F32ConvertI64S, - F32ConvertI64U => Instruction::F32ConvertI64U, - F32DemoteF64 => Instruction::F32DemoteF64, - F64ConvertI32S => Instruction::F64ConvertI32S, - F64ConvertI32U => Instruction::F64ConvertI32U, - F64ConvertI64S => Instruction::F64ConvertI64S, - F64ConvertI64U => Instruction::F64ConvertI64U, - F64PromoteF32 => Instruction::F64PromoteF32, - I32ReinterpretF32 => Instruction::I32ReinterpretF32, - I64ReinterpretF64 => Instruction::I64ReinterpretF64, - F32ReinterpretI32 => Instruction::F32ReinterpretI32, - F64ReinterpretI64 => Instruction::F64ReinterpretI64, - I32TruncSatF32S => Instruction::I32TruncSatF32S, - I32TruncSatF32U => Instruction::I32TruncSatF32U, - I32TruncSatF64S => Instruction::I32TruncSatF64S, - I32TruncSatF64U => Instruction::I32TruncSatF64U, - I64TruncSatF32S => Instruction::I64TruncSatF32S, - I64TruncSatF32U => Instruction::I64TruncSatF32U, - I64TruncSatF64S => Instruction::I64TruncSatF64S, - I64TruncSatF64U => Instruction::I64TruncSatF64U, - TableGet { table } => Instruction::TableGet(table), - TableSet { table } => Instruction::TableSet(table), - TableInit { table, elem_index } => Instruction::TableInit(table, elem_index), - TableCopy { src_table, dst_table } => Instruction::TableCopy { from: src_table, to: dst_table }, - TableGrow { table } => Instruction::TableGrow(table), - TableSize { table } => Instruction::TableSize(table), - TableFill { table } => Instruction::TableFill(table), - op => { - cold(); - log::error!("Unsupported instruction: {:?}", op); - return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", op))); - } - }; - instructions.push(res); - } - - if !labels_ptrs.is_empty() { - panic!("last_label_pointer should be None after processing all instructions: {:?}", labels_ptrs); - } - - validator.finish(offset)?; - Ok(instructions.into_boxed_slice()) -} - -#[cold] -fn cold() {} diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 72b3f81..dd4b931 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -7,6 +7,7 @@ #![forbid(unsafe_code)] #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! See [`tinywasm`](https://docs.rs/tinywasm) for documentation. +#![recursion_limit = "1028"] mod std; extern crate alloc; @@ -28,6 +29,7 @@ mod log { mod conversion; mod error; mod module; +mod visit; use alloc::{string::ToString, vec::Vec}; pub use error::*; use module::ModuleReader; @@ -155,11 +157,11 @@ impl TryFrom<ModuleReader> for TinyWasmModule { .code .into_iter() .zip(code_type_addrs) - .map(|(f, ty_idx)| TypedWasmFunction { + .map(|((instructions, locals), ty_idx)| TypedWasmFunction { type_addr: ty_idx, wasm_function: WasmFunction { - instructions: f.body, - locals: f.locals, + instructions, + locals, ty: reader.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(), }, }) diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index a18d343..813a65d 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -1,16 +1,9 @@ use crate::log::debug; use crate::{conversion, ParseError, Result}; use alloc::{boxed::Box, format, vec::Vec}; -use core::fmt::Debug; use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, ValType}; use wasmparser::{Payload, Validator}; -#[derive(Debug, Clone)] -pub(crate) struct CodeSection { - pub(crate) locals: Box<[ValType]>, - pub(crate) body: Box<[Instruction]>, -} - #[derive(Default)] pub(crate) struct ModuleReader { pub(crate) version: Option<u16>, @@ -18,7 +11,7 @@ pub(crate) struct ModuleReader { pub(crate) func_types: Vec<FuncType>, pub(crate) code_type_addrs: Vec<u32>, pub(crate) exports: Vec<Export>, - pub(crate) code: Vec<CodeSection>, + pub(crate) code: Vec<(Box<[Instruction]>, Box<[ValType]>)>, pub(crate) globals: Vec<Global>, pub(crate) table_types: Vec<TableType>, pub(crate) memory_types: Vec<MemoryType>, @@ -66,15 +59,7 @@ impl ModuleReader { .map(|t| conversion::convert_module_type(t?)) .collect::<Result<Vec<FuncType>>>()?; } - FunctionSection(reader) => { - if !self.code_type_addrs.is_empty() { - return Err(ParseError::DuplicateSection("Function section".into())); - } - debug!("Found function section"); - validator.function_section(&reader)?; - self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::<Result<Vec<_>>>()?; - } GlobalSection(reader) => { if !self.globals.is_empty() { return Err(ParseError::DuplicateSection("Global section".into())); @@ -122,11 +107,21 @@ impl ModuleReader { } validator.data_count_section(count, &range)?; } + FunctionSection(reader) => { + if !self.code_type_addrs.is_empty() { + return Err(ParseError::DuplicateSection("Function section".into())); + } + + debug!("Found function section"); + validator.function_section(&reader)?; + self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::<Result<Vec<_>>>()?; + } CodeSectionStart { count, range, .. } => { debug!("Found code section ({} functions)", count); if !self.code.is_empty() { return Err(ParseError::DuplicateSection("Code section".into())); } + self.code.reserve(count as usize); validator.code_section_start(count, &range)?; } CodeSectionEntry(function) => { diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs new file mode 100644 index 0000000..b42e462 --- /dev/null +++ b/crates/parser/src/visit.rs @@ -0,0 +1,487 @@ +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 wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources}; + +struct ValidateThenVisit<'a, T, U>(T, &'a mut U); +macro_rules! validate_then_visit { + ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { + self.0.$visit($($($arg.clone()),*)?)?; + Ok(self.1.$visit($($($arg),*)?)) + } + )* + }; +} + +impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U> +where + T: VisitOperator<'a, Output = wasmparser::Result<()>>, + U: VisitOperator<'a>, +{ + type Output = Result<U::Output>; + + wasmparser::for_each_operator!(validate_then_visit); +} + +pub(crate) fn process_operators<R: WasmModuleResources>( + validator: &mut FuncValidator<R>, + body: &FunctionBody<'_>, +) -> Result<Box<[Instruction]>> { + let mut reader = body.get_operators_reader()?; + let mut builder = FunctionBuilder::new(1024); + + while !reader.eof() { + let validate = validator.visitor(reader.original_position()); + reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))???; + } + + validator.finish(reader.original_position())?; + Ok(builder.instructions.into_boxed_slice()) +} + +macro_rules! define_operands { + ($($name:ident, $instr:expr),*) => { + $( + fn $name(&mut self) -> Self::Output { + self.instructions.push($instr); + Ok(()) + } + )* + }; +} + +macro_rules! define_primitive_operands { + ($($name:ident, $instr:expr, $ty:ty),*) => { + $( + fn $name(&mut self, arg: $ty) -> Self::Output { + self.instructions.push($instr(arg)); + Ok(()) + } + )* + }; +} + +macro_rules! define_mem_operands { + ($($name:ident, $instr:expr),*) => { + $( + fn $name(&mut self, mem_arg: wasmparser::MemArg) -> Self::Output { + self.instructions.push($instr( + convert_memarg(mem_arg) + )); + Ok(()) + } + )* + }; +} + +macro_rules! impl_visit_operator { + ( @mvp $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @sign_extension $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @saturating_float_to_int $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @bulk_memory $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @reference_types $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @@skipped $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + impl_visit_operator!($($rest)*); + }; + ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output { + self.unsupported(stringify!($op)) + } + impl_visit_operator!($($rest)*); + }; + () => {}; +} + +pub(crate) struct FunctionBuilder { + instructions: Vec<Instruction>, + label_ptrs: Vec<usize>, +} + +impl FunctionBuilder { + pub(crate) fn new(instr_capacity: usize) -> Self { + Self { instructions: Vec::with_capacity(instr_capacity), label_ptrs: Vec::with_capacity(64) } + } + + #[cold] + fn unsupported(&self, name: &str) -> Result<()> { + Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", name))) + } + + #[inline] + fn visit(&mut self, op: Instruction) -> Result<()> { + Ok(self.instructions.push(op)) + } +} + +impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { + type Output = Result<()>; + + 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, + visit_i64_const, Instruction::I64Const, i64 + } + + 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 + } + + define_operands! { + visit_unreachable, Instruction::Unreachable, + visit_nop, Instruction::Nop, + visit_return, Instruction::Return, + visit_drop, Instruction::Drop, + visit_select, Instruction::Select(None), + visit_i32_eqz, Instruction::I32Eqz, + visit_i32_eq, Instruction::I32Eq, + visit_i32_ne, Instruction::I32Ne, + visit_i32_lt_s, Instruction::I32LtS, + visit_i32_lt_u, Instruction::I32LtU, + visit_i32_gt_s, Instruction::I32GtS, + visit_i32_gt_u, Instruction::I32GtU, + visit_i32_le_s, Instruction::I32LeS, + visit_i32_le_u, Instruction::I32LeU, + visit_i32_ge_s, Instruction::I32GeS, + visit_i32_ge_u, Instruction::I32GeU, + visit_i64_eqz, Instruction::I64Eqz, + visit_i64_eq, Instruction::I64Eq, + visit_i64_ne, Instruction::I64Ne, + visit_i64_lt_s, Instruction::I64LtS, + visit_i64_lt_u, Instruction::I64LtU, + visit_i64_gt_s, Instruction::I64GtS, + visit_i64_gt_u, Instruction::I64GtU, + visit_i64_le_s, Instruction::I64LeS, + visit_i64_le_u, Instruction::I64LeU, + visit_i64_ge_s, Instruction::I64GeS, + visit_i64_ge_u, Instruction::I64GeU, + visit_f32_eq, Instruction::F32Eq, + visit_f32_ne, Instruction::F32Ne, + visit_f32_lt, Instruction::F32Lt, + visit_f32_gt, Instruction::F32Gt, + visit_f32_le, Instruction::F32Le, + visit_f32_ge, Instruction::F32Ge, + visit_f64_eq, Instruction::F64Eq, + visit_f64_ne, Instruction::F64Ne, + visit_f64_lt, Instruction::F64Lt, + visit_f64_gt, Instruction::F64Gt, + visit_f64_le, Instruction::F64Le, + visit_f64_ge, Instruction::F64Ge, + visit_i32_clz, Instruction::I32Clz, + visit_i32_ctz, Instruction::I32Ctz, + visit_i32_popcnt, Instruction::I32Popcnt, + visit_i32_add, Instruction::I32Add, + visit_i32_sub, Instruction::I32Sub, + visit_i32_mul, Instruction::I32Mul, + visit_i32_div_s, Instruction::I32DivS, + visit_i32_div_u, Instruction::I32DivU, + visit_i32_rem_s, Instruction::I32RemS, + visit_i32_rem_u, Instruction::I32RemU, + visit_i32_and, Instruction::I32And, + visit_i32_or, Instruction::I32Or, + visit_i32_xor, Instruction::I32Xor, + visit_i32_shl, Instruction::I32Shl, + visit_i32_shr_s, Instruction::I32ShrS, + visit_i32_shr_u, Instruction::I32ShrU, + visit_i32_rotl, Instruction::I32Rotl, + visit_i32_rotr, Instruction::I32Rotr, + visit_i64_clz, Instruction::I64Clz, + visit_i64_ctz, Instruction::I64Ctz, + visit_i64_popcnt, Instruction::I64Popcnt, + visit_i64_add, Instruction::I64Add, + visit_i64_sub, Instruction::I64Sub, + visit_i64_mul, Instruction::I64Mul, + visit_i64_div_s, Instruction::I64DivS, + visit_i64_div_u, Instruction::I64DivU, + visit_i64_rem_s, Instruction::I64RemS, + visit_i64_rem_u, Instruction::I64RemU, + visit_i64_and, Instruction::I64And, + visit_i64_or, Instruction::I64Or, + visit_i64_xor, Instruction::I64Xor, + visit_i64_shl, Instruction::I64Shl, + visit_i64_shr_s, Instruction::I64ShrS, + visit_i64_shr_u, Instruction::I64ShrU, + visit_i64_rotl, Instruction::I64Rotl, + visit_i64_rotr, Instruction::I64Rotr, + visit_f32_abs, Instruction::F32Abs, + visit_f32_neg, Instruction::F32Neg, + visit_f32_ceil, Instruction::F32Ceil, + visit_f32_floor, Instruction::F32Floor, + visit_f32_trunc, Instruction::F32Trunc, + visit_f32_nearest, Instruction::F32Nearest, + visit_f32_sqrt, Instruction::F32Sqrt, + visit_f32_add, Instruction::F32Add, + visit_f32_sub, Instruction::F32Sub, + visit_f32_mul, Instruction::F32Mul, + visit_f32_div, Instruction::F32Div, + visit_f32_min, Instruction::F32Min, + visit_f32_max, Instruction::F32Max, + visit_f32_copysign, Instruction::F32Copysign, + visit_f64_abs, Instruction::F64Abs, + visit_f64_neg, Instruction::F64Neg, + visit_f64_ceil, Instruction::F64Ceil, + visit_f64_floor, Instruction::F64Floor, + visit_f64_trunc, Instruction::F64Trunc, + visit_f64_nearest, Instruction::F64Nearest, + visit_f64_sqrt, Instruction::F64Sqrt, + visit_f64_add, Instruction::F64Add, + visit_f64_sub, Instruction::F64Sub, + visit_f64_mul, Instruction::F64Mul, + visit_f64_div, Instruction::F64Div, + visit_f64_min, Instruction::F64Min, + visit_f64_max, Instruction::F64Max, + visit_f64_copysign, Instruction::F64Copysign, + visit_i32_wrap_i64, Instruction::I32WrapI64, + visit_i32_trunc_f32_s, Instruction::I32TruncF32S, + visit_i32_trunc_f32_u, Instruction::I32TruncF32U, + visit_i32_trunc_f64_s, Instruction::I32TruncF64S, + visit_i32_trunc_f64_u, Instruction::I32TruncF64U, + visit_i64_extend_i32_s, Instruction::I64ExtendI32S, + visit_i64_extend_i32_u, Instruction::I64ExtendI32U, + visit_i64_trunc_f32_s, Instruction::I64TruncF32S, + visit_i64_trunc_f32_u, Instruction::I64TruncF32U, + visit_i64_trunc_f64_s, Instruction::I64TruncF64S, + visit_i64_trunc_f64_u, Instruction::I64TruncF64U, + visit_f32_convert_i32_s, Instruction::F32ConvertI32S, + visit_f32_convert_i32_u, Instruction::F32ConvertI32U, + visit_f32_convert_i64_s, Instruction::F32ConvertI64S, + visit_f32_convert_i64_u, Instruction::F32ConvertI64U, + visit_f32_demote_f64, Instruction::F32DemoteF64, + visit_f64_convert_i32_s, Instruction::F64ConvertI32S, + visit_f64_convert_i32_u, Instruction::F64ConvertI32U, + visit_f64_convert_i64_s, Instruction::F64ConvertI64S, + visit_f64_convert_i64_u, Instruction::F64ConvertI64U, + visit_f64_promote_f32, Instruction::F64PromoteF32, + visit_i32_reinterpret_f32, Instruction::I32ReinterpretF32, + visit_i64_reinterpret_f64, Instruction::I64ReinterpretF64, + visit_f32_reinterpret_i32, Instruction::F32ReinterpretI32, + visit_f64_reinterpret_i64, Instruction::F64ReinterpretI64, + + // sign_extension + visit_i32_extend8_s, Instruction::I32Extend8S, + visit_i32_extend16_s, Instruction::I32Extend16S, + visit_i64_extend8_s, Instruction::I64Extend8S, + visit_i64_extend16_s, Instruction::I64Extend16S, + visit_i64_extend32_s, Instruction::I64Extend32S, + + // Non-trapping Float-to-int Conversions + visit_i32_trunc_sat_f32_s, Instruction::I32TruncSatF32S, + visit_i32_trunc_sat_f32_u, Instruction::I32TruncSatF32U, + visit_i32_trunc_sat_f64_s, Instruction::I32TruncSatF64S, + visit_i32_trunc_sat_f64_u, Instruction::I32TruncSatF64U, + visit_i64_trunc_sat_f32_s, Instruction::I64TruncSatF32S, + visit_i64_trunc_sat_f32_u, Instruction::I64TruncSatF32U, + visit_i64_trunc_sat_f64_s, Instruction::I64TruncSatF64S, + visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U + } + + 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)) + } + + fn visit_loop(&mut self, ty: wasmparser::BlockType) -> Self::Output { + self.label_ptrs.push(self.instructions.len()); + self.visit(Instruction::Loop(convert_blocktype(ty), 0)) + } + + 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)) + } + + fn visit_else(&mut self) -> Self::Output { + self.label_ptrs.push(self.instructions.len()); + self.visit(Instruction::Else(0)) + } + + fn visit_end(&mut self) -> Self::Output { + let Some(label_pointer) = self.label_ptrs.pop() else { + return self.visit(Instruction::EndFunc); + }; + + let current_instr_ptr = self.instructions.len(); + + match self.instructions[label_pointer] { + Instruction::Else(ref mut else_instr_end_offset) => { + *else_instr_end_offset = current_instr_ptr - label_pointer; + + // since we're ending an else block, we need to end the if block as well + let if_label_pointer = self.label_ptrs.pop().ok_or(crate::ParseError::UnsupportedOperator( + "Expected to end an if block, but the last label was not an if".to_string(), + ))?; + + let if_instruction = &mut self.instructions[if_label_pointer]; + let Instruction::If(_, ref mut else_offset, ref mut end_offset) = if_instruction else { + return Err(crate::ParseError::UnsupportedOperator( + "Expected to end an if block, but the last label was not an if".to_string(), + )); + }; + + *else_offset = Some(label_pointer - if_label_pointer); + *end_offset = current_instr_ptr - if_label_pointer; + } + 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; + } + _ => { + return Err(crate::ParseError::UnsupportedOperator( + "Expected to end a block, but the last label was not a block".to_string(), + )) + } + }; + + self.visit(Instruction::EndBlockFrame) + } + + fn visit_br_table(&mut self, targets: wasmparser::BrTable<'_>) -> Self::Output { + let def = targets.default(); + let instrs = targets + .targets() + .map(|t| t.map(Instruction::BrLabel)) + .collect::<Result<Vec<Instruction>, wasmparser::BinaryReaderError>>() + .expect("BrTable targets are invalid, this should have been caught by the validator"); + + self.instructions + .extend(IntoIterator::into_iter([Instruction::BrTable(def, instrs.len())]).chain(instrs.into_iter())); + Ok(()) + } + + fn visit_call(&mut self, idx: u32) -> Self::Output { + self.visit(Instruction::Call(idx)) + } + + fn visit_call_indirect(&mut self, ty: u32, table: u32, _table_byte: u8) -> Self::Output { + self.visit(Instruction::CallIndirect(ty, table)) + } + + fn visit_memory_size(&mut self, mem: u32, mem_byte: u8) -> Self::Output { + self.visit(Instruction::MemorySize(mem, mem_byte)) + } + + fn visit_memory_grow(&mut self, mem: u32, mem_byte: u8) -> Self::Output { + self.visit(Instruction::MemoryGrow(mem, mem_byte)) + } + + fn visit_f32_const(&mut self, val: wasmparser::Ieee32) -> Self::Output { + self.visit(Instruction::F32Const(f32::from_bits(val.bits()))) + } + + fn visit_f64_const(&mut self, val: wasmparser::Ieee64) -> Self::Output { + self.visit(Instruction::F64Const(f64::from_bits(val.bits()))) + } + + // Bulk Memory Operations + + fn visit_memory_init(&mut self, data_index: u32, mem: u32) -> Self::Output { + self.visit(Instruction::MemoryInit(data_index, mem)) + } + + fn visit_data_drop(&mut self, data_index: u32) -> Self::Output { + self.visit(Instruction::DataDrop(data_index)) + } + + fn visit_memory_copy(&mut self, dst_mem: u32, src_mem: u32) -> Self::Output { + self.visit(Instruction::MemoryCopy(dst_mem, src_mem)) + } + + fn visit_memory_fill(&mut self, mem: u32) -> Self::Output { + self.visit(Instruction::MemoryFill(mem)) + } + + fn visit_table_init(&mut self, elem_index: u32, table: u32) -> Self::Output { + self.visit(Instruction::TableInit(elem_index, table)) + } + + fn visit_elem_drop(&mut self, _elem_index: u32) -> Self::Output { + self.unsupported("elem_drop") + } + + fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output { + self.visit(Instruction::TableCopy { from: src_table, to: dst_table }) + } + + // Reference Types + + fn visit_ref_null(&mut self, ty: wasmparser::HeapType) -> Self::Output { + self.visit(Instruction::RefNull(convert_heaptype(ty))) + } + + fn visit_ref_is_null(&mut self) -> Self::Output { + self.visit(Instruction::RefIsNull) + } + + fn visit_ref_func(&mut self, idx: u32) -> Self::Output { + self.visit(Instruction::RefFunc(idx)) + } + + fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output { + self.visit(Instruction::Select(Some(convert_valtype(&ty)))) + } + + fn visit_table_fill(&mut self, table: u32) -> Self::Output { + self.visit(Instruction::TableFill(table)) + } + + fn visit_table_get(&mut self, table: u32) -> Self::Output { + self.visit(Instruction::TableGet(table)) + } + + fn visit_table_set(&mut self, table: u32) -> Self::Output { + self.visit(Instruction::TableSet(table)) + } + + fn visit_table_grow(&mut self, table: u32) -> Self::Output { + self.visit(Instruction::TableGrow(table)) + } + + fn visit_table_size(&mut self, table: u32) -> Self::Output { + self.visit(Instruction::TableSize(table)) + } + + wasmparser::for_each_operator!(impl_visit_operator); +} diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index b5a68a4..fc12b54 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -13,10 +13,10 @@ pub enum BlockArgs { #[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, - pub offset: u64, } type BrTableDefault = u32; |
