use super::{Executor, UntypedValueExt};
use crate::{
core::wasm,
ir::{Const16, ShiftAmount, Sign, Slot},
Error,
TrapCode,
};
use core::num::{NonZeroI32, NonZeroI64, NonZeroU32, NonZeroU64};
#[cfg(doc)]
use crate::ir::Op;
impl Executor<'_> {
impl_binary_executors! {
(Op::I32Add, execute_i32_add, wasm::i32_add),
(Op::I32Sub, execute_i32_sub, wasm::i32_sub),
(Op::I32Mul, execute_i32_mul, wasm::i32_mul),
(Op::I32BitAnd, execute_i32_bitand, wasm::i32_bitand),
(Op::I32BitOr, execute_i32_bitor, wasm::i32_bitor),
(Op::I32BitXor, execute_i32_bitxor, wasm::i32_bitxor),
(Op::I32And, execute_i32_and, <i32 as UntypedValueExt>::and),
(Op::I32Or, execute_i32_or, <i32 as UntypedValueExt>::or),
(Op::I32Nand, execute_i32_nand, <i32 as UntypedValueExt>::nand),
(Op::I32Nor, execute_i32_nor, <i32 as UntypedValueExt>::nor),
(Op::I64Add, execute_i64_add, wasm::i64_add),
(Op::I64Sub, execute_i64_sub, wasm::i64_sub),
(Op::I64Mul, execute_i64_mul, wasm::i64_mul),
(Op::I64BitAnd, execute_i64_bitand, wasm::i64_bitand),
(Op::I64BitOr, execute_i64_bitor, wasm::i64_bitor),
(Op::I64BitXor, execute_i64_bitxor, wasm::i64_bitxor),
(Op::I64And, execute_i64_and, <i64 as UntypedValueExt>::and),
(Op::I64Or, execute_i64_or, <i64 as UntypedValueExt>::or),
(Op::I64Nand, execute_i64_nand, <i64 as UntypedValueExt>::nand),
(Op::I64Nor, execute_i64_nor, <i64 as UntypedValueExt>::nor),
(Op::I32Shl, execute_i32_shl, wasm::i32_shl),
(Op::I32ShrU, execute_i32_shr_u, wasm::i32_shr_u),
(Op::I32ShrS, execute_i32_shr_s, wasm::i32_shr_s),
(Op::I32Rotl, execute_i32_rotl, wasm::i32_rotl),
(Op::I32Rotr, execute_i32_rotr, wasm::i32_rotr),
(Op::I64Shl, execute_i64_shl, wasm::i64_shl),
(Op::I64ShrU, execute_i64_shr_u, wasm::i64_shr_u),
(Op::I64ShrS, execute_i64_shr_s, wasm::i64_shr_s),
(Op::I64Rotl, execute_i64_rotl, wasm::i64_rotl),
(Op::I64Rotr, execute_i64_rotr, wasm::i64_rotr),
(Op::F32Add, execute_f32_add, wasm::f32_add),
(Op::F32Sub, execute_f32_sub, wasm::f32_sub),
(Op::F32Mul, execute_f32_mul, wasm::f32_mul),
(Op::F32Div, execute_f32_div, wasm::f32_div),
(Op::F32Min, execute_f32_min, wasm::f32_min),
(Op::F32Max, execute_f32_max, wasm::f32_max),
(Op::F32Copysign, execute_f32_copysign, wasm::f32_copysign),
(Op::F64Add, execute_f64_add, wasm::f64_add),
(Op::F64Sub, execute_f64_sub, wasm::f64_sub),
(Op::F64Mul, execute_f64_mul, wasm::f64_mul),
(Op::F64Div, execute_f64_div, wasm::f64_div),
(Op::F64Min, execute_f64_min, wasm::f64_min),
(Op::F64Max, execute_f64_max, wasm::f64_max),
(Op::F64Copysign, execute_f64_copysign, wasm::f64_copysign),
}
}
macro_rules! impl_binary_imm16 {
( $( ($ty:ty, Op::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => {
$(
#[doc = concat!("Executes an [`Op::", stringify!($var_name), "`].")]
pub fn $fn_name(&mut self, result: Slot, lhs: Slot, rhs: Const16<$ty>) {
self.execute_binary_imm16_rhs(result, lhs, rhs, $op)
}
)*
};
}
impl Executor<'_> {
impl_binary_imm16! {
(i32, Op::I32AddImm16, execute_i32_add_imm16, wasm::i32_add),
(i32, Op::I32MulImm16, execute_i32_mul_imm16, wasm::i32_mul),
(i32, Op::I32BitAndImm16, execute_i32_bitand_imm16, wasm::i32_bitand),
(i32, Op::I32BitOrImm16, execute_i32_bitor_imm16, wasm::i32_bitor),
(i32, Op::I32BitXorImm16, execute_i32_bitxor_imm16, wasm::i32_bitxor),
(i32, Op::I32AndImm16, execute_i32_and_imm16, <i32 as UntypedValueExt>::and),
(i32, Op::I32OrImm16, execute_i32_or_imm16, <i32 as UntypedValueExt>::or),
(i32, Op::I32NandImm16, execute_i32_nand_imm16, <i32 as UntypedValueExt>::nand),
(i32, Op::I32NorImm16, execute_i32_nor_imm16, <i32 as UntypedValueExt>::nor),
(i64, Op::I64AddImm16, execute_i64_add_imm16, wasm::i64_add),
(i64, Op::I64MulImm16, execute_i64_mul_imm16, wasm::i64_mul),
(i64, Op::I64BitAndImm16, execute_i64_bitand_imm16, wasm::i64_bitand),
(i64, Op::I64BitOrImm16, execute_i64_bitor_imm16, wasm::i64_bitor),
(i64, Op::I64BitXorImm16, execute_i64_bitxor_imm16, wasm::i64_bitxor),
(i64, Op::I64AndImm16, execute_i64_and_imm16, <i64 as UntypedValueExt>::and),
(i64, Op::I64OrImm16, execute_i64_or_imm16, <i64 as UntypedValueExt>::or),
(i64, Op::I64NandImm16, execute_i64_nand_imm16, <i64 as UntypedValueExt>::nand),
(i64, Op::I64NorImm16, execute_i64_nor_imm16, <i64 as UntypedValueExt>::nor),
}
}
macro_rules! impl_shift_by {
( $( ($ty:ty, Op::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => {
$(
#[doc = concat!("Executes an [`Op::", stringify!($var_name), "`].")]
pub fn $fn_name(&mut self, result: Slot, lhs: Slot, rhs: ShiftAmount<$ty>) {
self.execute_shift_by(result, lhs, rhs, $op)
}
)*
};
}
impl Executor<'_> {
impl_shift_by! {
(i32, Op::I32ShlBy, execute_i32_shl_by, wasm::i32_shl),
(i32, Op::I32ShrUBy, execute_i32_shr_u_by, wasm::i32_shr_u),
(i32, Op::I32ShrSBy, execute_i32_shr_s_by, wasm::i32_shr_s),
(i32, Op::I32RotlBy, execute_i32_rotl_by, wasm::i32_rotl),
(i32, Op::I32RotrBy, execute_i32_rotr_by, wasm::i32_rotr),
(i64, Op::I64ShlBy, execute_i64_shl_by, wasm::i64_shl),
(i64, Op::I64ShrUBy, execute_i64_shr_u_by, wasm::i64_shr_u),
(i64, Op::I64ShrSBy, execute_i64_shr_s_by, wasm::i64_shr_s),
(i64, Op::I64RotlBy, execute_i64_rotl_by, wasm::i64_rotl),
(i64, Op::I64RotrBy, execute_i64_rotr_by, wasm::i64_rotr),
}
}
macro_rules! impl_binary_imm16_lhs {
( $( ($ty:ty, Op::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => {
$(
#[doc = concat!("Executes an [`Op::", stringify!($var_name), "`].")]
pub fn $fn_name(&mut self, result: Slot, lhs: Const16<$ty>, rhs: Slot) {
self.execute_binary_imm16_lhs(result, lhs, rhs, $op)
}
)*
};
}
impl Executor<'_> {
impl_binary_imm16_lhs! {
(i32, Op::I32SubImm16Lhs, execute_i32_sub_imm16_lhs, wasm::i32_sub),
(i64, Op::I64SubImm16Lhs, execute_i64_sub_imm16_lhs, wasm::i64_sub),
(i32, Op::I32ShlImm16, execute_i32_shl_imm16, wasm::i32_shl),
(i32, Op::I32ShrUImm16, execute_i32_shr_u_imm16, wasm::i32_shr_u),
(i32, Op::I32ShrSImm16, execute_i32_shr_s_imm16, wasm::i32_shr_s),
(i32, Op::I32RotlImm16, execute_i32_rotl_imm16, wasm::i32_rotl),
(i32, Op::I32RotrImm16, execute_i32_rotr_imm16, wasm::i32_rotr),
(i64, Op::I64ShlImm16, execute_i64_shl_imm16, wasm::i64_shl),
(i64, Op::I64ShrUImm16, execute_i64_shr_u_imm16, wasm::i64_shr_u),
(i64, Op::I64ShrSImm16, execute_i64_shr_s_imm16, wasm::i64_shr_s),
(i64, Op::I64RotlImm16, execute_i64_rotl_imm16, wasm::i64_rotl),
(i64, Op::I64RotrImm16, execute_i64_rotr_imm16, wasm::i64_rotr),
}
}
macro_rules! impl_fallible_binary {
( $( (Op::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => {
$(
#[doc = concat!("Executes an [`Op::", stringify!($var_name), "`].")]
pub fn $fn_name(&mut self, result: Slot, lhs: Slot, rhs: Slot) -> Result<(), Error> {
self.try_execute_binary(result, lhs, rhs, $op).map_err(Error::from)
}
)*
};
}
impl Executor<'_> {
impl_fallible_binary! {
(Op::I32DivS, execute_i32_div_s, wasm::i32_div_s),
(Op::I32DivU, execute_i32_div_u, wasm::i32_div_u),
(Op::I32RemS, execute_i32_rem_s, wasm::i32_rem_s),
(Op::I32RemU, execute_i32_rem_u, wasm::i32_rem_u),
(Op::I64DivS, execute_i64_div_s, wasm::i64_div_s),
(Op::I64DivU, execute_i64_div_u, wasm::i64_div_u),
(Op::I64RemS, execute_i64_rem_s, wasm::i64_rem_s),
(Op::I64RemU, execute_i64_rem_u, wasm::i64_rem_u),
}
}
pub trait DivRemExt: Sized {
type NonZeroS;
type NonZeroU;
fn div_s(self, rhs: Self::NonZeroS) -> Result<Self, Error>;
fn div_u(self, rhs: Self::NonZeroU) -> Self;
fn rem_s(self, rhs: Self::NonZeroS) -> Result<Self, Error>;
fn rem_u(self, rhs: Self::NonZeroU) -> Self;
}
impl DivRemExt for i32 {
type NonZeroS = NonZeroI32;
type NonZeroU = NonZeroU32;
fn div_s(self, rhs: Self::NonZeroS) -> Result<Self, Error> {
self.checked_div(rhs.get())
.ok_or_else(|| Error::from(TrapCode::IntegerOverflow))
}
fn div_u(self, rhs: Self::NonZeroU) -> Self {
((self as u32) / rhs) as Self
}
fn rem_s(self, rhs: Self::NonZeroS) -> Result<Self, Error> {
wasm::i32_rem_s(self, rhs.get()).map_err(Error::from)
}
fn rem_u(self, rhs: Self::NonZeroU) -> Self {
((self as u32) % rhs) as Self
}
}
impl DivRemExt for i64 {
type NonZeroS = NonZeroI64;
type NonZeroU = NonZeroU64;
fn div_s(self, rhs: Self::NonZeroS) -> Result<Self, Error> {
self.checked_div(rhs.get())
.ok_or_else(|| Error::from(TrapCode::IntegerOverflow))
}
fn div_u(self, rhs: Self::NonZeroU) -> Self {
((self as u64) / rhs) as Self
}
fn rem_s(self, rhs: Self::NonZeroS) -> Result<Self, Error> {
wasm::i64_rem_s(self, rhs.get()).map_err(Error::from)
}
fn rem_u(self, rhs: Self::NonZeroU) -> Self {
((self as u64) % rhs) as Self
}
}
macro_rules! impl_divrem_s_imm16_rhs {
( $( ($ty:ty, Op::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => {
$(
#[doc = concat!("Executes an [`Op::", stringify!($var_name), "`].")]
pub fn $fn_name(&mut self, result: Slot, lhs: Slot, rhs: Const16<$ty>) -> Result<(), Error> {
self.try_execute_divrem_imm16_rhs(result, lhs, rhs, $op)
}
)*
};
}
impl Executor<'_> {
impl_divrem_s_imm16_rhs! {
(NonZeroI32, Op::I32DivSImm16Rhs, execute_i32_div_s_imm16_rhs, <i32 as DivRemExt>::div_s),
(NonZeroI32, Op::I32RemSImm16Rhs, execute_i32_rem_s_imm16_rhs, <i32 as DivRemExt>::rem_s),
(NonZeroI64, Op::I64DivSImm16Rhs, execute_i64_div_s_imm16_rhs, <i64 as DivRemExt>::div_s),
(NonZeroI64, Op::I64RemSImm16Rhs, execute_i64_rem_s_imm16_rhs, <i64 as DivRemExt>::rem_s),
}
}
macro_rules! impl_divrem_u_imm16_rhs {
( $( ($ty:ty, Op::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => {
$(
#[doc = concat!("Executes an [`Op::", stringify!($var_name), "`].")]
pub fn $fn_name(&mut self, result: Slot, lhs: Slot, rhs: Const16<$ty>) {
self.execute_divrem_imm16_rhs(result, lhs, rhs, $op)
}
)*
};
}
impl Executor<'_> {
impl_divrem_u_imm16_rhs! {
(NonZeroU32, Op::I32DivUImm16Rhs, execute_i32_div_u_imm16_rhs, <i32 as DivRemExt>::div_u),
(NonZeroU32, Op::I32RemUImm16Rhs, execute_i32_rem_u_imm16_rhs, <i32 as DivRemExt>::rem_u),
(NonZeroU64, Op::I64DivUImm16Rhs, execute_i64_div_u_imm16_rhs, <i64 as DivRemExt>::div_u),
(NonZeroU64, Op::I64RemUImm16Rhs, execute_i64_rem_u_imm16_rhs, <i64 as DivRemExt>::rem_u),
}
}
macro_rules! impl_fallible_binary_imm16_lhs {
( $( ($ty:ty, Op::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => {
$(
#[doc = concat!("Executes an [`Op::", stringify!($var_name), "`].")]
pub fn $fn_name(&mut self, result: Slot, lhs: Const16<$ty>, rhs: Slot) -> Result<(), Error> {
self.try_execute_binary_imm16_lhs(result, lhs, rhs, $op).map_err(Error::from)
}
)*
};
}
impl Executor<'_> {
impl_fallible_binary_imm16_lhs! {
(i32, Op::I32DivSImm16Lhs, execute_i32_div_s_imm16_lhs, wasm::i32_div_s),
(u32, Op::I32DivUImm16Lhs, execute_i32_div_u_imm16_lhs, wasm::i32_div_u),
(i32, Op::I32RemSImm16Lhs, execute_i32_rem_s_imm16_lhs, wasm::i32_rem_s),
(u32, Op::I32RemUImm16Lhs, execute_i32_rem_u_imm16_lhs, wasm::i32_rem_u),
(i64, Op::I64DivSImm16Lhs, execute_i64_div_s_imm16_lhs, wasm::i64_div_s),
(u64, Op::I64DivUImm16Lhs, execute_i64_div_u_imm16_lhs, wasm::i64_div_u),
(i64, Op::I64RemSImm16Lhs, execute_i64_rem_s_imm16_lhs, wasm::i64_rem_s),
(u64, Op::I64RemUImm16Lhs, execute_i64_rem_u_imm16_lhs, wasm::i64_rem_u),
}
}
impl Executor<'_> {
pub fn execute_f32_copysign_imm(&mut self, result: Slot, lhs: Slot, rhs: Sign<f32>) {
let lhs = self.get_stack_slot_as::<f32>(lhs);
let rhs = f32::from(rhs);
self.set_stack_slot_as::<f32>(result, wasm::f32_copysign(lhs, rhs));
self.next_instr()
}
pub fn execute_f64_copysign_imm(&mut self, result: Slot, lhs: Slot, rhs: Sign<f64>) {
let lhs = self.get_stack_slot_as::<f64>(lhs);
let rhs = f64::from(rhs);
self.set_stack_slot_as::<f64>(result, wasm::f64_copysign(lhs, rhs));
self.next_instr()
}
}