Skip to content

Commit

Permalink
feat: support a bunch more new instructions
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 14, 2023
1 parent d2b30c3 commit 197c32f
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 2 deletions.
78 changes: 78 additions & 0 deletions crates/tinywasm/src/runtime/executor/macros.rs
Original file line number Diff line number Diff line change
@@ -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) => {{
Expand Down Expand Up @@ -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) => {{
Expand Down Expand Up @@ -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;
Expand Down
37 changes: 35 additions & 2 deletions crates/tinywasm/src/runtime/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,9 @@ fn exec_one(
store: &mut Store,
module: &ModuleInstance,
) -> Result<ExecResult> {
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
Expand Down Expand Up @@ -224,17 +223,33 @@ 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),
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),

// 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),

Expand All @@ -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),
};

Expand Down

0 comments on commit 197c32f

Please sign in to comment.