summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorHenry Gressmann <mail@henrygressmann.de>2023-12-14 20:46:11 +0100
committerHenry Gressmann <mail@henrygressmann.de>2023-12-14 20:46:11 +0100
commitb4cb5f9b93387922255acdb49863182372c708db (patch)
treea7cd7b9a9451a3c939b4d8f4a87ed19df9d7aeaa /crates
parent197c32f86108355e9a793346425008078ac06214 (diff)
chore: simplify instruction macros
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
Diffstat (limited to 'crates')
-rw-r--r--crates/tinywasm/src/runtime/executor/macros.rs236
-rw-r--r--crates/tinywasm/src/runtime/executor/mod.rs110
-rw-r--r--crates/tinywasm/src/runtime/stack/value_stack.rs5
-rw-r--r--crates/tinywasm/src/runtime/value.rs2
4 files changed, 111 insertions, 242 deletions
diff --git a/crates/tinywasm/src/runtime/executor/macros.rs b/crates/tinywasm/src/runtime/executor/macros.rs
index 1fc0a03..5a9a570 100644
--- a/crates/tinywasm/src/runtime/executor/macros.rs
+++ b/crates/tinywasm/src/runtime/executor/macros.rs
@@ -1,233 +1,97 @@
-/// More generic macros for various instructions
-///
-/// These macros are used to generate the actual instruction implementations.
-///
-/// A bunch of these could be simplified, but some copy-paste is sometimes just simpler - this is way nicer for debugging.
-/// Might also be nicer for the compiler to not have closures everywhere, the assembly from this in godbolt is pretty good like this.
+//! More generic macros for various instructions
+//!
+//! These macros are used to generate the actual instruction implementations.
-/// Add two values from the stack
-macro_rules! add_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).into());
- }};
-}
-
-/// Subtract the top two values on the stack
-macro_rules! sub_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).into());
- }};
-}
-
-/// 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>()?;
- let a: $ty = a.into();
- let b: $ty = b.into();
- $stack.values.push((a / b).into());
- }};
-}
-
-/// Less than signed instruction
-macro_rules! lts_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());
+/// Convert the top value on the stack to a specific type
+macro_rules! conv_1 {
+ ($from:ty, $to:ty, $stack:ident) => {{
+ let a: $from = $stack.values.pop()?.into();
+ $stack.values.push((a as $to).into());
}};
}
-/// 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());
+/// Convert the unsigned value on the top of the stack to a specific type
+macro_rules! conv_2 {
+ ($ty:ty, $uty:ty, $to:ty, $stack:ident) => {{
+ let a: $ty = $stack.values.pop()?.into();
+ $stack.values.push((a as $uty as $to).into());
}};
}
-/// Less than equal signed instruction
-macro_rules! les_instr {
- ($ty:ty, $stack:ident) => {{
+/// Compare two values on the stack
+macro_rules! comp {
+ ($op:tt, $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());
+ $stack.values.push(((a $op b) as i32).into());
}};
}
-/// Less than equal unsigned instruction
-macro_rules! leu_instr {
- ($ty:ty, $uty:ty, $stack:ident) => {{
+/// Compare two values on the stack (cast to ty2 before comparison)
+macro_rules! comp_cast {
+ ($op:tt, $ty:ty, $ty2: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) => {{
- let [a, b] = $stack.values.pop_n_const::<2>()?;
- let a: $ty = a.into();
- let b: $ty = b.into();
- $stack.values.push((a * b).into());
+ let a_unsigned: $ty2 = a as $ty2;
+ let b_unsigned: $ty2 = b as $ty2;
+ $stack.values.push(((a_unsigned $op b_unsigned) as i32).into());
}};
}
-/// Compare the top two values on the stack for equality
-macro_rules! eq_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());
- }};
-}
-
-/// Compare the top value on the stack for equality with zero
-macro_rules! eqz_instr {
- ($ty:ty, $stack:ident) => {{
+/// Compare a value on the stack to zero
+macro_rules! comp_zero {
+ ($op:tt, $ty:ty, $stack:ident) => {{
let a: $ty = $stack.values.pop()?.into();
- $stack.values.push(((a == 0) as i32).into());
+ $stack.values.push(((a $op 0) as i32).into());
}};
}
-/// Compare the top two values on the stack for inequality
-macro_rules! ne_instr {
- ($ty:ty, $stack:ident) => {{
+/// Apply an arithmetic operation to two values on the stack
+macro_rules! arithmetic {
+ ($op:tt, $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());
+ $stack.values.push((a $op b).into());
}};
}
-/// Greater or equal than signed instruction
-macro_rules! ges_instr {
- ($ty:ty, $stack:ident) => {{
+/// Apply an arithmetic operation to two values on the stack
+macro_rules! checked_arithmetic {
+ ($op:ident, $ty:ty, $stack:ident, $trap:expr) => {{
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());
+ let result = a.$op(b).ok_or_else(|| Error::Trap($trap))?;
+ $stack.values.push(result.into());
}};
}
-/// Greater or equal than unsigned instruction
-macro_rules! geu_instr {
- ($ty:ty, $uty:ty, $stack:ident) => {{
+/// Apply an arithmetic operation to two values on the stack (cast to ty2 before operation)
+macro_rules! checked_arithmetic_cast {
+ ($op:ident, $ty:ty, $ty2:ty, $stack:ident, $trap:expr) => {{
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());
- }};
-}
-/// Greater than instruction
-macro_rules! gts_instr {
- ($ty: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 operation
+ let a_unsigned: $ty2 = a as $ty2;
+ let b_unsigned: $ty2 = b as $ty2;
- $stack.values.push(((a > b) as i32).into());
- }};
-}
-
-/// Greater than instruction (convert to unsigned before comparison)
-macro_rules! gtu_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());
- }};
-}
-
-/// Convert the top value on the stack to a specific type
-macro_rules! conv_1 {
- ($from:ty, $to:ty, $stack:ident) => {{
- let a: $from = $stack.values.pop()?.into();
- $stack.values.push((a as $to).into());
- }};
-}
-
-/// Convert the unsigned value on the top of the stack to a specific type
-macro_rules! conv_2 {
- ($ty:ty, $uty:ty, $to:ty, $stack:ident) => {{
- let a: $ty = $stack.values.pop()?.into();
- $stack.values.push((a as $uty as $to).into());
+ let result = a_unsigned.$op(b_unsigned).ok_or_else(|| Error::Trap($trap))?;
+ $stack.values.push((result as $ty).into());
}};
}
-pub(super) use add_instr;
-pub(super) use checked_divs_instr;
-pub(super) use checked_divu_instr;
+pub(super) use arithmetic;
+pub(super) use checked_arithmetic;
+pub(super) use checked_arithmetic_cast;
+pub(super) use comp;
+pub(super) use comp_cast;
+pub(super) use comp_zero;
pub(super) use conv_1;
pub(super) use conv_2;
-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 gts_instr;
-pub(super) use gtu_instr;
-pub(super) use les_instr;
-pub(super) use leu_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 5b6be84..3577325 100644
--- a/crates/tinywasm/src/runtime/executor/mod.rs
+++ b/crates/tinywasm/src/runtime/executor/mod.rs
@@ -91,8 +91,7 @@ fn exec_one(
// if cond != 0, we already have the right value on the stack
if cond == 0 {
- let _ = stack.values.pop()?;
- stack.values.push(val2);
+ stack.values.last_mut().map(|v| *v = val2);
}
}
Call(v) => {
@@ -206,69 +205,70 @@ fn exec_one(
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),
- F64Add => add_instr!(f64, stack),
+ I64Eqz => comp_zero!(==, i64, stack),
+ I32Eqz => comp_zero!(==, i32, stack),
- I32Sub => sub_instr!(i32, stack),
- I64Sub => sub_instr!(i64, stack),
- F32Sub => sub_instr!(f32, stack),
- F64Sub => sub_instr!(f64, stack),
+ I32Eq => comp!(==, i32, stack),
+ I64Eq => comp!(==, i64, stack),
+ F32Eq => comp!(==, f32, stack),
+ F64Eq => comp!(==, f64, stack),
- 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),
+ I32Ne => comp!(!=, i32, stack),
+ I64Ne => comp!(!=, i64, stack),
+ F32Ne => comp!(!=, f32, stack),
+ F64Ne => comp!(!=, f64, stack),
- I32LeS => les_instr!(i32, stack),
- I64LeS => les_instr!(i64, stack),
- I32LeU => leu_instr!(i32, u32, stack),
- I64LeU => leu_instr!(i64, u64, stack),
- F32Le => les_instr!(f32, stack),
- F64Le => les_instr!(f64, stack),
+ I32LtS => comp!(<, i32, stack),
+ I64LtS => comp!(<, i64, stack),
+ I32LtU => comp_cast!(<, i32, u32, stack),
+ I64LtU => comp_cast!(<, i64, u64, stack),
+ F32Lt => comp!(<, f32, stack),
+ F64Lt => comp!(<, 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),
+ I32LeS => comp!(<=, i32, stack),
+ I64LeS => comp!(<=, i64, stack),
+ I32LeU => comp_cast!(<=, i32, u32, stack),
+ I64LeU => comp_cast!(<=, i64, u64, stack),
+ F32Le => comp!(<=, f32, stack),
+ F64Le => comp!(<=, f64, stack),
- I32GtS => gts_instr!(i32, stack),
- I64GtS => gts_instr!(i64, stack),
- I32GtU => gtu_instr!(i32, u32, stack),
- I64GtU => gtu_instr!(i64, u64, stack),
- F32Gt => gts_instr!(f32, stack),
- F64Gt => gts_instr!(f64, stack),
+ I32GeS => comp!(>=, i32, stack),
+ I64GeS => comp!(>=, i64, stack),
+ I32GeU => comp_cast!(>=, i32, u32, stack),
+ I64GeU => comp_cast!(>=, i64, u64, stack),
+ F32Ge => comp!(>=, f32, stack),
+ F64Ge => comp!(>=, f64, stack),
- // these can trap
- 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),
+ I32GtS => comp!(>, i32, stack),
+ I64GtS => comp!(>, i64, stack),
+ I32GtU => comp_cast!(>, i32, u32, stack),
+ I64GtU => comp_cast!(>, i64, u64, stack),
+ F32Gt => comp!(>, f32, stack),
+ F64Gt => comp!(>, f64, stack),
+
+ I64Add => arithmetic!(+, i64, stack),
+ I32Add => arithmetic!(+, i32, stack),
+ F32Add => arithmetic!(+, f32, stack),
+ F64Add => arithmetic!(+, f64, stack),
- F32Div => div_instr!(f32, stack),
- F64Div => div_instr!(f64, stack),
+ I32Sub => arithmetic!(-, i32, stack),
+ I64Sub => arithmetic!(-, i64, stack),
+ F32Sub => arithmetic!(-, f32, stack),
+ F64Sub => arithmetic!(-, f64, stack),
- I32Mul => mul_instr!(i32, stack),
- I64Mul => mul_instr!(i64, stack),
- F32Mul => mul_instr!(f32, stack),
- F64Mul => mul_instr!(f64, stack),
+ F32Div => arithmetic!(/, f32, stack),
+ F64Div => arithmetic!(/, f64, stack),
- 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),
+ I32Mul => arithmetic!(*, i32, stack),
+ I64Mul => arithmetic!(*, i64, stack),
+ F32Mul => arithmetic!(*, f32, stack),
+ F64Mul => arithmetic!(*, f64, stack),
- I32Ne => ne_instr!(i32, stack),
- I64Ne => ne_instr!(i64, stack),
- F32Ne => ne_instr!(f32, stack),
- F64Ne => ne_instr!(f64, stack),
+ // these can trap
+ I32DivS => checked_arithmetic!(checked_div, i32, stack, crate::Trap::DivisionByZero),
+ I64DivS => checked_arithmetic!(checked_div, i64, stack, crate::Trap::DivisionByZero),
+ I32DivU => checked_arithmetic_cast!(checked_div, i32, u32, stack, crate::Trap::DivisionByZero),
+ I64DivU => checked_arithmetic_cast!(checked_div, i64, u64, stack, crate::Trap::DivisionByZero),
F32ConvertI32S => conv_1!(i32, f32, stack),
F32ConvertI64S => conv_1!(i64, f32, stack),
diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs
index 59be3d2..717d251 100644
--- a/crates/tinywasm/src/runtime/stack/value_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/value_stack.rs
@@ -24,6 +24,11 @@ impl Default for ValueStack {
impl ValueStack {
#[inline]
+ pub(crate) fn last_mut(&mut self) -> Option<&mut RawWasmValue> {
+ self.stack.last_mut()
+ }
+
+ #[inline]
pub(crate) fn len(&self) -> usize {
assert!(self.top <= self.stack.len());
self.top
diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs
index 2476038..16fb42c 100644
--- a/crates/tinywasm/src/runtime/value.rs
+++ b/crates/tinywasm/src/runtime/value.rs
@@ -7,7 +7,7 @@ use tinywasm_types::{ValType, WasmValue};
/// This is the internal representation of all wasm values
///
/// See [`WasmValue`] for the public representation.
-#[derive(Clone, Copy, Default)]
+#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct RawWasmValue(u64);
impl Debug for RawWasmValue {