diff options
| -rw-r--r-- | crates/tinywasm/src/runtime/executor/macros.rs | 172 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/executor/mod.rs | 192 |
2 files changed, 157 insertions, 207 deletions
diff --git a/crates/tinywasm/src/runtime/executor/macros.rs b/crates/tinywasm/src/runtime/executor/macros.rs index d10a19e..3346732 100644 --- a/crates/tinywasm/src/runtime/executor/macros.rs +++ b/crates/tinywasm/src/runtime/executor/macros.rs @@ -2,12 +2,14 @@ //! //! These macros are used to generate the actual instruction implementations. +/// Load a value from memory macro_rules! mem_load { ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ mem_load!($type, $type, $arg, $stack, $store, $module) }}; ($load_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ + // TODO: there could be a lot of performance improvements here let mem_idx = $module.resolve_mem_addr($arg.mem_addr); let mem = $store.get_mem(mem_idx as usize)?; @@ -28,14 +30,6 @@ macro_rules! mem_load { }}; } -/// 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()); - }}; -} - /// Doing the actual conversion from float to int is a bit tricky, because /// we need to check for overflow. This macro generates the min/max values /// for a specific conversion, which are then used in the actual conversion. @@ -71,75 +65,56 @@ macro_rules! float_min_max { }; } -// Convert a float to an int, checking for overflow -macro_rules! checked_float_conv_1 { - ($from:tt, $to:tt, $stack:ident) => {{ - let (min, max) = float_min_max!($from, $to); +/// Convert a value on the stack +macro_rules! conv { + ($from:ty, $intermediate:ty, $to:ty, $stack:ident) => {{ + let a: $from = $stack.values.pop()?.into(); + $stack.values.push((a as $intermediate as $to).into()); + }}; + ($from:ty, $to:ty, $stack:ident) => {{ let a: $from = $stack.values.pop()?.into(); - - if a.is_nan() { - return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); - } - - if a <= min || a >= max { - return Err(Error::Trap(crate::Trap::IntegerOverflow)); - } - $stack.values.push((a as $to).into()); }}; } -// Convert a float to an int, checking for overflow -macro_rules! checked_float_conv_2 { - ($from:tt, $uty:tt, $to:tt, $stack:ident) => {{ - let (min, max) = float_min_max!($from, $uty); +/// Convert a value on the stack with error checking +macro_rules! checked_conv_float { + // Direct conversion with error checking (two types) + ($from:tt, $to:tt, $stack:ident) => {{ + checked_conv_float!($from, $to, $to, $stack) + }}; + // Conversion with an intermediate unsigned type and error checking (three types) + ($from:tt, $intermediate:tt, $to:tt, $stack:ident) => {{ + let (min, max) = float_min_max!($from, $intermediate); let a: $from = $stack.values.pop()?.into(); if a.is_nan() { return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); } - log::info!("a: {}", a); - log::info!("min: {}", min); - log::info!("max: {}", max); - if a <= min || a >= max { return Err(Error::Trap(crate::Trap::IntegerOverflow)); } - $stack.values.push((a as $uty 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()); + $stack.values.push((a as $intermediate as $to).into()); }}; } /// 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 $op b) as i32).into()); + comp!($op, $ty, $ty, $stack) }}; -} -/// Compare two values on the stack (cast to ty2 before comparison) -macro_rules! comp_cast { - ($op:tt, $ty:ty, $ty2:ty, $stack:ident) => {{ + ($op:tt, $intermediate:ty, $to:ty, $stack:ident) => {{ let [a, b] = $stack.values.pop_n_const::<2>()?; - let a: $ty = a.into(); - let b: $ty = b.into(); + let a: $intermediate = a.into(); + let b: $intermediate = b.into(); // Cast to unsigned type before comparison - 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()); + let a = a as $to; + let b = b as $to; + $stack.values.push(((a $op b) as i32).into()); }}; } @@ -151,27 +126,35 @@ macro_rules! comp_zero { }}; } -/// Apply an arithmetic operation to two values on the stack -macro_rules! arithmetic_op { +/// Apply an arithmetic method to two values on the stack +macro_rules! arithmetic { + ($op:ident, $ty:ty, $stack:ident) => {{ + arithmetic!($op, $ty, $ty, $stack) + }}; + + // also allow operators such as +, - ($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 $op b).into()); }}; -} -macro_rules! arithmetic_method { - ($op:ident, $ty:ty, $stack:ident) => {{ + ($op:ident, $intermediate:ty, $to:ty, $stack:ident) => {{ let [a, b] = $stack.values.pop_n_const::<2>()?; - let a: $ty = a.into(); - let b: $ty = b.into(); + let a: $to = a.into(); + let b: $to = b.into(); + + let a = a as $intermediate; + let b = b as $intermediate; + let result = a.$op(b); - $stack.values.push(result.into()); + $stack.values.push((result as $to).into()); }}; } -macro_rules! arithmetic_method_self { +/// Apply an arithmetic method to a single value on the stack +macro_rules! arithmetic_single { ($op:ident, $ty:ty, $stack:ident) => {{ let a: $ty = $stack.values.pop()?.into(); let result = a.$op(); @@ -179,67 +162,34 @@ macro_rules! arithmetic_method_self { }}; } -macro_rules! arithmetic_method_cast { - ($op:ident, $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 operation - let a_unsigned: $ty2 = a as $ty2; - let b_unsigned: $ty2 = b as $ty2; - - let result = a_unsigned.$op(b_unsigned); - $stack.values.push((result as $ty).into()); +/// Apply an arithmetic operation to two values on the stack with error checking +macro_rules! checked_arithmetic { + // Direct conversion with error checking (two types) + ($from:tt, $to:tt, $stack:ident, $trap:expr) => {{ + checked_arithmetic!($from, $to, $to, $stack, $trap) }}; -} -/// Apply an arithmetic operation to two values on the stack -macro_rules! checked_arithmetic_method { - ($op:ident, $ty:ty, $stack:ident, $trap:expr) => {{ + ($op:ident, $from:ty, $to:ty, $stack:ident, $trap:expr) => {{ let [a, b] = $stack.values.pop_n_const::<2>()?; - let a: $ty = a.into(); - let b: $ty = b.into(); - let result = a.$op(b).ok_or_else(|| Error::Trap($trap))?; - debug!( - "checked_arithmetic_method: {}, a: {}, b: {}, res: {}", - stringify!($op), - a, - b, - result - ); - $stack.values.push(result.into()); - }}; -} + let a: $from = a.into(); + let b: $from = b.into(); -/// Apply an arithmetic operation to two values on the stack (cast to ty2 before operation) -macro_rules! checked_arithmetic_method_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(); + let a_casted: $to = a as $to; + let b_casted: $to = b as $to; - // Cast to unsigned type before operation - let a_unsigned: $ty2 = a as $ty2; - let b_unsigned: $ty2 = b as $ty2; + let result = a_casted.$op(b_casted).ok_or_else(|| Error::Trap($trap))?; - let result = a_unsigned.$op(b_unsigned).ok_or_else(|| Error::Trap($trap))?; - $stack.values.push((result as $ty).into()); + // Cast back to original type if different + $stack.values.push((result as $from).into()); }}; } -pub(super) use arithmetic_method; -pub(super) use arithmetic_method_cast; -pub(super) use arithmetic_method_self; -pub(super) use arithmetic_op; -pub(super) use checked_arithmetic_method; -pub(super) use checked_arithmetic_method_cast; -pub(super) use checked_float_conv_1; -pub(super) use checked_float_conv_2; +pub(super) use arithmetic; +pub(super) use arithmetic_single; +pub(super) use checked_arithmetic; +pub(super) use checked_conv_float; 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 conv; pub(super) use float_min_max; pub(super) use mem_load; diff --git a/crates/tinywasm/src/runtime/executor/mod.rs b/crates/tinywasm/src/runtime/executor/mod.rs index 342e99b..5dcab99 100644 --- a/crates/tinywasm/src/runtime/executor/mod.rs +++ b/crates/tinywasm/src/runtime/executor/mod.rs @@ -343,122 +343,122 @@ fn exec_one( I32LtS => comp!(<, i32, stack), I64LtS => comp!(<, i64, stack), - I32LtU => comp_cast!(<, i32, u32, stack), - I64LtU => comp_cast!(<, i64, u64, stack), + I32LtU => comp!(<, i32, u32, stack), + I64LtU => comp!(<, 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), + I32LeU => comp!(<=, i32, u32, stack), + I64LeU => comp!(<=, 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), + I32GeU => comp!(>=, i32, u32, stack), + I64GeU => comp!(>=, 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), + I32GtU => comp!(>, i32, u32, stack), + I64GtU => comp!(>, i64, u64, stack), F32Gt => comp!(>, f32, stack), F64Gt => comp!(>, f64, stack), - I64Add => arithmetic_method!(wrapping_add, i64, stack), - I32Add => arithmetic_method!(wrapping_add, i32, stack), - F32Add => arithmetic_op!(+, f32, stack), - F64Add => arithmetic_op!(+, f64, stack), + I64Add => arithmetic!(wrapping_add, i64, stack), + I32Add => arithmetic!(wrapping_add, i32, stack), + F32Add => arithmetic!(+, f32, stack), + F64Add => arithmetic!(+, f64, stack), - I32Sub => arithmetic_method!(wrapping_sub, i32, stack), - I64Sub => arithmetic_method!(wrapping_sub, i64, stack), - F32Sub => arithmetic_op!(-, f32, stack), - F64Sub => arithmetic_op!(-, f64, stack), + I32Sub => arithmetic!(wrapping_sub, i32, stack), + I64Sub => arithmetic!(wrapping_sub, i64, stack), + F32Sub => arithmetic!(-, f32, stack), + F64Sub => arithmetic!(-, f64, stack), - F32Div => arithmetic_op!(/, f32, stack), - F64Div => arithmetic_op!(/, f64, stack), + F32Div => arithmetic!(/, f32, stack), + F64Div => arithmetic!(/, f64, stack), - I32Mul => arithmetic_method!(wrapping_mul, i32, stack), - I64Mul => arithmetic_method!(wrapping_mul, i64, stack), - F32Mul => arithmetic_op!(*, f32, stack), - F64Mul => arithmetic_op!(*, f64, stack), + I32Mul => arithmetic!(wrapping_mul, i32, stack), + I64Mul => arithmetic!(wrapping_mul, i64, stack), + F32Mul => arithmetic!(*, f32, stack), + F64Mul => arithmetic!(*, f64, stack), // these can trap - I32DivS => checked_arithmetic_method!(checked_div, i32, stack, crate::Trap::DivisionByZero), - I64DivS => checked_arithmetic_method!(checked_div, i64, stack, crate::Trap::DivisionByZero), - I32DivU => checked_arithmetic_method_cast!(checked_div, i32, u32, stack, crate::Trap::DivisionByZero), - I64DivU => checked_arithmetic_method_cast!(checked_div, i64, u64, stack, crate::Trap::DivisionByZero), + I32DivS => checked_arithmetic!(checked_div, i32, stack, crate::Trap::DivisionByZero), + I64DivS => checked_arithmetic!(checked_div, i64, stack, crate::Trap::DivisionByZero), + I32DivU => checked_arithmetic!(checked_div, i32, u32, stack, crate::Trap::DivisionByZero), + I64DivU => checked_arithmetic!(checked_div, i64, u64, stack, crate::Trap::DivisionByZero), - I32RemS => checked_arithmetic_method!(checked_wrapping_rem, i32, stack, crate::Trap::DivisionByZero), - I64RemS => checked_arithmetic_method!(checked_wrapping_rem, i64, stack, crate::Trap::DivisionByZero), - I32RemU => checked_arithmetic_method_cast!(checked_wrapping_rem, i32, u32, stack, crate::Trap::DivisionByZero), - I64RemU => checked_arithmetic_method_cast!(checked_wrapping_rem, i64, u64, stack, crate::Trap::DivisionByZero), + I32RemS => checked_arithmetic!(checked_wrapping_rem, i32, stack, crate::Trap::DivisionByZero), + I64RemS => checked_arithmetic!(checked_wrapping_rem, i64, stack, crate::Trap::DivisionByZero), + I32RemU => checked_arithmetic!(checked_wrapping_rem, i32, u32, stack, crate::Trap::DivisionByZero), + I64RemU => checked_arithmetic!(checked_wrapping_rem, i64, u64, stack, crate::Trap::DivisionByZero), - I32And => arithmetic_method!(bitand, i32, stack), - I64And => arithmetic_method!(bitand, i64, stack), - I32Or => arithmetic_method!(bitor, i32, stack), - I64Or => arithmetic_method!(bitor, i64, stack), - I32Xor => arithmetic_method!(bitxor, i32, stack), - I64Xor => arithmetic_method!(bitxor, i64, stack), - I32Shl => arithmetic_method!(wasm_shl, i32, stack), - I64Shl => arithmetic_method!(wasm_shl, i64, stack), - I32ShrS => arithmetic_method!(wasm_shr, i32, stack), - I64ShrS => arithmetic_method!(wasm_shr, i64, stack), - I32ShrU => arithmetic_method_cast!(wasm_shr, i32, u32, stack), - I64ShrU => arithmetic_method_cast!(wasm_shr, i64, u64, stack), - I32Rotl => arithmetic_method!(wasm_rotl, i32, stack), - I64Rotl => arithmetic_method!(wasm_rotl, i64, stack), - I32Rotr => arithmetic_method!(wasm_rotr, i32, stack), - I64Rotr => arithmetic_method!(wasm_rotr, i64, stack), + I32And => arithmetic!(bitand, i32, stack), + I64And => arithmetic!(bitand, i64, stack), + I32Or => arithmetic!(bitor, i32, stack), + I64Or => arithmetic!(bitor, i64, stack), + I32Xor => arithmetic!(bitxor, i32, stack), + I64Xor => arithmetic!(bitxor, i64, stack), + I32Shl => arithmetic!(wasm_shl, i32, stack), + I64Shl => arithmetic!(wasm_shl, i64, stack), + I32ShrS => arithmetic!(wasm_shr, i32, stack), + I64ShrS => arithmetic!(wasm_shr, i64, stack), + I32ShrU => arithmetic!(wasm_shr, u32, i32, stack), + I64ShrU => arithmetic!(wasm_shr, u64, i64, stack), + I32Rotl => arithmetic!(wasm_rotl, i32, stack), + I64Rotl => arithmetic!(wasm_rotl, i64, stack), + I32Rotr => arithmetic!(wasm_rotr, i32, stack), + I64Rotr => arithmetic!(wasm_rotr, i64, stack), - I32Clz => arithmetic_method_self!(leading_zeros, i32, stack), - I64Clz => arithmetic_method_self!(leading_zeros, i64, stack), - I32Ctz => arithmetic_method_self!(trailing_zeros, i32, stack), - I64Ctz => arithmetic_method_self!(trailing_zeros, i64, stack), - I32Popcnt => arithmetic_method_self!(count_ones, i32, stack), - I64Popcnt => arithmetic_method_self!(count_ones, i64, stack), + I32Clz => arithmetic_single!(leading_zeros, i32, stack), + I64Clz => arithmetic_single!(leading_zeros, i64, stack), + I32Ctz => arithmetic_single!(trailing_zeros, i32, stack), + I64Ctz => arithmetic_single!(trailing_zeros, i64, stack), + I32Popcnt => arithmetic_single!(count_ones, i32, stack), + I64Popcnt => arithmetic_single!(count_ones, i64, 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), - I32Extend8S => conv_2!(i32, i8, i32, stack), - I32Extend16S => conv_2!(i32, i16, i32, stack), - I64Extend8S => conv_2!(i64, i8, i64, stack), - I64Extend16S => conv_2!(i64, i16, i64, stack), - I64Extend32S => conv_2!(i64, i32, i64, stack), - I64ExtendI32U => conv_2!(i32, u32, i64, stack), - I64ExtendI32S => conv_1!(i32, i64, stack), - I32WrapI64 => conv_1!(i64, i32, stack), + F32ConvertI32S => conv!(i32, f32, stack), + F32ConvertI64S => conv!(i64, f32, stack), + F64ConvertI32S => conv!(i32, f64, stack), + F64ConvertI64S => conv!(i64, f64, stack), + F32ConvertI32U => conv!(i32, u32, f32, stack), + F32ConvertI64U => conv!(i64, u64, f32, stack), + F64ConvertI32U => conv!(i32, u32, f64, stack), + F64ConvertI64U => conv!(i64, u64, f64, stack), + I32Extend8S => conv!(i32, i8, i32, stack), + I32Extend16S => conv!(i32, i16, i32, stack), + I64Extend8S => conv!(i64, i8, i64, stack), + I64Extend16S => conv!(i64, i16, i64, stack), + I64Extend32S => conv!(i64, i32, i64, stack), + I64ExtendI32U => conv!(i32, u32, i64, stack), + I64ExtendI32S => conv!(i32, i64, stack), + I32WrapI64 => conv!(i64, i32, stack), - F32Abs => arithmetic_method_self!(abs, f32, stack), - F64Abs => arithmetic_method_self!(abs, f64, stack), - F32Neg => arithmetic_method_self!(neg, f32, stack), - F64Neg => arithmetic_method_self!(neg, f64, stack), - F32Ceil => arithmetic_method_self!(ceil, f32, stack), - F64Ceil => arithmetic_method_self!(ceil, f64, stack), - F32Floor => arithmetic_method_self!(floor, f32, stack), - F64Floor => arithmetic_method_self!(floor, f64, stack), - F32Trunc => arithmetic_method_self!(trunc, f32, stack), - F64Trunc => arithmetic_method_self!(trunc, f64, stack), - F32Nearest => arithmetic_method_self!(wasm_nearest, f32, stack), - F64Nearest => arithmetic_method_self!(wasm_nearest, f64, stack), - F32Sqrt => arithmetic_method_self!(sqrt, f32, stack), - F64Sqrt => arithmetic_method_self!(sqrt, f64, stack), - F32Min => arithmetic_method!(wasm_min, f32, stack), - F64Min => arithmetic_method!(wasm_min, f64, stack), - F32Max => arithmetic_method!(wasm_max, f32, stack), - F64Max => arithmetic_method!(wasm_max, f64, stack), - F32Copysign => arithmetic_method!(copysign, f32, stack), - F64Copysign => arithmetic_method!(copysign, f64, stack), + F32Abs => arithmetic_single!(abs, f32, stack), + F64Abs => arithmetic_single!(abs, f64, stack), + F32Neg => arithmetic_single!(neg, f32, stack), + F64Neg => arithmetic_single!(neg, f64, stack), + F32Ceil => arithmetic_single!(ceil, f32, stack), + F64Ceil => arithmetic_single!(ceil, f64, stack), + F32Floor => arithmetic_single!(floor, f32, stack), + F64Floor => arithmetic_single!(floor, f64, stack), + F32Trunc => arithmetic_single!(trunc, f32, stack), + F64Trunc => arithmetic_single!(trunc, f64, stack), + F32Nearest => arithmetic_single!(wasm_nearest, f32, stack), + F64Nearest => arithmetic_single!(wasm_nearest, f64, stack), + F32Sqrt => arithmetic_single!(sqrt, f32, stack), + F64Sqrt => arithmetic_single!(sqrt, f64, stack), + F32Min => arithmetic!(wasm_min, f32, stack), + F64Min => arithmetic!(wasm_min, f64, stack), + F32Max => arithmetic!(wasm_max, f32, stack), + F64Max => arithmetic!(wasm_max, f64, stack), + F32Copysign => arithmetic!(copysign, f32, stack), + F64Copysign => arithmetic!(copysign, f64, stack), // no-op instructions since types are erased at runtime I32ReinterpretF32 => {} @@ -467,14 +467,14 @@ fn exec_one( F64ReinterpretI64 => {} // unsigned versions of these are a bit broken atm - I32TruncF32S => checked_float_conv_1!(f32, i32, stack), - I32TruncF64S => checked_float_conv_1!(f64, i32, stack), - I32TruncF32U => checked_float_conv_2!(f32, u32, i32, stack), - I32TruncF64U => checked_float_conv_2!(f64, u32, i32, stack), - I64TruncF32S => checked_float_conv_1!(f32, i64, stack), - I64TruncF64S => checked_float_conv_1!(f64, i64, stack), - I64TruncF32U => checked_float_conv_2!(f32, u64, i64, stack), - I64TruncF64U => checked_float_conv_2!(f64, u64, i64, stack), + I32TruncF32S => checked_conv_float!(f32, i32, stack), + I32TruncF64S => checked_conv_float!(f64, i32, stack), + I32TruncF32U => checked_conv_float!(f32, u32, i32, stack), + I32TruncF64U => checked_conv_float!(f64, u32, i32, stack), + I64TruncF32S => checked_conv_float!(f32, i64, stack), + I64TruncF64S => checked_conv_float!(f64, i64, stack), + I64TruncF32U => checked_conv_float!(f32, u64, i64, stack), + I64TruncF64U => checked_conv_float!(f64, u64, i64, stack), i => { log::error!("unimplemented instruction: {:?}", i); |
