summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorHenry <mail@henrygressmann.de>2026-07-14 20:48:18 +0200
committerHenry <mail@henrygressmann.de>2026-07-14 20:48:18 +0200
commitdc366165b8cd0f8c43cfe7017cb37f0a16887d6d (patch)
treea0e9eeba6211fe9fddde96547a6338aca79d7c3f /crates
parentfe43e4816efbf622e8cdc106a552dfb9aadfb80e (diff)
chore: simplify runtime, parser, types, and SIMD internals
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates')
-rw-r--r--crates/parser/src/conversion.rs45
-rw-r--r--crates/parser/src/lib.rs19
-rw-r--r--crates/parser/src/macros.rs65
-rw-r--r--crates/parser/src/optimize.rs210
-rw-r--r--crates/parser/src/parallel.rs96
-rw-r--r--crates/parser/src/visit.rs31
-rw-r--r--crates/tinywasm/src/func.rs88
-rw-r--r--crates/tinywasm/src/imports.rs145
-rw-r--r--crates/tinywasm/src/instance.rs109
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs149
-rw-r--r--crates/tinywasm/src/interpreter/num_helpers.rs52
-rw-r--r--crates/tinywasm/src/interpreter/simd/instructions.rs563
-rw-r--r--crates/tinywasm/src/interpreter/simd/macros.rs55
-rw-r--r--crates/tinywasm/src/interpreter/stack/value_stack.rs41
-rw-r--r--crates/tinywasm/src/lib.rs7
-rw-r--r--crates/tinywasm/src/reference.rs21
-rw-r--r--crates/tinywasm/src/std.rs5
-rw-r--r--crates/tinywasm/src/store/data.rs6
-rw-r--r--crates/tinywasm/src/store/element.rs6
-rw-r--r--crates/tinywasm/src/store/function.rs6
-rw-r--r--crates/tinywasm/src/store/memory/vec.rs39
-rw-r--r--crates/tinywasm/src/store/mod.rs159
-rw-r--r--crates/tinywasm/src/store/table.rs72
-rw-r--r--crates/types/src/archive.rs3
-rw-r--r--crates/types/src/lib.rs115
-rw-r--r--crates/types/src/reference.rs112
-rw-r--r--crates/types/src/value.rs209
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";
+}