Skip to content

Commit

Permalink
feat: fully working block and loop implementations
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 13, 2023
1 parent 6e8ea78 commit 366ef54
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 116 deletions.
24 changes: 16 additions & 8 deletions crates/parser/src/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
use log::info;
use tinywasm_types::{BlockArgs, Export, ExternalKind, FuncType, Instruction, MemArg, ValType};
use wasmparser::{FuncValidator, ValidatorResources};

Expand Down Expand Up @@ -113,9 +114,11 @@ pub fn process_operators<'a>(
mut validator: FuncValidator<ValidatorResources>,
) -> Result<Box<[Instruction]>> {
let mut instructions = Vec::new();
let mut last_label_pointer = Option::None;
let mut labels_ptrs = Vec::new();

for (i, op) in ops.enumerate() {
info!("op: {:?}", op);

let op = op?;
validator.op(offset, &op)?;
offset += 1;
Expand All @@ -134,27 +137,26 @@ pub fn process_operators<'a>(
Unreachable => Instruction::Unreachable,
Nop => Instruction::Nop,
Block { blockty } => {
last_label_pointer = Some(i);
labels_ptrs.push(instructions.len());
Instruction::Block(convert_blocktype(blockty), 0)
}
Loop { blockty } => {
last_label_pointer = Some(i);
labels_ptrs.push(instructions.len());
Instruction::Loop(convert_blocktype(blockty), 0)
}
If { blockty } => {
last_label_pointer = Some(i);
labels_ptrs.push(instructions.len());
Instruction::If(convert_blocktype(blockty), 0)
}
End => {
if let Some(label_pointer) = last_label_pointer {
if let Some(label_pointer) = labels_ptrs.pop() {
// last_label_pointer is Some if we're ending a block
match instructions[label_pointer] {
Instruction::Block(_, ref mut end)
| Instruction::Loop(_, ref mut end)
| Instruction::Else(ref mut end)
| Instruction::If(_, ref mut end) => {
*end = i + 1; // Set the end position to be one after the End instruction
last_label_pointer = None;
}
_ => {
return Err(crate::ParseError::UnsupportedOperator(
Expand All @@ -170,7 +172,7 @@ pub fn process_operators<'a>(
}
}
Else => {
let Some(label_pointer) = last_label_pointer else {
let Some(label_pointer) = labels_ptrs.pop() else {
return Err(crate::ParseError::UnsupportedOperator(
"Expected to end an if block, but the last label was None".to_string(),
));
Expand All @@ -187,7 +189,6 @@ pub fn process_operators<'a>(
}
}

last_label_pointer = Some(i);
Instruction::Else(0)
}
Br { relative_depth } => Instruction::Br(relative_depth),
Expand Down Expand Up @@ -369,6 +370,13 @@ pub fn process_operators<'a>(
instructions.push(res);
}

if !labels_ptrs.is_empty() {
panic!(
"last_label_pointer should be None after processing all instructions: {:?}",
labels_ptrs
);
}

validator.finish(offset)?;

Ok(instructions.into_boxed_slice())
Expand Down
6 changes: 3 additions & 3 deletions crates/tinywasm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ pub enum Error {
/// The stack is empty
StackUnderflow,

/// The block stack is empty
BlockStackUnderflow,
/// The label stack is empty
LabelStackUnderflow,

/// The call stack is empty
CallStackEmpty,
Expand All @@ -63,7 +63,7 @@ impl Display for Error {
Self::Other(message) => write!(f, "unknown error: {}", message),
Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {}", feature),
Self::FuncDidNotReturn => write!(f, "function did not return"),
Self::BlockStackUnderflow => write!(f, "block stack underflow"),
Self::LabelStackUnderflow => write!(f, "label stack underflow"),
Self::StackUnderflow => write!(f, "stack underflow"),
Self::CallStackEmpty => write!(f, "call stack empty"),
Self::InvalidStore => write!(f, "invalid store"),
Expand Down
13 changes: 12 additions & 1 deletion crates/tinywasm/src/runtime/executor/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ macro_rules! div_instr {
/// Less than signed instruction
macro_rules! lts_instr {
($ty:ty, $stack:ident) => {{
let [b, a] = $stack.values.pop_n_const::<2>()?;
let [a, b] = $stack.values.pop_n_const::<2>()?;
let a: $ty = a.into();
let b: $ty = b.into();
$stack.values.push(((a < b) as i32).into());
Expand All @@ -58,9 +58,20 @@ macro_rules! eq_instr {
}};
}

/// Greater or equal than signed instruction
macro_rules! ges_instr {
($ty:ty, $stack:ident) => {{
let [a, b] = $stack.values.pop_n_const::<2>()?;
let a: $ty = a.into();
let b: $ty = b.into();
$stack.values.push(((a >= b) as i32).into());
}};
}

pub(super) use add_instr;
pub(super) use div_instr;
pub(super) use eq_instr;
pub(super) use ges_instr;
pub(super) use lts_instr;
pub(super) use mul_instr;
pub(super) use sub_instr;
68 changes: 27 additions & 41 deletions crates/tinywasm/src/runtime/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::{DefaultRuntime, Stack};
use crate::{
log::debug,
runtime::{BlockFrame, BlockFrameInner, RawWasmValue},
runtime::{BlockType, LabelFrame, RawWasmValue},
CallFrame, Error, ModuleInstance, Result, Store,
};
use alloc::vec::Vec;
use log::info;
use tinywasm_types::{BlockArgs, Instruction};

mod macros;
Expand Down Expand Up @@ -64,6 +65,9 @@ enum ExecResult {
Trap(crate::Trap),
}

/// Run a single step of the interpreter
/// A seperate function is used so later, we can more easily implement
/// a step-by-step debugger (using generators once they're stable?)
#[inline]
fn exec_one(
cf: &mut CallFrame,
Expand All @@ -74,6 +78,9 @@ fn exec_one(
module: &ModuleInstance,
) -> Result<ExecResult> {
use tinywasm_types::Instruction::*;

info!("ptr: {} instr: {:?}", cf.instr_ptr, instr);

match instr {
Nop => { /* do nothing */ }
Unreachable => return Ok(ExecResult::Trap(crate::Trap::Unreachable)), // we don't need to include the call frame here because it's already on the stack
Expand Down Expand Up @@ -117,23 +124,23 @@ fn exec_one(
}

Loop(args, end_offset) => {
cf.block_frames.push(BlockFrame {
cf.labels.push(LabelFrame {
instr_ptr: cf.instr_ptr,
end_instr_ptr: cf.instr_ptr + *end_offset,
stack_ptr: stack.values.len(),
args: *args,
block: BlockFrameInner::Loop,
ty: BlockType::Loop,
});
stack.values.block_args(*args)?;
}

Block(args, end_offset) => {
cf.block_frames.push(BlockFrame {
cf.labels.push(LabelFrame {
instr_ptr: cf.instr_ptr,
end_instr_ptr: cf.instr_ptr + *end_offset,
stack_ptr: stack.values.len(),
args: *args,
block: BlockFrameInner::Block,
ty: BlockType::Block,
});
stack.values.block_args(*args)?;
}
Expand Down Expand Up @@ -163,7 +170,7 @@ fn exec_one(
}

EndFunc => {
if cf.block_frames.len() > 0 {
if cf.labels.len() > 0 {
panic!("endfunc: block frames not empty, this should have been validated by the parser");
}

Expand All @@ -178,44 +185,24 @@ fn exec_one(
}

EndBlockFrame => {
let blocks = &mut cf.block_frames;
let blocks = &mut cf.labels;

// remove the label from the label stack
let Some(block) = blocks.pop() else {
panic!("end: no block to end, this should have been validated by the parser");
panic!("end: no label to end, this should have been validated by the parser");
};

debug!("end, blocks: {:?}", blocks);
debug!(" instr_ptr: {}", cf.instr_ptr);

let res: &[RawWasmValue] = match block.args {
BlockArgs::Empty => &[],
BlockArgs::Type(_t) => todo!(),
BlockArgs::FuncType(_t) => todo!(),
};

match block.block {
BlockFrameInner::Loop => {
debug!("end(loop): continue loop");
// trim the lable's stack from the stack
stack.values.trim(block.stack_ptr);

// remove the loop values from the stack
stack.values.trim(block.stack_ptr);

// set the instruction pointer to the start of the loop
cf.instr_ptr = block.instr_ptr;

// push the loop back onto the stack
blocks.push(block);
}
BlockFrameInner::Block => {
// remove the block values from the stack
stack.values.trim(block.stack_ptr);

// push the block result values to the stack
stack.values.extend(res.iter().copied());
}
_ => {
panic!("end: unimplemented block type end: {:?}", block.block);
}
}
// push the block result values to the stack
stack.values.extend(res.iter().copied());
}

LocalGet(local_index) => {
Expand Down Expand Up @@ -244,17 +231,16 @@ fn exec_one(
F32Sub => sub_instr!(f32, stack),
F64Sub => sub_instr!(f64, stack),

I32LtS => {
let [b, a] = stack.values.pop_n_const::<2>()?;
let a: i32 = a.into();
let b: i32 = b.into();
stack.values.push(((a < b) as i32).into());
debug!("i32.lt_s: {} < {} = {}", a, b, a < b);
}
I32LtS => lts_instr!(i32, stack),
I64LtS => lts_instr!(i64, stack),
F32Lt => lts_instr!(f32, stack),
F64Lt => lts_instr!(f64, stack),

I32GeS => ges_instr!(i32, stack),
I64GeS => ges_instr!(i64, stack),
F32Ge => ges_instr!(f32, stack),
F64Ge => ges_instr!(f64, stack),

I32DivS => div_instr!(i32, stack),
I64DivS => div_instr!(i64, stack),
F32Div => div_instr!(f32, stack),
Expand Down
2 changes: 1 addition & 1 deletion crates/tinywasm/src/runtime/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod call_stack;
mod value_stack;

use self::{call_stack::CallStack, value_stack::ValueStack};
pub(crate) use blocks::{BlockFrame, BlockFrameInner};
pub(crate) use blocks::{BlockType, LabelFrame};
pub(crate) use call_stack::CallFrame;

/// A WebAssembly Stack
Expand Down
21 changes: 13 additions & 8 deletions crates/tinywasm/src/runtime/stack/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,33 @@ use log::info;
use tinywasm_types::BlockArgs;

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

impl Blocks {
impl Labels {
pub(crate) fn len(&self) -> usize {
self.0.len()
}

#[inline]
pub(crate) fn push(&mut self, block: BlockFrame) {
pub(crate) fn push(&mut self, block: LabelFrame) {
self.0.push(block);
}

#[inline]
pub(crate) fn top(&self) -> Option<&LabelFrame> {
self.0.last()
}

#[inline]
/// get the block at the given index, where 0 is the top of the stack
pub(crate) fn get_relative_to_top(&self, index: usize) -> Option<&BlockFrame> {
pub(crate) fn get_relative_to_top(&self, index: usize) -> Option<&LabelFrame> {
info!("get block: {}", index);
info!("blocks: {:?}", self.0);
self.0.get(self.0.len() - index - 1)
}

#[inline]
pub(crate) fn pop(&mut self) -> Option<BlockFrame> {
pub(crate) fn pop(&mut self) -> Option<LabelFrame> {
self.0.pop()
}

Expand All @@ -36,7 +41,7 @@ impl Blocks {
}

#[derive(Debug, Clone)]
pub(crate) struct BlockFrame {
pub(crate) struct LabelFrame {
// position of the instruction pointer when the block was entered
pub(crate) instr_ptr: usize,
// position of the end instruction of the block
Expand All @@ -45,12 +50,12 @@ pub(crate) struct BlockFrame {
// position of the stack pointer when the block was entered
pub(crate) stack_ptr: usize,
pub(crate) args: BlockArgs,
pub(crate) block: BlockFrameInner,
pub(crate) ty: BlockType,
}

#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
pub(crate) enum BlockFrameInner {
pub(crate) enum BlockType {
Loop,
If,
Else,
Expand Down
Loading

0 comments on commit 366ef54

Please sign in to comment.