Skip to content

Commit

Permalink
feat: prepare executor for function calls
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
  • Loading branch information
explodingcamera committed Dec 9, 2023
1 parent 7a810ae commit d860a70
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 37 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions crates/tinywasm/src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ use crate::{
#[derive(Debug)]
/// A function handle
pub struct FuncHandle {
pub(crate) _module: ModuleInstance,
pub(crate) module: ModuleInstance,
pub(crate) addr: FuncAddr,
pub(crate) ty: FuncType,

/// The name of the function, if it has one
pub name: Option<String>,
}

impl FuncHandle {
/// Call a function
///
Expand All @@ -26,11 +27,7 @@ impl FuncHandle {

// 1. Assert: funcs[func_addr] exists
// 2. let func_inst be the functiuon instance funcs[func_addr]
let func_inst = store
.data
.funcs
.get(self.addr as usize)
.ok_or(Error::Other(format!("function {} not found", self.addr)))?;
let func_inst = store.get_func(self.addr as usize)?;

// 3. Let func_ty be the function type
let func_ty = &self.ty;
Expand Down Expand Up @@ -64,8 +61,8 @@ impl FuncHandle {
// 8. Push the values to the stack (Not needed since the call frame owns the values)

// 9. Invoke the function instance
let instrs = func_inst.instructions();
store.runtime.exec(&mut stack, instrs)?;
let runtime = store.runtime();
runtime.exec(store, &mut stack, self.module.clone())?;

// Once the function returns:
let result_m = func_ty.results.len();
Expand Down Expand Up @@ -125,6 +122,7 @@ macro_rules! impl_into_wasm_value_tuple {
}
}

impl_into_wasm_value_tuple!();
impl_into_wasm_value_tuple!(T1);
impl_into_wasm_value_tuple!(T1, T2);
impl_into_wasm_value_tuple!(T1, T2, T3);
Expand All @@ -141,6 +139,7 @@ macro_rules! impl_from_wasm_value_tuple {
$($T: TryFrom<WasmValue, Error = ()>),*
{
fn from_wasm_value_tuple(values: Vec<WasmValue>) -> Result<Self> {
#[allow(unused_variables, unused_mut)]
let mut iter = values.into_iter();
Ok((
$(
Expand All @@ -156,6 +155,7 @@ macro_rules! impl_from_wasm_value_tuple {
}
}

impl_from_wasm_value_tuple!();
impl_from_wasm_value_tuple!(T1);
impl_from_wasm_value_tuple!(T1, T2);
impl_from_wasm_value_tuple!(T1, T2, T3);
Expand Down
8 changes: 6 additions & 2 deletions crates/tinywasm/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ impl ModuleInstance {
}))
}

pub(crate) fn func_ty(&self, addr: FuncAddr) -> &FuncType {
&self.0.types[addr as usize]
}

/// Get an exported function by name
pub fn get_func(&self, store: &Store, name: &str) -> Result<FuncHandle> {
if self.0.store_id != store.id() {
Expand All @@ -67,7 +71,7 @@ impl ModuleInstance {

Ok(FuncHandle {
addr: export.index,
_module: self.clone(),
module: self.clone(),
name: Some(name.to_string()),
ty,
})
Expand Down Expand Up @@ -115,7 +119,7 @@ impl ModuleInstance {
let ty = self.0.types[func.ty_addr() as usize].clone();

Ok(Some(FuncHandle {
_module: self.clone(),
module: self.clone(),
addr: func_addr,
ty,
name: None,
Expand Down
41 changes: 31 additions & 10 deletions crates/tinywasm/src/runtime/executer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
use super::{Runtime, Stack};
use super::{DefaultRuntime, Stack};
use crate::{
log::debug,
runtime::{BlockFrame, BlockFrameType, RawWasmValue},
Error, Result,
CallFrame, Error, ModuleInstance, Result, Store,
};
use alloc::vec::Vec;
use tinywasm_types::{BlockArgs, Instruction};
use tinywasm_types::BlockArgs;

mod macros;
use macros::*;

impl<const CHECK_TYPES: bool> Runtime<CHECK_TYPES> {
pub(crate) fn exec(&self, stack: &mut Stack, instrs: &[Instruction]) -> Result<()> {
let cf = stack.call_stack.top_mut()?;
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();

// TODO: maybe we don't need to check if the instr_ptr is valid since
// it should be validated by the parser
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) => {
Expand Down Expand Up @@ -55,8 +70,14 @@ impl<const CHECK_TYPES: bool> Runtime<CHECK_TYPES> {
End => {
let blocks = &mut cf.blocks;
let Some(block) = blocks.pop() else {
debug!("end: no block to end, returning");
return Ok(());
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);
Expand Down
5 changes: 3 additions & 2 deletions crates/tinywasm/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub(crate) use value::RawWasmValue;
/// that the module is valid.
///
/// See <https://webassembly.github.io/spec/core/exec/runtime.html>
// Execution is implemented in the `executer` module
///
/// Execution is implemented in the [`crate::runtime::executer`] module
#[derive(Debug, Default)]
pub struct Runtime<const CHECK_TYPES: bool> {}
pub struct DefaultRuntime {}
4 changes: 2 additions & 2 deletions crates/tinywasm/src/runtime/stack/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use alloc::vec::Vec;
use log::info;
use tinywasm_types::BlockArgs;

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub(crate) struct Blocks(Vec<BlockFrame>);

impl Blocks {
Expand Down Expand Up @@ -31,7 +31,7 @@ impl Blocks {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct BlockFrame {
// where to resume execution when the block is broken
pub(crate) instr_ptr: usize,
Expand Down
26 changes: 22 additions & 4 deletions crates/tinywasm/src/runtime/stack/call_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use super::{blocks::Blocks, BlockFrameType};

// minimum call stack size
const CALL_STACK_SIZE: usize = 1024;
const CALL_STACK_MAX_SIZE: usize = 1024 * 1024;

#[derive(Debug)]
pub(crate) struct CallStack {
Expand All @@ -23,6 +24,20 @@ impl Default for CallStack {
}

impl CallStack {
pub(crate) fn is_empty(&self) -> bool {
self.top == 0
}

pub(crate) fn pop(&mut self) -> Result<CallFrame> {
assert!(self.top <= self.stack.len());
if self.top == 0 {
return Err(Error::CallStackEmpty);
}

self.top -= 1;
Ok(self.stack.pop().unwrap())
}

#[inline]
pub(crate) fn _top(&self) -> Result<&CallFrame> {
assert!(self.top <= self.stack.len());
Expand All @@ -33,7 +48,7 @@ impl CallStack {
}

#[inline]
pub(crate) fn top_mut(&mut self) -> Result<&mut CallFrame> {
pub(crate) fn _top_mut(&mut self) -> Result<&mut CallFrame> {
assert!(self.top <= self.stack.len());
if self.top == 0 {
return Err(Error::CallStackEmpty);
Expand All @@ -43,15 +58,18 @@ impl CallStack {

#[inline]
pub(crate) fn push(&mut self, call_frame: CallFrame) {
assert!(self.top <= self.stack.len());
assert!(self.stack.len() <= CALL_STACK_MAX_SIZE);

self.top += 1;
self.stack.push(call_frame);
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct CallFrame {
pub(crate) instr_ptr: usize,
pub(crate) _func_ptr: usize,
pub(crate) func_ptr: usize,

pub(crate) blocks: Blocks,
pub(crate) locals: Box<[RawWasmValue]>,
Expand Down Expand Up @@ -90,7 +108,7 @@ impl CallFrame {

Self {
instr_ptr: 0,
_func_ptr: func_ptr,
func_ptr,
local_count: locals.len(),
locals: locals.into_boxed_slice(),
blocks: Blocks::default(),
Expand Down
28 changes: 25 additions & 3 deletions crates/tinywasm/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::{format, vec::Vec};
use tinywasm_types::{FuncAddr, Function, Instruction, ModuleInstanceAddr, TypeAddr, ValType};

use crate::{runtime::Runtime, Error, ModuleInstance, Result};
use crate::{
runtime::{self, DefaultRuntime},
Error, ModuleInstance, Result,
};

// global store id counter
static STORE_ID: AtomicUsize = AtomicUsize::new(0);
Expand All @@ -24,7 +27,26 @@ pub struct Store {
module_instance_count: usize,

pub(crate) data: StoreData,
pub(crate) runtime: Runtime<true>,
pub(crate) runtime: Runtime,
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum Runtime {
Default,
}

impl Store {
/// Create a new store
pub fn new() -> Self {
Self::default()
}

/// Create a new store with the given runtime
pub(crate) fn runtime(&self) -> runtime::DefaultRuntime {
match self.runtime {
Runtime::Default => DefaultRuntime::default(),
}
}
}

impl PartialEq for Store {
Expand All @@ -42,7 +64,7 @@ impl Default for Store {
module_instances: Vec::new(),
module_instance_count: 0,
data: StoreData::default(),
runtime: Runtime::default(),
runtime: Runtime::Default,
}
}
}
Expand Down

0 comments on commit d860a70

Please sign in to comment.