summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorHenry Gressmann <mail@henrygressmann.de>2023-12-14 18:00:46 +0100
committerHenry Gressmann <mail@henrygressmann.de>2023-12-14 18:00:46 +0100
commitd2b30c3e5ee51b76472dd7cef29963b7757298a6 (patch)
tree68f7a08ca4e03e3c709d111aaba5e55218d4afdd /crates
parent366ef5438468c4f13c6bf7d970ce450160942b94 (diff)
feat: support more wasm opcodes
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
Diffstat (limited to 'crates')
-rw-r--r--crates/tinywasm/src/error.rs6
-rw-r--r--crates/tinywasm/src/runtime/executor/macros.rs78
-rw-r--r--crates/tinywasm/src/runtime/executor/mod.rs83
-rw-r--r--crates/tinywasm/src/runtime/stack/value_stack.rs16
4 files changed, 131 insertions, 52 deletions
diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs
index bea97fa..8776365 100644
--- a/crates/tinywasm/src/error.rs
+++ b/crates/tinywasm/src/error.rs
@@ -11,6 +11,12 @@ use tinywasm_parser::ParseError;
pub enum Trap {
/// An unreachable instruction was executed
Unreachable,
+
+ /// An out-of-bounds memory access occurred
+ MemoryOutOfBounds,
+
+ /// A division by zero occurred
+ DivisionByZero,
}
#[derive(Debug)]
diff --git a/crates/tinywasm/src/runtime/executor/macros.rs b/crates/tinywasm/src/runtime/executor/macros.rs
index 91d48d3..12e8ce8 100644
--- a/crates/tinywasm/src/runtime/executor/macros.rs
+++ b/crates/tinywasm/src/runtime/executor/macros.rs
@@ -19,6 +19,34 @@ macro_rules! sub_instr {
}
/// Divide the top two values on the stack
+macro_rules! checked_divs_instr {
+ ($ty:ty, $stack:ident) => {{
+ let [a, b] = $stack.values.pop_n_const::<2>()?;
+ let a: $ty = a.into();
+ let b: $ty = b.into();
+ let Some(res) = a.checked_div(b) else {
+ return Err(Error::Trap(crate::Trap::DivisionByZero));
+ };
+
+ $stack.values.push(res.into());
+ }};
+}
+
+/// Divide the top two values on the stack
+macro_rules! checked_divu_instr {
+ ($ty:ty, $uty:ty, $stack:ident) => {{
+ let [a, b] = $stack.values.pop_n_const::<2>()?;
+ let a: $ty = a.into();
+ let b: $ty = b.into();
+ let Some(res) = (a as $uty).checked_div(b as $uty) else {
+ return Err(Error::Trap(crate::Trap::DivisionByZero));
+ };
+
+ $stack.values.push((res as $ty).into());
+ }};
+}
+
+/// Divide the top two values on the stack
macro_rules! div_instr {
($ty:ty, $stack:ident) => {{
let [a, b] = $stack.values.pop_n_const::<2>()?;
@@ -38,6 +66,19 @@ macro_rules! lts_instr {
}};
}
+/// Less than unsigned instruction
+macro_rules! ltu_instr {
+ ($ty:ty, $uty:ty, $stack:ident) => {{
+ let [a, b] = $stack.values.pop_n_const::<2>()?;
+ let a: $ty = a.into();
+ let b: $ty = b.into();
+ // Cast to unsigned type before comparison
+ let a_unsigned: $uty = a as $uty;
+ let b_unsigned: $uty = b as $uty;
+ $stack.values.push(((a_unsigned < b_unsigned) as i32).into());
+ }};
+}
+
/// Multiply the top two values on the stack
macro_rules! mul_instr {
($ty:ty, $stack:ident) => {{
@@ -58,6 +99,24 @@ macro_rules! eq_instr {
}};
}
+/// Compare the top value on the stack for equality with zero
+macro_rules! eqz_instr {
+ ($ty:ty, $stack:ident) => {{
+ let a: $ty = $stack.values.pop()?.into();
+ $stack.values.push(((a == 0) as i32).into());
+ }};
+}
+
+/// Compare the top two values on the stack for inequality
+macro_rules! ne_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());
+ }};
+}
+
/// Greater or equal than signed instruction
macro_rules! ges_instr {
($ty:ty, $stack:ident) => {{
@@ -68,10 +127,29 @@ macro_rules! ges_instr {
}};
}
+/// Greater or equal than unsigned instruction
+macro_rules! geu_instr {
+ ($ty:ty, $uty:ty, $stack:ident) => {{
+ let [a, b] = $stack.values.pop_n_const::<2>()?;
+ let a: $ty = a.into();
+ let b: $ty = b.into();
+ // Cast to unsigned type before comparison
+ let a_unsigned: $uty = a as $uty;
+ let b_unsigned: $uty = b as $uty;
+ $stack.values.push(((a_unsigned >= b_unsigned) as i32).into());
+ }};
+}
+
pub(super) use add_instr;
+pub(super) use checked_divs_instr;
+pub(super) use checked_divu_instr;
pub(super) use div_instr;
pub(super) use eq_instr;
+pub(super) use eqz_instr;
pub(super) use ges_instr;
+pub(super) use geu_instr;
pub(super) use lts_instr;
+pub(super) use ltu_instr;
pub(super) use mul_instr;
+pub(super) use ne_instr;
pub(super) use sub_instr;
diff --git a/crates/tinywasm/src/runtime/executor/mod.rs b/crates/tinywasm/src/runtime/executor/mod.rs
index 25c7601..422726e 100644
--- a/crates/tinywasm/src/runtime/executor/mod.rs
+++ b/crates/tinywasm/src/runtime/executor/mod.rs
@@ -84,23 +84,18 @@ fn exec_one(
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
- Drop => {
- stack.values.pop().ok_or(Error::StackUnderflow)?;
- }
+ Drop => stack.values.pop().map(|_| ())?,
+ Return => todo!("called function returned"),
Select => {
- let cond: i32 = stack.values.pop().ok_or(Error::StackUnderflow)?.into();
- let val2 = stack.values.pop().ok_or(Error::StackUnderflow)?;
+ let cond: i32 = stack.values.pop()?.into();
+ let val2 = stack.values.pop()?;
// if cond != 0, we already have the right value on the stack
if cond == 0 {
- let _ = stack.values.pop().ok_or(Error::StackUnderflow)?;
+ let _ = stack.values.pop()?;
stack.values.push(val2);
}
}
- Return => {
- debug!("return");
- }
-
Call(v) => {
debug!("start call");
// prepare the call frame
@@ -131,7 +126,7 @@ fn exec_one(
args: *args,
ty: BlockType::Loop,
});
- stack.values.block_args(*args)?;
+ stack.values.push_block_args(*args)?;
}
Block(args, end_offset) => {
@@ -142,7 +137,7 @@ fn exec_one(
args: *args,
ty: BlockType::Block,
});
- stack.values.block_args(*args)?;
+ stack.values.push_block_args(*args)?;
}
BrTable(_default, len) => {
@@ -160,11 +155,10 @@ fn exec_one(
todo!()
}
+
Br(v) => cf.break_to(*v, &mut stack.values)?,
BrIf(v) => {
- let val: i32 = stack.values.pop().ok_or(Error::StackUnderflow)?.into();
- debug!("br_if: {}", val);
- if val > 0 {
+ if stack.values.pop_t::<i32>()? > 0 {
cf.break_to(*v, &mut stack.values)?
};
}
@@ -174,13 +168,12 @@ fn exec_one(
panic!("endfunc: block frames not empty, this should have been validated by the parser");
}
- 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()?;
- return Ok(ExecResult::Call);
+ match stack.call_stack.is_empty() {
+ true => return Ok(ExecResult::Return),
+ false => {
+ *cf = stack.call_stack.pop()?;
+ return Ok(ExecResult::Call);
+ }
}
}
@@ -205,22 +198,15 @@ fn exec_one(
stack.values.extend(res.iter().copied());
}
- LocalGet(local_index) => {
- debug!("local.get: {:?}", local_index);
- let val = cf.get_local(*local_index as usize);
- stack.values.push(val);
- }
- LocalSet(local_index) => {
- let val = stack.values.pop().ok_or(Error::StackUnderflow)?;
- cf.set_local(*local_index as usize, val);
- }
- // Equivalent to local.set, local.get
- LocalTee(local_index) => {
- let val = stack.values.last().ok_or(Error::StackUnderflow)?;
- cf.set_local(*local_index as usize, *val);
- }
+ 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, *stack.values.last()?),
+
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()),
+
I64Add => add_instr!(i64, stack),
I32Add => add_instr!(i32, stack),
F32Add => add_instr!(f32, stack),
@@ -233,16 +219,22 @@ fn exec_one(
I32LtS => lts_instr!(i32, stack),
I64LtS => lts_instr!(i64, stack),
+ I32LtU => ltu_instr!(i32, u32, stack),
+ I64LtU => ltu_instr!(i64, u64, stack),
F32Lt => lts_instr!(f32, stack),
F64Lt => lts_instr!(f64, stack),
I32GeS => ges_instr!(i32, stack),
I64GeS => ges_instr!(i64, stack),
+ I32GeU => geu_instr!(i32, u32, stack),
+ I64GeU => geu_instr!(i64, u64, stack),
F32Ge => ges_instr!(f32, stack),
F64Ge => ges_instr!(f64, stack),
- I32DivS => div_instr!(i32, stack),
- I64DivS => div_instr!(i64, stack),
+ I32DivS => checked_divs_instr!(i32, stack),
+ I64DivS => checked_divs_instr!(i64, stack),
+ I32DivU => checked_divu_instr!(i32, u32, stack),
+ I64DivU => checked_divu_instr!(i64, u64, stack),
F32Div => div_instr!(f32, stack),
F64Div => div_instr!(f64, stack),
@@ -253,18 +245,15 @@ fn exec_one(
I32Eq => eq_instr!(i32, stack),
I64Eq => eq_instr!(i64, stack),
+ I32Eqz => eqz_instr!(i32, stack),
+ I64Eqz => eqz_instr!(i64, stack),
F32Eq => eq_instr!(f32, stack),
F64Eq => eq_instr!(f64, stack),
- I32Eqz => {
- let val: i32 = stack.values.pop().ok_or(Error::StackUnderflow)?.into();
- stack.values.push(((val == 0) as i32).into());
- }
-
- I64Eqz => {
- let val: i64 = stack.values.pop().ok_or(Error::StackUnderflow)?.into();
- stack.values.push(((val == 0) as i32).into());
- }
+ I32Ne => ne_instr!(i32, stack),
+ I64Ne => ne_instr!(i64, stack),
+ F32Ne => ne_instr!(f32, stack),
+ F64Ne => ne_instr!(f64, stack),
i => todo!("{:?}", i),
};
diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs
index 9dc9bcb..59be3d2 100644
--- a/crates/tinywasm/src/runtime/stack/value_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/value_stack.rs
@@ -37,7 +37,7 @@ impl ValueStack {
}
#[inline]
- pub(crate) fn block_args(&self, args: BlockArgs) -> Result<()> {
+ pub(crate) fn push_block_args(&self, args: BlockArgs) -> Result<()> {
match args {
BlockArgs::Empty => Ok(()),
BlockArgs::Type(_t) => todo!(),
@@ -58,14 +58,20 @@ impl ValueStack {
}
#[inline]
- pub(crate) fn last(&self) -> Option<&RawWasmValue> {
- self.stack.last()
+ pub(crate) fn last(&self) -> Result<&RawWasmValue> {
+ self.stack.last().ok_or(Error::StackUnderflow)
}
#[inline]
- pub(crate) fn pop(&mut self) -> Option<RawWasmValue> {
+ pub(crate) fn pop_t<T: From<RawWasmValue>>(&mut self) -> Result<T> {
self.top -= 1;
- self.stack.pop()
+ Ok(self.pop()?.into())
+ }
+
+ #[inline]
+ pub(crate) fn pop(&mut self) -> Result<RawWasmValue> {
+ self.top -= 1;
+ self.stack.pop().ok_or(Error::StackUnderflow)
}
#[inline]