From 82e01c1797875bb07887283d5673e0dda238033e Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Thu, 30 Nov 2023 15:04:40 +0100 Subject: chore: improve api Signed-off-by: Henry Gressmann --- crates/cli/bin.rs | 49 ++++++++++--- crates/tinywasm/src/engine/executer.rs | 1 + crates/tinywasm/src/engine/mod.rs | 20 ++++++ crates/tinywasm/src/engine/stack/call.rs | 9 +++ crates/tinywasm/src/engine/stack/mod.rs | 39 +++++++++++ crates/tinywasm/src/engine/types.rs | 61 ++++++++++++++++ crates/tinywasm/src/instance.rs | 0 crates/tinywasm/src/instructions.rs | 106 ---------------------------- crates/tinywasm/src/lib.rs | 32 ++++++--- crates/tinywasm/src/module/mod.rs | 111 +++++++++++++++--------------- crates/tinywasm/src/module/reader.rs | 2 +- crates/tinywasm/src/naive/mod.rs | 107 ++++++++++++++++++++++++++++ crates/tinywasm/src/naive/module.rs | 92 +++++++++++++++++++++++++ crates/tinywasm/src/naive_runtime.rs | 104 ---------------------------- crates/tinywasm/src/runtime/mod.rs | 6 -- crates/tinywasm/src/runtime/stack/call.rs | 8 --- crates/tinywasm/src/runtime/stack/mod.rs | 23 ------- crates/tinywasm/src/runtime/store.rs | 14 ---- crates/tinywasm/src/runtime/types.rs | 67 ------------------ crates/tinywasm/src/store.rs | 51 ++++++++++++++ 20 files changed, 498 insertions(+), 404 deletions(-) create mode 100644 crates/tinywasm/src/engine/executer.rs create mode 100644 crates/tinywasm/src/engine/mod.rs create mode 100644 crates/tinywasm/src/engine/stack/call.rs create mode 100644 crates/tinywasm/src/engine/stack/mod.rs create mode 100644 crates/tinywasm/src/engine/types.rs delete mode 100644 crates/tinywasm/src/instance.rs delete mode 100644 crates/tinywasm/src/instructions.rs create mode 100644 crates/tinywasm/src/naive/mod.rs create mode 100644 crates/tinywasm/src/naive/module.rs delete mode 100644 crates/tinywasm/src/naive_runtime.rs delete mode 100644 crates/tinywasm/src/runtime/mod.rs delete mode 100644 crates/tinywasm/src/runtime/stack/call.rs delete mode 100644 crates/tinywasm/src/runtime/stack/mod.rs delete mode 100644 crates/tinywasm/src/runtime/store.rs delete mode 100644 crates/tinywasm/src/runtime/types.rs create mode 100644 crates/tinywasm/src/store.rs (limited to 'crates') diff --git a/crates/cli/bin.rs b/crates/cli/bin.rs index 6875eac..18b61cc 100644 --- a/crates/cli/bin.rs +++ b/crates/cli/bin.rs @@ -1,6 +1,8 @@ +use std::str::FromStr; + use argh::FromArgs; use color_eyre::eyre::Result; -use tinywasm::{self, Module, WasmValue}; +use tinywasm::{self, WasmValue}; use util::install_tracing; mod util; @@ -18,6 +20,23 @@ enum TinyWasmSubcommand { Run(Run), } +enum Engine { + Main, + Naive, +} + +impl FromStr for Engine { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "naive" => Ok(Self::Naive), + "main" => Ok(Self::Main), + _ => Err(format!("unknown engine: {}", s)), + } + } +} + #[derive(FromArgs)] /// run a wasm file #[argh(subcommand, name = "run")] @@ -25,6 +44,10 @@ struct Run { /// wasm file to run #[argh(positional)] wasm_file: String, + + /// engine to use + #[argh(option, short = 'e', default = "Engine::Main")] + engine: Engine, } fn main() -> Result<()> { @@ -34,22 +57,32 @@ fn main() -> Result<()> { let args: TinyWasmCli = argh::from_env(); match args.nested { - TinyWasmSubcommand::Run(Run { wasm_file }) => { - let wasm = std::fs::read(wasm_file).unwrap(); - run(&wasm)?; - Ok(()) + TinyWasmSubcommand::Run(Run { wasm_file, engine }) => { + let wasm = std::fs::read(wasm_file)?; + match engine { + Engine::Main => run(&wasm), + Engine::Naive => run_naive(&wasm), + } } } } fn run(wasm: &[u8]) -> Result<()> { - let mut module = Module::new(wasm)?; + let mut store = tinywasm::Store::default(); + let mut module = tinywasm::Module::try_new(&mut store, wasm)?; + let instance = tinywasm::ModuleInstance::new(&mut module)?; + + Ok(()) +} + +fn run_naive(wasm: &[u8]) -> Result<()> { + let mut module = tinywasm::naive::Module::new(wasm)?; let args = [WasmValue::I32(1), WasmValue::I32(2)]; - let res = tinywasm::naive_runtime::run(&mut module, "add", &args)?; + let res = tinywasm::naive::run(&mut module, "add", &args)?; println!("res: {:?}", res); let args = [WasmValue::I64(1), WasmValue::I64(2)]; - let res = tinywasm::naive_runtime::run(&mut module, "add_64", &args)?; + let res = tinywasm::naive::run(&mut module, "add_64", &args)?; println!("res: {:?}", res); Ok(()) diff --git a/crates/tinywasm/src/engine/executer.rs b/crates/tinywasm/src/engine/executer.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/crates/tinywasm/src/engine/executer.rs @@ -0,0 +1 @@ + diff --git a/crates/tinywasm/src/engine/mod.rs b/crates/tinywasm/src/engine/mod.rs new file mode 100644 index 0000000..a134659 --- /dev/null +++ b/crates/tinywasm/src/engine/mod.rs @@ -0,0 +1,20 @@ +mod executer; +mod stack; +mod types; + +pub use executer::*; +pub use stack::*; +pub use types::*; + +#[derive(Debug)] +pub struct Engine { + pub stack: Stack, +} + +impl Default for Engine { + fn default() -> Self { + Self { + stack: Stack::default(), + } + } +} diff --git a/crates/tinywasm/src/engine/stack/call.rs b/crates/tinywasm/src/engine/stack/call.rs new file mode 100644 index 0000000..0d0ffae --- /dev/null +++ b/crates/tinywasm/src/engine/stack/call.rs @@ -0,0 +1,9 @@ +use alloc::vec::Vec; + +#[derive(Debug)] +pub struct CallFrame { + pub instr_ptr: usize, + pub func_ptr: usize, + + pub local_addrs: Vec, +} diff --git a/crates/tinywasm/src/engine/stack/mod.rs b/crates/tinywasm/src/engine/stack/mod.rs new file mode 100644 index 0000000..f7ffdf3 --- /dev/null +++ b/crates/tinywasm/src/engine/stack/mod.rs @@ -0,0 +1,39 @@ +use crate::WasmValue; +use alloc::vec::Vec; + +mod call; +pub use call::CallFrame; + +// minimum stack size +pub const STACK_SIZE: usize = 1024; +// minimum call stack size +pub const CALL_STACK_SIZE: usize = 1024; + +/// A WebAssembly Stack +#[derive(Debug)] +pub struct Stack { + /// Locals + // TODO: maybe store the locals on the stack instead? + pub locals: Vec, + + /// The value stack + // TODO: Split into Vec and Vec for better memory usage? + pub value_stack: Vec, // keeping this typed for now to make it easier to debug + pub value_stack_top: usize, + + /// The call stack + pub call_stack: Vec, + pub call_stack_top: usize, +} + +impl Default for Stack { + fn default() -> Self { + Self { + locals: Vec::new(), + value_stack: Vec::with_capacity(STACK_SIZE), + value_stack_top: 0, + call_stack: Vec::with_capacity(CALL_STACK_SIZE), + call_stack_top: 0, + } + } +} diff --git a/crates/tinywasm/src/engine/types.rs b/crates/tinywasm/src/engine/types.rs new file mode 100644 index 0000000..ab81b9d --- /dev/null +++ b/crates/tinywasm/src/engine/types.rs @@ -0,0 +1,61 @@ +use alloc::{string::String, vec::Vec}; +use wasmparser::{FuncType, OperatorsIterator, ValType}; + +use crate::module::ModuleInstance; + +/// A WebAssembly Label +pub struct Label(Addr); + +/// A WebAssembly Address. +/// These are indexes into the respective stores. +/// See https://webassembly.github.io/spec/core/exec/runtime.html#addresses +pub type Addr = u32; +pub type FuncAddr = Addr; +pub type TableAddr = Addr; +pub type MemAddr = Addr; +pub type GlobalAddr = Addr; +pub type ElmAddr = Addr; +pub type DataAddr = Addr; +pub type ExternAddr = Addr; + +/// A WebAssembly Function Instance. +/// See https://webassembly.github.io/spec/core/exec/runtime.html#function-instances +#[derive(Debug)] +pub enum FuncInst { + Host(HostFunc), + Module(ModuleFunc), +} +#[derive(Debug)] +pub struct HostFunc { + pub ty: FuncType, + pub hostcode: fn() -> (), +} +#[derive(Debug)] +pub struct ModuleFunc { + pub ty: FuncType, + pub module: Addr, + pub code: FuncAddr, +} +pub struct Func<'a> { + pub ty: FuncType, + pub locals: Vec, + pub body: Vec>, +} + +/// A WebAssembly Export Instance. +/// https://webassembly.github.io/spec/core/exec/runtime.html#export-instances +#[derive(Debug)] +pub struct ExportInst { + pub name: String, + pub value: ExternVal, +} + +/// A WebAssembly External Value. +/// https://webassembly.github.io/spec/core/exec/runtime.html#external-values +#[derive(Debug)] +pub enum ExternVal { + Func(FuncAddr), + Table(TableAddr), + Mem(MemAddr), + Global(GlobalAddr), +} diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs deleted file mode 100644 index e69de29..0000000 diff --git a/crates/tinywasm/src/instructions.rs b/crates/tinywasm/src/instructions.rs deleted file mode 100644 index e3e6a08..0000000 --- a/crates/tinywasm/src/instructions.rs +++ /dev/null @@ -1,106 +0,0 @@ -// https://webassembly.github.io/spec/core/binary/instructions.html - -// Controll Instructions -pub mod control { - pub const WASM_UNREACHABLE: u8 = 0x00; - pub const WASM_NOP: u8 = 0x01; // do nothing - - // stuctured instructions - pub const WASM_BLOCK: u8 = 0x02; - pub const WASM_LOOP: u8 = 0x03; - pub const WASM_IF: u8 = 0x04; - - pub const WASM_ELSE: u8 = 0x05; - pub const WASM_END: u8 = 0x0B; - pub const WASM_BR: u8 = 0x0C; - pub const WASM_BR_IF: u8 = 0x0D; - pub const WASM_BR_TABLE: u8 = 0x0E; - pub const WASM_RETURN: u8 = 0x0F; - pub const WASM_CALL: u8 = 0x10; - pub const WASM_CALL_INDIRECT: u8 = 0x11; - pub const WASM_DROP: u8 = 0x1A; -} - -// Reference Instructions -pub mod reference { - pub const WASM_REF_NULL: u8 = 0xD0; - pub const WASM_REF_IS_NULL: u8 = 0xD1; - pub const WASM_REF_FUNC: u8 = 0xD2; -} - -// Parametric Instructions -pub mod parametric { - pub const WASM_DROP: u8 = 0x1A; - pub const WASM_SELECT: u8 = 0x1B; - pub const WASM_SELECT_T: u8 = 0x1C; -} - -// Variable Instructions -pub mod variable { - pub const WASM_LOCAL_GET: u8 = 0x20; - pub const WASM_LOCAL_SET: u8 = 0x21; - pub const WASM_LOCAL_TEE: u8 = 0x22; - pub const WASM_GLOBAL_GET: u8 = 0x23; - pub const WASM_GLOBAL_SET: u8 = 0x24; -} - -// Table Instructions -pub mod table { - pub const WASM_TABLE_GET: u8 = 0x25; - pub const WASM_TABLE_SET: u8 = 0x26; - pub const WASM_TABLE_INIT: u8 = 0xFC; - pub const WASM_ELEM_DROP: u8 = 0xFC; - pub const WASM_TABLE_COPY: u8 = 0xFC; - pub const WASM_TABLE_GROW: u8 = 0xFC; - pub const WASM_TABLE_SIZE: u8 = 0xFC; - pub const WASM_TABLE_FILL: u8 = 0xFC; -} - -// Memory Instructions -pub mod memory { - pub const WASM_I32_LOAD: u8 = 0x28; - pub const WASM_I64_LOAD: u8 = 0x29; - pub const WASM_F32_LOAD: u8 = 0x2A; - pub const WASM_F64_LOAD: u8 = 0x2B; - pub const WASM_I32_LOAD8_S: u8 = 0x2C; - pub const WASM_I32_LOAD8_U: u8 = 0x2D; - pub const WASM_I32_LOAD16_S: u8 = 0x2E; - pub const WASM_I32_LOAD16_U: u8 = 0x2F; - pub const WASM_I64_LOAD8_S: u8 = 0x30; - pub const WASM_I64_LOAD8_U: u8 = 0x31; - pub const WASM_I64_LOAD16_S: u8 = 0x32; - pub const WASM_I64_LOAD16_U: u8 = 0x33; - pub const WASM_I64_LOAD32_S: u8 = 0x34; - pub const WASM_I64_LOAD32_U: u8 = 0x35; - pub const WASM_I32_STORE: u8 = 0x36; - pub const WASM_I64_STORE: u8 = 0x37; - pub const WASM_F32_STORE: u8 = 0x38; - pub const WASM_F64_STORE: u8 = 0x39; - pub const WASM_I32_STORE8: u8 = 0x3A; - pub const WASM_I32_STORE16: u8 = 0x3B; - pub const WASM_I64_STORE8: u8 = 0x3C; - pub const WASM_I64_STORE16: u8 = 0x3D; - pub const WASM_I64_STORE32: u8 = 0x3E; - pub const WASM_MEMORY_SIZE: u8 = 0x3F; - pub const WASM_MEMORY_GROW: u8 = 0x40; - pub const WASM_MEMORY_INIT: u8 = 0xFC; - pub const WASM_DATA_DROP: u8 = 0xFC; - pub const WASM_MEMORY_COPY: u8 = 0xFC; - pub const WASM_MEMORY_FILL: u8 = 0xFC; -} - -// Numeric Instructions -pub mod numeric { - // Constants - pub const WASM_I32_CONST: u8 = 0x41; - pub const WASM_I64_CONST: u8 = 0x42; - pub const WASM_F32_CONST: u8 = 0x43; - pub const WASM_F64_CONST: u8 = 0x44; - - // Operations - pub const START_NUMERIC: u8 = 0x45; - pub const END_NUMERIC: u8 = 0xC4; - pub const WASM_SATURATING_TRUNC: u8 = 0xFC; -} - -pub const WASM_VEC: u8 = 0xFD; diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 6b2cd3f..f4bdd65 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -6,28 +6,38 @@ mod std; extern crate alloc; mod error; -pub mod instructions; -pub mod module; pub use error::*; + +pub mod store; +pub use store::Store; + +pub mod module; pub use module::Module; +pub use module::ModuleInstance; + pub mod types; pub use types::*; -pub mod runtime; -pub mod naive_runtime; - -pub struct Store {} - -pub struct Instance {} +pub mod engine; +pub mod naive; #[cfg(test)] mod tests { - use crate::{error::Result, Module}; + use crate::std::println; + use crate::{error::Result, naive, WasmValue}; #[test] - fn it_works() -> Result<()> { + fn naive_add() -> Result<()> { let wasm = include_bytes!("../../../examples/wasm/add.wasm"); - let module = Module::new(wasm)?; + let mut module = naive::Module::new(wasm)?; + + let args = [WasmValue::I32(1), WasmValue::I32(2)]; + let res = naive::run(&mut module, "add", &args)?; + println!("res: {:?}", res); + + let args = [WasmValue::I64(1), WasmValue::I64(2)]; + let res = naive::run(&mut module, "add_64", &args)?; + println!("res: {:?}", res); Ok(()) } diff --git a/crates/tinywasm/src/module/mod.rs b/crates/tinywasm/src/module/mod.rs index 23c2a93..51731e9 100644 --- a/crates/tinywasm/src/module/mod.rs +++ b/crates/tinywasm/src/module/mod.rs @@ -1,63 +1,44 @@ -use core::fmt::Debug; - -use crate::error::{Error, Result}; use alloc::vec::Vec; -use wasmparser::*; +use wasmparser::{Export, FuncType, Validator}; + +use crate::{engine::FuncAddr, Error, Result, Store}; -mod reader; use self::reader::ModuleReader; +pub mod reader; + #[derive(Debug)] -pub struct ModuleMetadata { - pub version: u16, +pub struct Module<'a> { + store: &'a mut Store<'a>, + reader: ModuleReader<'a>, } -pub struct Module<'data> { - pub meta: ModuleMetadata, +/// A WebAssembly Module Instance. +/// See https://webassembly.github.io/spec/core/exec/runtime.html#module-instances +#[derive(Debug)] +pub struct ModuleInstance<'m, 'data> { + pub module: &'m Module<'data>, pub types: Vec, - pub functions: Vec, + pub func_addrs: Vec, + // pub table_addrs: Vec, + // pub mem_addrs: Vec, + // pub global_addrs: Vec, + // pub elem_addrs: Vec, + // pub data_addrs: Vec, pub exports: Vec>, - pub code: Vec>, - - marker: core::marker::PhantomData<&'data ()>, } -impl Debug for Module<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Module") - .field("meta", &self.meta) - .field("types", &self.types) - .field("functions", &self.functions) - .field("exports", &self.exports) - .field("code", &self.code) - .finish() - } -} - -impl<'data> Module<'data> { - pub fn new(wasm: &'data [u8]) -> Result { - let mut validator = Validator::new(); - let mut reader = ModuleReader::new(); - - for payload in wasmparser::Parser::new(0).parse_all(wasm) { - reader.process_payload(payload?, &mut validator)?; - } - - if !reader.end_reached { - return Error::other("End not reached"); - } - - Self::from_reader(reader) - } - - fn from_reader(reader: ModuleReader<'data>) -> Result { - let types = reader +impl<'m, 'data> ModuleInstance<'m, 'data> { + pub fn new(module: &'m mut Module<'data>) -> Result { + let types = module + .reader .type_section + .take() .map(|s| { s.into_iter() .map(|ty| { - let Type::Func(func) = ty?; + let wasmparser::Type::Func(func) = ty?; Ok(func) }) .collect::>>() @@ -65,31 +46,49 @@ impl<'data> Module<'data> { .transpose()? .unwrap_or_default(); - let functions = reader + let func_addrs = module + .reader .function_section + .take() .map(|s| s.into_iter().map(|f| Ok(f?)).collect::>>()) .transpose()? .unwrap_or_default(); - let exports = reader + let exports = module + .reader .export_section + .take() .map(|s| s.into_iter().map(|e| Ok(e?)).collect::>>()) .transpose()? .unwrap_or_default(); - let code = reader.code_section.map(|s| s.functions).unwrap_or_default(); - - let meta = ModuleMetadata { - version: reader.version.unwrap_or(1), - }; - Ok(Self { - marker: core::marker::PhantomData, - meta, + module, + types, + func_addrs, + // table_addrs, + // mem_addrs, + // global_addrs, + // elem_addrs, + // data_addrs, exports, - functions, - code, }) } } + +impl<'a> Module<'a> { + pub fn try_new(store: &'a mut Store<'a>, wasm: &'a [u8]) -> Result> { + let mut validator = Validator::new(); + let mut reader = ModuleReader::new(); + + for payload in wasmparser::Parser::new(0).parse_all(&wasm) { + reader.process_payload(payload?, &mut validator)?; + } + if !reader.end_reached { + return Error::other("End not reached"); + } + + Ok(Self { store, reader }) + } +} diff --git a/crates/tinywasm/src/module/reader.rs b/crates/tinywasm/src/module/reader.rs index 5ccadea..50d1012 100644 --- a/crates/tinywasm/src/module/reader.rs +++ b/crates/tinywasm/src/module/reader.rs @@ -46,7 +46,7 @@ impl Debug for ModuleReader<'_> { } impl<'a> ModuleReader<'a> { - pub fn new() -> Self { + pub fn new() -> ModuleReader<'a> { Self::default() } diff --git a/crates/tinywasm/src/naive/mod.rs b/crates/tinywasm/src/naive/mod.rs new file mode 100644 index 0000000..44b40af --- /dev/null +++ b/crates/tinywasm/src/naive/mod.rs @@ -0,0 +1,107 @@ +use alloc::{format, string::ToString, vec, vec::Vec}; +use tracing::info; +use wasmparser::Operator; + +mod module; +pub use self::module::Module; + +use crate::{Error, Result, WasmValue}; + +pub fn run(module: &mut Module, func_name: &str, args: &[WasmValue]) -> Result> { + let func = module + .exports + .iter() + .find(|e| e.name == func_name) + .ok_or_else(|| Error::Other(format!("Function {} not found", func_name)))?; + + let func_type_index = module.functions[func.index as usize]; + let func_type = &module.types[func_type_index as usize]; + + info!("func_type: {:#?}", func_type); + let code = &mut module.code[func.index as usize]; + code.allow_memarg64(false); + + let mut locals = vec![]; + for ty in func_type.params() { + locals.push(*ty); + } + + let mut returns = vec![]; + for ty in func_type.results() { + returns.push(*ty); + } + + let locals_reader = code.get_locals_reader().unwrap(); + for local in locals_reader.into_iter() { + let local = local.unwrap(); + if locals.len() != local.0 as usize { + panic!("Invalid local index"); + } + locals.push(local.1); + } + + let mut local_values = vec![]; + let body = code.get_operators_reader().unwrap().into_iter(); + for (i, arg) in args.iter().enumerate() { + if !arg.is(locals[i]) { + return Error::other(&format!( + "Invalid argument type for {}, index {}: expected {:?}, got {:?}", + func_name, + i, + locals[i], + arg.type_of() + )); + } + + local_values.push(arg); + } + + let mut stack: Vec = vec![]; + for op in body { + let op = op.unwrap(); + info!("op: {:#?}", op); + + match op { + Operator::LocalGet { local_index } => { + let local = locals.get(local_index as usize).unwrap(); + let val = local_values[local_index as usize]; + info!("local: {:#?}", local); + stack.push(val.clone()); + } + Operator::I64Add => { + let a = stack.pop().unwrap(); + let b = stack.pop().unwrap(); + let (WasmValue::I64(a), WasmValue::I64(b)) = (a, b) else { + panic!("Invalid type"); + }; + let c = WasmValue::I64(a + b); + stack.push(c); + } + Operator::I32Add => { + let a = stack.pop().unwrap(); + let b = stack.pop().unwrap(); + let (WasmValue::I32(a), WasmValue::I32(b)) = (a, b) else { + panic!("Invalid type"); + }; + let c = WasmValue::I32(a + b); + stack.push(c); + } + Operator::End => { + info!("stack: {:#?}", stack); + let res = returns + .iter() + .map(|ty| { + let val = stack.pop()?; + (val.is(*ty)).then_some(val) + }) + .collect::>>() + .ok_or_else(|| Error::Other("Invalid return type".to_string()))?; + + return Ok(res); + } + _ => {} + } + } + + Error::other("End not reached") +} diff --git a/crates/tinywasm/src/naive/module.rs b/crates/tinywasm/src/naive/module.rs new file mode 100644 index 0000000..24f2d55 --- /dev/null +++ b/crates/tinywasm/src/naive/module.rs @@ -0,0 +1,92 @@ +use core::fmt::Debug; + +use crate::{ + error::{Error, Result}, + module::reader::ModuleReader, +}; +use alloc::vec::Vec; +use wasmparser::*; + +#[derive(Debug)] +pub struct ModuleMetadata { + pub version: u16, +} + +pub struct Module<'data> { + pub meta: ModuleMetadata, + + pub types: Vec, + pub functions: Vec, + pub exports: Vec>, + pub code: Vec>, +} + +impl Debug for Module<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Module") + .field("meta", &self.meta) + .field("types", &self.types) + .field("functions", &self.functions) + .field("exports", &self.exports) + .field("code", &self.code) + .finish() + } +} + +impl<'data> Module<'data> { + pub fn new(wasm: &'data [u8]) -> Result { + let mut validator = Validator::new(); + let mut reader = ModuleReader::new(); + + for payload in wasmparser::Parser::new(0).parse_all(wasm) { + reader.process_payload(payload?, &mut validator)?; + } + + if !reader.end_reached { + return Error::other("End not reached"); + } + + Self::from_reader(reader) + } + + fn from_reader(reader: ModuleReader<'data>) -> Result { + let types = reader + .type_section + .map(|s| { + s.into_iter() + .map(|ty| { + let Type::Func(func) = ty?; + Ok(func) + }) + .collect::>>() + }) + .transpose()? + .unwrap_or_default(); + + let functions = reader + .function_section + .map(|s| s.into_iter().map(|f| Ok(f?)).collect::>>()) + .transpose()? + .unwrap_or_default(); + + let exports = reader + .export_section + .map(|s| s.into_iter().map(|e| Ok(e?)).collect::>>()) + .transpose()? + .unwrap_or_default(); + + let code = reader.code_section.map(|s| s.functions).unwrap_or_default(); + + let meta = ModuleMetadata { + version: reader.version.unwrap_or(1), + }; + + Ok(Self { + meta, + types, + exports, + functions, + code, + }) + } +} diff --git a/crates/tinywasm/src/naive_runtime.rs b/crates/tinywasm/src/naive_runtime.rs deleted file mode 100644 index 6e6c709..0000000 --- a/crates/tinywasm/src/naive_runtime.rs +++ /dev/null @@ -1,104 +0,0 @@ -use alloc::{format, string::ToString, vec, vec::Vec}; -use tracing::info; -use wasmparser::Operator; - -use crate::{Error, Module, Result, WasmValue}; - -pub fn run(module: &mut Module, func_name: &str, args: &[WasmValue]) -> Result> { - let func = module - .exports - .iter() - .find(|e| e.name == func_name) - .ok_or_else(|| Error::Other(format!("Function {} not found", func_name)))?; - - let func_type_index = module.functions[func.index as usize]; - let func_type = &module.types[func_type_index as usize]; - - info!("func_type: {:#?}", func_type); - let code = &mut module.code[func.index as usize]; - code.allow_memarg64(false); - - let mut locals = vec![]; - for ty in func_type.params() { - locals.push(*ty); - } - - let mut returns = vec![]; - for ty in func_type.results() { - returns.push(*ty); - } - - let locals_reader = code.get_locals_reader().unwrap(); - for local in locals_reader.into_iter() { - let local = local.unwrap(); - if locals.len() != local.0 as usize { - panic!("Invalid local index"); - } - locals.push(local.1); - } - - let mut local_values = vec![]; - let body = code.get_operators_reader().unwrap().into_iter(); - for (i, arg) in args.iter().enumerate() { - if !arg.is(locals[i]) { - return Error::other(&format!( - "Invalid argument type for {}, index {}: expected {:?}, got {:?}", - func_name, - i, - locals[i], - arg.type_of() - )); - } - - local_values.push(arg); - } - - let mut stack: Vec = vec![]; - for op in body { - let op = op.unwrap(); - info!("op: {:#?}", op); - - match op { - Operator::LocalGet { local_index } => { - let local = locals.get(local_index as usize).unwrap(); - let val = local_values[local_index as usize]; - info!("local: {:#?}", local); - stack.push(val.clone()); - } - Operator::I64Add => { - let a = stack.pop().unwrap(); - let b = stack.pop().unwrap(); - let (WasmValue::I64(a), WasmValue::I64(b)) = (a, b) else { - panic!("Invalid type"); - }; - let c = WasmValue::I64(a + b); - stack.push(c); - } - Operator::I32Add => { - let a = stack.pop().unwrap(); - let b = stack.pop().unwrap(); - let (WasmValue::I32(a), WasmValue::I32(b)) = (a, b) else { - panic!("Invalid type"); - }; - let c = WasmValue::I32(a + b); - stack.push(c); - } - Operator::End => { - info!("stack: {:#?}", stack); - let res = returns - .iter() - .map(|ty| { - let val = stack.pop()?; - (val.is(*ty)).then_some(val) - }) - .collect::>>() - .ok_or_else(|| Error::Other("Invalid return type".to_string()))?; - - return Ok(res); - } - _ => {} - } - } - - Error::other("End not reached") -} diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/runtime/mod.rs deleted file mode 100644 index 0504365..0000000 --- a/crates/tinywasm/src/runtime/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod stack; -mod store; -mod types; -pub use stack::*; -pub use store::*; -pub use types::*; diff --git a/crates/tinywasm/src/runtime/stack/call.rs b/crates/tinywasm/src/runtime/stack/call.rs deleted file mode 100644 index a58e2ef..0000000 --- a/crates/tinywasm/src/runtime/stack/call.rs +++ /dev/null @@ -1,8 +0,0 @@ -use alloc::vec::Vec; - -pub struct CallFrame { - pub instr_ptr: usize, - pub func_ptr: usize, - - pub local_addrs: Vec, -} diff --git a/crates/tinywasm/src/runtime/stack/mod.rs b/crates/tinywasm/src/runtime/stack/mod.rs deleted file mode 100644 index 68f02ff..0000000 --- a/crates/tinywasm/src/runtime/stack/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::WasmValue; -use alloc::vec::Vec; - -mod call; -pub use call::CallFrame; - -pub const STACK_SIZE: usize = 1024; - -/// A WebAssembly Stack -pub struct Stack { - /// Locals - // TODO: maybe store the locals on the stack instead? - pub locals: Vec, - - /// The value stack - // TODO: Split into Vec and Vec for better memory usage? - pub value_stack: Vec, // keeping this typed for now to make it easier to debug - pub value_stack_top: usize, - - /// The call stack - pub call_stack: Vec, - pub call_stack_top: usize, -} diff --git a/crates/tinywasm/src/runtime/store.rs b/crates/tinywasm/src/runtime/store.rs deleted file mode 100644 index cf0163b..0000000 --- a/crates/tinywasm/src/runtime/store.rs +++ /dev/null @@ -1,14 +0,0 @@ -use alloc::vec::Vec; - -use super::FuncInst; - -/// global state that can be manipulated by WebAssembly programs -/// https://webassembly.github.io/spec/core/exec/runtime.html#store -pub struct Store { - pub funcs: Vec, - // tables: Vec, - // mems: Vec, - // globals: Vec, - // elems: Vec, - // data: Vec, -} diff --git a/crates/tinywasm/src/runtime/types.rs b/crates/tinywasm/src/runtime/types.rs deleted file mode 100644 index a9be605..0000000 --- a/crates/tinywasm/src/runtime/types.rs +++ /dev/null @@ -1,67 +0,0 @@ -use alloc::{string::String, vec::Vec}; -use wasmparser::{FuncType, OperatorsIterator, ValType}; - -/// A WebAssembly Label -pub struct Label(Addr); - -/// A WebAssembly Address. -/// These are indexes into the respective stores. -/// See https://webassembly.github.io/spec/core/exec/runtime.html#addresses -pub type Addr = u32; -pub struct FuncAddr(pub Addr); -pub struct TableAddr(pub Addr); -pub struct MemAddr(pub Addr); -pub struct GlobalAddr(pub Addr); -pub struct ElmAddr(pub Addr); -pub struct DataAddr(pub Addr); -pub struct ExternAddr(pub Addr); - -/// A WebAssembly Module Instance. -/// See https://webassembly.github.io/spec/core/exec/runtime.html#module-instances -pub struct ModuleInstance { - pub types: Vec, - pub func_addrs: Vec, - pub table_addrs: Vec, - pub mem_addrs: Vec, - pub global_addrs: Vec, - pub elem_addrs: Vec, - pub data_addrs: Vec, - pub exports: Vec, -} - -/// A WebAssembly Function Instance. -/// See https://webassembly.github.io/spec/core/exec/runtime.html#function-instances -pub enum FuncInst { - Host(HostFunc), - Module(ModuleFunc), -} -pub struct HostFunc { - pub ty: FuncType, - pub hostcode: fn() -> (), -} -pub struct ModuleFunc { - pub ty: FuncType, - pub module: ModuleInstance, - pub code: FuncAddr, -} -pub struct Func<'a> { - pub ty: FuncType, - pub locals: Vec, - pub body: Vec>, -} - -/// A WebAssembly Export Instance. -/// https://webassembly.github.io/spec/core/exec/runtime.html#export-instances -pub struct ExportInst { - pub name: String, - pub value: ExternVal, -} - -/// A WebAssembly External Value. -/// https://webassembly.github.io/spec/core/exec/runtime.html#external-values -pub enum ExternVal { - Func(FuncAddr), - Table(TableAddr), - Mem(MemAddr), - Global(GlobalAddr), -} diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs new file mode 100644 index 0000000..3c70c2f --- /dev/null +++ b/crates/tinywasm/src/store.rs @@ -0,0 +1,51 @@ +use alloc::vec::Vec; +use wasmparser::FunctionBody; + +use crate::{engine::Engine, module::reader::ModuleReader, Result}; + +/// global state that can be manipulated by WebAssembly programs +/// https://webassembly.github.io/spec/core/exec/runtime.html#store +#[derive(Debug)] +pub struct Store<'data> { + pub data: StoreData<'data>, + pub engine: Engine, +} + +#[derive(Debug)] +pub struct StoreData<'data> { + pub funcs: Vec>, + // pub tables: Vec, + // pub mems: Vec, + // pub globals: Vec, +} + +impl<'data> Default for StoreData<'data> { + fn default() -> Self { + Self { + funcs: Vec::new(), + // tables: Vec::new(), + // mems: Vec::new(), + // globals: Vec::new(), + } + } +} + +impl<'data> Store<'data> { + pub(crate) fn initialize(&'data mut self, reader: &'data mut ModuleReader) -> Result<()> { + let code = reader.code_section.take().ok_or_else(|| { + crate::Error::Other("Module must have a code section to initialize the store".into()) + })?; + + self.data.funcs = code.functions; + Ok(()) + } +} + +impl Default for Store<'_> { + fn default() -> Self { + Self { + data: StoreData::default(), + engine: Engine::default(), + } + } +} -- cgit v1.3.1