summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/tinywasm/src/runtime/executor/macros.rs78
-rw-r--r--crates/tinywasm/src/runtime/executor/mod.rs37
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<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
@@ -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),
};