From b4cb5f9b93387922255acdb49863182372c708db Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Thu, 14 Dec 2023 20:46:11 +0100 Subject: chore: simplify instruction macros Signed-off-by: Henry Gressmann --- crates/tinywasm/src/runtime/executor/macros.rs | 236 +++++------------------ crates/tinywasm/src/runtime/executor/mod.rs | 126 ++++++------ crates/tinywasm/src/runtime/stack/value_stack.rs | 5 + crates/tinywasm/src/runtime/value.rs | 2 +- 4 files changed, 119 insertions(+), 250 deletions(-) (limited to 'crates') 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), - - 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), - I32LtU => ltu_instr!(i32, u32, stack), - I64LtU => ltu_instr!(i64, u64, stack), - F32Lt => lts_instr!(f32, stack), - F64Lt => lts_instr!(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), - - 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), - - 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), + I64Eqz => comp_zero!(==, i64, stack), + I32Eqz => comp_zero!(==, i32, stack), + + I32Eq => comp!(==, i32, stack), + I64Eq => comp!(==, i64, stack), + F32Eq => comp!(==, f32, stack), + F64Eq => comp!(==, f64, stack), + + I32Ne => comp!(!=, i32, stack), + I64Ne => comp!(!=, i64, stack), + F32Ne => comp!(!=, f32, stack), + F64Ne => comp!(!=, 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), + + 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), + + 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), + + 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), + + I32Sub => arithmetic!(-, i32, stack), + I64Sub => arithmetic!(-, i64, stack), + F32Sub => arithmetic!(-, f32, stack), + F64Sub => arithmetic!(-, f64, stack), + + F32Div => arithmetic!(/, f32, stack), + F64Div => arithmetic!(/, f64, stack), + + I32Mul => arithmetic!(*, i32, stack), + I64Mul => arithmetic!(*, i64, stack), + F32Mul => arithmetic!(*, f32, stack), + F64Mul => arithmetic!(*, 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), - - F32Div => div_instr!(f32, stack), - F64Div => div_instr!(f64, stack), - - I32Mul => mul_instr!(i32, stack), - I64Mul => mul_instr!(i64, stack), - F32Mul => mul_instr!(f32, stack), - F64Mul => mul_instr!(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), - - I32Ne => ne_instr!(i32, stack), - I64Ne => ne_instr!(i64, stack), - F32Ne => ne_instr!(f32, stack), - F64Ne => ne_instr!(f64, stack), + 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 @@ -23,6 +23,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()); 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 { -- cgit v1.3.1