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