From 197c32f86108355e9a793346425008078ac06214 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Thu, 14 Dec 2023 20:12:35 +0100 Subject: [PATCH] feat: support a bunch more new instructions Signed-off-by: Henry Gressmann --- .../tinywasm/src/runtime/executor/macros.rs | 78 +++++++++++++++++++ crates/tinywasm/src/runtime/executor/mod.rs | 37 ++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/crates/tinywasm/src/runtime/executor/macros.rs b/crates/tinywasm/src/runtime/executor/macros.rs index 12e8ce8..1fc0a03 100644 --- a/crates/tinywasm/src/runtime/executor/macros.rs +++ b/crates/tinywasm/src/runtime/executor/macros.rs @@ -1,3 +1,10 @@ +/// 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. + /// Add two values from the stack macro_rules! add_instr { ($ty:ty, $stack:ident) => {{ @@ -79,6 +86,31 @@ macro_rules! ltu_instr { }}; } +/// Less than equal signed instruction +macro_rules! les_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()); + }}; +} + +/// Less than equal unsigned instruction +macro_rules! leu_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) => {{ @@ -140,14 +172,60 @@ macro_rules! geu_instr { }}; } +/// 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(); + + $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()); + }}; +} + pub(super) use add_instr; pub(super) use checked_divs_instr; pub(super) use checked_divu_instr; +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; diff --git a/crates/tinywasm/src/runtime/executor/mod.rs b/crates/tinywasm/src/runtime/executor/mod.rs index 422726e..5b6be84 100644 --- a/crates/tinywasm/src/runtime/executor/mod.rs +++ b/crates/tinywasm/src/runtime/executor/mod.rs @@ -77,10 +77,9 @@ fn exec_one( store: &mut Store, module: &ModuleInstance, ) -> Result { - use tinywasm_types::Instruction::*; - info!("ptr: {} instr: {:?}", cf.instr_ptr, instr); + use tinywasm_types::Instruction::*; 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 @@ -224,6 +223,13 @@ fn exec_one( 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), @@ -231,10 +237,19 @@ fn exec_one( 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), + + // 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), @@ -255,6 +270,24 @@ fn exec_one( F32Ne => ne_instr!(f32, stack), F64Ne => ne_instr!(f64, stack), + F32ConvertI32S => conv_1!(i32, f32, stack), + F32ConvertI64S => conv_1!(i64, f32, stack), + F64ConvertI32S => conv_1!(i32, f64, stack), + F64ConvertI64S => conv_1!(i64, f64, stack), + F32ConvertI32U => conv_2!(i32, u32, f32, stack), + F32ConvertI64U => conv_2!(i64, u64, f32, stack), + F64ConvertI32U => conv_2!(i32, u32, f64, stack), + F64ConvertI64U => conv_2!(i64, u64, f64, stack), + I64ExtendI32U => conv_2!(i32, u32, i64, stack), + I64ExtendI32S => conv_1!(i32, i64, stack), + I32WrapI64 => conv_1!(i64, i32, stack), + + // no-op instructions since types are erased at runtime + I32ReinterpretF32 => {} + I64ReinterpretF64 => {} + F32ReinterpretI32 => {} + F64ReinterpretI64 => {} + i => todo!("{:?}", i), };