diff options
| author | Henry <mail@henrygressmann.de> | 2026-07-14 20:48:18 +0200 |
|---|---|---|
| committer | Henry <mail@henrygressmann.de> | 2026-07-14 20:48:18 +0200 |
| commit | dc366165b8cd0f8c43cfe7017cb37f0a16887d6d (patch) | |
| tree | a0e9eeba6211fe9fddde96547a6338aca79d7c3f /crates | |
| parent | fe43e4816efbf622e8cdc106a552dfb9aadfb80e (diff) | |
chore: simplify runtime, parser, types, and SIMD internals
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates')
27 files changed, 740 insertions, 1688 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 4561974..99cf1ba 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -58,18 +58,11 @@ pub(crate) fn convert_module_data(data: wasmparser::Data<'_>) -> Result<tinywasm pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result<Import> { let kind = match import.ty { wasmparser::TypeRef::Func(ty) => ImportKind::Function(ty), - wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType { - element_type: convert_reftype(ty.element_type)?, - size_initial: ty.initial.try_into().map_err(|_| { - crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", ty.initial)) - })?, - size_max: match ty.maximum { - Some(max) => Some(max.try_into().map_err(|_| { - crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}")) - })?), - None => None, - }, - }), + wasmparser::TypeRef::Table(ty) => { + let element_type = convert_reftype(ty.element_type)?; + let (size_initial, size_max) = convert_table_limits(ty)?; + ImportKind::Table(TableType { element_type, size_initial, size_max }) + } wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)), wasmparser::TypeRef::Global(ty) => { ImportKind::Global(GlobalType::new(convert_valtype(&ty.content_type)?, ty.mutable)) @@ -95,16 +88,24 @@ pub(crate) fn convert_module_memory(memory: wasmparser::MemoryType) -> MemoryTyp } pub(crate) fn convert_module_table(table: wasmparser::Table<'_>) -> Result<TableType> { - let size_initial = table.ty.initial.try_into().map_err(|_| { - crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", table.ty.initial)) - })?; - - let size_max = table.ty.maximum.map(|max| max.try_into()).transpose(); - let size_max = - size_max.map_err(|e| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {e}")))?; + let (size_initial, size_max) = convert_table_limits(table.ty)?; Ok(TableType { element_type: convert_reftype(table.ty.element_type)?, size_initial, size_max }) } +fn convert_table_limits(table: wasmparser::TableType) -> Result<(u32, Option<u32>)> { + let size_initial = table.initial.try_into().map_err(|_| { + crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", table.initial)) + })?; + let size_max = table + .maximum + .map(|max| { + u32::try_from(max) + .map_err(|_| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}"))) + }) + .transpose()?; + Ok((size_initial, size_max)) +} + pub(crate) fn convert_module_globals( globals: wasmparser::SectionLimited<'_, wasmparser::Global<'_>>, ) -> Result<Box<[Global]>> { @@ -153,7 +154,7 @@ pub(crate) fn convert_module_code( for i in 0..validator.len_locals() { match validator.get_local_type(i) { - Some(wasmparser::ValType::I32 | wasmparser::ValType::F32) => { + Some(wasmparser::ValType::I32 | wasmparser::ValType::F32 | wasmparser::ValType::Ref(_)) => { local_addr_map.push(local_counts.c32); local_counts.c32 += 1; } @@ -165,10 +166,6 @@ pub(crate) fn convert_module_code( local_addr_map.push(local_counts.c128); local_counts.c128 += 1; } - Some(wasmparser::ValType::Ref(_)) => { - local_addr_map.push(local_counts.c32); - local_counts.c32 += 1; - } None => return Err(crate::ParseError::UnsupportedOperator("Unknown local type".to_string())), } } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index c6ae326..42fddf6 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -250,18 +250,15 @@ impl Parser { #[cfg(not(parallel_parser))] let _ = defer; - - buffer.drain(..consumed); } wasmparser::Payload::CodeSectionEntry(function) => { reader.process_inline_code_section_entry(function, &mut validator, &self.options)?; - buffer.drain(..consumed); } payload => { reader.process_payload(payload, &mut validator)?; - buffer.drain(..consumed); } } + buffer.drain(..consumed); #[cfg(parallel_parser)] if let Some((count, body_offset, section_size)) = deferred_code_section { @@ -296,12 +293,9 @@ impl Parser { return Err(ParseError::Other("trailing bytes after end of module".into())); } } - - reader.process_pending_functions(&self.options)?; - return reader.into_module(&self.options); } - if eof { + if reader.end_reached || eof { reader.process_pending_functions(&self.options)?; return reader.into_module(&self.options); } @@ -321,20 +315,17 @@ impl TryFrom<ModuleReader<'_>> for Module { /// Parse a module from bytes pub fn parse_bytes(wasm: &[u8]) -> Result<Module> { - let data = Parser::new().parse_module_bytes(wasm)?; - Ok(data) + Parser::new().parse_module_bytes(wasm) } #[cfg(feature = "std")] /// Parse a module from a file. Requires the `std` feature. pub fn parse_file(path: impl AsRef<crate::std::path::Path> + Clone) -> Result<Module> { - let data = Parser::new().parse_module_file(path)?; - Ok(data) + Parser::new().parse_module_file(path) } #[cfg(feature = "std")] /// Parse a module from a stream. Requires `parser` and `std` features. pub fn parse_stream(stream: impl crate::std::io::Read) -> Result<Module> { - let data = Parser::new().parse_module_stream(stream)?; - Ok(data) + Parser::new().parse_module_stream(stream) } diff --git a/crates/parser/src/macros.rs b/crates/parser/src/macros.rs index b21c1e8..8a5330a 100644 --- a/crates/parser/src/macros.rs +++ b/crates/parser/src/macros.rs @@ -105,61 +105,18 @@ pub(crate) mod visit { pub(crate) mod optimize { macro_rules! replace { - ($instructions:ident, $read:ident, 1 => [$a:expr $(,)?]) => {{ - $instructions[$read - 1] = Instruction::Nop; - $instructions[$read] = $a; - }}; - ($instructions:ident, $read:ident, 1 => [$a:expr, $b:expr $(,)?]) => {{ - $instructions[$read - 1] = $a; - $instructions[$read] = $b; - }}; - ($instructions:ident, $read:ident, 2 => [$a:expr $(,)?]) => {{ - $instructions[$read - 2] = Instruction::Nop; - $instructions[$read - 1] = Instruction::Nop; - $instructions[$read] = $a; - }}; - ($instructions:ident, $read:ident, 2 => [$a:expr, $b:expr $(,)?]) => {{ - $instructions[$read - 2] = Instruction::Nop; - $instructions[$read - 1] = $a; - $instructions[$read] = $b; - }}; - ($instructions:ident, $read:ident, 2 => [$a:expr, $b:expr, $c:expr $(,)?]) => {{ - $instructions[$read - 2] = $a; - $instructions[$read - 1] = $b; - $instructions[$read] = $c; - }}; - ($instructions:ident, $read:ident, 3 => [$a:expr $(,)?]) => {{ - $instructions[$read - 3] = Instruction::Nop; - $instructions[$read - 2] = Instruction::Nop; - $instructions[$read - 1] = Instruction::Nop; - $instructions[$read] = $a; - }}; - ($instructions:ident, $read:ident, 3 => [$a:expr, $b:expr $(,)?]) => {{ - $instructions[$read - 3] = Instruction::Nop; - $instructions[$read - 2] = Instruction::Nop; - $instructions[$read - 1] = $a; - $instructions[$read] = $b; - }}; - ($instructions:ident, $read:ident, 3 => [$a:expr, $b:expr, $c:expr $(,)?]) => {{ - $instructions[$read - 3] = Instruction::Nop; - $instructions[$read - 2] = $a; - $instructions[$read - 1] = $b; - $instructions[$read] = $c; - }}; - ($instructions:ident, $read:ident, 3 => [$a:expr, $b:expr, $c:expr, $d:expr $(,)?]) => {{ - $instructions[$read - 3] = $a; - $instructions[$read - 2] = $b; - $instructions[$read - 1] = $c; - $instructions[$read] = $d; + ($instructions:ident, $read:ident, $consumed:literal => [$($out:expr),+ $(,)?]) => {{ + const { + assert!($consumed >= 1 && $consumed <= 3); + assert!([$(stringify!($out)),+].len() <= $consumed + 1); + } + let replacements = [$($out),+]; + let replacement_start = $read + 1 - replacements.len(); + $instructions[$read - $consumed..replacement_start].fill(Instruction::Nop); + $instructions[replacement_start..=$read].copy_from_slice(&replacements); }}; - ($instructions:ident, $read:ident, 1 => $out:expr) => { - replace!($instructions, $read, 1 => [$out]); - }; - ($instructions:ident, $read:ident, 2 => $out:expr) => { - replace!($instructions, $read, 2 => [$out]); - }; - ($instructions:ident, $read:ident, 3 => $out:expr) => { - replace!($instructions, $read, 3 => [$out]); + ($instructions:ident, $read:ident, $consumed:literal => $out:expr) => { + replace!($instructions, $read, $consumed => [$out]); }; } diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs index aad5b1a..e94e517 100644 --- a/crates/parser/src/optimize.rs +++ b/crates/parser/src/optimize.rs @@ -53,7 +53,7 @@ fn rewrite( ReturnCall(addr) if addr == self_func_addr => instrs[i] = ReturnCallSelf, Return if let Some(return_instr) = return_instr => instrs[i] = return_instr, instr @ (I32Add | I32Mul | I32And | I32Or | I32Xor) => { - let Some(op) = int_bin_op_32(instr) else { unreachable!() }; + let Some(op) = int_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b)); rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c)); rewrite!(instrs, i, [Const32(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c)); @@ -64,7 +64,7 @@ fn rewrite( } } instr @ (I32Sub | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr) => { - let Some(op) = int_bin_op_32(instr) else { unreachable!() }; + let Some(op) = int_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b)); rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c)); rewrite!(instrs, i, [GlobalGet(global)] => [Nop, BinOpStackGlobal32(op, global)]); @@ -74,7 +74,7 @@ fn rewrite( } } instr @ (I64Add | I64Mul | I64And | I64Or | I64Xor) => { - let Some(op) = int_bin_op_64(instr) else { unreachable!() }; + let Some(op) = int_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b)); rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c)); rewrite!(instrs, i, [Const64(c), LocalGet64(local)] => BinOpLocalConst64(op, local, c)); @@ -85,7 +85,7 @@ fn rewrite( } } instr @ (I64Sub | I64Shl | I64ShrS | I64ShrU | I64Rotl | I64Rotr) => { - let Some(op) = int_bin_op_64(instr) else { unreachable!() }; + let Some(op) = int_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b)); rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c)); rewrite!(instrs, i, [GlobalGet(global)] => [Nop, BinOpStackGlobal64(op, global)]); @@ -96,24 +96,24 @@ fn rewrite( } } instr @ (F32Add | F32Mul | F32Min | F32Max) => { - let Some(op) = float_bin_op_32(instr) else { unreachable!() }; + let Some(op) = float_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b)); rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c)); rewrite!(instrs, i, [Const32(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c)); } instr @ (F32Sub | F32Div | F32Copysign) => { - let Some(op) = float_bin_op_32(instr) else { unreachable!() }; + let Some(op) = float_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b)); rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c)); } instr @ (F64Add | F64Mul | F64Min | F64Max) => { - let Some(op) = float_bin_op_64(instr) else { unreachable!() }; + let Some(op) = float_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b)); rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c)); rewrite!(instrs, i, [Const64(c), LocalGet64(local)] => BinOpLocalConst64(op, local, c)); } instr @ (F64Sub | F64Div | F64Copysign) => { - let Some(op) = float_bin_op_64(instr) else { unreachable!() }; + let Some(op) = float_bin_op(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b)); rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c)); } @@ -166,7 +166,7 @@ fn rewrite( fold_local_binop!( instrs, i, dst, source = resolve_local_source_32, - op = scalar_bin_op_32, + op = scalar_bin_op, const = scalar_const_32, local_local = BinOpLocalLocalSet32, local_const = |dst, lhs, op, imm| match (dst == lhs, op) { @@ -205,7 +205,7 @@ fn rewrite( fold_local_binop!( instrs, i, dst, source = resolve_local_source_64, - op = scalar_bin_op_64, + op = scalar_bin_op, const = scalar_const_64, local_local = BinOpLocalLocalSet64, local_const = |dst, lhs, op, imm| match (dst == lhs, op) { @@ -266,7 +266,7 @@ fn rewrite( fold_local_binop!( instrs, i, dst, source = resolve_local_source_32, - op = scalar_bin_op_32, + op = scalar_bin_op, const = scalar_const_32, local_local = BinOpLocalLocalTee32, local_const = |dst, lhs, op, imm| Instruction::BinOpLocalConstTee32(op, lhs, imm, dst) @@ -302,7 +302,7 @@ fn rewrite( fold_local_binop!( instrs, i, dst, source = resolve_local_source_64, - op = scalar_bin_op_64, + op = scalar_bin_op, const = scalar_const_64, local_local = BinOpLocalLocalTee64, local_const = |dst, lhs, op, imm| Instruction::BinOpLocalConstTee64(op, lhs, imm, dst) @@ -423,7 +423,7 @@ fn rewrite( ); rewrite!(instrs, i, [LocalGet64(local), Const64(imm), cmp] if - (let Some(op) = cmp_op_64(cmp) && let Ok(imm) = i32::try_from(imm)) => + (let Some(op) = cmp_op(cmp) && let Ok(imm) = i32::try_from(imm)) => match (imm, inverse_cmp_op(op)) { (0, CmpOp::Eq) => JumpIfLocalZero64 { target_ip: target, local }, (0, CmpOp::Ne) => JumpIfLocalNonZero64 { target_ip: target, local }, @@ -435,7 +435,7 @@ fn rewrite( JumpCmpLocalLocal32 { target_ip: target, left, right, op: inverse_cmp_op(op) } ); rewrite!(instrs, i, - [LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) => + [LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op(cmp)) => JumpCmpLocalLocal64 { target_ip: target, left, right, op: inverse_cmp_op(op) } ); rewrite!(instrs, i, [Const32(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, inverse_cmp_op(op)) { @@ -443,7 +443,7 @@ fn rewrite( (0, CmpOp::Ne) => JumpIfNonZero32(target), (imm, op) => JumpCmpStackConst32 { target_ip: target, imm, op }, }); - rewrite!(instrs, i, [Const64(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, inverse_cmp_op(op)) { + rewrite!(instrs, i, [Const64(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, inverse_cmp_op(op)) { (0, CmpOp::Eq) => JumpIfZero64(target), (0, CmpOp::Ne) => JumpIfNonZero64(target), (imm, op) => JumpCmpStackConst64 { target_ip: target, imm, op }, @@ -479,7 +479,7 @@ fn rewrite( ); rewrite!(instrs, i, [LocalGet64(local), Const64(imm), cmp] if - (let Some(op) = cmp_op_64(cmp) && let Ok(imm) = i32::try_from(imm)) => + (let Some(op) = cmp_op(cmp) && let Ok(imm) = i32::try_from(imm)) => match (imm, op) { (0, CmpOp::Eq) => JumpIfLocalZero64 { target_ip: target, local }, (0, CmpOp::Ne) => JumpIfLocalNonZero64 { target_ip: target, local }, @@ -491,7 +491,7 @@ fn rewrite( JumpCmpLocalLocal32 { target_ip: target, left, right, op } ); rewrite!(instrs, i, - [LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) => + [LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op(cmp)) => JumpCmpLocalLocal64 { target_ip: target, left, right, op } ); rewrite!(instrs, i, [Const32(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, op) { @@ -499,7 +499,7 @@ fn rewrite( (0, CmpOp::Ne) => JumpIfNonZero32(target), (imm, op) => JumpCmpStackConst32 { target_ip: target, imm, op }, }); - rewrite!(instrs, i, [Const64(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, op) { + rewrite!(instrs, i, [Const64(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, op) { (0, CmpOp::Eq) => JumpIfZero64(target), (0, CmpOp::Ne) => JumpIfNonZero64(target), (imm, op) => JumpCmpStackConst64 { target_ip: target, imm, op }, @@ -573,98 +573,64 @@ fn rewrite( fn cmp_op(instr: Instruction) -> Option<CmpOp> { Some(match instr { - Instruction::I32Eq => CmpOp::Eq, - Instruction::I32Ne => CmpOp::Ne, - Instruction::I32LtS => CmpOp::LtS, - Instruction::I32LtU => CmpOp::LtU, - Instruction::I32GtS => CmpOp::GtS, - Instruction::I32GtU => CmpOp::GtU, - Instruction::I32LeS => CmpOp::LeS, - Instruction::I32LeU => CmpOp::LeU, - Instruction::I32GeS => CmpOp::GeS, - Instruction::I32GeU => CmpOp::GeU, + Instruction::I32Eq | Instruction::I64Eq => CmpOp::Eq, + Instruction::I32Ne | Instruction::I64Ne => CmpOp::Ne, + Instruction::I32LtS | Instruction::I64LtS => CmpOp::LtS, + Instruction::I32LtU | Instruction::I64LtU => CmpOp::LtU, + Instruction::I32GtS | Instruction::I64GtS => CmpOp::GtS, + Instruction::I32GtU | Instruction::I64GtU => CmpOp::GtU, + Instruction::I32LeS | Instruction::I64LeS => CmpOp::LeS, + Instruction::I32LeU | Instruction::I64LeU => CmpOp::LeU, + Instruction::I32GeS | Instruction::I64GeS => CmpOp::GeS, + Instruction::I32GeU | Instruction::I64GeU => CmpOp::GeU, _ => return None, }) } -fn int_bin_op_32(instr: Instruction) -> Option<BinOp> { +fn int_bin_op(instr: Instruction) -> Option<BinOp> { Some(match instr { - Instruction::I32Add => BinOp::IAdd, - Instruction::I32Sub => BinOp::ISub, - Instruction::I32Mul => BinOp::IMul, - Instruction::I32And => BinOp::IAnd, - Instruction::I32Or => BinOp::IOr, - Instruction::I32Xor => BinOp::IXor, - Instruction::I32Shl => BinOp::IShl, - Instruction::I32ShrS => BinOp::IShrS, - Instruction::I32ShrU => BinOp::IShrU, - Instruction::I32Rotl => BinOp::IRotl, - Instruction::I32Rotr => BinOp::IRotr, + Instruction::I32Add | Instruction::I64Add => BinOp::IAdd, + Instruction::I32Sub | Instruction::I64Sub => BinOp::ISub, + Instruction::I32Mul | Instruction::I64Mul => BinOp::IMul, + Instruction::I32And | Instruction::I64And => BinOp::IAnd, + Instruction::I32Or | Instruction::I64Or => BinOp::IOr, + Instruction::I32Xor | Instruction::I64Xor => BinOp::IXor, + Instruction::I32Shl | Instruction::I64Shl => BinOp::IShl, + Instruction::I32ShrS | Instruction::I64ShrS => BinOp::IShrS, + Instruction::I32ShrU | Instruction::I64ShrU => BinOp::IShrU, + Instruction::I32Rotl | Instruction::I64Rotl => BinOp::IRotl, + Instruction::I32Rotr | Instruction::I64Rotr => BinOp::IRotr, _ => return None, }) } -fn int_bin_op_64(instr: Instruction) -> Option<BinOp> { +fn float_bin_op(instr: Instruction) -> Option<BinOp> { Some(match instr { - Instruction::I64Add => BinOp::IAdd, - Instruction::I64Sub => BinOp::ISub, - Instruction::I64Mul => BinOp::IMul, - Instruction::I64And => BinOp::IAnd, - Instruction::I64Or => BinOp::IOr, - Instruction::I64Xor => BinOp::IXor, - Instruction::I64Shl => BinOp::IShl, - Instruction::I64ShrS => BinOp::IShrS, - Instruction::I64ShrU => BinOp::IShrU, - Instruction::I64Rotl => BinOp::IRotl, - Instruction::I64Rotr => BinOp::IRotr, + Instruction::F32Add | Instruction::F64Add => BinOp::FAdd, + Instruction::F32Sub | Instruction::F64Sub => BinOp::FSub, + Instruction::F32Mul | Instruction::F64Mul => BinOp::FMul, + Instruction::F32Div | Instruction::F64Div => BinOp::FDiv, + Instruction::F32Min | Instruction::F64Min => BinOp::FMin, + Instruction::F32Max | Instruction::F64Max => BinOp::FMax, + Instruction::F32Copysign | Instruction::F64Copysign => BinOp::FCopysign, _ => return None, }) } -fn float_bin_op_32(instr: Instruction) -> Option<BinOp> { - Some(match instr { - Instruction::F32Add => BinOp::FAdd, - Instruction::F32Sub => BinOp::FSub, - Instruction::F32Mul => BinOp::FMul, - Instruction::F32Div => BinOp::FDiv, - Instruction::F32Min => BinOp::FMin, - Instruction::F32Max => BinOp::FMax, - Instruction::F32Copysign => BinOp::FCopysign, - _ => return None, - }) -} - -fn float_bin_op_64(instr: Instruction) -> Option<BinOp> { - Some(match instr { - Instruction::F64Add => BinOp::FAdd, - Instruction::F64Sub => BinOp::FSub, - Instruction::F64Mul => BinOp::FMul, - Instruction::F64Div => BinOp::FDiv, - Instruction::F64Min => BinOp::FMin, - Instruction::F64Max => BinOp::FMax, - Instruction::F64Copysign => BinOp::FCopysign, - _ => return None, - }) -} - -fn scalar_bin_op_32(instr: Instruction) -> Option<BinOp> { - int_bin_op_32(instr).or_else(|| float_bin_op_32(instr)) -} - -fn scalar_bin_op_64(instr: Instruction) -> Option<BinOp> { - int_bin_op_64(instr).or_else(|| float_bin_op_64(instr)) +fn scalar_bin_op(instr: Instruction) -> Option<BinOp> { + int_bin_op(instr).or_else(|| float_bin_op(instr)) } fn scalar_const_32(instr: Instruction, op_instr: Instruction) -> Option<i32> { match instr { - Instruction::Const32(c) if int_bin_op_32(op_instr).is_some() || float_bin_op_32(op_instr).is_some() => Some(c), + Instruction::Const32(c) if scalar_bin_op(op_instr).is_some() => Some(c), _ => None, } } fn scalar_const_64(instr: Instruction, op_instr: Instruction) -> Option<i64> { match instr { - Instruction::Const64(c) if int_bin_op_64(op_instr).is_some() || float_bin_op_64(op_instr).is_some() => Some(c), + Instruction::Const64(c) if scalar_bin_op(op_instr).is_some() => Some(c), _ => None, } } @@ -725,22 +691,6 @@ fn bin_op_128(instr: Instruction) -> Option<BinOp128> { }) } -fn cmp_op_64(instr: Instruction) -> Option<CmpOp> { - Some(match instr { - Instruction::I64Eq => CmpOp::Eq, - Instruction::I64Ne => CmpOp::Ne, - Instruction::I64LtS => CmpOp::LtS, - Instruction::I64LtU => CmpOp::LtU, - Instruction::I64GtS => CmpOp::GtS, - Instruction::I64GtU => CmpOp::GtU, - Instruction::I64LeS => CmpOp::LeS, - Instruction::I64LeU => CmpOp::LeU, - Instruction::I64GeS => CmpOp::GeS, - Instruction::I64GeU => CmpOp::GeU, - _ => return None, - }) -} - fn inverse_cmp_op(op: CmpOp) -> CmpOp { match op { CmpOp::Eq => CmpOp::Ne, @@ -806,33 +756,12 @@ fn resolve_jump_target(instrs: &[Instruction], target: u32) -> u32 { idx as u32 } -fn jump_target(instr: Instruction) -> Option<u32> { +fn instruction_target_mut(instr: &mut Instruction, include_branch_table: bool) -> Option<&mut u32> { Some(match instr { Instruction::Jump(ip) | Instruction::JumpIfZero32(ip) | Instruction::JumpIfNonZero32(ip) | Instruction::JumpIfZero64(ip) - | Instruction::JumpIfNonZero64(ip) => ip, - Instruction::JumpCmpStackConst32 { target_ip, .. } - | Instruction::JumpCmpStackConst64 { target_ip, .. } - | Instruction::JumpIfLocalZero32 { target_ip, .. } - | Instruction::JumpIfLocalNonZero32 { target_ip, .. } - | Instruction::JumpIfLocalZero64 { target_ip, .. } - | Instruction::JumpIfLocalNonZero64 { target_ip, .. } - | Instruction::JumpCmpLocalConst32 { target_ip, .. } - | Instruction::JumpCmpLocalConst64 { target_ip, .. } - | Instruction::JumpCmpLocalLocal32 { target_ip, .. } - | Instruction::JumpCmpLocalLocal64 { target_ip, .. } => target_ip, - _ => return None, - }) -} - -fn set_jump_target(instr: &mut Instruction, target: u32) { - match instr { - Instruction::Jump(ip) - | Instruction::JumpIfZero32(ip) - | Instruction::JumpIfNonZero32(ip) - | Instruction::JumpIfZero64(ip) | Instruction::JumpIfNonZero64(ip) | Instruction::JumpCmpStackConst32 { target_ip: ip, .. } | Instruction::JumpCmpStackConst64 { target_ip: ip, .. } @@ -843,13 +772,14 @@ fn set_jump_target(instr: &mut Instruction, target: u32) { | Instruction::JumpCmpLocalConst32 { target_ip: ip, .. } | Instruction::JumpCmpLocalConst64 { target_ip: ip, .. } | Instruction::JumpCmpLocalLocal32 { target_ip: ip, .. } - | Instruction::JumpCmpLocalLocal64 { target_ip: ip, .. } => *ip = target, - _ => {} - } + | Instruction::JumpCmpLocalLocal64 { target_ip: ip, .. } => ip, + Instruction::BranchTable(ip, _, _) if include_branch_table => ip, + _ => return None, + }) } fn canonicalize_jump_like(instrs: &mut [Instruction], idx: usize) { - let Some(target) = jump_target(instrs[idx]) else { + let Some(target) = instruction_target_mut(&mut instrs[idx], false).map(|target| *target) else { return; }; @@ -859,8 +789,8 @@ fn canonicalize_jump_like(instrs: &mut [Instruction], idx: usize) { fn canonicalize_jump_like_with_target(instrs: &mut [Instruction], idx: usize, target: u32) { if matches!(instrs[idx], Instruction::Jump(_)) && target == next_non_nop(instrs, idx + 1) as u32 { instrs[idx] = Instruction::Nop; - } else { - set_jump_target(&mut instrs[idx], target); + } else if let Some(ip) = instruction_target_mut(&mut instrs[idx], false) { + *ip = target; } } @@ -894,24 +824,8 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct }); instructions.retain_mut(|instr| { - let ip = match instr { - Instruction::Jump(ip) - | Instruction::JumpIfZero32(ip) - | Instruction::JumpIfNonZero32(ip) - | Instruction::JumpIfZero64(ip) - | Instruction::JumpIfNonZero64(ip) - | Instruction::JumpIfLocalZero32 { target_ip: ip, .. } - | Instruction::JumpIfLocalNonZero32 { target_ip: ip, .. } - | Instruction::JumpIfLocalZero64 { target_ip: ip, .. } - | Instruction::JumpIfLocalNonZero64 { target_ip: ip, .. } - | Instruction::JumpCmpStackConst32 { target_ip: ip, .. } - | Instruction::JumpCmpStackConst64 { target_ip: ip, .. } - | Instruction::JumpCmpLocalConst32 { target_ip: ip, .. } - | Instruction::JumpCmpLocalConst64 { target_ip: ip, .. } - | Instruction::JumpCmpLocalLocal32 { target_ip: ip, .. } - | Instruction::JumpCmpLocalLocal64 { target_ip: ip, .. } - | Instruction::BranchTable(ip, _, _) => ip, - _ => return !matches!(instr, Instruction::Nop | Instruction::MergeBarrier), + let Some(ip) = instruction_target_mut(instr, true) else { + return !matches!(instr, Instruction::Nop | Instruction::MergeBarrier); }; let old_target = *ip as usize; diff --git a/crates/parser/src/parallel.rs b/crates/parser/src/parallel.rs index 4400053..8833970 100644 --- a/crates/parser/src/parallel.rs +++ b/crates/parser/src/parallel.rs @@ -94,10 +94,6 @@ pub(crate) fn process_pending( imported_func_count: usize, imported_memory_count: u32, ) -> Result<Vec<FunctionCode>> { - if pending.is_empty() { - return Ok(Vec::new()); - } - let (small_jobs, large_jobs): (Vec<_>, Vec<_>) = pending.into_iter().partition(|job| !should_parallelize_function(body_len(&job.body))); @@ -106,11 +102,6 @@ pub(crate) fn process_pending( .map(|job| process_function_job(job, options, func_types, imported_func_count, imported_memory_count)) .collect::<Result<Vec<_>>>()?; - if large_jobs.is_empty() { - codes.sort_by_key(|(ordinal, _)| *ordinal); - return Ok(codes.into_iter().map(|(_, code)| code).collect()); - } - let num_workers = worker_count(options, large_jobs.len()); if num_workers == 1 { codes.extend( @@ -119,54 +110,57 @@ pub(crate) fn process_pending( .map(|job| process_function_job(job, options, func_types, imported_func_count, imported_memory_count)) .collect::<Result<Vec<_>>>()?, ); - codes.sort_by_key(|(ordinal, _)| *ordinal); - return Ok(codes.into_iter().map(|(_, code)| code).collect()); - } - - let chunk_size = large_jobs.len().div_ceil(num_workers); - let chunks = { - let mut chunks = Vec::with_capacity(num_workers); - let mut iter = large_jobs.into_iter(); - while let Some(first) = iter.next() { - let mut chunk = alloc::vec![first]; - for _ in 1..chunk_size { - match iter.next() { - Some(job) => chunk.push(job), - None => break, + } else { + let chunk_size = large_jobs.len().div_ceil(num_workers); + let chunks = { + let mut chunks = Vec::with_capacity(num_workers); + let mut iter = large_jobs.into_iter(); + while let Some(first) = iter.next() { + let mut chunk = alloc::vec![first]; + for _ in 1..chunk_size { + match iter.next() { + Some(job) => chunk.push(job), + None => break, + } } + chunks.push(chunk); } - chunks.push(chunk); - } - chunks - }; + chunks + }; - let results: Vec<Result<(usize, FunctionCode)>> = std::thread::scope(|s| { - let handles: Vec<_> = chunks - .into_iter() - .map(|chunk| { - s.spawn(move || { - chunk - .into_iter() - .map(|job| { - process_function_job(job, options, func_types, imported_func_count, imported_memory_count) - }) - .collect::<Vec<_>>() + let results: Vec<Result<(usize, FunctionCode)>> = std::thread::scope(|s| { + let handles: Vec<_> = chunks + .into_iter() + .map(|chunk| { + s.spawn(move || { + chunk + .into_iter() + .map(|job| { + process_function_job( + job, + options, + func_types, + imported_func_count, + imported_memory_count, + ) + }) + .collect::<Vec<_>>() + }) }) - }) - .collect(); + .collect(); - handles - .into_iter() - .flat_map(|handle| match handle.join() { - Ok(results) => results, - Err(_) => alloc::vec![Err(ParseError::Other("worker thread panicked".into()))], - }) - .collect() - }); + handles + .into_iter() + .flat_map(|handle| match handle.join() { + Ok(results) => results, + Err(_) => alloc::vec![Err(ParseError::Other("worker thread panicked".into()))], + }) + .collect() + }); - for result in results { - let (ordinal, code) = result?; - codes.push((ordinal, code)); + for result in results { + codes.push(result?); + } } codes.sort_by_key(|(ordinal, _)| *ordinal); diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 4d233d3..4e941ad 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -303,7 +303,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild if let Some(ctx) = self.ctx_stack.last_mut() { ctx.has_else = true; ctx.branch_jumps.push(jump_ip); - self.patch_jump_if_zero(cond_jump_ip, self.instructions.len()); + self.patch_jump(cond_jump_ip, self.instructions.len()); if !matches!(self.instructions.last(), Some(Instruction::Nop | Instruction::MergeBarrier)) { self.instructions.push(Instruction::MergeBarrier); // prevent superinstructions from merging across block boundaries } @@ -343,7 +343,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild } self.emit_branch_jump_or_return(depth); - self.patch_jump_if_zero(cond_jump_ip, self.instructions.len()); + self.patch_jump(cond_jump_ip, self.instructions.len()); } fn visit_br_table(&mut self, targets: wasmparser::BrTable<'_>) -> Self::Output { @@ -366,33 +366,28 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild jump_or_ret_ip: usize, is_return: bool, } - let mut seen = Vec::<(u32, usize)>::new(); let mut pads: Vec<PadInfo> = Vec::new(); for &depth in target_depths.iter().chain(core::iter::once(&default_depth)) { - if seen.iter().any(|&(seen_depth, _)| seen_depth == depth) { + if pads.iter().any(|pad| pad.depth == depth) { continue; } - seen.push((depth, pads.len())); let (pad_start, jump_or_ret_ip, is_return) = self.emit_br_table_pad(depth); pads.push(PadInfo { depth, pad_start, jump_or_ret_ip, is_return }); } for &depth in &target_depths { - let pad_idx = seen - .iter() - .find_map(|&(seen_depth, idx)| (seen_depth == depth).then_some(idx)) - .expect("visit_br_table: missing branch table target"); - self.data.branch_table_targets.push(pads[pad_idx].pad_start as u32); + let pad = pads.iter().find(|pad| pad.depth == depth).expect("visit_br_table: missing branch table target"); + self.data.branch_table_targets.push(pad.pad_start as u32); } - let default_pad_idx = seen + let default_pad = pads .iter() - .find_map(|&(seen_depth, idx)| (seen_depth == default_depth).then_some(idx)) + .find(|pad| pad.depth == default_depth) .expect("visit_br_table: missing default branch table target"); if let Instruction::BranchTable(default_ip, _, _) = &mut self.instructions[header_ip] { - *default_ip = pads[default_pad_idx].pad_start as u32; + *default_ip = default_pad.pad_start as u32; } for pad in &pads { @@ -621,19 +616,13 @@ impl<R: WasmModuleResources> FunctionBuilder<R> { fn patch_jump(&mut self, jump_ip: usize, target: usize) { match &mut self.instructions[jump_ip] { - Instruction::Jump(ip) | Instruction::JumpIfNonZero32(ip) => { + Instruction::Jump(ip) | Instruction::JumpIfZero32(ip) | Instruction::JumpIfNonZero32(ip) => { *ip = target as u32; } _ => {} } } - fn patch_jump_if_zero(&mut self, jump_ip: usize, target: usize) { - if let Instruction::JumpIfZero32(ip) = &mut self.instructions[jump_ip] { - *ip = target as u32; - } - } - fn label_keep_counts(label_types: &[wasmparser::ValType]) -> (u16, u16, u16) { let (mut c32, mut c64, mut c128) = (0, 0, 0); for &ty in label_types { @@ -738,7 +727,7 @@ impl<R: WasmModuleResources> FunctionBuilder<R> { BlockKind::If => { if let Some((&cond_jump_ip, branch_jumps)) = ctx.branch_jumps.split_first() { if !ctx.has_else { - self.patch_jump_if_zero(cond_jump_ip, end_ip); + self.patch_jump(cond_jump_ip, end_ip); } for &jump_ip in branch_jumps { self.patch_jump(jump_ip, end_ip); diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index cb95945..76583f2 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,7 +1,8 @@ use crate::interpreter::stack::{CallFrame, ValueStack}; use crate::reference::StoreItem; -use crate::{Error, FunctionInstance, InterpreterRuntime, Result, Store, unlikely}; +use crate::{Error, FunctionInstance, InterpreterRuntime, Result, Store, Trap}; use alloc::{boxed::Box, format, rc::Rc, sync::Arc, vec, vec::Vec}; +use core::hint::cold_path; use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, WasmType, WasmValue}; impl Function { @@ -420,46 +421,56 @@ pub struct FuncExecutionTyped<'store, R> { } impl<'store> FuncExecution<'store> { - /// Resume execution with up to `fuel` units of fuel. - /// - /// Fuel is accounted in chunks, so execution may overshoot the requested - /// fuel before returning [`ExecProgress::Suspended`] (currently the chunk size is 128 instructions between fuel checks, but this may change in the future). - /// - /// Returns [`ExecProgress::Suspended`] when fuel is exhausted, or - /// [`ExecProgress::Completed`] with the final values once the invocation - /// returns. - /// - /// Reentrant calls made by host functions through [`FuncContext::call`] are - /// currently blocking. They do not suspend and later resume the host - /// function in the middle of the nested call. - pub fn resume_with_fuel(&mut self, fuel: u32) -> Result<ExecProgress<Vec<WasmValue>>> { - let FuncExecutionState::Running { exec_state, root_func_addr } = &mut self.state else { - let FuncExecutionState::Completed { result } = &mut self.state else { - unreachable!("invalid function execution state") - }; - return match result.take() { - Some(res) => Ok(ExecProgress::Completed(res)), - None => Err(Error::other("execution already completed")), - }; + fn resume( + &mut self, + run: impl FnOnce(&mut Store, CallFrame) -> Result<crate::interpreter::ExecState, Trap>, + ) -> Result<ExecProgress<Vec<WasmValue>>> { + let (callframe, root_func_addr) = match &mut self.state { + FuncExecutionState::Running { exec_state, root_func_addr } => (exec_state.callframe, *root_func_addr), + FuncExecutionState::Completed { result } => { + return match result.take() { + Some(res) => Ok(ExecProgress::Completed(res)), + None => Err(Error::other("execution already completed")), + }; + } }; self.store.enter_execution()?; - let result = InterpreterRuntime::exec_with_fuel(self.store, exec_state.callframe, fuel); + let result = run(self.store, callframe); self.store.exit_execution(); match result? { crate::interpreter::ExecState::Completed => { - let result_ty = self.store.state.get_func(*root_func_addr).ty().clone(); + let result_ty = self.store.state.get_func(root_func_addr).ty().clone(); self.state = FuncExecutionState::Completed { result: None }; Ok(ExecProgress::Completed(collect_call_results(&mut self.store.value_stack, &result_ty)?)) } crate::interpreter::ExecState::Suspended(callframe) => { + let FuncExecutionState::Running { exec_state, .. } = &mut self.state else { + unreachable!("invalid function execution state") + }; exec_state.callframe = callframe; Ok(ExecProgress::Suspended) } } } + /// Resume execution with up to `fuel` units of fuel. + /// + /// Fuel is accounted in chunks, so execution may overshoot the requested + /// fuel before returning [`ExecProgress::Suspended`] (currently the chunk size is 128 instructions between fuel checks, but this may change in the future). + /// + /// Returns [`ExecProgress::Suspended`] when fuel is exhausted, or + /// [`ExecProgress::Completed`] with the final values once the invocation + /// returns. + /// + /// Reentrant calls made by host functions through [`FuncContext::call`] are + /// currently blocking. They do not suspend and later resume the host + /// function in the middle of the nested call. + pub fn resume_with_fuel(&mut self, fuel: u32) -> Result<ExecProgress<Vec<WasmValue>>> { + self.resume(|store, callframe| InterpreterRuntime::exec_with_fuel(store, callframe, fuel)) + } + #[cfg(feature = "std")] /// Resume execution for at most `time_budget` wall-clock time. /// @@ -477,36 +488,13 @@ impl<'store> FuncExecution<'store> { &mut self, time_budget: crate::std::time::Duration, ) -> Result<ExecProgress<Vec<WasmValue>>> { - let FuncExecutionState::Running { exec_state, root_func_addr } = &mut self.state else { - let FuncExecutionState::Completed { result } = &mut self.state else { - unreachable!("invalid function execution state") - }; - return match result.take() { - Some(res) => Ok(ExecProgress::Completed(res)), - None => Err(Error::other("execution already completed")), - }; - }; - - self.store.enter_execution()?; - let result = InterpreterRuntime::exec_with_time_budget(self.store, exec_state.callframe, time_budget); - self.store.exit_execution(); - - match result? { - crate::interpreter::ExecState::Completed => { - let result_ty = self.store.state.get_func(*root_func_addr).ty().clone(); - self.state = FuncExecutionState::Completed { result: None }; - Ok(ExecProgress::Completed(collect_call_results(&mut self.store.value_stack, &result_ty)?)) - } - crate::interpreter::ExecState::Suspended(callframe) => { - exec_state.callframe = callframe; - Ok(ExecProgress::Suspended) - } - } + self.resume(|store, callframe| InterpreterRuntime::exec_with_time_budget(store, callframe, time_budget)) } } fn validate_call_params(func_ty: &FuncType, params: &[WasmValue]) -> Result<()> { - if unlikely(func_ty.params().len() != params.len()) { + if func_ty.params().len() != params.len() { + cold_path(); return Err(Error::Other(format!( "param count mismatch: expected {}, got {}", func_ty.params().len(), diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 900b8bd..aa1f350 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -149,7 +149,6 @@ impl Imports { self.externs.get(&name).cloned() } - #[cfg(not(feature = "debug"))] fn compare_types<T: PartialEq>(import: &Import, actual: &T, expected: &T) -> Result<()> { if expected != actual { cold_path(); @@ -158,15 +157,6 @@ impl Imports { Ok(()) } - #[cfg(feature = "debug")] - fn compare_types<T: PartialEq + Debug>(import: &Import, actual: &T, expected: &T) -> Result<()> { - if expected != actual { - cold_path(); - return Err(LinkingError::incompatible_import_type(import).into()); - } - Ok(()) - } - fn compare_table_types(import: &Import, expected: &TableType, actual: &TableType) -> Result<()> { Self::compare_types(import, &actual.element_type, &expected.element_type)?; if actual.size_initial > expected.size_initial { @@ -228,100 +218,61 @@ impl Imports { }; for import in &*module.imports { - if let Some(defined) = self.take_defined(import) { + let (val, func_handle) = if let Some(defined) = self.take_defined(import) { match defined { - Extern::Global(global) => { - let ImportKind::Global(import_ty) = &import.kind else { - cold_path(); - return Err(LinkingError::incompatible_import_type(import).into()); - }; - let global_instance = store.state.get_global(global.0.addr); - Self::compare_types(import, &global_instance.ty, import_ty)?; - imports.globals.push(global.0.addr); - } - Extern::Table(table) => { - let ImportKind::Table(import_ty) = &import.kind else { - cold_path(); - return Err(LinkingError::incompatible_import_type(import).into()); - }; - let table_instance = store.state.get_table(table.0.addr); - let mut kind = table_instance.kind.clone(); - kind.size_initial = table_instance.size() as u32; - Self::compare_table_types(import, &kind, import_ty)?; - imports.tables.push(table.0.addr); - } - Extern::Memory(memory) => { - let ImportKind::Memory(import_ty) = &import.kind else { - cold_path(); - return Err(LinkingError::incompatible_import_type(import).into()); - }; - let mem = store.state.get_mem(memory.0.addr); - Self::compare_memory_types(import, &mem.kind, import_ty, mem.page_count)?; - imports.memories.push(memory.0.addr); - } - Extern::Function(func_handle) => { - let ImportKind::Function(ty) = &import.kind else { - cold_path(); - return Err(LinkingError::incompatible_import_type(import).into()); - }; - let import_func_type = module - .func_types - .get(*ty as usize) - .ok_or_else(|| LinkingError::incompatible_import_type(import))?; - func_handle.item.validate_store(store)?; - Self::compare_types(import, &func_handle.ty, import_func_type)?; - imports.funcs.push(func_handle.addr); - } + Extern::Global(global) => (ExternVal::Global(global.0.addr), None), + Extern::Table(table) => (ExternVal::Table(table.0.addr), None), + Extern::Memory(memory) => (ExternVal::Memory(memory.0.addr), None), + Extern::Function(func) => (ExternVal::Func(func.addr), Some(func)), } - continue; - } - - let name = ExternName::from(import); - let Some(instance) = self.modules.get(&name.module) else { - cold_path(); - return Err(LinkingError::unknown_import(import).into()); + } else { + let name = ExternName::from(import); + let Some(instance) = self.modules.get(&name.module) else { + cold_path(); + return Err(LinkingError::unknown_import(import).into()); + }; + instance.validate_store(store)?; + (instance.export_addr(&import.name).ok_or_else(|| LinkingError::unknown_import(import))?, None) }; - instance.validate_store(store)?; - let val = instance.export_addr(&import.name).ok_or_else(|| LinkingError::unknown_import(import))?; + if val.kind() != (&import.kind).into() { + cold_path(); + return Err(LinkingError::incompatible_import_type(import).into()); + } - { - // check if the kind matches - if val.kind() != (&import.kind).into() { - cold_path(); - return Err(LinkingError::incompatible_import_type(import).into()); + match (val, &import.kind) { + (ExternVal::Global(global_addr), ImportKind::Global(ty)) => { + let global = store.state.get_global(global_addr); + Self::compare_types(import, &global.ty, ty)?; + imports.globals.push(global_addr); } - - match (val, &import.kind) { - (ExternVal::Global(global_addr), ImportKind::Global(ty)) => { - let global = store.state.get_global(global_addr); - Self::compare_types(import, &global.ty, ty)?; - imports.globals.push(global_addr); - } - (ExternVal::Table(table_addr), ImportKind::Table(ty)) => { - let table = store.state.get_table(table_addr); - let mut kind = table.kind.clone(); - kind.size_initial = table.size() as u32; - Self::compare_table_types(import, &kind, ty)?; - imports.tables.push(table_addr); - } - (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => { - let mem = store.state.get_mem(memory_addr); - Self::compare_memory_types(import, &mem.kind, ty, mem.page_count)?; - imports.memories.push(memory_addr); - } - (ExternVal::Func(func_addr), ImportKind::Function(ty)) => { - let func = store.state.get_func(func_addr); - let import_func_type = module - .func_types - .get(*ty as usize) - .ok_or_else(|| LinkingError::incompatible_import_type(import))?; - - Self::compare_types(import, func.ty(), import_func_type)?; - imports.funcs.push(func_addr); - } - _ => return Err(LinkingError::incompatible_import_type(import).into()), + (ExternVal::Table(table_addr), ImportKind::Table(ty)) => { + let table = store.state.get_table(table_addr); + let mut kind = table.kind.clone(); + kind.size_initial = table.size() as u32; + Self::compare_table_types(import, &kind, ty)?; + imports.tables.push(table_addr); + } + (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => { + let mem = store.state.get_mem(memory_addr); + Self::compare_memory_types(import, &mem.kind, ty, mem.page_count)?; + imports.memories.push(memory_addr); + } + (ExternVal::Func(func_addr), ImportKind::Function(ty)) => { + let import_func_type = module + .func_types + .get(*ty as usize) + .ok_or_else(|| LinkingError::incompatible_import_type(import))?; + let actual_ty = if let Some(func) = &func_handle { + func.item.validate_store(store)?; + &func.ty + } else { + store.state.get_func(func_addr).ty() + }; + Self::compare_types(import, actual_ty, import_func_type)?; + imports.funcs.push(func_addr); } + _ => unreachable!("import kind checked above"), } } diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index f1003ab..358b706 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -3,7 +3,8 @@ use core::hint::cold_path; use tinywasm_types::*; use crate::func::{FromWasmValues, IntoWasmValues, ToWasmTypes}; -use crate::{Error, Function, FunctionTyped, Global, Imports, Memory, Result, Store, Table, Trap}; +use crate::store::MemoryInstance; +use crate::{Error, Function, FunctionTyped, Global, Imports, Memory, Result, Store, StoreItem, Table, Trap}; /// A typed view over an exported extern value. pub enum ExternItem { @@ -67,25 +68,13 @@ impl ModuleInstance { /// Type indices come from the module type section and are used by indirect calls. #[inline] pub(crate) fn func_type_by_type_index(&self, type_idx: u32) -> &Arc<FuncType> { - match self.0.types.get(type_idx as usize) { - Some(ty) => ty, - None => { - cold_path(); - unreachable!("invalid type index: {type_idx}") - } - } + self.0.types.get(type_idx as usize).unwrap_or_else(|| unreachable!("invalid type index: {type_idx}")) } /// Function indices need their own lookup because they are not type-section indices. #[inline] pub(crate) fn func_type_idx(&self, addr: FuncAddr) -> u32 { - match self.0.func_type_idxs.get(addr as usize) { - Some(idx) => *idx, - None => { - cold_path(); - unreachable!("invalid function address: {addr}") - } - } + *self.0.func_type_idxs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid function address: {addr}")) } #[inline] @@ -96,73 +85,37 @@ impl ModuleInstance { /// resolve a function address to the global store address #[inline] pub(crate) fn resolve_func_addr(&self, addr: FuncAddr) -> FuncAddr { - match self.0.func_addrs.get(addr as usize) { - Some(addr) => *addr, - None => { - cold_path(); - unreachable!("invalid function address: {addr}") - } - } + *self.0.func_addrs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid function address: {addr}")) } /// resolve a table address to the global store address #[inline] pub(crate) fn resolve_table_addr(&self, addr: TableAddr) -> TableAddr { - match self.0.table_addrs.get(addr as usize) { - Some(addr) => *addr, - None => { - cold_path(); - unreachable!("invalid table address: {addr}") - } - } + *self.0.table_addrs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid table address: {addr}")) } /// resolve a memory address to the global store address #[inline] pub(crate) fn resolve_mem_addr(&self, addr: MemAddr) -> MemAddr { - match self.0.mem_addrs.get(addr as usize) { - Some(addr) => *addr, - None => { - cold_path(); - unreachable!("invalid memory address: {addr}") - } - } + *self.0.mem_addrs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid memory address: {addr}")) } /// resolve a data address to the global store address #[inline] pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> DataAddr { - match self.0.data_addrs.get(addr as usize) { - Some(addr) => *addr, - None => { - cold_path(); - unreachable!("invalid data address: {addr}") - } - } + *self.0.data_addrs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid data address: {addr}")) } /// resolve an element address to the global store address #[inline] pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> ElemAddr { - match self.0.elem_addrs.get(addr as usize) { - Some(addr) => *addr, - None => { - cold_path(); - unreachable!("invalid element address: {addr}") - } - } + *self.0.elem_addrs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid element address: {addr}")) } /// resolve a global address to the global store address #[inline] pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> GlobalAddr { - match self.0.global_addrs.get(addr as usize) { - Some(addr) => *addr, - None => { - cold_path(); - unreachable!("invalid global address: {addr}") - } - } + *self.0.global_addrs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid global address: {addr}")) } #[inline] @@ -222,8 +175,12 @@ impl ModuleInstance { addrs.tables.extend(store.init_tables(&module.table_types)); match module.local_memory_allocation { LocalMemoryAllocation::Skip => {} - LocalMemoryAllocation::Lazy => addrs.memories.extend(store.init_lazy_memories(&module.memory_types)?), - LocalMemoryAllocation::Eager => addrs.memories.extend(store.init_memories(&module.memory_types)?), + LocalMemoryAllocation::Lazy => { + addrs.memories.extend(store.init_memories(&module.memory_types, MemoryInstance::new_lazy)?) + } + LocalMemoryAllocation::Eager => { + addrs.memories.extend(store.init_memories(&module.memory_types, MemoryInstance::new)?) + } } store.init_globals(&mut addrs.globals, &module.globals, &addrs.funcs)?; @@ -305,20 +262,20 @@ impl ModuleInstance { ExternalKind::Func => { let func_addr = self.resolve_func_addr(export.index); ExternItem::Func(Function { - item: crate::StoreItem::new(self.0.store_id, func_addr), + item: StoreItem::new(self.0.store_id, func_addr), module_addr: self.id(), addr: func_addr, ty: self.func_type_by_type_index(self.func_type_idx(export.index)).clone(), }) } ExternalKind::Table => { - ExternItem::Table(Table::from_store_addr(self.0.store_id, self.resolve_table_addr(export.index))) + ExternItem::Table(Table(StoreItem::new(self.0.store_id, self.resolve_table_addr(export.index)))) } ExternalKind::Memory => { - ExternItem::Memory(Memory::from_store_addr(self.0.store_id, self.resolve_mem_addr(export.index))) + ExternItem::Memory(Memory(StoreItem::new(self.0.store_id, self.resolve_mem_addr(export.index)))) } ExternalKind::Global => { - ExternItem::Global(Global::from_store_addr(self.0.store_id, self.resolve_global_addr(export.index))) + ExternItem::Global(Global(StoreItem::new(self.0.store_id, self.resolve_global_addr(export.index)))) } }; @@ -376,15 +333,15 @@ impl ModuleInstance { let export = self.0.exports.iter().find(|e| e.name == name.into()); let export = export.ok_or_else(|| Error::Other(format!("Export not found: {name}")))?; Ok(ExternItem::Func(Function { - item: crate::StoreItem::new(self.0.store_id, addr), + item: StoreItem::new(self.0.store_id, addr), module_addr: self.id(), addr, ty: self.func_type_by_type_index(self.func_type_idx(export.index)).clone(), })) } - ExternVal::Memory(addr) => Ok(ExternItem::Memory(Memory::from_store_addr(self.0.store_id, addr))), - ExternVal::Table(addr) => Ok(ExternItem::Table(Table::from_store_addr(self.0.store_id, addr))), - ExternVal::Global(addr) => Ok(ExternItem::Global(Global::from_store_addr(self.0.store_id, addr))), + ExternVal::Memory(addr) => Ok(ExternItem::Memory(Memory(StoreItem::new(self.0.store_id, addr)))), + ExternVal::Table(addr) => Ok(ExternItem::Table(Table(StoreItem::new(self.0.store_id, addr)))), + ExternVal::Global(addr) => Ok(ExternItem::Global(Global(StoreItem::new(self.0.store_id, addr)))), } } @@ -425,7 +382,7 @@ impl ModuleInstance { }; Ok(Function { - item: crate::StoreItem::new(self.0.store_id, func_addr), + item: StoreItem::new(self.0.store_id, func_addr), addr: func_addr, module_addr: self.id(), ty: store.state.get_func(func_addr).ty().clone(), @@ -446,7 +403,7 @@ impl ModuleInstance { let ty = store.state.get_func(func_addr).ty(); Ok(Function { - item: crate::StoreItem::new(self.0.store_id, func_addr), + item: StoreItem::new(self.0.store_id, func_addr), addr: func_addr, module_addr: self.id(), ty: ty.clone(), @@ -523,7 +480,7 @@ impl ModuleInstance { /// Get a memory export by name. pub fn memory(&self, name: &str) -> Result<Memory> { match self.require_export(name)? { - ExternVal::Memory(mem_addr) => Ok(Memory::from_store_addr(self.0.store_id, mem_addr)), + ExternVal::Memory(mem_addr) => Ok(Memory(StoreItem::new(self.0.store_id, mem_addr))), _ => { cold_path(); Err(Error::Other(format!("Export is not a memory: {name}"))) @@ -540,13 +497,13 @@ impl ModuleInstance { #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] #[cfg(feature = "guest-debug")] pub fn memory_by_index(&self, memory_index: MemAddr) -> Result<Memory> { - Ok(Memory::from_store_addr(self.0.store_id, Self::index_addr(&self.0.mem_addrs, memory_index, "memory")?)) + Ok(Memory(StoreItem::new(self.0.store_id, Self::index_addr(&self.0.mem_addrs, memory_index, "memory")?))) } /// Get a table export by name. pub fn table(&self, name: &str) -> Result<Table> { match self.require_export(name)? { - ExternVal::Table(table_addr) => Ok(Table::from_store_addr(self.0.store_id, table_addr)), + ExternVal::Table(table_addr) => Ok(Table(StoreItem::new(self.0.store_id, table_addr))), _ => Err(Error::Other(format!("Export is not a table: {name}"))), } } @@ -560,7 +517,7 @@ impl ModuleInstance { #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] #[cfg(feature = "guest-debug")] pub fn table_by_index(&self, table_index: TableAddr) -> Result<Table> { - Ok(Table::from_store_addr(self.0.store_id, Self::index_addr(&self.0.table_addrs, table_index, "table")?)) + Ok(Table(StoreItem::new(self.0.store_id, Self::index_addr(&self.0.table_addrs, table_index, "table")?))) } /// Get the value of a global export by name. @@ -571,7 +528,7 @@ impl ModuleInstance { /// Get a global export by name. pub fn global(&self, name: &str) -> Result<Global> { match self.require_export(name)? { - ExternVal::Global(global_addr) => Ok(Global::from_store_addr(self.0.store_id, global_addr)), + ExternVal::Global(global_addr) => Ok(Global(StoreItem::new(self.0.store_id, global_addr))), _ => Err(Error::Other(format!("Export is not a global: {name}"))), } } @@ -590,7 +547,7 @@ impl ModuleInstance { #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] #[cfg(feature = "guest-debug")] pub fn global_by_index(&self, global_index: GlobalAddr) -> Result<Global> { - Ok(Global::from_store_addr(self.0.store_id, Self::index_addr(&self.0.global_addrs, global_index, "global")?)) + Ok(Global(StoreItem::new(self.0.store_id, Self::index_addr(&self.0.global_addrs, global_index, "global")?))) } /// Get the start function of the module @@ -626,7 +583,7 @@ impl ModuleInstance { let func_addr = self.resolve_func_addr(func_addr); Ok(Some(Function { - item: crate::StoreItem::new(self.0.store_id, func_addr), + item: StoreItem::new(self.0.store_id, func_addr), module_addr: self.id(), addr: func_addr, ty: store.state.get_func(func_addr).ty().clone(), diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index e54b5e5..b87c3b5 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -53,21 +53,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { #[inline(always)] fn exec(&mut self) -> Result<Option<()>, Trap> { macro_rules! stack_op { - (unary $ty:ty, |$v:ident| $expr:expr) => {{ - (|| -> Result<(), Trap> { - let $v = <$ty>::stack_pop(&mut self.store.value_stack); - <$ty>::stack_push(&mut self.store.value_stack, $expr)?; - Ok(()) - })()?; - }}; - (binary $ty:ty, |$lhs:ident, $rhs:ident| $expr:expr) => {{ - (|| -> Result<(), Trap> { - let $rhs = <$ty>::stack_pop(&mut self.store.value_stack); - let $lhs = <$ty>::stack_pop(&mut self.store.value_stack); - <$ty>::stack_push(&mut self.store.value_stack, $expr)?; - Ok(()) - })()?; - }}; + (unary $ty:ty, |$v:ident| $expr:expr) => { + stack_op!(unary $ty => $ty, |$v| $expr) + }; + (binary $ty:ty, |$lhs:ident, $rhs:ident| $expr:expr) => { + stack_op!(binary $ty => $ty, |$lhs, $rhs| $expr) + }; (binary try $ty:ty, |$lhs:ident, $rhs:ident| $expr:expr) => {{ (|| -> Result<(), Trap> { let $rhs = <$ty>::stack_pop(&mut self.store.value_stack); @@ -148,29 +139,13 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { } macro_rules! exec_binop { - (32, $op:expr, $lhs:expr, $rhs:expr) => {{ - match $op { - BinOp::IAdd => $lhs.wrapping_add($rhs), - BinOp::ISub => $lhs.wrapping_sub($rhs), - BinOp::IMul => $lhs.wrapping_mul($rhs), - BinOp::IAnd => $lhs & $rhs, - BinOp::IOr => $lhs | $rhs, - BinOp::IXor => $lhs ^ $rhs, - BinOp::IShl => (($lhs as i32).wasm_shl($rhs as i32)) as u32, - BinOp::IShrS => (($lhs as i32).wasm_shr($rhs as i32)) as u32, - BinOp::IShrU => $lhs.wasm_shr($rhs), - BinOp::IRotl => (($lhs as i32).wasm_rotl($rhs as i32)) as u32, - BinOp::IRotr => (($lhs as i32).wasm_rotr($rhs as i32)) as u32, - BinOp::FAdd => (f32::from_bits($lhs) + f32::from_bits($rhs)).to_bits(), - BinOp::FSub => (f32::from_bits($lhs) - f32::from_bits($rhs)).to_bits(), - BinOp::FMul => (f32::from_bits($lhs) * f32::from_bits($rhs)).to_bits(), - BinOp::FDiv => (f32::from_bits($lhs) / f32::from_bits($rhs)).to_bits(), - BinOp::FMin => f32::from_bits($lhs).tw_minimum(f32::from_bits($rhs)).to_bits(), - BinOp::FMax => f32::from_bits($lhs).tw_maximum(f32::from_bits($rhs)).to_bits(), - BinOp::FCopysign => f32::from_bits($lhs).copysign(f32::from_bits($rhs)).to_bits(), - } - }}; - (64, $op:expr, $lhs:expr, $rhs:expr) => {{ + (32, $op:expr, $lhs:expr, $rhs:expr) => { + exec_binop!(@scalar i32, u32, f32, $op, $lhs, $rhs) + }; + (64, $op:expr, $lhs:expr, $rhs:expr) => { + exec_binop!(@scalar i64, u64, f64, $op, $lhs, $rhs) + }; + (@scalar $signed:ty, $unsigned:ty, $float:ty, $op:expr, $lhs:expr, $rhs:expr) => {{ match $op { BinOp::IAdd => $lhs.wrapping_add($rhs), BinOp::ISub => $lhs.wrapping_sub($rhs), @@ -178,18 +153,18 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { BinOp::IAnd => $lhs & $rhs, BinOp::IOr => $lhs | $rhs, BinOp::IXor => $lhs ^ $rhs, - BinOp::IShl => (($lhs as i64).wasm_shl($rhs as i64)) as u64, - BinOp::IShrS => (($lhs as i64).wasm_shr($rhs as i64)) as u64, - BinOp::IShrU => $lhs.wasm_shr($rhs), - BinOp::IRotl => (($lhs as i64).wasm_rotl($rhs as i64)) as u64, - BinOp::IRotr => (($lhs as i64).wasm_rotr($rhs as i64)) as u64, - BinOp::FAdd => (f64::from_bits($lhs) + f64::from_bits($rhs)).to_bits(), - BinOp::FSub => (f64::from_bits($lhs) - f64::from_bits($rhs)).to_bits(), - BinOp::FMul => (f64::from_bits($lhs) * f64::from_bits($rhs)).to_bits(), - BinOp::FDiv => (f64::from_bits($lhs) / f64::from_bits($rhs)).to_bits(), - BinOp::FMin => f64::from_bits($lhs).tw_minimum(f64::from_bits($rhs)).to_bits(), - BinOp::FMax => f64::from_bits($lhs).tw_maximum(f64::from_bits($rhs)).to_bits(), - BinOp::FCopysign => f64::from_bits($lhs).copysign(f64::from_bits($rhs)).to_bits(), + BinOp::IShl => (($lhs as $signed).wrapping_shl($rhs as u32)) as $unsigned, + BinOp::IShrS => (($lhs as $signed).wrapping_shr($rhs as u32)) as $unsigned, + BinOp::IShrU => $lhs.wrapping_shr($rhs as u32), + BinOp::IRotl => (($lhs as $signed).rotate_left($rhs as u32)) as $unsigned, + BinOp::IRotr => (($lhs as $signed).rotate_right($rhs as u32)) as $unsigned, + BinOp::FAdd => (<$float>::from_bits($lhs) + <$float>::from_bits($rhs)).to_bits(), + BinOp::FSub => (<$float>::from_bits($lhs) - <$float>::from_bits($rhs)).to_bits(), + BinOp::FMul => (<$float>::from_bits($lhs) * <$float>::from_bits($rhs)).to_bits(), + BinOp::FDiv => (<$float>::from_bits($lhs) / <$float>::from_bits($rhs)).to_bits(), + BinOp::FMin => <$float>::from_bits($lhs).tw_minimum(<$float>::from_bits($rhs)).to_bits(), + BinOp::FMax => <$float>::from_bits($lhs).tw_maximum(<$float>::from_bits($rhs)).to_bits(), + BinOp::FCopysign => <$float>::from_bits($lhs).copysign(<$float>::from_bits($rhs)).to_bits(), } }}; (128, $op:expr, $lhs:expr, $rhs:expr) => {{ @@ -265,16 +240,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { }}; } - let next = match self.func.instructions.get(self.cf.instr_ptr) { - Some(instr) => instr, - None => { - cold_path(); - unreachable!( - "Instruction pointer out of bounds: {} ({} instructions)", - self.cf.instr_ptr, - self.func.instructions.len() - ) - } + let Some(next) = self.func.instructions.get(self.cf.instr_ptr) else { + unreachable!( + "Instruction pointer out of bounds: {} ({} instructions)", + self.cf.instr_ptr, + self.func.instructions.len() + ) }; use tinywasm_types::Instruction::*; @@ -431,30 +402,30 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { I64Mul => stack_op!(binary i64, |a, b| a.wrapping_mul(b)), F32Mul => stack_op!(binary f32, |a, b| a * b), F64Mul => stack_op!(binary f64, |a, b| a * b), - I32DivS => stack_op!(binary try i32, |a, b| a.wasm_checked_div(b)), - I64DivS => stack_op!(binary try i64, |a, b| a.wasm_checked_div(b)), - I32DivU => stack_op!(binary try u32, |a, b| a.checked_div(b).ok_or_else(trap_0)), - I64DivU => stack_op!(binary try u64, |a, b| a.checked_div(b).ok_or_else(trap_0)), - I32RemS => stack_op!(binary try i32, |a, b| a.checked_wrapping_rem(b)), - I64RemS => stack_op!(binary try i64, |a, b| a.checked_wrapping_rem(b)), - I32RemU => stack_op!(binary try u32, |a, b| a.checked_wrapping_rem(b)), - I64RemU => stack_op!(binary try u64, |a, b| a.checked_wrapping_rem(b)), + I32DivS => stack_op!(binary try i32, |a, b| a.tw_checked_div(b)), + I64DivS => stack_op!(binary try i64, |a, b| a.tw_checked_div(b)), + I32DivU => stack_op!(binary try u32, |a, b| a.checked_div(b).ok_or(Trap::DivisionByZero)), + I64DivU => stack_op!(binary try u64, |a, b| a.checked_div(b).ok_or(Trap::DivisionByZero)), + I32RemS => stack_op!(binary try i32, |a, b| a.tw_checked_wrapping_rem(b)), + I64RemS => stack_op!(binary try i64, |a, b| a.tw_checked_wrapping_rem(b)), + I32RemU => stack_op!(binary try u32, |a, b| a.tw_checked_wrapping_rem(b)), + I64RemU => stack_op!(binary try u64, |a, b| a.tw_checked_wrapping_rem(b)), I32And => stack_op!(binary i32, |a, b| a & b), I64And => stack_op!(binary i64, |a, b| a & b), I32Or => stack_op!(binary i32, |a, b| a | b), I64Or => stack_op!(binary i64, |a, b| a | b), I32Xor => stack_op!(binary i32, |a, b| a ^ b), I64Xor => stack_op!(binary i64, |a, b| a ^ b), - I32Shl => stack_op!(binary i32, |a, b| a.wasm_shl(b)), - I64Shl => stack_op!(binary i64, |a, b| a.wasm_shl(b)), - I32ShrS => stack_op!(binary i32, |a, b| a.wasm_shr(b)), - I64ShrS => stack_op!(binary i64, |a, b| a.wasm_shr(b)), - I32ShrU => stack_op!(binary u32, |a, b| a.wasm_shr(b)), - I64ShrU => stack_op!(binary u64, |a, b| a.wasm_shr(b)), - I32Rotl => stack_op!(binary i32, |a, b| a.wasm_rotl(b)), - I64Rotl => stack_op!(binary i64, |a, b| a.wasm_rotl(b)), - I32Rotr => stack_op!(binary i32, |a, b| a.wasm_rotr(b)), - I64Rotr => stack_op!(binary i64, |a, b| a.wasm_rotr(b)), + I32Shl => stack_op!(binary i32, |a, b| a.wrapping_shl(b as u32)), + I64Shl => stack_op!(binary i64, |a, b| a.wrapping_shl(b as u32)), + I32ShrS => stack_op!(binary i32, |a, b| a.wrapping_shr(b as u32)), + I64ShrS => stack_op!(binary i64, |a, b| a.wrapping_shr(b as u32)), + I32ShrU => stack_op!(binary u32, |a, b| a.wrapping_shr(b)), + I64ShrU => stack_op!(binary u64, |a, b| a.wrapping_shr(b as u32)), + I32Rotl => stack_op!(binary i32, |a, b| a.rotate_left(b as u32)), + I64Rotl => stack_op!(binary i64, |a, b| a.rotate_left(b as u32)), + I32Rotr => stack_op!(binary i32, |a, b| a.rotate_right(b as u32)), + I64Rotr => stack_op!(binary i64, |a, b| a.rotate_right(b as u32)), I64Add128 => stack_op!(quaternary_into2 i64 => i64, |a_lo, a_hi, b_lo, b_hi| { let lo = a_lo.wrapping_add(b_lo); let carry = u64::from((lo as u64) < (a_lo as u64)); @@ -789,11 +760,8 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { I64x2ExtendHighI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_u()), I8x16Popcnt => stack_op!(unary Value128, |v| v.i8x16_popcnt()), I8x16Shuffle(idx) => { - let Some(mask) = self.func.data.v128_constants.get(*idx as usize) else { - cold_path(); - unreachable!("invalid i128 constant index") - }; - stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, Value128(*mask))) + let mask = self.func.data.v128_const(*idx); + stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, Value128(mask))) }, I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)), I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)), @@ -1120,20 +1088,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { fn exec_return(&mut self) -> bool { self.store.value_stack.truncate_keep_counts(self.cf.locals_base, self.func.results); - let Some(caller) = self.store.call_stack.pop_frame(self.call_stack_base) else { - return true; - }; - if caller.func_addr == self.cf.func_addr { - self.cf = caller; - return false; - } - let wasm_func = self.store.state.get_wasm_func(caller.func_addr); - self.func = wasm_func.func.clone(); - if wasm_func.owner != self.module.idx() { - self.module = self.store.get_module_instance_internal(wasm_func.owner); - } - self.cf = caller; - false + self.finish_return() } #[inline(always)] diff --git a/crates/tinywasm/src/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs index 74bca57..7b3807e 100644 --- a/crates/tinywasm/src/interpreter/num_helpers.rs +++ b/crates/tinywasm/src/interpreter/num_helpers.rs @@ -1,6 +1,6 @@ pub(crate) trait TinywasmIntExt: Sized { - fn checked_wrapping_rem(self, rhs: Self) -> Result<Self, Trap>; - fn wasm_checked_div(self, rhs: Self) -> Result<Self, Trap>; + fn tw_checked_wrapping_rem(self, rhs: Self) -> Result<Self, Trap>; + fn tw_checked_div(self, rhs: Self) -> Result<Self, Trap>; } /// Doing the actual conversion from float to int is a bit tricky, because @@ -30,10 +30,12 @@ macro_rules! checked_conv_float { ($from:tt, $intermediate:tt, $to:tt, $self:expr) => {{ let v = <$from>::stack_pop(&mut $self.store.value_stack); let (min, max) = float_min_max!($from, $intermediate); - if unlikely(v.is_nan()) { + if v.is_nan() { + core::hint::cold_path(); return Err(crate::Trap::InvalidConversionToInt); } - if unlikely(v <= min || v >= max) { + if v <= min || v >= max { + core::hint::cold_path(); return Err(crate::Trap::IntegerOverflow); } $self.store.value_stack.push::<$to>((v as $intermediate as $to).into())?; @@ -43,9 +45,6 @@ macro_rules! checked_conv_float { pub(crate) use checked_conv_float; pub(crate) use float_min_max; -pub(super) fn trap_0() -> Trap { - crate::Trap::DivisionByZero -} pub(crate) trait TinywasmFloatExt { fn tw_minimum(self, other: Self) -> Self; fn tw_maximum(self, other: Self) -> Self; @@ -119,45 +118,10 @@ macro_rules! impl_wasm_float_ops { impl_wasm_float_ops! { f32 f64 } -pub(crate) trait WasmIntOps { - fn wasm_shl(self, rhs: Self) -> Self; - fn wasm_shr(self, rhs: Self) -> Self; - fn wasm_rotl(self, rhs: Self) -> Self; - fn wasm_rotr(self, rhs: Self) -> Self; -} - -macro_rules! impl_wrapping_self_sh { - ($($t:ty)*) => ($( - impl WasmIntOps for $t { - #[inline] - fn wasm_shl(self, rhs: Self) -> Self { - self.wrapping_shl(rhs as u32) - } - - #[inline] - fn wasm_shr(self, rhs: Self) -> Self { - self.wrapping_shr(rhs as u32) - } - - #[inline] - fn wasm_rotl(self, rhs: Self) -> Self { - self.rotate_left(rhs as u32) - } - - #[inline] - fn wasm_rotr(self, rhs: Self) -> Self { - self.rotate_right(rhs as u32) - } - } - )*) -} - -impl_wrapping_self_sh! { i32 i64 u32 u64 } - macro_rules! impl_checked_wrapping_rem { ($($t:ty)*) => ($( impl TinywasmIntExt for $t { - fn checked_wrapping_rem(self, rhs: Self) -> Result<Self, crate::Trap> { + fn tw_checked_wrapping_rem(self, rhs: Self) -> Result<Self, crate::Trap> { if rhs == 0 { Err(crate::Trap::DivisionByZero) } else { @@ -165,7 +129,7 @@ macro_rules! impl_checked_wrapping_rem { } } - fn wasm_checked_div(self, rhs: Self) -> Result<Self, crate::Trap> { + fn tw_checked_div(self, rhs: Self) -> Result<Self, crate::Trap> { if rhs == 0 { Err(crate::Trap::DivisionByZero) } else { diff --git a/crates/tinywasm/src/interpreter/simd/instructions.rs b/crates/tinywasm/src/interpreter/simd/instructions.rs index 00c387e..c859caf 100644 --- a/crates/tinywasm/src/interpreter/simd/instructions.rs +++ b/crates/tinywasm/src/interpreter/simd/instructions.rs @@ -336,159 +336,41 @@ impl Value128 { Self(self.0.map(|lane| lane.count_ones() as u8)) } - #[doc(alias = "i8x16.shl")] - pub fn i8x16_shl(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i8x16, from_i8x16, 7, shl) + impl_simd_shifts! { + "i8x16.shl" => i8x16_shl(as_i8x16, from_i8x16, 7, shl); + "i16x8.shl" => i16x8_shl(as_i16x8, from_i16x8, 15, shl); + "i32x4.shl" => i32x4_shl(as_i32x4, from_i32x4, 31, shl); + "i64x2.shl" => i64x2_shl(as_i64x2, from_i64x2, 63, shl); + "i8x16.shr_s" => i8x16_shr_s(as_i8x16, from_i8x16, 7, shr); + "i16x8.shr_s" => i16x8_shr_s(as_i16x8, from_i16x8, 15, shr); + "i32x4.shr_s" => i32x4_shr_s(as_i32x4, from_i32x4, 31, shr); + "i64x2.shr_s" => i64x2_shr_s(as_i64x2, from_i64x2, 63, shr); + "i8x16.shr_u" => i8x16_shr_u(as_u8x16, from_u8x16, 7, shr); + "i16x8.shr_u" => i16x8_shr_u(as_u16x8, from_u16x8, 15, shr); + "i32x4.shr_u" => i32x4_shr_u(as_u32x4, from_u32x4, 31, shr); + "i64x2.shr_u" => i64x2_shr_u(as_u64x2, from_u64x2, 63, shr); } - #[doc(alias = "i16x8.shl")] - pub fn i16x8_shl(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i16x8, from_i16x8, 15, shl) - } - - #[doc(alias = "i32x4.shl")] - pub fn i32x4_shl(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i32x4, from_i32x4, 31, shl) - } - - #[doc(alias = "i64x2.shl")] - pub fn i64x2_shl(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i64x2, from_i64x2, 63, shl) - } - - #[doc(alias = "i8x16.shr_s")] - pub fn i8x16_shr_s(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i8x16, from_i8x16, 7, shr) - } - - #[doc(alias = "i16x8.shr_s")] - pub fn i16x8_shr_s(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i16x8, from_i16x8, 15, shr) - } - - #[doc(alias = "i32x4.shr_s")] - pub fn i32x4_shr_s(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i32x4, from_i32x4, 31, shr) - } - - #[doc(alias = "i64x2.shr_s")] - pub fn i64x2_shr_s(self, shift: u32) -> Self { - simd_shift!(self, shift, as_i64x2, from_i64x2, 63, shr) - } - - #[doc(alias = "i8x16.shr_u")] - pub fn i8x16_shr_u(self, shift: u32) -> Self { - simd_shift!(self, shift, as_u8x16, from_u8x16, 7, shr) - } - - #[doc(alias = "i16x8.shr_u")] - pub fn i16x8_shr_u(self, shift: u32) -> Self { - simd_shift!(self, shift, as_u16x8, from_u16x8, 15, shr) - } - - #[doc(alias = "i32x4.shr_u")] - pub fn i32x4_shr_u(self, shift: u32) -> Self { - simd_shift!(self, shift, as_u32x4, from_u32x4, 31, shr) - } - - #[doc(alias = "i64x2.shr_u")] - pub fn i64x2_shr_u(self, shift: u32) -> Self { - simd_shift!(self, shift, as_u64x2, from_u64x2, 63, shr) - } - - #[doc(alias = "i8x16.add")] - pub fn i8x16_add(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i8x16_add, as_i8x16, from_i8x16, wrapping_add) - } - - #[doc(alias = "i16x8.add")] - pub fn i16x8_add(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i16x8_add, as_i16x8, from_i16x8, wrapping_add) - } - - #[doc(alias = "i32x4.add")] - pub fn i32x4_add(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i32x4_add, as_i32x4, from_i32x4, wrapping_add) - } - - #[doc(alias = "i64x2.add")] - pub fn i64x2_add(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i64x2_add, as_i64x2, from_i64x2, wrapping_add) - } - - #[doc(alias = "i8x16.sub")] - pub fn i8x16_sub(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i8x16_sub, as_i8x16, from_i8x16, wrapping_sub) - } - - #[doc(alias = "i16x8.sub")] - pub fn i16x8_sub(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i16x8_sub, as_i16x8, from_i16x8, wrapping_sub) - } - - #[doc(alias = "i32x4.sub")] - pub fn i32x4_sub(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i32x4_sub, as_i32x4, from_i32x4, wrapping_sub) - } - - #[doc(alias = "i64x2.sub")] - pub fn i64x2_sub(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i64x2_sub, as_i64x2, from_i64x2, wrapping_sub) - } - - #[doc(alias = "i16x8.mul")] - pub fn i16x8_mul(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i16x8_mul, as_i16x8, from_i16x8, wrapping_mul) - } - - #[doc(alias = "i32x4.mul")] - pub fn i32x4_mul(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i32x4_mul, as_i32x4, from_i32x4, wrapping_mul) - } - - #[doc(alias = "i64x2.mul")] - pub fn i64x2_mul(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i64x2_mul, as_i64x2, from_i64x2, wrapping_mul) - } - - #[doc(alias = "i8x16.add_sat_s")] - pub fn i8x16_add_sat_s(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i8x16_add_sat, as_i8x16, from_i8x16, saturating_add) - } - - #[doc(alias = "i16x8.add_sat_s")] - pub fn i16x8_add_sat_s(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i16x8_add_sat, as_i16x8, from_i16x8, saturating_add) - } - - #[doc(alias = "i8x16.add_sat_u")] - pub fn i8x16_add_sat_u(self, rhs: Self) -> Self { - simd_binop!(self, rhs, u8x16_add_sat, as_u8x16, from_u8x16, saturating_add) - } - - #[doc(alias = "i16x8.add_sat_u")] - pub fn i16x8_add_sat_u(self, rhs: Self) -> Self { - simd_binop!(self, rhs, u16x8_add_sat, as_u16x8, from_u16x8, saturating_add) - } - - #[doc(alias = "i8x16.sub_sat_s")] - pub fn i8x16_sub_sat_s(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i8x16_sub_sat, as_i8x16, from_i8x16, saturating_sub) - } - - #[doc(alias = "i16x8.sub_sat_s")] - pub fn i16x8_sub_sat_s(self, rhs: Self) -> Self { - simd_binop!(self, rhs, i16x8_sub_sat, as_i16x8, from_i16x8, saturating_sub) - } - - #[doc(alias = "i8x16.sub_sat_u")] - pub fn i8x16_sub_sat_u(self, rhs: Self) -> Self { - simd_binop!(self, rhs, u8x16_sub_sat, as_u8x16, from_u8x16, saturating_sub) - } - - #[doc(alias = "i16x8.sub_sat_u")] - pub fn i16x8_sub_sat_u(self, rhs: Self) -> Self { - simd_binop!(self, rhs, u16x8_sub_sat, as_u16x8, from_u16x8, saturating_sub) + impl_simd_binary_methods! { simd_binop; + "i8x16.add" => i8x16_add(i8x16_add, as_i8x16, from_i8x16, wrapping_add); + "i16x8.add" => i16x8_add(i16x8_add, as_i16x8, from_i16x8, wrapping_add); + "i32x4.add" => i32x4_add(i32x4_add, as_i32x4, from_i32x4, wrapping_add); + "i64x2.add" => i64x2_add(i64x2_add, as_i64x2, from_i64x2, wrapping_add); + "i8x16.sub" => i8x16_sub(i8x16_sub, as_i8x16, from_i8x16, wrapping_sub); + "i16x8.sub" => i16x8_sub(i16x8_sub, as_i16x8, from_i16x8, wrapping_sub); + "i32x4.sub" => i32x4_sub(i32x4_sub, as_i32x4, from_i32x4, wrapping_sub); + "i64x2.sub" => i64x2_sub(i64x2_sub, as_i64x2, from_i64x2, wrapping_sub); + "i16x8.mul" => i16x8_mul(i16x8_mul, as_i16x8, from_i16x8, wrapping_mul); + "i32x4.mul" => i32x4_mul(i32x4_mul, as_i32x4, from_i32x4, wrapping_mul); + "i64x2.mul" => i64x2_mul(i64x2_mul, as_i64x2, from_i64x2, wrapping_mul); + "i8x16.add_sat_s" => i8x16_add_sat_s(i8x16_add_sat, as_i8x16, from_i8x16, saturating_add); + "i16x8.add_sat_s" => i16x8_add_sat_s(i16x8_add_sat, as_i16x8, from_i16x8, saturating_add); + "i8x16.add_sat_u" => i8x16_add_sat_u(u8x16_add_sat, as_u8x16, from_u8x16, saturating_add); + "i16x8.add_sat_u" => i16x8_add_sat_u(u16x8_add_sat, as_u16x8, from_u16x8, saturating_add); + "i8x16.sub_sat_s" => i8x16_sub_sat_s(i8x16_sub_sat, as_i8x16, from_i8x16, saturating_sub); + "i16x8.sub_sat_s" => i16x8_sub_sat_s(i16x8_sub_sat, as_i16x8, from_i16x8, saturating_sub); + "i8x16.sub_sat_u" => i8x16_sub_sat_u(u8x16_sub_sat, as_u8x16, from_u8x16, saturating_sub); + "i16x8.sub_sat_u" => i16x8_sub_sat_u(u16x8_sub_sat, as_u16x8, from_u16x8, saturating_sub); } #[doc(alias = "i8x16.avgr_u")] @@ -577,124 +459,34 @@ impl Value128 { Self::from_u32x4(array::from_fn(|i| lanes[i * 2] as u32 + lanes[i * 2 + 1] as u32)) } - #[doc(alias = "i16x8.extend_low_i8x16_s")] - pub fn i16x8_extend_low_i8x16_s(self) -> Self { - simd_extend_cast!(self, as_i8x16, from_i16x8, i16, 0) - } - - #[doc(alias = "i16x8.extend_low_i8x16_u")] - pub fn i16x8_extend_low_i8x16_u(self) -> Self { - simd_extend_cast!(self, as_u8x16, from_u16x8, u16, 0) - } - - #[doc(alias = "i16x8.extend_high_i8x16_s")] - pub fn i16x8_extend_high_i8x16_s(self) -> Self { - simd_extend_cast!(self, as_i8x16, from_i16x8, i16, 8) - } - - #[doc(alias = "i16x8.extend_high_i8x16_u")] - pub fn i16x8_extend_high_i8x16_u(self) -> Self { - simd_extend_cast!(self, as_u8x16, from_u16x8, u16, 8) - } - - #[doc(alias = "i32x4.extend_low_i16x8_s")] - pub fn i32x4_extend_low_i16x8_s(self) -> Self { - simd_extend_cast!(self, as_i16x8, from_i32x4, i32, 0) - } - - #[doc(alias = "i32x4.extend_low_i16x8_u")] - pub fn i32x4_extend_low_i16x8_u(self) -> Self { - simd_extend_cast!(self, as_u16x8, from_u32x4, u32, 0) - } - - #[doc(alias = "i32x4.extend_high_i16x8_s")] - pub fn i32x4_extend_high_i16x8_s(self) -> Self { - simd_extend_cast!(self, as_i16x8, from_i32x4, i32, 4) - } - - #[doc(alias = "i32x4.extend_high_i16x8_u")] - pub fn i32x4_extend_high_i16x8_u(self) -> Self { - simd_extend_cast!(self, as_u16x8, from_u32x4, u32, 4) - } - - #[doc(alias = "i64x2.extend_low_i32x4_s")] - pub fn i64x2_extend_low_i32x4_s(self) -> Self { - simd_extend_cast!(self, as_i32x4, from_i64x2, i64, 0) - } - - #[doc(alias = "i64x2.extend_low_i32x4_u")] - pub fn i64x2_extend_low_i32x4_u(self) -> Self { - simd_extend_cast!(self, as_u32x4, from_u64x2, u64, 0) - } - - #[doc(alias = "i64x2.extend_high_i32x4_s")] - pub fn i64x2_extend_high_i32x4_s(self) -> Self { - simd_extend_cast!(self, as_i32x4, from_i64x2, i64, 2) - } - - #[doc(alias = "i64x2.extend_high_i32x4_u")] - pub fn i64x2_extend_high_i32x4_u(self) -> Self { - simd_extend_cast!(self, as_u32x4, from_u64x2, u64, 2) - } - - #[doc(alias = "i16x8.extmul_low_i8x16_s")] - pub fn i16x8_extmul_low_i8x16_s(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_i8x16, from_i16x8, i16, 0) - } - - #[doc(alias = "i16x8.extmul_low_i8x16_u")] - pub fn i16x8_extmul_low_i8x16_u(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_u8x16, from_u16x8, u16, 0) - } - - #[doc(alias = "i16x8.extmul_high_i8x16_s")] - pub fn i16x8_extmul_high_i8x16_s(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_i8x16, from_i16x8, i16, 8) - } - - #[doc(alias = "i16x8.extmul_high_i8x16_u")] - pub fn i16x8_extmul_high_i8x16_u(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_u8x16, from_u16x8, u16, 8) + impl_simd_extend! { + "i16x8.extend_low_i8x16_s" => i16x8_extend_low_i8x16_s(as_i8x16, from_i16x8, i16, 0); + "i16x8.extend_low_i8x16_u" => i16x8_extend_low_i8x16_u(as_u8x16, from_u16x8, u16, 0); + "i16x8.extend_high_i8x16_s" => i16x8_extend_high_i8x16_s(as_i8x16, from_i16x8, i16, 8); + "i16x8.extend_high_i8x16_u" => i16x8_extend_high_i8x16_u(as_u8x16, from_u16x8, u16, 8); + "i32x4.extend_low_i16x8_s" => i32x4_extend_low_i16x8_s(as_i16x8, from_i32x4, i32, 0); + "i32x4.extend_low_i16x8_u" => i32x4_extend_low_i16x8_u(as_u16x8, from_u32x4, u32, 0); + "i32x4.extend_high_i16x8_s" => i32x4_extend_high_i16x8_s(as_i16x8, from_i32x4, i32, 4); + "i32x4.extend_high_i16x8_u" => i32x4_extend_high_i16x8_u(as_u16x8, from_u32x4, u32, 4); + "i64x2.extend_low_i32x4_s" => i64x2_extend_low_i32x4_s(as_i32x4, from_i64x2, i64, 0); + "i64x2.extend_low_i32x4_u" => i64x2_extend_low_i32x4_u(as_u32x4, from_u64x2, u64, 0); + "i64x2.extend_high_i32x4_s" => i64x2_extend_high_i32x4_s(as_i32x4, from_i64x2, i64, 2); + "i64x2.extend_high_i32x4_u" => i64x2_extend_high_i32x4_u(as_u32x4, from_u64x2, u64, 2); } - #[doc(alias = "i32x4.extmul_low_i16x8_s")] - pub fn i32x4_extmul_low_i16x8_s(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_i16x8, from_i32x4, i32, 0) - } - - #[doc(alias = "i32x4.extmul_low_i16x8_u")] - pub fn i32x4_extmul_low_i16x8_u(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_u16x8, from_u32x4, u32, 0) - } - - #[doc(alias = "i32x4.extmul_high_i16x8_s")] - pub fn i32x4_extmul_high_i16x8_s(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_i16x8, from_i32x4, i32, 4) - } - - #[doc(alias = "i32x4.extmul_high_i16x8_u")] - pub fn i32x4_extmul_high_i16x8_u(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_u16x8, from_u32x4, u32, 4) - } - - #[doc(alias = "i64x2.extmul_low_i32x4_s")] - pub fn i64x2_extmul_low_i32x4_s(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_i32x4, from_i64x2, i64, 0) - } - - #[doc(alias = "i64x2.extmul_low_i32x4_u")] - pub fn i64x2_extmul_low_i32x4_u(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_u32x4, from_u64x2, u64, 0) - } - - #[doc(alias = "i64x2.extmul_high_i32x4_s")] - pub fn i64x2_extmul_high_i32x4_s(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_i32x4, from_i64x2, i64, 2) - } - - #[doc(alias = "i64x2.extmul_high_i32x4_u")] - pub fn i64x2_extmul_high_i32x4_u(self, rhs: Self) -> Self { - simd_extmul!(self, rhs, as_u32x4, from_u64x2, u64, 2) + impl_simd_binary_methods! { simd_extmul; + "i16x8.extmul_low_i8x16_s" => i16x8_extmul_low_i8x16_s(as_i8x16, from_i16x8, i16, 0); + "i16x8.extmul_low_i8x16_u" => i16x8_extmul_low_i8x16_u(as_u8x16, from_u16x8, u16, 0); + "i16x8.extmul_high_i8x16_s" => i16x8_extmul_high_i8x16_s(as_i8x16, from_i16x8, i16, 8); + "i16x8.extmul_high_i8x16_u" => i16x8_extmul_high_i8x16_u(as_u8x16, from_u16x8, u16, 8); + "i32x4.extmul_low_i16x8_s" => i32x4_extmul_low_i16x8_s(as_i16x8, from_i32x4, i32, 0); + "i32x4.extmul_low_i16x8_u" => i32x4_extmul_low_i16x8_u(as_u16x8, from_u32x4, u32, 0); + "i32x4.extmul_high_i16x8_s" => i32x4_extmul_high_i16x8_s(as_i16x8, from_i32x4, i32, 4); + "i32x4.extmul_high_i16x8_u" => i32x4_extmul_high_i16x8_u(as_u16x8, from_u32x4, u32, 4); + "i64x2.extmul_low_i32x4_s" => i64x2_extmul_low_i32x4_s(as_i32x4, from_i64x2, i64, 0); + "i64x2.extmul_low_i32x4_u" => i64x2_extmul_low_i32x4_u(as_u32x4, from_u64x2, u64, 0); + "i64x2.extmul_high_i32x4_s" => i64x2_extmul_high_i32x4_s(as_i32x4, from_i64x2, i64, 2); + "i64x2.extmul_high_i32x4_u" => i64x2_extmul_high_i32x4_u(as_u32x4, from_u64x2, u64, 2); } #[doc(alias = "i16x8.q15mulr_sat_s")] @@ -721,24 +513,11 @@ impl Value128 { })) } - #[doc(alias = "i8x16.relaxed_laneselect")] - pub fn i8x16_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { - Self::v128_bitselect(v1, v2, c) - } - - #[doc(alias = "i16x8.relaxed_laneselect")] - pub fn i16x8_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { - Self::v128_bitselect(v1, v2, c) - } - - #[doc(alias = "i32x4.relaxed_laneselect")] - pub fn i32x4_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { - Self::v128_bitselect(v1, v2, c) - } - - #[doc(alias = "i64x2.relaxed_laneselect")] - pub fn i64x2_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { - Self::v128_bitselect(v1, v2, c) + impl_simd_relaxed_laneselect! { + "i8x16.relaxed_laneselect" => i8x16_relaxed_laneselect; + "i16x8.relaxed_laneselect" => i16x8_relaxed_laneselect; + "i32x4.relaxed_laneselect" => i32x4_relaxed_laneselect; + "i64x2.relaxed_laneselect" => i64x2_relaxed_laneselect; } #[doc(alias = "i16x8.relaxed_q15mulr_s")] @@ -773,184 +552,46 @@ impl Value128 { })) } - #[doc(alias = "i8x16.eq")] - pub fn i8x16_eq(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i8x16_eq, as_i8x16, from_i8x16, ==) - } - - #[doc(alias = "i16x8.eq")] - pub fn i16x8_eq(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i16x8_eq, as_i16x8, from_i16x8, ==) - } - - #[doc(alias = "i32x4.eq")] - pub fn i32x4_eq(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i32x4_eq, as_i32x4, from_i32x4, ==) - } - - #[doc(alias = "i64x2.eq")] - pub fn i64x2_eq(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i64x2_eq, as_i64x2, from_i64x2, ==) - } - - #[doc(alias = "i8x16.ne")] - pub fn i8x16_ne(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i8x16_ne, as_i8x16, from_i8x16, !=) - } - - #[doc(alias = "i16x8.ne")] - pub fn i16x8_ne(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i16x8_ne, as_i16x8, from_i16x8, !=) - } - - #[doc(alias = "i32x4.ne")] - pub fn i32x4_ne(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i32x4_ne, as_i32x4, from_i32x4, !=) - } - - #[doc(alias = "i64x2.ne")] - pub fn i64x2_ne(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i64x2_ne, as_i64x2, from_i64x2, !=) - } - - #[doc(alias = "i8x16.lt_s")] - pub fn i8x16_lt_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i8x16_lt, as_i8x16, from_i8x16, <) - } - - #[doc(alias = "i16x8.lt_s")] - pub fn i16x8_lt_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i16x8_lt, as_i16x8, from_i16x8, <) - } - - #[doc(alias = "i32x4.lt_s")] - pub fn i32x4_lt_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i32x4_lt, as_i32x4, from_i32x4, <) - } - - #[doc(alias = "i64x2.lt_s")] - pub fn i64x2_lt_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i64x2_lt, as_i64x2, from_i64x2, <) - } - - #[doc(alias = "i8x16.lt_u")] - pub fn i8x16_lt_u(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, u8x16_lt, as_u8x16, from_i8x16, <) - } - - #[doc(alias = "i16x8.lt_u")] - pub fn i16x8_lt_u(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, u16x8_lt, as_u16x8, from_i16x8, <) - } - - #[doc(alias = "i32x4.lt_u")] - pub fn i32x4_lt_u(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, u32x4_lt, as_u32x4, from_i32x4, <) - } - - #[doc(alias = "i8x16.gt_s")] - pub fn i8x16_gt_s(self, rhs: Self) -> Self { - rhs.i8x16_lt_s(self) - } - - #[doc(alias = "i16x8.gt_s")] - pub fn i16x8_gt_s(self, rhs: Self) -> Self { - rhs.i16x8_lt_s(self) - } - - #[doc(alias = "i32x4.gt_s")] - pub fn i32x4_gt_s(self, rhs: Self) -> Self { - rhs.i32x4_lt_s(self) - } - - #[doc(alias = "i64x2.gt_s")] - pub fn i64x2_gt_s(self, rhs: Self) -> Self { - rhs.i64x2_lt_s(self) - } - - #[doc(alias = "i8x16.gt_u")] - pub fn i8x16_gt_u(self, rhs: Self) -> Self { - rhs.i8x16_lt_u(self) - } - - #[doc(alias = "i16x8.gt_u")] - pub fn i16x8_gt_u(self, rhs: Self) -> Self { - rhs.i16x8_lt_u(self) - } - - #[doc(alias = "i32x4.gt_u")] - pub fn i32x4_gt_u(self, rhs: Self) -> Self { - rhs.i32x4_lt_u(self) - } - - #[doc(alias = "i8x16.le_s")] - pub fn i8x16_le_s(self, rhs: Self) -> Self { - rhs.i8x16_ge_s(self) - } - - #[doc(alias = "i16x8.le_s")] - pub fn i16x8_le_s(self, rhs: Self) -> Self { - rhs.i16x8_ge_s(self) - } - - #[doc(alias = "i32x4.le_s")] - pub fn i32x4_le_s(self, rhs: Self) -> Self { - rhs.i32x4_ge_s(self) - } - - #[doc(alias = "i64x2.le_s")] - pub fn i64x2_le_s(self, rhs: Self) -> Self { - rhs.i64x2_ge_s(self) - } - - #[doc(alias = "i8x16.le_u")] - pub fn i8x16_le_u(self, rhs: Self) -> Self { - rhs.i8x16_ge_u(self) - } - - #[doc(alias = "i16x8.le_u")] - pub fn i16x8_le_u(self, rhs: Self) -> Self { - rhs.i16x8_ge_u(self) - } - - #[doc(alias = "i32x4.le_u")] - pub fn i32x4_le_u(self, rhs: Self) -> Self { - rhs.i32x4_ge_u(self) - } - - #[doc(alias = "i8x16.ge_s")] - pub fn i8x16_ge_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i8x16_ge, as_i8x16, from_i8x16, >=) - } - - #[doc(alias = "i16x8.ge_s")] - pub fn i16x8_ge_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i16x8_ge, as_i16x8, from_i16x8, >=) - } - - #[doc(alias = "i32x4.ge_s")] - pub fn i32x4_ge_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i32x4_ge, as_i32x4, from_i32x4, >=) - } - - #[doc(alias = "i64x2.ge_s")] - pub fn i64x2_ge_s(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, i64x2_ge, as_i64x2, from_i64x2, >=) - } - - #[doc(alias = "i8x16.ge_u")] - pub fn i8x16_ge_u(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, u8x16_ge, as_u8x16, from_i8x16, >=) - } - - #[doc(alias = "i16x8.ge_u")] - pub fn i16x8_ge_u(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, u16x8_ge, as_u16x8, from_i16x8, >=) + impl_simd_binary_methods! { simd_cmp_mask; + "i8x16.eq" => i8x16_eq(i8x16_eq, as_i8x16, from_i8x16, ==); + "i16x8.eq" => i16x8_eq(i16x8_eq, as_i16x8, from_i16x8, ==); + "i32x4.eq" => i32x4_eq(i32x4_eq, as_i32x4, from_i32x4, ==); + "i64x2.eq" => i64x2_eq(i64x2_eq, as_i64x2, from_i64x2, ==); + "i8x16.ne" => i8x16_ne(i8x16_ne, as_i8x16, from_i8x16, !=); + "i16x8.ne" => i16x8_ne(i16x8_ne, as_i16x8, from_i16x8, !=); + "i32x4.ne" => i32x4_ne(i32x4_ne, as_i32x4, from_i32x4, !=); + "i64x2.ne" => i64x2_ne(i64x2_ne, as_i64x2, from_i64x2, !=); + "i8x16.lt_s" => i8x16_lt_s(i8x16_lt, as_i8x16, from_i8x16, <); + "i16x8.lt_s" => i16x8_lt_s(i16x8_lt, as_i16x8, from_i16x8, <); + "i32x4.lt_s" => i32x4_lt_s(i32x4_lt, as_i32x4, from_i32x4, <); + "i64x2.lt_s" => i64x2_lt_s(i64x2_lt, as_i64x2, from_i64x2, <); + "i8x16.lt_u" => i8x16_lt_u(u8x16_lt, as_u8x16, from_i8x16, <); + "i16x8.lt_u" => i16x8_lt_u(u16x8_lt, as_u16x8, from_i16x8, <); + "i32x4.lt_u" => i32x4_lt_u(u32x4_lt, as_u32x4, from_i32x4, <); + "i8x16.ge_s" => i8x16_ge_s(i8x16_ge, as_i8x16, from_i8x16, >=); + "i16x8.ge_s" => i16x8_ge_s(i16x8_ge, as_i16x8, from_i16x8, >=); + "i32x4.ge_s" => i32x4_ge_s(i32x4_ge, as_i32x4, from_i32x4, >=); + "i64x2.ge_s" => i64x2_ge_s(i64x2_ge, as_i64x2, from_i64x2, >=); + "i8x16.ge_u" => i8x16_ge_u(u8x16_ge, as_u8x16, from_i8x16, >=); + "i16x8.ge_u" => i16x8_ge_u(u16x8_ge, as_u16x8, from_i16x8, >=); + "i32x4.ge_u" => i32x4_ge_u(u32x4_ge, as_u32x4, from_i32x4, >=); } - #[doc(alias = "i32x4.ge_u")] - pub fn i32x4_ge_u(self, rhs: Self) -> Self { - simd_cmp_mask!(self, rhs, u32x4_ge, as_u32x4, from_i32x4, >=) + impl_simd_reverse_comparisons! { + "i8x16.gt_s" => i8x16_gt_s(i8x16_lt_s); + "i16x8.gt_s" => i16x8_gt_s(i16x8_lt_s); + "i32x4.gt_s" => i32x4_gt_s(i32x4_lt_s); + "i64x2.gt_s" => i64x2_gt_s(i64x2_lt_s); + "i8x16.gt_u" => i8x16_gt_u(i8x16_lt_u); + "i16x8.gt_u" => i16x8_gt_u(i16x8_lt_u); + "i32x4.gt_u" => i32x4_gt_u(i32x4_lt_u); + "i8x16.le_s" => i8x16_le_s(i8x16_ge_s); + "i16x8.le_s" => i16x8_le_s(i16x8_ge_s); + "i32x4.le_s" => i32x4_le_s(i32x4_ge_s); + "i64x2.le_s" => i64x2_le_s(i64x2_ge_s); + "i8x16.le_u" => i8x16_le_u(i8x16_ge_u); + "i16x8.le_u" => i16x8_le_u(i16x8_ge_u); + "i32x4.le_u" => i32x4_le_u(i32x4_ge_u); } #[doc(alias = "i8x16.abs")] diff --git a/crates/tinywasm/src/interpreter/simd/macros.rs b/crates/tinywasm/src/interpreter/simd/macros.rs index 7831586..bc7b805 100644 --- a/crates/tinywasm/src/interpreter/simd/macros.rs +++ b/crates/tinywasm/src/interpreter/simd/macros.rs @@ -114,6 +114,61 @@ macro_rules! simd_minmax { }}; } +macro_rules! impl_simd_shifts { + ($($alias:literal => $name:ident($as_lanes:ident, $from_lanes:ident, $mask:expr, $op:ident);)*) => { + $( + #[doc(alias = $alias)] + pub fn $name(self, shift: u32) -> Self { + simd_shift!(self, shift, $as_lanes, $from_lanes, $mask, $op) + } + )* + }; +} + +macro_rules! impl_simd_binary_methods { + ($helper:ident; $($alias:literal => $name:ident($($args:tt)*);)*) => { + $( + #[doc(alias = $alias)] + pub fn $name(self, rhs: Self) -> Self { + $helper!(self, rhs, $($args)*) + } + )* + }; +} + +macro_rules! impl_simd_extend { + ($($alias:literal => $name:ident($src_as:ident, $dst_from:ident, $dst_ty:ty, $offset:expr);)*) => { + $( + #[doc(alias = $alias)] + pub fn $name(self) -> Self { + simd_extend_cast!(self, $src_as, $dst_from, $dst_ty, $offset) + } + )* + }; +} + +macro_rules! impl_simd_relaxed_laneselect { + ($($alias:literal => $name:ident;)*) => { + $( + #[doc(alias = $alias)] + pub fn $name(v1: Self, v2: Self, c: Self) -> Self { + Self::v128_bitselect(v1, v2, c) + } + )* + }; +} + +macro_rules! impl_simd_reverse_comparisons { + ($($alias:literal => $name:ident($reverse:ident);)*) => { + $( + #[doc(alias = $alias)] + pub fn $name(self, rhs: Self) -> Self { + rhs.$reverse(self) + } + )* + }; +} + #[rustfmt::skip] macro_rules! lane_read { (i8, $bytes:expr, $offset:expr) => { $bytes[$offset] as i8 }; diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index 3aebacd..1b06317 100644 --- a/crates/tinywasm/src/interpreter/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -48,46 +48,28 @@ impl<T: Copy + Default> Stack<T> { #[inline(always)] pub(crate) fn pop(&mut self) -> T { - self.data.pop().unwrap_or_else(|| { - cold_path(); - unreachable!("ValueStack underflow, this is a bug"); - }) + self.data.pop().unwrap_or_else(|| unreachable!("ValueStack underflow, this is a bug")) } #[inline(always)] pub(crate) fn last(&self) -> &T { - self.data.last().unwrap_or_else(|| { - cold_path(); - unreachable!("ValueStack underflow, this is a bug"); - }) + self.data.last().unwrap_or_else(|| unreachable!("ValueStack underflow, this is a bug")) } #[inline(always)] pub(crate) fn get(&self, index: usize) -> &T { - self.data.get(index).unwrap_or_else(|| { - cold_path(); - unreachable!("Stack index out of bounds, this is a bug"); - }) + self.data.get(index).unwrap_or_else(|| unreachable!("Stack index out of bounds, this is a bug")) } #[inline(always)] pub(crate) fn set(&mut self, index: usize, value: T) { - *self.data.get_mut(index).unwrap_or_else(|| { - cold_path(); - unreachable!("Stack index out of bounds, this is a bug"); - }) = value; + *self.data.get_mut(index).unwrap_or_else(|| unreachable!("Stack index out of bounds, this is a bug")) = value; } #[inline(always)] pub(crate) fn copy(&mut self, from: usize, to: usize) { - let val = self.data.get(from).unwrap_or_else(|| { - cold_path(); - unreachable!("Stack index out of bounds, this is a bug"); - }); - *self.data.get_mut(to).unwrap_or_else(|| { - cold_path(); - unreachable!("Stack index out of bounds, this is a bug"); - }) = *val; + let val = self.data.get(from).unwrap_or_else(|| unreachable!("Stack index out of bounds, this is a bug")); + *self.data.get_mut(to).unwrap_or_else(|| unreachable!("Stack index out of bounds, this is a bug")) = *val; } #[inline(always)] @@ -112,10 +94,7 @@ impl<T: Copy + Default> Stack<T> { #[inline(always)] pub(crate) fn truncate_to_one_tail(&mut self, n: usize) { debug_assert!(n < self.data.len()); - let Some(last) = self.data.pop() else { - cold_path(); - unreachable!("ValueStack underflow, this is a bug"); - }; + let last = self.data.pop().unwrap_or_else(|| unreachable!("ValueStack underflow, this is a bug")); self.data.truncate(n); self.data.push(last); } @@ -172,13 +151,9 @@ impl<T: Copy + Default> Stack<T> { } let len = self.data.len(); - let needed = count.checked_mul(2).unwrap_or_else(|| { - cold_path(); - unreachable!("Stack underflow, this is a bug"); - }); + let needed = count.checked_mul(2).unwrap_or_else(|| unreachable!("Stack underflow, this is a bug")); if len < needed { - cold_path(); unreachable!("Stack underflow, this is a bug"); } diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 9d06951..a48d9d7 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -145,10 +145,3 @@ pub mod types { } pub use tinywasm_types::Module; - -pub(crate) fn unlikely(b: bool) -> bool { - if b { - core::hint::cold_path(); - }; - b -} diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index 036eaf1..b439760 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -157,16 +157,11 @@ impl crate::std::io::Seek for MemoryCursor<'_> { } impl Memory { - #[inline] - pub(crate) const fn from_store_addr(store_id: usize, addr: MemAddr) -> Self { - Self(StoreItem::new(store_id, addr)) - } - /// Create a new memory in the given store. pub fn new(store: &mut Store, ty: MemoryType) -> Result<Self> { let addr = store.state.memories.len() as MemAddr; store.state.memories.push(MemoryInstance::new(ty, &store.engine.config().memory_backend)?); - Ok(Self::from_store_addr(store.id(), addr)) + Ok(Self(StoreItem::new(store.id(), addr))) } /// Creates a cursor positioned at the start of this memory. @@ -343,11 +338,6 @@ fn table_value_to_element(element_type: WasmType, value: WasmValue) -> Result<Ta } impl Table { - #[inline] - pub(crate) const fn from_store_addr(store_id: usize, addr: TableAddr) -> Self { - Self(StoreItem::new(store_id, addr)) - } - /// Create a new table in the given store. pub fn new(store: &mut Store, ty: TableType, init: WasmValue) -> Result<Self> { let init = match (ty.element_type, init) { @@ -357,7 +347,7 @@ impl Table { }; let addr = store.state.tables.len() as TableAddr; store.state.tables.push(TableInstance::new_with_init(ty, init)); - Ok(Self::from_store_addr(store.id(), addr)) + Ok(Self(StoreItem::new(store.id(), addr))) } #[inline] @@ -424,11 +414,6 @@ impl Table { } impl Global { - #[inline] - pub(crate) const fn from_store_addr(store_id: usize, addr: GlobalAddr) -> Self { - Self(StoreItem::new(store_id, addr)) - } - /// Create a new global in the given store. pub fn new(store: &mut Store, ty: GlobalType, value: WasmValue) -> Result<Self> { if WasmType::from(value) != ty.ty { @@ -437,7 +422,7 @@ impl Global { } let addr = store.state.globals.len() as GlobalAddr; store.state.globals.push(GlobalInstance::new(ty, value.into())); - Ok(Self::from_store_addr(store.id(), addr)) + Ok(Self(StoreItem::new(store.id(), addr))) } #[inline] diff --git a/crates/tinywasm/src/std.rs b/crates/tinywasm/src/std.rs index 90368e1..76da67a 100644 --- a/crates/tinywasm/src/std.rs +++ b/crates/tinywasm/src/std.rs @@ -5,8 +5,3 @@ pub(crate) use core::*; extern crate std; #[cfg(feature = "std")] pub(crate) use std::*; - -pub(crate) mod error { - #[cfg(feature = "std")] - extern crate std; -} diff --git a/crates/tinywasm/src/store/data.rs b/crates/tinywasm/src/store/data.rs index fe6c4e7..f9d5f5d 100644 --- a/crates/tinywasm/src/store/data.rs +++ b/crates/tinywasm/src/store/data.rs @@ -9,11 +9,7 @@ pub(crate) struct DataInstance { } impl DataInstance { - pub(crate) fn new(data: Option<Vec<u8>>) -> Self { - Self { data } - } - pub(crate) fn drop(&mut self) { - self.data.is_some().then(|| self.data.take()); + self.data.take(); } } diff --git a/crates/tinywasm/src/store/element.rs b/crates/tinywasm/src/store/element.rs index c643f80..940a6de 100644 --- a/crates/tinywasm/src/store/element.rs +++ b/crates/tinywasm/src/store/element.rs @@ -12,11 +12,7 @@ pub(crate) struct ElementInstance { } impl ElementInstance { - pub(crate) fn new(kind: ElementKind, items: Option<Vec<TableElement>>) -> Self { - Self { kind, items } - } - pub(crate) fn drop(&mut self) { - self.items.is_some().then(|| self.items.take()); + self.items.take(); } } diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs index e499f61..5f5a90e 100644 --- a/crates/tinywasm/src/store/function.rs +++ b/crates/tinywasm/src/store/function.rs @@ -26,12 +26,6 @@ impl FunctionInstance { } } -impl FunctionInstance { - pub(crate) fn new_wasm(func: Arc<WasmFunction>, owner: ModuleInstanceAddr) -> Self { - Self::Wasm(WasmFunctionInstance { func, owner }) - } -} - #[derive(Clone)] #[cfg_attr(feature = "debug", derive(Debug))] pub(crate) struct WasmFunctionInstance { diff --git a/crates/tinywasm/src/store/memory/vec.rs b/crates/tinywasm/src/store/memory/vec.rs index d84890b..5f6288c 100644 --- a/crates/tinywasm/src/store/memory/vec.rs +++ b/crates/tinywasm/src/store/memory/vec.rs @@ -31,6 +31,13 @@ impl VecMemory { data.resize(len, 0); Ok(Self { data }) } + + #[inline(always)] + fn read_fixed<const N: usize>(&self, addr: usize) -> [u8; N] { + self.data[addr..addr + N] + .try_into() + .unwrap_or_else(|_| unreachable!("fixed-width memory read has incorrect length")) + } } impl LinearMemory for VecMemory { @@ -120,49 +127,25 @@ impl LinearMemory for VecMemory { #[inline(always)] fn read_16(&self, base: u64, offset: u64) -> core::result::Result<[u8; 2], crate::Trap> { let addr = checked_effective_addr::<2>(self.data.len(), base, offset)?; - match self.data[addr..addr + 2].try_into() { - Ok(bytes) => Ok(bytes), - Err(_) => { - cold_path(); - unreachable!(); - } - } + Ok(self.read_fixed::<2>(addr)) } #[inline(always)] fn read_32(&self, base: u64, offset: u64) -> core::result::Result<[u8; 4], crate::Trap> { let addr = checked_effective_addr::<4>(self.data.len(), base, offset)?; - match self.data[addr..addr + 4].try_into() { - Ok(bytes) => Ok(bytes), - Err(_) => { - cold_path(); - unreachable!(); - } - } + Ok(self.read_fixed::<4>(addr)) } #[inline(always)] fn read_64(&self, base: u64, offset: u64) -> core::result::Result<[u8; 8], crate::Trap> { let addr = checked_effective_addr::<8>(self.data.len(), base, offset)?; - match self.data[addr..addr + 8].try_into() { - Ok(bytes) => Ok(bytes), - Err(_) => { - cold_path(); - unreachable!(); - } - } + Ok(self.read_fixed::<8>(addr)) } #[inline(always)] fn read_128(&self, base: u64, offset: u64) -> core::result::Result<[u8; 16], crate::Trap> { let addr = checked_effective_addr::<16>(self.data.len(), base, offset)?; - match self.data[addr..addr + 16].try_into() { - Ok(bytes) => Ok(bytes), - Err(_) => { - cold_path(); - unreachable!(); - } - } + Ok(self.read_fixed::<16>(addr)) } #[inline(always)] diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 1599d81..d02631b 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -85,13 +85,7 @@ impl Store { #[inline] pub(crate) fn get_module_instance_internal(&self, addr: ModuleInstanceAddr) -> ModuleInstance { - match self.module_instances.get(addr as usize) { - Some(instance) => instance.clone(), - None => { - cold_path(); - unreachable!("module instance {addr} not found. This should be unreachable") - } - } + self.get_module_instance(addr).unwrap_or_else(|| unreachable!("invalid module instance: {addr}")) } pub(crate) fn enter_execution(&mut self) -> Result<()> { @@ -137,81 +131,57 @@ pub(crate) struct State { } impl State { + fn get<'a, T>(items: &'a [T], addr: Addr, kind: &str) -> &'a T { + items.get(addr as usize).unwrap_or_else(|| unreachable!("invalid {kind} address: {addr}")) + } + + fn get_mut<'a, T>(items: &'a mut [T], addr: Addr, kind: &str) -> &'a mut T { + items.get_mut(addr as usize).unwrap_or_else(|| unreachable!("invalid {kind} address: {addr}")) + } + + fn get_disjoint_mut<'a, T>(items: &'a mut [T], addr: Addr, addr2: Addr, kind: &str) -> (&'a mut T, &'a mut T) { + let [item_a, item_b] = items + .get_disjoint_mut([addr as usize, addr2 as usize]) + .unwrap_or_else(|_| unreachable!("invalid {kind} addresses: {addr}, {addr2}")); + (item_a, item_b) + } + /// Get the function at the actual index in the store pub(crate) fn get_func(&self, addr: FuncAddr) -> &FunctionInstance { - match self.funcs.get(addr as usize) { - Some(func) => func, - None => { - cold_path(); - unreachable!("function {addr} not found. This should be unreachable") - } - } + Self::get(&self.funcs, addr, "function") } /// Get a wasm function at the actual index in the store, panicking if it's a host function (which should be guaranteed by the validator) pub(crate) fn get_wasm_func(&self, addr: FuncAddr) -> &WasmFunctionInstance { match self.funcs.get(addr as usize) { Some(FunctionInstance::Wasm(wasm_func)) => wasm_func, - _ => { - cold_path(); - unreachable!("function {addr} not found. This should be unreachable") - } + _ => unreachable!("invalid wasm function address: {addr}"), } } /// Get the memory at the actual index in the store pub(crate) fn get_mem(&self, addr: MemAddr) -> &MemoryInstance { - match self.memories.get(addr as usize) { - Some(mem) => mem, - None => { - cold_path(); - unreachable!("memory {addr} not found. This should be unreachable") - } - } + Self::get(&self.memories, addr, "memory") } /// Get the memory at the actual index in the store pub(crate) fn get_mem_mut(&mut self, addr: MemAddr) -> &mut MemoryInstance { - match self.memories.get_mut(addr as usize) { - Some(mem) => mem, - None => { - cold_path(); - unreachable!("memory {addr} not found. This should be unreachable") - } - } + Self::get_mut(&mut self.memories, addr, "memory") } /// Get the memory at the actual index in the store pub(crate) fn get_mems_mut(&mut self, addr: MemAddr, addr2: MemAddr) -> (&mut MemoryInstance, &mut MemoryInstance) { - match self.memories.get_disjoint_mut([addr as usize, addr2 as usize]) { - Ok([mem_a, mem_b]) => (mem_a, mem_b), - Err(_) => { - cold_path(); - unreachable!("memory {addr} or {addr2} not found. This should be unreachable") - } - } + Self::get_disjoint_mut(&mut self.memories, addr, addr2, "memory") } /// Get the table at the actual index in the store pub(crate) fn get_table(&self, addr: TableAddr) -> &TableInstance { - match self.tables.get(addr as usize) { - Some(table) => table, - None => { - cold_path(); - unreachable!("table {addr} not found. This should be unreachable") - } - } + Self::get(&self.tables, addr, "table") } /// Get the table at the actual index in the store pub(crate) fn get_table_mut(&mut self, addr: TableAddr) -> &mut TableInstance { - match self.tables.get_mut(addr as usize) { - Some(table) => table, - None => { - cold_path(); - unreachable!("table {addr} not found. This should be unreachable") - } - } + Self::get_mut(&mut self.tables, addr, "table") } /// Get two mutable tables at the actual index in the store @@ -220,79 +190,37 @@ impl State { addr: TableAddr, addr2: TableAddr, ) -> (&mut TableInstance, &mut TableInstance) { - match self.tables.get_disjoint_mut([addr as usize, addr2 as usize]) { - Ok([table_a, table_b]) => (table_a, table_b), - Err(_) => { - cold_path(); - unreachable!("table {addr} or {addr2} not found. This should be unreachable") - } - } + Self::get_disjoint_mut(&mut self.tables, addr, addr2, "table") } /// Get the data at the actual index in the store pub(crate) fn get_data_mut(&mut self, addr: DataAddr) -> &mut DataInstance { - match self.data.get_mut(addr as usize) { - Some(data) => data, - None => { - cold_path(); - unreachable!("data {addr} not found. This should be unreachable") - } - } + Self::get_mut(&mut self.data, addr, "data") } /// Get the element at the actual index in the store pub(crate) fn get_elem_mut(&mut self, addr: ElemAddr) -> &mut ElementInstance { - match self.elements.get_mut(addr as usize) { - Some(elem) => elem, - None => { - cold_path(); - unreachable!("element {addr} not found. This should be unreachable") - } - } + Self::get_mut(&mut self.elements, addr, "element") } /// Get the global at the actual index in the store pub(crate) fn get_global(&self, addr: GlobalAddr) -> &GlobalInstance { - match self.globals.get(addr as usize) { - Some(global) => global, - None => { - cold_path(); - unreachable!("global {addr} not found. This should be unreachable") - } - } + Self::get(&self.globals, addr, "global") } /// Get the global at the actual index in the store pub(crate) fn get_global_mut(&mut self, addr: GlobalAddr) -> &mut GlobalInstance { - match self.globals.get_mut(addr as usize) { - Some(global) => global, - None => { - cold_path(); - unreachable!("global {addr} not found. This should be unreachable") - } - } + Self::get_mut(&mut self.globals, addr, "global") } /// Get the global at the actual index in the store pub(crate) fn get_global_val(&self, addr: GlobalAddr) -> TinyWasmValue { - match self.globals.get(addr as usize) { - Some(global) => global.value.get(), - None => { - cold_path(); - unreachable!("global {addr} not found. This should be unreachable") - } - } + self.get_global(addr).value.get() } /// Set the global at the actual index in the store pub(crate) fn set_global_val(&mut self, addr: GlobalAddr, value: TinyWasmValue) { - match self.globals.get_mut(addr as usize) { - Some(global) => global.value.set(value), - None => { - cold_path(); - unreachable!("global {addr} not found. This should be unreachable") - } - } + self.get_global_mut(addr).value.set(value); } } @@ -333,7 +261,9 @@ impl Store { idx: ModuleInstanceAddr, ) -> impl ExactSizeIterator<Item = FuncAddr> { let start = self.state.funcs.len() as FuncAddr; - self.state.funcs.extend(funcs.iter().map(|func| FunctionInstance::new_wasm(func.clone(), idx))); + self.state.funcs.extend( + funcs.iter().map(|func| FunctionInstance::Wasm(WasmFunctionInstance { func: func.clone(), owner: idx })), + ); start..start + funcs.len() as FuncAddr } @@ -345,23 +275,15 @@ impl Store { } /// Add memories to the store, returning their addresses in the store - pub(crate) fn init_memories(&mut self, memories: &[MemoryType]) -> Result<impl ExactSizeIterator<Item = MemAddr>> { - let start = self.state.memories.len() as MemAddr; - self.state.memories.reserve_exact(memories.len()); - for &mem in memories { - self.state.memories.push(MemoryInstance::new(mem, &self.engine.config().memory_backend)?); - } - Ok(start..start + memories.len() as MemAddr) - } - - pub(crate) fn init_lazy_memories( + pub(crate) fn init_memories( &mut self, memories: &[MemoryType], + init: fn(MemoryType, &MemoryBackend) -> Result<MemoryInstance>, ) -> Result<impl ExactSizeIterator<Item = MemAddr>> { let start = self.state.memories.len() as MemAddr; self.state.memories.reserve_exact(memories.len()); for &mem in memories { - self.state.memories.push(MemoryInstance::new_lazy(mem, &self.engine.config().memory_backend)?); + self.state.memories.push(init(mem, &self.engine.config().memory_backend)?); } Ok(start..start + memories.len() as MemAddr) } @@ -461,7 +383,7 @@ impl Store { } }; - self.state.elements.push(ElementInstance::new(element.kind.clone(), items)); + self.state.elements.push(ElementInstance { kind: element.kind.clone(), items }); elem_addrs.push((i + elem_count) as Addr); } @@ -508,7 +430,7 @@ impl Store { tinywasm_types::DataKind::Passive => Some(data.data.to_vec()), }; - self.state.data.push(DataInstance::new(data_val)); + self.state.data.push(DataInstance { data: data_val }); data_addrs.push((i + data_count) as Addr); } @@ -641,10 +563,7 @@ impl Store { I64Add => lhs.wrapping_add(rhs), I64Sub => lhs.wrapping_sub(rhs), I64Mul => lhs.wrapping_mul(rhs), - _ => { - cold_path(); - unreachable!("invalid const instruction in i64 op") - } + _ => unreachable!("invalid const instruction in i64 op"), }; stack.push(TinyWasmValue::Value64(out as u64)); } diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index 50b0d88..d3dbee7 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -1,5 +1,6 @@ use crate::{Result, Trap}; use alloc::{vec, vec::Vec}; +use core::ops::Range; use tinywasm_types::*; const MAX_TABLE_SIZE: u32 = 10_000_000; @@ -28,6 +29,14 @@ impl TableInstance { crate::Trap::TableOutOfBounds { offset: addr, len, max: self.elements.len() } } + fn checked_range(&self, addr: usize, len: usize) -> Result<Range<usize>, Trap> { + let end = addr.checked_add(len).ok_or_else(|| self.trap_oob(addr, len))?; + if end > self.elements.len() { + return Err(self.trap_oob(addr, len)); + } + Ok(addr..end) + } + pub(crate) fn get_wasm_val(&self, addr: TableAddr) -> Result<WasmValue, Trap> { let val = self.get(addr)?.addr(); @@ -40,12 +49,8 @@ impl TableInstance { pub(crate) fn fill(&mut self, func_addrs: &[u32], addr: usize, len: usize, val: TableElement) -> Result<(), Trap> { let val = val.map(|addr| self.resolve_func_ref(func_addrs, addr)); - let end = addr.checked_add(len).ok_or_else(|| self.trap_oob(addr, len))?; - if end > self.elements.len() { - return Err(self.trap_oob(addr, len)); - } - - self.elements[addr..end].fill(val); + let range = self.checked_range(addr, len)?; + self.elements[range].fill(val); Ok(()) } @@ -58,52 +63,25 @@ impl TableInstance { } pub(crate) fn copy_from_slice(&mut self, dst: usize, src: &[TableElement]) -> Result<(), Trap> { - let end = dst.checked_add(src.len()).ok_or_else(|| self.trap_oob(dst, src.len()))?; - - if end > self.elements.len() { - return Err(self.trap_oob(dst, src.len())); - } - - self.elements[dst..end].copy_from_slice(src); + let range = self.checked_range(dst, src.len())?; + self.elements[range].copy_from_slice(src); Ok(()) } pub(crate) fn load(&self, addr: usize, len: usize) -> Result<&[TableElement], Trap> { - let Some(end) = addr.checked_add(len) else { - return Err(self.trap_oob(addr, len)); - }; - - if end > self.elements.len() || end < addr { - return Err(self.trap_oob(addr, len)); - } - - Ok(&self.elements[addr..end]) + Ok(&self.elements[self.checked_range(addr, len)?]) } pub(crate) fn copy_within(&mut self, dst: usize, src: usize, len: usize) -> Result<(), Trap> { - // Calculate the end of the source slice - let src_end = src.checked_add(len).ok_or_else(|| self.trap_oob(src, len))?; - if src_end > self.elements.len() { - return Err(self.trap_oob(src, len)); - } - - // Calculate the end of the destination slice - let dst_end = dst.checked_add(len).ok_or_else(|| self.trap_oob(dst, len))?; - if dst_end > self.elements.len() { - return Err(self.trap_oob(dst, len)); - } - - // Perform the copy - self.elements.copy_within(src..src_end, dst); + let src = self.checked_range(src, len)?; + self.checked_range(dst, len)?; + self.elements.copy_within(src, dst); Ok(()) } pub(crate) fn set(&mut self, table_idx: TableAddr, value: TableElement) -> Result<(), Trap> { - if table_idx as usize >= self.elements.len() { - return Err(self.trap_oob(table_idx as usize, 1)); - } - - self.elements[table_idx as usize] = value; + let range = self.checked_range(table_idx as usize, 1)?; + self.elements[range.start] = value; Ok(()) } @@ -138,16 +116,8 @@ impl TableInstance { pub(crate) fn init(&mut self, offset: i64, init: &[TableElement]) -> Result<(), Trap> { let offset = offset as usize; - let end = offset.checked_add(init.len()).ok_or(crate::Trap::TableOutOfBounds { - offset, - len: init.len(), - max: self.elements.len(), - })?; - - if end > self.elements.len() || end < offset { - return Err(crate::Trap::TableOutOfBounds { offset, len: init.len(), max: self.elements.len() }); - } - self.elements[offset..end].copy_from_slice(init); + let range = self.checked_range(offset, init.len())?; + self.elements[range].copy_from_slice(init); Ok(()) } } diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index 92bb87a..5a6f94b 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -42,9 +42,6 @@ impl Display for TwasmError { } } -#[cfg(feature = "std")] -extern crate std; - impl core::error::Error for TwasmError {} impl Module { diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index e576027..e918e46 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -10,7 +10,6 @@ extern crate alloc; use alloc::{boxed::Box, sync::Arc}; -use core::hint::cold_path; use core::ops::{Deref, Range}; // Memory defaults @@ -21,26 +20,11 @@ const fn max_page_count(page_size: u64) -> u64 { MAX_MEMORY_SIZE / page_size } -// log for logging (optional). -#[cfg(feature = "log")] -#[allow(clippy::single_component_path_imports, unused_imports)] -use log; - -// noop fallback if logging is disabled. -#[cfg(not(feature = "log"))] -#[allow(unused_imports, unused_macros)] -pub(crate) mod log { - macro_rules! debug ( ($($tt:tt)*) => {{}} ); - macro_rules! info ( ($($tt:tt)*) => {{}} ); - macro_rules! error ( ($($tt:tt)*) => {{}} ); - pub(crate) use debug; - pub(crate) use error; - pub(crate) use info; -} - mod instructions; +mod reference; mod value; pub use instructions::*; +pub use reference::*; pub use value::*; #[cfg(feature = "archive")] @@ -48,7 +32,7 @@ pub mod archive; #[cfg(not(feature = "archive"))] pub mod archive { - #[derive(Debug)] + #[derive(Debug, PartialEq, Eq)] pub enum TwasmError {} impl core::fmt::Display for TwasmError { fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -181,56 +165,24 @@ impl Module { .count() } - fn imported_func_type(module: &ModuleInner, function_index: usize) -> Option<&FuncType> { - let mut seen = 0usize; - for import in module.imports.iter() { - if let ImportKind::Function(type_idx) = import.kind { - if seen == function_index { - return module.func_types.get(type_idx as usize).map(|ty| &**ty); - } - seen += 1; - } - } - None - } + fn imported_type(module: &ModuleInner, kind: ExternalKind, index: usize) -> Option<ExportType<'_>> { + let mut imports = module.imports.iter().filter(|import| { + matches!( + (kind, &import.kind), + (ExternalKind::Func, ImportKind::Function(_)) + | (ExternalKind::Table, ImportKind::Table(_)) + | (ExternalKind::Memory, ImportKind::Memory(_)) + | (ExternalKind::Global, ImportKind::Global(_)) + ) + }); + let import = imports.nth(index)?; - fn imported_table_type(module: &ModuleInner, table_index: usize) -> Option<&TableType> { - let mut seen = 0usize; - for import in module.imports.iter() { - if let ImportKind::Table(table_ty) = &import.kind { - if seen == table_index { - return Some(table_ty); - } - seen += 1; - } - } - None - } - - fn imported_memory_type(module: &ModuleInner, memory_index: usize) -> Option<&MemoryType> { - let mut seen = 0usize; - for import in module.imports.iter() { - if let ImportKind::Memory(memory_ty) = &import.kind { - if seen == memory_index { - return Some(memory_ty); - } - seen += 1; - } - } - None - } - - fn imported_global_type(module: &Module, global_index: usize) -> Option<&GlobalType> { - let mut seen = 0usize; - for import in module.imports.iter() { - if let ImportKind::Global(global_ty) = &import.kind { - if seen == global_index { - return Some(global_ty); - } - seen += 1; - } + match &import.kind { + ImportKind::Function(type_idx) => Some(ExportType::Func(module.func_types.get(*type_idx as usize)?)), + ImportKind::Table(table_ty) => Some(ExportType::Table(table_ty)), + ImportKind::Memory(memory_ty) => Some(ExportType::Memory(memory_ty)), + ImportKind::Global(global_ty) => Some(ExportType::Global(global_ty)), } - None } self.0.exports.iter().filter_map(move |export| { @@ -239,37 +191,33 @@ impl Module { ExternalKind::Func => { let imported_funcs = imported_count(&self.0, ExternalKind::Func); if idx < imported_funcs { - ExportType::Func(imported_func_type(&self.0, idx)?) + imported_type(&self.0, ExternalKind::Func, idx)? } else { - let local_idx = idx - imported_funcs; - ExportType::Func(&self.0.funcs.get(local_idx)?.ty) + ExportType::Func(&self.0.funcs.get(idx - imported_funcs)?.ty) } } ExternalKind::Table => { let imported_tables = imported_count(&self.0, ExternalKind::Table); if idx < imported_tables { - ExportType::Table(imported_table_type(&self.0, idx)?) + imported_type(&self.0, ExternalKind::Table, idx)? } else { - let local_idx = idx - imported_tables; - ExportType::Table(self.0.table_types.get(local_idx)?) + ExportType::Table(self.0.table_types.get(idx - imported_tables)?) } } ExternalKind::Memory => { let imported_memories = imported_count(&self.0, ExternalKind::Memory); if idx < imported_memories { - ExportType::Memory(imported_memory_type(&self.0, idx)?) + imported_type(&self.0, ExternalKind::Memory, idx)? } else { - let local_idx = idx - imported_memories; - ExportType::Memory(self.0.memory_types.get(local_idx)?) + ExportType::Memory(self.0.memory_types.get(idx - imported_memories)?) } } ExternalKind::Global => { let imported_globals = imported_count(&self.0, ExternalKind::Global); if idx < imported_globals { - ExportType::Global(imported_global_type(self, idx)?) + imported_type(&self.0, ExternalKind::Global, idx)? } else { - let local_idx = idx - imported_globals; - ExportType::Global(&self.0.globals.get(local_idx)?.ty) + ExportType::Global(&self.0.globals.get(idx - imported_globals)?.ty) } } }; @@ -422,9 +370,8 @@ pub struct FuncType { impl FuncType { /// Create a new function type. pub fn new(params: &[WasmType], results: &[WasmType]) -> Self { - let param_count = params.len() as u16; let data: Box<[WasmType]> = params.iter().cloned().chain(results.iter().cloned()).collect(); - Self { data, param_count } + Self { data, param_count: params.len() as u16 } } /// Get the parameter types of this function type. @@ -494,11 +441,7 @@ impl WasmFunctionData { /// Panics if `idx` is out of bounds. #[inline(always)] pub fn v128_const(&self, idx: ConstIdx) -> [u8; 16] { - let Some(val) = self.v128_constants.get(idx as usize) else { - cold_path(); - unreachable!("invalid v128 constant index"); - }; - *val + *self.v128_constants.get(idx as usize).unwrap_or_else(|| unreachable!("invalid v128 constant index: {idx}")) } } diff --git a/crates/types/src/reference.rs b/crates/types/src/reference.rs new file mode 100644 index 0000000..7970dc8 --- /dev/null +++ b/crates/types/src/reference.rs @@ -0,0 +1,112 @@ +use crate::{ExternAddr, FuncAddr}; + +const NULL_REF: u32 = u32::MAX; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ExternRef(u32); + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct FuncRef(u32); + +#[cfg(feature = "debug")] +impl core::fmt::Debug for ExternRef { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self.addr() { + Some(addr) => write!(f, "extern({addr:?})"), + None => write!(f, "extern(null)"), + } + } +} + +#[cfg(feature = "debug")] +impl core::fmt::Debug for FuncRef { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self.addr() { + Some(addr) => write!(f, "func({addr:?})"), + None => write!(f, "func(null)"), + } + } +} + +impl FuncRef { + #[inline] + /// Create a new [`FuncRef`] from a [`FuncAddr`]. + pub const fn new(addr: Option<FuncAddr>) -> Self { + match addr { + Some(addr) => Self(addr), + None => Self::null(), + } + } + + #[inline] + /// Create a null [`FuncRef`]. + pub const fn null() -> Self { + Self(NULL_REF) + } + + #[inline] + /// Check if the [`FuncRef`] is null. + pub const fn is_null(&self) -> bool { + self.0 == NULL_REF + } + + #[inline] + /// Get the [`FuncAddr`] from the [`FuncRef`]. + pub const fn addr(&self) -> Option<FuncAddr> { + if self.is_null() { None } else { Some(self.0) } + } + + #[inline] + #[doc(hidden)] + pub const fn from_raw(raw: u32) -> Self { + Self(raw) + } + + #[inline] + #[doc(hidden)] + pub const fn raw(&self) -> u32 { + self.0 + } +} + +impl ExternRef { + #[inline] + /// Create a new [`ExternRef`] from an [`ExternAddr`]. + /// Should only be used by the runtime. + pub const fn new(addr: Option<ExternAddr>) -> Self { + match addr { + Some(addr) => Self(addr), + None => Self::null(), + } + } + + /// Create a null [`ExternRef`]. + #[inline] + pub const fn null() -> Self { + Self(NULL_REF) + } + + /// Check if the [`ExternRef`] is null. + #[inline] + pub const fn is_null(&self) -> bool { + self.0 == NULL_REF + } + + /// Get the [`ExternAddr`] from the [`ExternRef`]. + #[inline] + pub const fn addr(&self) -> Option<ExternAddr> { + if self.is_null() { None } else { Some(self.0) } + } + + #[inline] + #[doc(hidden)] + pub const fn from_raw(raw: u32) -> Self { + Self(raw) + } + + #[inline] + #[doc(hidden)] + pub const fn raw(&self) -> u32 { + self.0 + } +} diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index d01214f..782cdb8 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -1,6 +1,6 @@ use core::fmt::Debug; -use crate::{ConstInstruction, ExternAddr, FuncAddr}; +use crate::{ConstInstruction, ExternRef, FuncRef}; /// A WebAssembly value. /// @@ -42,117 +42,6 @@ impl Debug for WasmValue { } } -const NULL_REF: u32 = u32::MAX; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct ExternRef(u32); - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FuncRef(u32); - -#[cfg(feature = "debug")] -impl Debug for ExternRef { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self.addr() { - Some(addr) => write!(f, "extern({addr:?})"), - None => write!(f, "extern(null)"), - } - } -} - -#[cfg(feature = "debug")] -impl Debug for FuncRef { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self.addr() { - Some(addr) => write!(f, "func({addr:?})"), - None => write!(f, "func(null)"), - } - } -} - -impl FuncRef { - #[inline] - /// Create a new [`FuncRef`] from a [`FuncAddr`]. - pub const fn new(addr: Option<FuncAddr>) -> Self { - match addr { - Some(addr) => Self(addr), - None => Self::null(), - } - } - - #[inline] - /// Create a null [`FuncRef`]. - pub const fn null() -> Self { - Self(NULL_REF) - } - - #[inline] - /// Check if the [`FuncRef`] is null. - pub const fn is_null(&self) -> bool { - self.0 == NULL_REF - } - - #[inline] - /// Get the [`FuncAddr`] from the [`FuncRef`]. - pub const fn addr(&self) -> Option<FuncAddr> { - if self.is_null() { None } else { Some(self.0) } - } - - #[inline] - #[doc(hidden)] - pub const fn from_raw(raw: u32) -> Self { - Self(raw) - } - - #[inline] - #[doc(hidden)] - pub const fn raw(&self) -> u32 { - self.0 - } -} - -impl ExternRef { - #[inline] - /// Create a new [`ExternRef`] from an [`ExternAddr`]. - /// Should only be used by the runtime. - pub const fn new(addr: Option<ExternAddr>) -> Self { - match addr { - Some(addr) => Self(addr), - None => Self::null(), - } - } - - /// Create a null [`ExternRef`]. - #[inline] - pub const fn null() -> Self { - Self(NULL_REF) - } - - /// Check if the [`ExternRef`] is null. - #[inline] - pub const fn is_null(&self) -> bool { - self.0 == NULL_REF - } - - /// Get the [`ExternAddr`] from the [`ExternRef`]. - #[inline] - pub const fn addr(&self) -> Option<ExternAddr> { - if self.is_null() { None } else { Some(self.0) } - } - - #[inline] - #[doc(hidden)] - pub const fn from_raw(raw: u32) -> Self { - Self(raw) - } - - #[inline] - #[doc(hidden)] - pub const fn raw(&self) -> u32 { - self.0 - } -} - impl WasmValue { #[inline] /// Get the matching [`ConstInstruction`] for this value. @@ -191,20 +80,8 @@ impl WasmValue { (Self::V128(a), Self::V128(b)) => a == b || Self::v128_nan_eq(*a, *b), (Self::RefExtern(addr), Self::RefExtern(addr2)) => addr == addr2, (Self::RefFunc(addr), Self::RefFunc(addr2)) => addr == addr2, - (Self::F32(a), Self::F32(b)) => { - if a.is_nan() && b.is_nan() { - true - } else { - a.to_bits() == b.to_bits() - } - } - (Self::F64(a), Self::F64(b)) => { - if a.is_nan() && b.is_nan() { - true - } else { - a.to_bits() == b.to_bits() - } - } + (Self::F32(a), Self::F32(b)) => a.is_nan() && b.is_nan() || a.to_bits() == b.to_bits(), + (Self::F64(a), Self::F64(b)) => a.is_nan() && b.is_nan() || a.to_bits() == b.to_bits(), _ => false, } } @@ -256,62 +133,6 @@ impl WasmValue { } }) && a_f64x2.iter().any(|x| x.is_nan()) } - - /// Return the `i32` from a `WasmValue`, if it is an `I32`. - pub const fn as_i32(&self) -> Option<i32> { - match self { - Self::I32(i) => Some(*i), - _ => None, - } - } - - /// Return the `i64` from a `WasmValue`, if it is an `I64`. - pub const fn as_i64(&self) -> Option<i64> { - match self { - Self::I64(i) => Some(*i), - _ => None, - } - } - - /// Return the `f32` from a `WasmValue`, if it is a `F32`. - pub const fn as_f32(&self) -> Option<f32> { - match self { - Self::F32(i) => Some(*i), - _ => None, - } - } - - /// Return the `f64` from a `WasmValue`, if it is a `F64`. - pub const fn as_f64(&self) -> Option<f64> { - match self { - Self::F64(i) => Some(*i), - _ => None, - } - } - - /// Return the raw little-endian bytes from a `WasmValue`, if it is a `V128`. - pub const fn as_v128(&self) -> Option<[u8; 16]> { - match self { - Self::V128(i) => Some(*i), - _ => None, - } - } - - /// Return the [[`ExternRef`]] from a `WasmValue`, if it is one - pub const fn as_ref_extern(&self) -> Option<ExternRef> { - match self { - Self::RefExtern(ref_extern) => Some(*ref_extern), - _ => None, - } - } - - /// Return the [`FuncRef`] from a `WasmValue`, if it is one - pub const fn as_ref_func(&self) -> Option<FuncRef> { - match self { - Self::RefFunc(ref_func) => Some(*ref_func), - _ => None, - } - } } impl From<&WasmValue> for WasmType { @@ -369,7 +190,19 @@ impl WasmType { } macro_rules! impl_conversion_for_wasmvalue { - ($($t:ty => $variant:ident),*) => { + ($($t:ty => $variant:ident, $accessor:ident, $doc:literal);* $(;)?) => { + impl WasmValue { + $( + #[doc = $doc] + pub const fn $accessor(&self) -> Option<$t> { + match self { + Self::$variant(value) => Some(*value), + _ => None, + } + } + )* + } + $( impl From<$t> for WasmValue { #[inline] @@ -390,4 +223,12 @@ macro_rules! impl_conversion_for_wasmvalue { } } -impl_conversion_for_wasmvalue! { i32 => I32, i64 => I64, f32 => F32, f64 => F64, [u8; 16] => V128, ExternRef => RefExtern, FuncRef => RefFunc } +impl_conversion_for_wasmvalue! { + i32 => I32, as_i32, "Return the `i32` from a `WasmValue`, if it is an `I32`."; + i64 => I64, as_i64, "Return the `i64` from a `WasmValue`, if it is an `I64`."; + f32 => F32, as_f32, "Return the `f32` from a `WasmValue`, if it is a `F32`."; + f64 => F64, as_f64, "Return the `f64` from a `WasmValue`, if it is a `F64`."; + [u8; 16] => V128, as_v128, "Return the raw little-endian bytes from a `WasmValue`, if it is a `V128`."; + ExternRef => RefExtern, as_ref_extern, "Return the [`ExternRef`] from a `WasmValue`, if it is one"; + FuncRef => RefFunc, as_ref_func, "Return the [`FuncRef`] from a `WasmValue`, if it is one"; +} |
