diff options
| -rw-r--r-- | crates/tinywasm/Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/tinywasm/src/lib.rs | 6 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/executer/mod.rs | 156 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/executor/macros.rs (renamed from crates/tinywasm/src/runtime/executer/macros.rs) | 0 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/executor/mod.rs | 186 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/mod.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/store.rs | 10 |
7 files changed, 198 insertions, 163 deletions
diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index bf19f89..28ec5f5 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -22,3 +22,4 @@ default=["std", "parser", "logging"] logging=["log", "tinywasm-types/logging", "tinywasm-parser?/logging"] std=["tinywasm-parser?/std", "tinywasm-types/std"] parser=["tinywasm-parser"] +nightly=[] diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 616d2ab..aa01ef9 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![forbid(unsafe_code)] -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![doc(test( no_crate_inject, attr( @@ -9,9 +8,14 @@ ) ))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![cfg_attr(feature = "nightly", feature(error_in_core))] //! ## A tiny WebAssembly Runtime written in Rust +// compiler error when using no_std without nightly +#[cfg(all(not(feature = "std"), not(nightly)))] +const _: () = { compile_error!("`nightly` feature is required for `no_std`") }; + mod std; extern crate alloc; diff --git a/crates/tinywasm/src/runtime/executer/mod.rs b/crates/tinywasm/src/runtime/executer/mod.rs deleted file mode 100644 index 2428b61..0000000 --- a/crates/tinywasm/src/runtime/executer/mod.rs +++ /dev/null @@ -1,156 +0,0 @@ -use super::{DefaultRuntime, Stack}; -use crate::{ - log::debug, - runtime::{BlockFrame, BlockFrameType, RawWasmValue}, - CallFrame, Error, ModuleInstance, Result, Store, -}; -use alloc::vec::Vec; -use tinywasm_types::BlockArgs; - -mod macros; -use macros::*; - -impl DefaultRuntime { - pub(crate) fn exec(&self, store: &mut Store, stack: &mut Stack, module: ModuleInstance) -> Result<()> { - let mut cf = stack.call_stack.pop()?; - let func = store.get_func(cf.func_ptr)?; - let instrs = func.instructions(); - - while let Some(instr) = instrs.get(cf.instr_ptr) { - use tinywasm_types::Instruction::*; - match instr { - Call(v) => { - // prepare the call frame - let func = store.get_func(*v as usize)?; - let func_ty = module.func_ty(*v); - debug!("call: {:?}", func_ty); - let call_frame = CallFrame::new(*v as usize, &[], func.locals().to_vec()); - - // push the call frame - stack.call_stack.push(cf.clone()); - stack.call_stack.push(call_frame); - debug!("call: {:?}", func); - - // call the function - cf = stack.call_stack.pop()?; - } - Nop => {} // do nothing - Unreachable => return Err(Error::Trap(crate::Trap::Unreachable)), - Loop(args) => { - cf.blocks.push(BlockFrame { - instr_ptr: cf.instr_ptr, - stack_ptr: stack.values.len(), - args: *args, - ty: BlockFrameType::Loop, - }); - stack.values.block_args(*args)?; - } - BrTable(_default, len) => { - let instr = instrs[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len] - .iter() - .map(|i| match i { - BrLabel(l) => Ok(*l), - _ => panic!("Expected BrLabel, this should have been validated by the parser"), - }) - .collect::<Result<Vec<_>>>()?; - - if instr.len() != *len { - panic!("Expected {} BrLabel instructions, got {}", len, instr.len()); - } - - todo!() - } - Br(v) => cf.break_to(*v, &mut stack.values)?, - BrIf(v) => { - let val: i32 = stack.values.pop().ok_or(Error::StackUnderflow)?.into(); - if val > 0 { - cf.break_to(*v, &mut stack.values)? - }; - } - End => { - let blocks = &mut cf.blocks; - let Some(block) = blocks.pop() else { - if stack.call_stack.is_empty() { - debug!("end: no block to end and no parent call frame, returning"); - return Ok(()); - } else { - debug!("end: no block to end, returning to parent call frame"); - cf = stack.call_stack.pop()?; - continue; - } - }; - debug!("end, blocks: {:?}", blocks); - debug!(" instr_ptr: {}", cf.instr_ptr); - - match block.ty { - BlockFrameType::Loop => { - debug!("end(loop): break loop"); - let res: &[RawWasmValue] = match block.args { - BlockArgs::Empty => &[], - BlockArgs::Type(_t) => todo!(), - BlockArgs::FuncType(_t) => todo!(), - }; - - // remove the loop values from the stack - stack.values.trim(block.stack_ptr); - - // push the loop result values to the stack - stack.values.extend(res.iter().copied()); - } - _ => { - panic!("Attempted to end a block that is not the top block"); - } - } - } - LocalGet(local_index) => { - debug!("local.get: {:?}", local_index); - let val = cf.get_local(*local_index as usize); - debug!("local: {:#?}", val); - stack.values.push(val); - } - LocalSet(local_index) => { - debug!("local.set: {:?}", local_index); - let val = stack.values.pop().ok_or(Error::StackUnderflow)?; - cf.set_local(*local_index as usize, val); - } - LocalTee(local_index) => { - debug!("local.tee: {:?}", local_index); - let val = stack.values.pop().ok_or(Error::StackUnderflow)?; - cf.set_local(*local_index as usize, val); - stack.values.push(val); - } - I32Const(val) => stack.values.push((*val).into()), - I64Const(val) => stack.values.push((*val).into()), - I64Add => add_instr!(i64, stack), - I32Add => add_instr!(i32, stack), - F32Add => add_instr!(f32, stack), - F64Add => add_instr!(f64, stack), - - I32Sub => sub_instr!(i32, stack), - I64Sub => sub_instr!(i64, stack), - F32Sub => sub_instr!(f32, stack), - F64Sub => sub_instr!(f64, stack), - - I32LtS => lts_instr!(i32, stack), - I64LtS => lts_instr!(i64, stack), - F32Lt => lts_instr!(f32, stack), - F64Lt => lts_instr!(f64, stack), - - I32DivS => div_instr!(i32, stack), - I64DivS => div_instr!(i64, stack), - F32Div => div_instr!(f32, stack), - F64Div => div_instr!(f64, stack), - - i => todo!("{:?}", i), - } - - cf.instr_ptr += 1; - } - - debug!("end of exec"); - debug!("stack: {:?}", stack.values); - debug!("insts: {:?}", instrs); - debug!("instr_ptr: {}", cf.instr_ptr); - Err(Error::FuncDidNotReturn) - } -} diff --git a/crates/tinywasm/src/runtime/executer/macros.rs b/crates/tinywasm/src/runtime/executor/macros.rs index 154d7f7..154d7f7 100644 --- a/crates/tinywasm/src/runtime/executer/macros.rs +++ b/crates/tinywasm/src/runtime/executor/macros.rs diff --git a/crates/tinywasm/src/runtime/executor/mod.rs b/crates/tinywasm/src/runtime/executor/mod.rs new file mode 100644 index 0000000..1fbcf3f --- /dev/null +++ b/crates/tinywasm/src/runtime/executor/mod.rs @@ -0,0 +1,186 @@ +use super::{DefaultRuntime, Stack}; +use crate::{ + log::debug, + runtime::{BlockFrame, BlockFrameType, RawWasmValue}, + CallFrame, Error, ModuleInstance, Result, Store, +}; +use alloc::vec::Vec; +use tinywasm_types::{BlockArgs, Instruction}; + +mod macros; +use macros::*; + +impl DefaultRuntime { + pub(crate) fn exec(&self, store: &mut Store, stack: &mut Stack, module: ModuleInstance) -> Result<()> { + let mut cf = stack.call_stack.pop()?; + let func = store.get_func(cf.func_ptr)?.clone(); + let instrs = func.instructions(); + + while let Some(instr) = instrs.get(cf.instr_ptr) { + match exec_one(&mut cf, instr, instrs, stack, store, &module)? { + // return from the function + ExecResult::Return => return Ok(()), + + // continue to the next instruction and don't increment the instruction pointer + ExecResult::Continue => continue, + + // continue to the next instruction and increment the instruction pointer + ExecResult::Ok => { + cf.instr_ptr += 1; + } + } + } + + debug!("end of exec"); + debug!("stack: {:?}", stack.values); + debug!("insts: {:?}", instrs); + debug!("instr_ptr: {}", cf.instr_ptr); + Err(Error::FuncDidNotReturn) + } +} + +enum ExecResult { + Ok, + Continue, + Return, +} + +#[inline] +fn exec_one( + cf: &mut CallFrame, + instr: &Instruction, + instrs: &[Instruction], + stack: &mut Stack, + store: &mut Store, + module: &ModuleInstance, +) -> Result<ExecResult> { + use tinywasm_types::Instruction::*; + match instr { + Call(v) => { + // prepare the call frame + let func = store.get_func(*v as usize)?; + let func_ty = module.func_ty(*v); + debug!("call: {:?}", func_ty); + let call_frame = CallFrame::new(*v as usize, &[], func.locals().to_vec()); + + // push the call frame + stack.call_stack.push(cf.clone()); + stack.call_stack.push(call_frame); + debug!("call: {:?}", func); + + // call the function + *cf = stack.call_stack.pop()?; + } + Nop => {} // do nothing + Unreachable => return Err(Error::Trap(crate::Trap::Unreachable)), + Loop(args) => { + cf.blocks.push(BlockFrame { + instr_ptr: cf.instr_ptr, + stack_ptr: stack.values.len(), + args: *args, + ty: BlockFrameType::Loop, + }); + stack.values.block_args(*args)?; + } + BrTable(_default, len) => { + let instr = instrs[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len] + .iter() + .map(|i| match i { + BrLabel(l) => Ok(*l), + _ => panic!("Expected BrLabel, this should have been validated by the parser"), + }) + .collect::<Result<Vec<_>>>()?; + + if instr.len() != *len { + panic!("Expected {} BrLabel instructions, got {}", len, instr.len()); + } + + todo!() + } + Br(v) => cf.break_to(*v, &mut stack.values)?, + BrIf(v) => { + let val: i32 = stack.values.pop().ok_or(Error::StackUnderflow)?.into(); + if val > 0 { + cf.break_to(*v, &mut stack.values)? + }; + } + End => { + let blocks = &mut cf.blocks; + let Some(block) = blocks.pop() else { + if stack.call_stack.is_empty() { + debug!("end: no block to end and no parent call frame, returning"); + return Ok(ExecResult::Return); + } else { + debug!("end: no block to end, returning to parent call frame"); + *cf = stack.call_stack.pop()?; + cf.instr_ptr = cf.instr_ptr.saturating_sub(1); + return Ok(ExecResult::Continue); + } + }; + debug!("end, blocks: {:?}", blocks); + debug!(" instr_ptr: {}", cf.instr_ptr); + + match block.ty { + BlockFrameType::Loop => { + debug!("end(loop): break loop"); + let res: &[RawWasmValue] = match block.args { + BlockArgs::Empty => &[], + BlockArgs::Type(_t) => todo!(), + BlockArgs::FuncType(_t) => todo!(), + }; + + // remove the loop values from the stack + stack.values.trim(block.stack_ptr); + + // push the loop result values to the stack + stack.values.extend(res.iter().copied()); + } + _ => { + panic!("Attempted to end a block that is not the top block"); + } + } + } + LocalGet(local_index) => { + debug!("local.get: {:?}", local_index); + let val = cf.get_local(*local_index as usize); + debug!("local: {:#?}", val); + stack.values.push(val); + } + LocalSet(local_index) => { + debug!("local.set: {:?}", local_index); + let val = stack.values.pop().ok_or(Error::StackUnderflow)?; + cf.set_local(*local_index as usize, val); + } + LocalTee(local_index) => { + debug!("local.tee: {:?}", local_index); + let val = stack.values.pop().ok_or(Error::StackUnderflow)?; + cf.set_local(*local_index as usize, val); + stack.values.push(val); + } + I32Const(val) => stack.values.push((*val).into()), + I64Const(val) => stack.values.push((*val).into()), + I64Add => add_instr!(i64, stack), + I32Add => add_instr!(i32, stack), + F32Add => add_instr!(f32, stack), + F64Add => add_instr!(f64, stack), + + I32Sub => sub_instr!(i32, stack), + I64Sub => sub_instr!(i64, stack), + F32Sub => sub_instr!(f32, stack), + F64Sub => sub_instr!(f64, stack), + + I32LtS => lts_instr!(i32, stack), + I64LtS => lts_instr!(i64, stack), + F32Lt => lts_instr!(f32, stack), + F64Lt => lts_instr!(f64, stack), + + I32DivS => div_instr!(i32, stack), + I64DivS => div_instr!(i64, stack), + F32Div => div_instr!(f32, stack), + F64Div => div_instr!(f64, stack), + + i => todo!("{:?}", i), + }; + + Ok(ExecResult::Ok) +} diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/runtime/mod.rs index e79a3a3..07cff38 100644 --- a/crates/tinywasm/src/runtime/mod.rs +++ b/crates/tinywasm/src/runtime/mod.rs @@ -1,4 +1,4 @@ -mod executer; +mod executor; mod stack; mod value; diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs index 2e7ebdd..3214540 100644 --- a/crates/tinywasm/src/store.rs +++ b/crates/tinywasm/src/store.rs @@ -1,6 +1,6 @@ use core::sync::atomic::{AtomicUsize, Ordering}; -use alloc::{format, vec::Vec}; +use alloc::{format, rc::Rc, vec::Vec}; use tinywasm_types::{FuncAddr, Function, Instruction, ModuleInstanceAddr, TypeAddr, ValType}; use crate::{ @@ -99,7 +99,7 @@ impl FunctionInstance { #[derive(Debug, Default)] /// Global state that can be manipulated by WebAssembly programs pub struct StoreData { - pub(crate) funcs: Vec<FunctionInstance>, + pub(crate) funcs: Vec<Rc<FunctionInstance>>, // pub tables: Vec<TableAddr>, // pub mems: Vec<MemAddr>, // pub globals: Vec<GlobalAddr>, @@ -127,16 +127,16 @@ impl Store { pub(crate) fn add_funcs(&mut self, funcs: Vec<Function>, idx: ModuleInstanceAddr) -> Vec<FuncAddr> { let mut func_addrs = Vec::with_capacity(funcs.len()); for func in funcs.into_iter() { - self.data.funcs.push(FunctionInstance { + self.data.funcs.push(Rc::new(FunctionInstance { func, _module_instance: idx, - }); + })); func_addrs.push(idx as FuncAddr); } func_addrs } - pub(crate) fn get_func(&self, addr: usize) -> Result<&FunctionInstance> { + pub(crate) fn get_func(&self, addr: usize) -> Result<&Rc<FunctionInstance>> { self.data .funcs .get(addr) |
