From 25997287cfd2f11d42a29c8089078cbed13b6cb6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 3 Feb 2023 12:34:20 +0100 Subject: [PATCH] Use `VisitOperator` trait in new `FuncTranslator` (#645) * separate visit_ from non visit_ functions * rename internal funcs to translate_ Also make them private. * move acquire_target method * move FuncTranslator into translator.rs Also move VisitOperator for FuncBuilder into mod.rs. * refactor validate_then_translate method * impl VisitOperator for FuncTranslator So far all the translation (or visit) methods were simple methods but mimicking the VisitOperator trait. This commit now turns all those free standing methods into trait method impls. --- crates/wasmi/src/engine/func_builder/mod.rs | 2167 +---------------- .../src/engine/func_builder/translator.rs | 1948 +++++++++++++++ crates/wasmi/src/engine/func_builder/visit.rs | 78 - 3 files changed, 2037 insertions(+), 2156 deletions(-) create mode 100644 crates/wasmi/src/engine/func_builder/translator.rs delete mode 100644 crates/wasmi/src/engine/func_builder/visit.rs diff --git a/crates/wasmi/src/engine/func_builder/mod.rs b/crates/wasmi/src/engine/func_builder/mod.rs index fcf4494c13..07e791182f 100644 --- a/crates/wasmi/src/engine/func_builder/mod.rs +++ b/crates/wasmi/src/engine/func_builder/mod.rs @@ -4,88 +4,42 @@ mod error; mod inst_builder; mod labels; mod locals_registry; +mod translator; mod value_stack; -mod visit; use self::{ - control_frame::{ - BlockControlFrame, - ControlFrame, - ControlFrameKind, - IfControlFrame, - LoopControlFrame, - UnreachableControlFrame, - }, + control_frame::{BlockControlFrame, ControlFrame}, control_stack::ControlFlowStack, - labels::LabelRef, locals_registry::LocalsRegistry, - value_stack::ValueStackHeight, + translator::FuncTranslator, }; pub use self::{ error::TranslationError, inst_builder::{Instr, InstructionsBuilder, RelativeDepth}, }; -use super::{bytecode, DropKeep, FuncBody, Instruction}; +use super::{FuncBody, Instruction}; use crate::{ - engine::bytecode::{ - BranchParams, - DataSegmentIdx, - ElementSegmentIdx, - Offset, - SignatureIdx, - TableIdx, - }, - module::{ - BlockType, - FuncIdx, - FuncTypeIdx, - GlobalIdx, - InitExpr, - MemoryIdx, - ModuleResources, - ReusableAllocations, - DEFAULT_MEMORY_INDEX, - }, + module::{BlockType, FuncIdx, ModuleResources, ReusableAllocations}, Engine, - FuncType, - GlobalType, - Mutability, - Value, }; use alloc::vec::Vec; -use wasmi_core::{ValueType, F32, F64}; +use wasmparser::{BinaryReaderError, VisitOperator}; /// The used function validator type. type FuncValidator = wasmparser::FuncValidator; -/// The interface to translate a `wasmi` bytecode function using Wasm bytecode. +/// The interface to build a `wasmi` bytecode function using Wasm bytecode. +/// +/// # Note +/// +/// This includes validation of the incoming Wasm bytecode. pub struct FuncBuilder<'parser> { - /// The [`Engine`] for which the function is translated. - engine: Engine, - /// The function under construction. - func: FuncIdx, - /// The immutable `wasmi` module resources. - res: ModuleResources<'parser>, - /// This represents the reachability of the currently translated code. - /// - /// - `true`: The currently translated code is reachable. - /// - `false`: The currently translated code is unreachable and can be skipped. - /// - /// # Note - /// - /// Visiting the Wasm `Else` or `End` control flow operator resets - /// reachability to `true` again. - reachable: bool, - /// The Wasm function validator. - validator: FuncValidator, - /// The height of the emulated value stack. - stack_height: ValueStackHeight, - /// Stores and resolves local variable types. - locals: LocalsRegistry, - /// The reusable data structures of the [`FuncBuilder`]. - alloc: FunctionBuilderAllocations, /// The current position in the Wasm binary while parsing operators. pos: usize, + /// The Wasm function validator. + validator: FuncValidator, + /// The underlying Wasm to `wasmi` bytecode translator. + translator: FuncTranslator<'parser>, } /// Reusable allocations of a [`FuncBuilder`]. @@ -124,64 +78,26 @@ impl<'parser> FuncBuilder<'parser> { func: FuncIdx, res: ModuleResources<'parser>, validator: FuncValidator, - mut allocations: FunctionBuilderAllocations, + allocations: FunctionBuilderAllocations, ) -> Self { + let mut allocations = allocations; + allocations.reset(); let mut locals = LocalsRegistry::default(); Self::register_func_body_block(func, res, &mut allocations); Self::register_func_params(func, res, &mut locals); Self { - engine: engine.clone(), - func, - res, - reachable: true, - validator, - stack_height: ValueStackHeight::default(), - locals, - alloc: allocations, pos: 0, + validator, + translator: FuncTranslator::new(engine, func, res, allocations), } } - /// Updates the current position within the Wasm binary while parsing operators. - pub fn update_pos(&mut self, pos: usize) { - self.pos = pos; - } - - /// Returns the current position within the Wasm binary while parsing operators. - pub fn current_pos(&self) -> usize { - self.pos - } - - /// Returns the [`FuncType`] of the function that is currently translated. - fn func_type(&self) -> FuncType { - let dedup_func_type = self.res.get_type_of_func(self.func); - self.engine.resolve_func_type(dedup_func_type, Clone::clone) - } - - /// Resolves the [`FuncType`] of the given [`FuncTypeIdx`]. - fn func_type_at(&self, func_type_index: SignatureIdx) -> FuncType { - let func_type_index = FuncTypeIdx(func_type_index.into_inner()); // TODO: use the same type - let dedup_func_type = self.res.get_func_type(func_type_index); - self.res - .engine() - .resolve_func_type(dedup_func_type, Clone::clone) - } - - /// Resolves the [`FuncType`] of the given [`FuncIdx`]. - fn func_type_of(&self, func_index: FuncIdx) -> FuncType { - let dedup_func_type = self.res.get_type_of_func(func_index); - self.res - .engine() - .resolve_func_type(dedup_func_type, Clone::clone) - } - /// Registers the `block` control frame surrounding the entire function body. fn register_func_body_block( func: FuncIdx, res: ModuleResources<'parser>, allocations: &mut FunctionBuilderAllocations, ) { - allocations.reset(); let func_type = res.get_type_of_func(func); let block_type = BlockType::func_type(func_type); let end_label = allocations.inst_builder.new_label(); @@ -214,16 +130,18 @@ impl<'parser> FuncBuilder<'parser> { value_type: wasmparser::ValType, ) -> Result<(), TranslationError> { self.validator.define_locals(offset, amount, value_type)?; - self.locals.register_locals(amount); + self.translator.register_locals(amount); Ok(()) } - /// Returns the number of local variables of the function under construction. - fn len_locals(&self) -> usize { - let len_params_locals = self.locals.len_registered() as usize; - let len_params = self.func_type().params().len(); - debug_assert!(len_params_locals >= len_params); - len_params_locals - len_params + /// Updates the current position within the Wasm binary while parsing operators. + pub fn update_pos(&mut self, pos: usize) { + self.pos = pos; + } + + /// Returns the current position within the Wasm binary while parsing operators. + pub fn current_pos(&self) -> usize { + self.pos } /// Finishes constructing the function and returns its [`FuncBody`]. @@ -232,1990 +150,83 @@ impl<'parser> FuncBuilder<'parser> { offset: usize, ) -> Result<(FuncBody, ReusableAllocations), TranslationError> { self.validator.finish(offset)?; - let func_body = self.alloc.inst_builder.finish( - &self.engine, - self.len_locals(), - self.stack_height.max_stack_height() as usize, - ); + let func_body = self.translator.finish()?; let allocations = ReusableAllocations { - translation: self.alloc, + translation: self.translator.into_allocations(), validation: self.validator.into_allocations(), }; Ok((func_body, allocations)) } - /// Returns `true` if the code at the current translation position is reachable. - fn is_reachable(&self) -> bool { - self.reachable - } - /// Translates into `wasmi` bytecode if the current code path is reachable. - /// - /// # Note - /// - /// Ignores the `translator` closure if the current code path is unreachable. - fn translate_if_reachable(&mut self, translator: F) -> Result<(), TranslationError> + fn validate_then_translate( + &mut self, + validate: V, + translate: T, + ) -> Result<(), TranslationError> where - F: FnOnce(&mut Self) -> Result<(), TranslationError>, + V: FnOnce(&mut FuncValidator) -> Result<(), BinaryReaderError>, + T: FnOnce(&mut FuncTranslator<'parser>) -> Result<(), TranslationError>, { - if self.is_reachable() { - translator(self)?; - } + validate(&mut self.validator)?; + translate(&mut self.translator)?; Ok(()) } - - /// Computes how many values should be dropped and kept for the specific branch. - /// - /// # Panics - /// - /// If underflow of the value stack is detected. - fn compute_drop_keep(&self, depth: u32) -> Result { - debug_assert!(self.is_reachable()); - let frame = self.alloc.control_frames.nth_back(depth); - // Find out how many values we need to keep (copy to the new stack location after the drop). - let keep = match frame.kind() { - ControlFrameKind::Block | ControlFrameKind::If => { - frame.block_type().len_results(&self.engine) - } - ControlFrameKind::Loop => frame.block_type().len_params(&self.engine), - }; - // Find out how many values we need to drop. - let current_height = self.stack_height.height(); - let origin_height = frame.stack_height().expect("frame is reachable"); - assert!( - origin_height <= current_height, - "encountered value stack underflow: \ - current height {current_height}, original height {origin_height}", - ); - let height_diff = current_height - origin_height; - assert!( - keep <= height_diff, - "tried to keep {keep} values while having \ - only {height_diff} values available on the frame", - ); - let drop = height_diff - keep; - DropKeep::new(drop as usize, keep as usize).map_err(Into::into) - } - - /// Compute [`DropKeep`] for the return statement. - /// - /// # Panics - /// - /// - If the control flow frame stack is empty. - /// - If the value stack is underflown. - fn drop_keep_return(&self) -> Result { - debug_assert!(self.is_reachable()); - assert!( - !self.alloc.control_frames.is_empty(), - "drop_keep_return cannot be called with the frame stack empty" - ); - let max_depth = self - .alloc - .control_frames - .len() - .checked_sub(1) - .expect("control flow frame stack must not be empty") as u32; - let drop_keep = self.compute_drop_keep(max_depth)?; - let len_params_locals = self.locals.len_registered() as usize; - DropKeep::new( - // Drop all local variables and parameters upon exit. - drop_keep.drop() + len_params_locals, - drop_keep.keep(), - ) - .map_err(Into::into) - } - - /// Returns the relative depth on the stack of the local variable. - fn relative_local_depth(&self, local_idx: u32) -> usize { - debug_assert!(self.is_reachable()); - let stack_height = self.stack_height.height() as usize; - let len_params_locals = self.locals.len_registered() as usize; - stack_height - .checked_add(len_params_locals) - .and_then(|x| x.checked_sub(local_idx as usize)) - .unwrap_or_else(|| panic!("cannot convert local index into local depth: {local_idx}")) - } - - /// Returns the target at the given `depth` together with its [`DropKeep`]. - /// - /// # Panics - /// - /// - If the `depth` is greater than the current height of the control frame stack. - /// - If the value stack underflowed. - fn acquire_target(&self, relative_depth: u32) -> Result { - debug_assert!(self.is_reachable()); - if self.alloc.control_frames.is_root(relative_depth) { - let drop_keep = self.drop_keep_return()?; - Ok(AcquiredTarget::Return(drop_keep)) - } else { - let label = self - .alloc - .control_frames - .nth_back(relative_depth) - .branch_destination(); - let drop_keep = self.compute_drop_keep(relative_depth)?; - Ok(AcquiredTarget::Branch(label, drop_keep)) - } - } - - /// Creates [`BranchParams`] to `target` using `drop_keep` for the current instruction. - fn branch_params(&mut self, target: LabelRef, drop_keep: DropKeep) -> BranchParams { - BranchParams::new(self.alloc.inst_builder.try_resolve_label(target), drop_keep) - } -} - -/// An acquired target. -/// -/// Returned by [`FuncBuilder::acquire_target`]. -#[derive(Debug)] -pub enum AcquiredTarget { - /// The branch jumps to the label. - Branch(LabelRef, DropKeep), - /// The branch returns to the caller. - /// - /// # Note - /// - /// This is returned if the `relative_depth` points to the outmost - /// function body `block`. WebAssembly defines branches to this control - /// flow frame as equivalent to returning from the function. - Return(DropKeep), } -impl<'parser> FuncBuilder<'parser> { - /// Translates a Wasm `unreachable` instruction. - pub fn visit_nop(&mut self) -> Result<(), TranslationError> { - Ok(()) - } - - /// Translates a Wasm `unreachable` instruction. - pub fn visit_unreachable(&mut self) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder - .alloc - .inst_builder - .push_inst(Instruction::Unreachable); - builder.reachable = false; - Ok(()) - }) - } - - /// Calculates the stack height upon entering a control flow frame. - /// - /// # Note - /// - /// This does not include the parameters of the control flow frame - /// so that when shrinking the emulated value stack to the control flow - /// frame's original stack height the control flow frame parameters are - /// no longer on the emulated value stack. - /// - /// # Panics - /// - /// When the emulated value stack underflows. This should not happen - /// since we have already validated the input Wasm prior. - fn frame_stack_height(&self, block_type: BlockType) -> u32 { - let len_params = block_type.len_params(&self.engine); - let stack_height = self.stack_height.height(); - stack_height.checked_sub(len_params).unwrap_or_else(|| { - panic!( - "encountered emulated value stack underflow with \ - stack height {stack_height} and {len_params} block parameters", +macro_rules! impl_visit_operator { + ( @mvp BrTable { $arg:ident: $argty:ty } => $visit:ident $($rest:tt)* ) => { + // We need to special case the `BrTable` operand since its + // arguments (a.k.a. `BrTable<'a>`) are not `Copy` which all + // the other impls make use of. + fn $visit(&mut self, $arg: $argty) -> Self::Output { + let offset = self.current_pos(); + let arg_cloned = $arg.clone(); + self.validate_then_translate( + |validator| validator.visitor(offset).$visit(arg_cloned), + |translator| translator.$visit($arg), ) - }) - } - - /// Translates a Wasm `block` control flow operator. - pub fn visit_block( - &mut self, - block_type: wasmparser::BlockType, - ) -> Result<(), TranslationError> { - let block_type = BlockType::try_from_wasmparser(block_type, self.res)?; - if self.is_reachable() { - let stack_height = self.frame_stack_height(block_type); - let end_label = self.alloc.inst_builder.new_label(); - self.alloc.control_frames.push_frame(BlockControlFrame::new( - block_type, - end_label, - stack_height, - )); - } else { - self.alloc - .control_frames - .push_frame(UnreachableControlFrame::new( - ControlFrameKind::Block, - block_type, - )); - } - Ok(()) - } - - /// Translates a Wasm `loop` control flow operator. - pub fn visit_loop( - &mut self, - block_type: wasmparser::BlockType, - ) -> Result<(), TranslationError> { - let block_type = BlockType::try_from_wasmparser(block_type, self.res)?; - if self.is_reachable() { - let stack_height = self.frame_stack_height(block_type); - let header = self.alloc.inst_builder.new_label(); - self.alloc.inst_builder.pin_label(header); - self.alloc.control_frames.push_frame(LoopControlFrame::new( - block_type, - header, - stack_height, - )); - } else { - self.alloc - .control_frames - .push_frame(UnreachableControlFrame::new( - ControlFrameKind::Loop, - block_type, - )); - } - Ok(()) - } - - /// Translates a Wasm `if` control flow operator. - pub fn visit_if(&mut self, block_type: wasmparser::BlockType) -> Result<(), TranslationError> { - let block_type = BlockType::try_from_wasmparser(block_type, self.res)?; - if self.is_reachable() { - self.stack_height.pop1(); - let stack_height = self.frame_stack_height(block_type); - let else_label = self.alloc.inst_builder.new_label(); - let end_label = self.alloc.inst_builder.new_label(); - self.alloc.control_frames.push_frame(IfControlFrame::new( - block_type, - end_label, - else_label, - stack_height, - )); - let branch_params = self.branch_params(else_label, DropKeep::none()); - self.alloc - .inst_builder - .push_inst(Instruction::BrIfEqz(branch_params)); - } else { - self.alloc - .control_frames - .push_frame(UnreachableControlFrame::new( - ControlFrameKind::If, - block_type, - )); } - Ok(()) - } - - /// Translates a Wasm `else` control flow operator. - pub fn visit_else(&mut self) -> Result<(), TranslationError> { - let mut if_frame = match self.alloc.control_frames.pop_frame() { - ControlFrame::If(if_frame) => if_frame, - ControlFrame::Unreachable(frame) if matches!(frame.kind(), ControlFrameKind::If) => { - // Encountered `Else` block for unreachable `If` block. - // - // In this case we can simply ignore the entire `Else` block - // since it is unreachable anyways. - self.alloc.control_frames.push_frame(frame); - return Ok(()); - } - unexpected => panic!( - "expected `if` control flow frame on top \ - for `else` but found: {unexpected:?}", - ), - }; - let reachable = self.is_reachable(); - // At this point we know if the end of the `then` block of the paren - // `if` block is reachable so we update the parent `if` frame. - // - // Note: This information is important to decide whether code is - // reachable after the `if` block (including `else`) ends. - if_frame.update_end_of_then_reachability(reachable); - // Create the jump from the end of the `then` block to the `if` - // block's end label in case the end of `then` is reachable. - if reachable { - let params = self.branch_params(if_frame.end_label(), DropKeep::none()); - self.alloc.inst_builder.push_inst(Instruction::Br(params)); - } - // Now resolve labels for the instructions of the `else` block - self.alloc.inst_builder.pin_label(if_frame.else_label()); - // We need to reset the value stack to exactly how it has been - // when entering the `if` in the first place so that the `else` - // block has the same parameters on top of the stack. - self.stack_height.shrink_to(if_frame.stack_height()); - if_frame.block_type().foreach_param(&self.engine, |_param| { - self.stack_height.push(); - }); - self.alloc.control_frames.push_frame(if_frame); - // We can reset reachability now since the parent `if` block was reachable. - self.reachable = true; - Ok(()) - } - - /// Translates a Wasm `end` control flow operator. - pub fn visit_end(&mut self) -> Result<(), TranslationError> { - let frame = self.alloc.control_frames.last(); - if let ControlFrame::If(if_frame) = &frame { - // At this point we can resolve the `Else` label. - // - // Note: The `Else` label might have already been resolved - // in case there was an `Else` block. - self.alloc - .inst_builder - .pin_label_if_unpinned(if_frame.else_label()); - } - if frame.is_reachable() && !matches!(frame.kind(), ControlFrameKind::Loop) { - // At this point we can resolve the `End` labels. - // Note that `loop` control frames do not have an `End` label. - self.alloc.inst_builder.pin_label(frame.end_label()); - } - // These bindings are required because of borrowing issues. - let frame_reachable = frame.is_reachable(); - let frame_stack_height = frame.stack_height(); - if self.alloc.control_frames.len() == 1 { - // If the control flow frames stack is empty after this point - // we know that we are ending the function body `block` - // frame and therefore we have to return from the function. - self.visit_return()?; - } else { - // The following code is only reachable if the ended control flow - // frame was reachable upon entering to begin with. - self.reachable = frame_reachable; - } - if let Some(frame_stack_height) = frame_stack_height { - self.stack_height.shrink_to(frame_stack_height); + impl_visit_operator!($($rest)*); + }; + ( @mvp $($rest:tt)* ) => { + impl_visit_operator!(@@supported $($rest)*); + }; + ( @sign_extension $($rest:tt)* ) => { + impl_visit_operator!(@@supported $($rest)*); + }; + ( @saturating_float_to_int $($rest:tt)* ) => { + impl_visit_operator!(@@supported $($rest)*); + }; + ( @bulk_memory $($rest:tt)* ) => { + impl_visit_operator!(@@supported $($rest)*); + }; + ( @reference_types $($rest:tt)* ) => { + impl_visit_operator!(@@supported $($rest)*); + }; + ( @@supported $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { + let offset = self.current_pos(); + self.validate_then_translate( + |v| v.visitor(offset).$visit($($($arg),*)?), + |t| t.$visit($($($arg),*)?), + ) } - let frame = self.alloc.control_frames.pop_frame(); - frame - .block_type() - .foreach_result(&self.engine, |_result| self.stack_height.push()); - Ok(()) - } - - /// Translates a Wasm `br` control flow operator. - pub fn visit_br(&mut self, relative_depth: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - match builder.acquire_target(relative_depth)? { - AcquiredTarget::Branch(end_label, drop_keep) => { - let params = builder.branch_params(end_label, drop_keep); - builder - .alloc - .inst_builder - .push_inst(Instruction::Br(params)); - } - AcquiredTarget::Return(_) => { - // In this case the `br` can be directly translated as `return`. - builder.visit_return()?; - } - } - builder.reachable = false; - Ok(()) - }) - } - - /// Translates a Wasm `br_if` control flow operator. - pub fn visit_br_if(&mut self, relative_depth: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop1(); - match builder.acquire_target(relative_depth)? { - AcquiredTarget::Branch(end_label, drop_keep) => { - let params = builder.branch_params(end_label, drop_keep); - builder - .alloc - .inst_builder - .push_inst(Instruction::BrIfNez(params)); - } - AcquiredTarget::Return(drop_keep) => { - builder - .alloc - .inst_builder - .push_inst(Instruction::ReturnIfNez(drop_keep)); - } - } - Ok(()) - }) - } - - /// Translates a Wasm `br_table` control flow operator. - pub fn visit_br_table( - &mut self, - table: wasmparser::BrTable<'parser>, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - fn offset_instr(base: Instr, offset: usize) -> Instr { - Instr::from_u32(base.into_u32() + offset as u32) - } - - fn compute_instr( - builder: &mut FuncBuilder, - n: usize, - depth: RelativeDepth, - ) -> Result { - match builder.acquire_target(depth.into_u32())? { - AcquiredTarget::Branch(label, drop_keep) => { - let base = builder.alloc.inst_builder.current_pc(); - let instr = offset_instr(base, n + 1); - let offset = builder - .alloc - .inst_builder - .try_resolve_label_for(label, instr); - let params = BranchParams::new(offset, drop_keep); - Ok(Instruction::Br(params)) - } - AcquiredTarget::Return(drop_keep) => Ok(Instruction::Return(drop_keep)), - } - } - - let default = RelativeDepth::from_u32(table.default()); - let targets = table - .targets() - .map(|relative_depth| { - relative_depth.unwrap_or_else(|error| { - panic!( - "encountered unexpected invalid relative depth \ - for `br_table` target: {error}", - ) - }) - }) - .map(RelativeDepth::from_u32); - - builder.stack_height.pop1(); - builder.alloc.br_table_branches.clear(); - for (n, depth) in targets.into_iter().enumerate() { - let relative_depth = compute_instr(builder, n, depth)?; - builder.alloc.br_table_branches.push(relative_depth); - } - - // We include the default target in `len_branches`. - let len_branches = builder.alloc.br_table_branches.len(); - let default_branch = compute_instr(builder, len_branches, default)?; - builder.alloc.inst_builder.push_inst(Instruction::BrTable { - len_targets: len_branches + 1, - }); - for branch in builder.alloc.br_table_branches.drain(..) { - builder.alloc.inst_builder.push_inst(branch); - } - builder.alloc.inst_builder.push_inst(default_branch); - builder.reachable = false; - Ok(()) - }) - } - - /// Translates a Wasm `return` control flow operator. - pub fn visit_return(&mut self) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let drop_keep = builder.drop_keep_return()?; - builder - .alloc - .inst_builder - .push_inst(Instruction::Return(drop_keep)); - builder.reachable = false; - Ok(()) - }) - } - - /// Adjusts the emulated value stack given the [`FuncType`] of the call. - fn adjust_value_stack_for_call(&mut self, func_type: &FuncType) { - let (params, results) = func_type.params_results(); - self.stack_height.pop_n(params.len() as u32); - self.stack_height.push_n(results.len() as u32); - } - - /// Translates a Wasm `call` instruction. - pub fn visit_call(&mut self, func_idx: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let func_idx = FuncIdx(func_idx); - let func_type = builder.func_type_of(func_idx); - builder.adjust_value_stack_for_call(&func_type); - let func_idx = func_idx.into_u32().into(); - builder - .alloc - .inst_builder - .push_inst(Instruction::Call(func_idx)); - Ok(()) - }) - } - - /// Translates a Wasm `call_indirect` instruction. - pub fn visit_call_indirect( - &mut self, - func_type_index: u32, - table_index: u32, - _table_byte: u8, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let func_type_index = SignatureIdx::from(func_type_index); - let table = TableIdx::from(table_index); - builder.stack_height.pop1(); - let func_type = builder.func_type_at(func_type_index); - builder.adjust_value_stack_for_call(&func_type); - builder - .alloc - .inst_builder - .push_inst(Instruction::CallIndirect { - table, - func_type: func_type_index, - }); - Ok(()) - }) - } - - /// Translates a Wasm `drop` instruction. - pub fn visit_drop(&mut self) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop1(); - builder.alloc.inst_builder.push_inst(Instruction::Drop); - Ok(()) - }) - } - - /// Translates a Wasm `select` instruction. - pub fn visit_select(&mut self) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop3(); - builder.stack_height.push(); - builder.alloc.inst_builder.push_inst(Instruction::Select); - Ok(()) - }) - } - - pub fn visit_typed_select(&mut self, _ty: wasmparser::ValType) -> Result<(), TranslationError> { - // The `ty` parameter is only important for Wasm validation. - // Since `wasmi` bytecode is untyped we are not interested in this additional information. - self.visit_select() - } - - /// Translate a Wasm `ref.null` instruction. - pub fn visit_ref_null(&mut self, _ty: wasmparser::ValType) -> Result<(), TranslationError> { - // Since `wasmi` bytecode is untyped we have no special `null` instructions - // but simply reuse the `constant` instruction with an immediate value of 0. - // Note that `FuncRef` and `ExternRef` are encoded as 64-bit values in `wasmi`. - self.translate_const(0i64) - } - - /// Translate a Wasm `ref.is_null` instruction. - pub fn visit_ref_is_null(&mut self) -> Result<(), TranslationError> { - // Since `wasmi` bytecode is untyped we have no special `null` instructions - // but simply reuse the `i64.eqz` instruction with an immediate value of 0. - // Note that `FuncRef` and `ExternRef` are encoded as 64-bit values in `wasmi`. - self.visit_i64_eqz() - } - - /// Translate a Wasm `ref.func` instruction. - pub fn visit_ref_func(&mut self, func_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let func_index = bytecode::FuncIdx::from(func_index); - builder - .alloc - .inst_builder - .push_inst(Instruction::RefFunc { func_index }); - builder.stack_height.push(); - Ok(()) - }) - } - - /// Translate a Wasm `local.get` instruction. - pub fn visit_local_get(&mut self, local_idx: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let local_depth = builder.relative_local_depth(local_idx); - builder - .alloc - .inst_builder - .push_inst(Instruction::local_get(local_depth)); - builder.stack_height.push(); - Ok(()) - }) - } - - /// Translate a Wasm `local.set` instruction. - pub fn visit_local_set(&mut self, local_idx: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop1(); - let local_depth = builder.relative_local_depth(local_idx); - builder - .alloc - .inst_builder - .push_inst(Instruction::local_set(local_depth)); - Ok(()) - }) - } - - /// Translate a Wasm `local.tee` instruction. - pub fn visit_local_tee(&mut self, local_idx: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let local_depth = builder.relative_local_depth(local_idx); - builder - .alloc - .inst_builder - .push_inst(Instruction::local_tee(local_depth)); - Ok(()) - }) - } - - /// Translate a Wasm `global.get` instruction. - pub fn visit_global_get(&mut self, global_idx: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let global_idx = GlobalIdx(global_idx); - builder.stack_height.push(); - let (global_type, init_value) = builder.res.get_global(global_idx); - let instr = Self::optimize_global_get(&global_type, init_value).unwrap_or_else(|| { - // No optimization took place in this case. - Instruction::GlobalGet(global_idx.into_u32().into()) - }); - builder.alloc.inst_builder.push_inst(instr); - Ok(()) - }) - } - - /// Returns `Some` equivalent instruction if the `global.get` can be optimzied. - /// - /// # Note - /// - /// Only internal (non-imported) and constant (non-mutable) globals - /// have a chance to be optimized to more efficient instructions. - fn optimize_global_get( - global_type: &GlobalType, - init_value: Option<&InitExpr>, - ) -> Option { - let content_type = global_type.content(); - if let (Mutability::Const, Some(init_expr)) = (global_type.mutability(), init_value) { - if let Some(value) = init_expr.to_const(content_type) { - // We can optimize `global.get` to the constant value. - return Some(Instruction::constant(value)); - } - if let Some(func_index) = init_expr.func_ref() { - // We can optimize `global.get` to the equivalent `ref.func x` instruction. - let func_index = bytecode::FuncIdx::from(func_index.into_u32()); - return Some(Instruction::RefFunc { func_index }); - } + impl_visit_operator!($($rest)*); + }; + ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + // Wildcard match arm for all the other (yet) unsupported Wasm proposals. + fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output { + let offset = self.current_pos(); + self.validator.visitor(offset).$visit($($($arg),*)?).map_err(::core::convert::Into::into) } - None - } - - /// Translate a Wasm `global.set` instruction. - pub fn visit_global_set(&mut self, global_idx: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let global_idx = GlobalIdx(global_idx); - let global_type = builder.res.get_type_of_global(global_idx); - debug_assert_eq!(global_type.mutability(), Mutability::Var); - builder.stack_height.pop1(); - let global_idx = global_idx.into_u32().into(); - builder - .alloc - .inst_builder - .push_inst(Instruction::GlobalSet(global_idx)); - Ok(()) - }) - } - - /// Decompose a [`wasmparser::MemArg`] into its raw parts. - fn decompose_memarg(memarg: wasmparser::MemArg) -> (MemoryIdx, u32) { - let memory_idx = MemoryIdx(memarg.memory); - let offset = memarg.offset as u32; - (memory_idx, offset) - } - - /// Translate a Wasm `.load` instruction. - /// - /// # Note - /// - /// This is used as the translation backend of the following Wasm instructions: - /// - /// - `i32.load` - /// - `i64.load` - /// - `f32.load` - /// - `f64.load` - /// - `i32.load_i8` - /// - `i32.load_u8` - /// - `i32.load_i16` - /// - `i32.load_u16` - /// - `i64.load_i8` - /// - `i64.load_u8` - /// - `i64.load_i16` - /// - `i64.load_u16` - /// - `i64.load_i32` - /// - `i64.load_u32` - fn translate_load( - &mut self, - memarg: wasmparser::MemArg, - _loaded_type: ValueType, - make_inst: fn(Offset) -> Instruction, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let (memory_idx, offset) = Self::decompose_memarg(memarg); - debug_assert_eq!(memory_idx.into_u32(), DEFAULT_MEMORY_INDEX); - builder.stack_height.pop1(); - builder.stack_height.push(); - let offset = Offset::from(offset); - builder.alloc.inst_builder.push_inst(make_inst(offset)); - Ok(()) - }) - } - - /// Translate a Wasm `i32.load` instruction. - pub fn visit_i32_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I32, Instruction::I32Load) - } - - /// Translate a Wasm `i64.load` instruction. - pub fn visit_i64_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I64, Instruction::I64Load) - } - - /// Translate a Wasm `f32.load` instruction. - pub fn visit_f32_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::F32, Instruction::F32Load) - } - - /// Translate a Wasm `f64.load` instruction. - pub fn visit_f64_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::F64, Instruction::F64Load) - } - - /// Translate a Wasm `i32.load_i8` instruction. - pub fn visit_i32_load8_s( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I32, Instruction::I32Load8S) - } - - /// Translate a Wasm `i32.load_u8` instruction. - pub fn visit_i32_load8_u( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I32, Instruction::I32Load8U) - } - - /// Translate a Wasm `i32.load_i16` instruction. - pub fn visit_i32_load16_s( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I32, Instruction::I32Load16S) - } - - /// Translate a Wasm `i32.load_u16` instruction. - pub fn visit_i32_load16_u( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I32, Instruction::I32Load16U) - } - - /// Translate a Wasm `i64.load_i8` instruction. - pub fn visit_i64_load8_s( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I64, Instruction::I64Load8S) - } - - /// Translate a Wasm `i64.load_u8` instruction. - pub fn visit_i64_load8_u( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I64, Instruction::I64Load8U) - } - - /// Translate a Wasm `i64.load_i16` instruction. - pub fn visit_i64_load16_s( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I64, Instruction::I64Load16S) - } - - /// Translate a Wasm `i64.load_u16` instruction. - pub fn visit_i64_load16_u( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I64, Instruction::I64Load16U) - } - - /// Translate a Wasm `i64.load_i32` instruction. - pub fn visit_i64_load32_s( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I64, Instruction::I64Load32S) - } - - /// Translate a Wasm `i64.load_u32` instruction. - pub fn visit_i64_load32_u( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_load(memarg, ValueType::I64, Instruction::I64Load32U) - } - - /// Translate a Wasm `.store` instruction. - /// - /// # Note - /// - /// This is used as the translation backend of the following Wasm instructions: - /// - /// - `i32.store` - /// - `i64.store` - /// - `f32.store` - /// - `f64.store` - /// - `i32.store_i8` - /// - `i32.store_i16` - /// - `i64.store_i8` - /// - `i64.store_i16` - /// - `i64.store_i32` - fn translate_store( - &mut self, - memarg: wasmparser::MemArg, - _stored_value: ValueType, - make_inst: fn(Offset) -> Instruction, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let (memory_idx, offset) = Self::decompose_memarg(memarg); - debug_assert_eq!(memory_idx.into_u32(), DEFAULT_MEMORY_INDEX); - builder.stack_height.pop2(); - let offset = Offset::from(offset); - builder.alloc.inst_builder.push_inst(make_inst(offset)); - Ok(()) - }) - } - - /// Translate a Wasm `i32.store` instruction. - pub fn visit_i32_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::I32, Instruction::I32Store) - } - - /// Translate a Wasm `i64.store` instruction. - pub fn visit_i64_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::I64, Instruction::I64Store) - } - - /// Translate a Wasm `f32.store` instruction. - pub fn visit_f32_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::F32, Instruction::F32Store) - } - - /// Translate a Wasm `f64.store` instruction. - pub fn visit_f64_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::F64, Instruction::F64Store) - } - - /// Translate a Wasm `i32.store_i8` instruction. - pub fn visit_i32_store8(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::I32, Instruction::I32Store8) - } - - /// Translate a Wasm `i32.store_i16` instruction. - pub fn visit_i32_store16( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::I32, Instruction::I32Store16) - } - - /// Translate a Wasm `i64.store_i8` instruction. - pub fn visit_i64_store8(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::I64, Instruction::I64Store8) - } - - /// Translate a Wasm `i64.store_i16` instruction. - pub fn visit_i64_store16( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::I64, Instruction::I64Store16) - } - - /// Translate a Wasm `i64.store_i32` instruction. - pub fn visit_i64_store32( - &mut self, - memarg: wasmparser::MemArg, - ) -> Result<(), TranslationError> { - self.translate_store(memarg, ValueType::I64, Instruction::I64Store32) - } + impl_visit_operator!($($rest)*); + }; + () => {}; +} - /// Translate a Wasm `memory.size` instruction. - pub fn visit_memory_size( - &mut self, - memory_idx: u32, - _mem_byte: u8, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let memory_idx = MemoryIdx(memory_idx); - debug_assert_eq!(memory_idx.into_u32(), DEFAULT_MEMORY_INDEX); - builder.stack_height.push(); - builder - .alloc - .inst_builder - .push_inst(Instruction::MemorySize); - Ok(()) - }) - } +impl<'a> VisitOperator<'a> for FuncBuilder<'a> { + type Output = Result<(), TranslationError>; - /// Translate a Wasm `memory.grow` instruction. - pub fn visit_memory_grow( - &mut self, - memory_index: u32, - _mem_byte: u8, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - debug_assert_eq!(memory_index, DEFAULT_MEMORY_INDEX); - builder - .alloc - .inst_builder - .push_inst(Instruction::MemoryGrow); - Ok(()) - }) - } - - /// Translate a Wasm `memory.init` instruction. - pub fn visit_memory_init( - &mut self, - segment_index: u32, - memory_index: u32, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - debug_assert_eq!(memory_index, DEFAULT_MEMORY_INDEX); - builder.stack_height.pop3(); - builder - .alloc - .inst_builder - .push_inst(Instruction::MemoryInit(DataSegmentIdx::from(segment_index))); - Ok(()) - }) - } - - /// Translate a Wasm `memory.fill` instruction. - pub fn visit_memory_fill(&mut self, memory_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - debug_assert_eq!(memory_index, DEFAULT_MEMORY_INDEX); - builder.stack_height.pop3(); - builder - .alloc - .inst_builder - .push_inst(Instruction::MemoryFill); - Ok(()) - }) - } - - /// Translate a Wasm `memory.copy` instruction. - pub fn visit_memory_copy( - &mut self, - dst_mem: u32, - src_mem: u32, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - debug_assert_eq!(dst_mem, DEFAULT_MEMORY_INDEX); - debug_assert_eq!(src_mem, DEFAULT_MEMORY_INDEX); - builder.stack_height.pop3(); - builder - .alloc - .inst_builder - .push_inst(Instruction::MemoryCopy); - Ok(()) - }) - } - - /// Translate a Wasm `data.drop` instruction. - pub fn visit_data_drop(&mut self, segment_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let segment_index = DataSegmentIdx::from(segment_index); - builder - .alloc - .inst_builder - .push_inst(Instruction::DataDrop(segment_index)); - Ok(()) - }) - } - - /// Translate a Wasm `table.size` instruction. - pub fn visit_table_size(&mut self, table_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let table = TableIdx::from(table_index); - builder.stack_height.push(); - builder - .alloc - .inst_builder - .push_inst(Instruction::TableSize { table }); - Ok(()) - }) - } - - /// Translate a Wasm `table.grow` instruction. - pub fn visit_table_grow(&mut self, table_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let table = TableIdx::from(table_index); - builder.stack_height.pop1(); - builder - .alloc - .inst_builder - .push_inst(Instruction::TableGrow { table }); - Ok(()) - }) - } - - /// Translate a Wasm `table.copy` instruction. - pub fn visit_table_copy( - &mut self, - dst_table: u32, - src_table: u32, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let dst = TableIdx::from(dst_table); - let src = TableIdx::from(src_table); - builder.stack_height.pop3(); - builder - .alloc - .inst_builder - .push_inst(Instruction::TableCopy { dst, src }); - Ok(()) - }) - } - - /// Translate a Wasm `table.fill` instruction. - pub fn visit_table_fill(&mut self, table_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let table = TableIdx::from(table_index); - builder.stack_height.pop3(); - builder - .alloc - .inst_builder - .push_inst(Instruction::TableFill { table }); - Ok(()) - }) - } - - /// Translate a Wasm `table.get` instruction. - pub fn visit_table_get(&mut self, table_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let table = TableIdx::from(table_index); - builder - .alloc - .inst_builder - .push_inst(Instruction::TableGet { table }); - Ok(()) - }) - } - - /// Translate a Wasm `table.set` instruction. - pub fn visit_table_set(&mut self, table_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - let table = TableIdx::from(table_index); - builder.stack_height.pop2(); - builder - .alloc - .inst_builder - .push_inst(Instruction::TableSet { table }); - Ok(()) - }) - } - - /// Translate a Wasm `table.init` instruction. - pub fn visit_table_init( - &mut self, - segment_index: u32, - table_index: u32, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop3(); - let table = TableIdx::from(table_index); - let elem = ElementSegmentIdx::from(segment_index); - builder - .alloc - .inst_builder - .push_inst(Instruction::TableInit { table, elem }); - Ok(()) - }) - } - - /// Translate a Wasm `elem.drop` instruction. - pub fn visit_elem_drop(&mut self, segment_index: u32) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder - .alloc - .inst_builder - .push_inst(Instruction::ElemDrop(ElementSegmentIdx::from( - segment_index, - ))); - Ok(()) - }) - } - - /// Translate a Wasm `.const` instruction. - /// - /// # Note - /// - /// This is used as the translation backend of the following Wasm instructions: - /// - /// - `i32.const` - /// - `i64.const` - /// - `f32.const` - /// - `f64.const` - fn translate_const(&mut self, value: T) -> Result<(), TranslationError> - where - T: Into, - { - self.translate_if_reachable(|builder| { - let value = value.into(); - builder.stack_height.push(); - builder - .alloc - .inst_builder - .push_inst(Instruction::constant(value)); - Ok(()) - }) - } - - /// Translate a Wasm `i32.const` instruction. - pub fn visit_i32_const(&mut self, value: i32) -> Result<(), TranslationError> { - self.translate_const(value) - } - - /// Translate a Wasm `i64.const` instruction. - pub fn visit_i64_const(&mut self, value: i64) -> Result<(), TranslationError> { - self.translate_const(value) - } - - /// Translate a Wasm `f32.const` instruction. - pub fn visit_f32_const(&mut self, value: wasmparser::Ieee32) -> Result<(), TranslationError> { - self.translate_const(F32::from_bits(value.bits())) - } - - /// Translate a Wasm `f64.const` instruction. - pub fn visit_f64_const(&mut self, value: wasmparser::Ieee64) -> Result<(), TranslationError> { - self.translate_const(F64::from_bits(value.bits())) - } - - /// Translate a Wasm unary comparison instruction. - /// - /// # Note - /// - /// This is used to translate the following Wasm instructions: - /// - /// - `i32.eqz` - /// - `i64.eqz` - fn translate_unary_cmp( - &mut self, - _input_type: ValueType, - inst: Instruction, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop1(); - builder.stack_height.push(); - builder.alloc.inst_builder.push_inst(inst); - Ok(()) - }) - } - - /// Translate a Wasm `i32.eqz` instruction. - pub fn visit_i32_eqz(&mut self) -> Result<(), TranslationError> { - self.translate_unary_cmp(ValueType::I32, Instruction::I32Eqz) - } - - /// Translate a Wasm binary comparison instruction. - /// - /// # Note - /// - /// This is used to translate the following Wasm instructions: - /// - /// - `{i32, i64, f32, f64}.eq` - /// - `{i32, i64, f32, f64}.ne` - /// - `{i32, u32, i64, u64, f32, f64}.lt` - /// - `{i32, u32, i64, u64, f32, f64}.le` - /// - `{i32, u32, i64, u64, f32, f64}.gt` - /// - `{i32, u32, i64, u64, f32, f64}.ge` - fn translate_binary_cmp( - &mut self, - _input_type: ValueType, - inst: Instruction, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop2(); - builder.stack_height.push(); - builder.alloc.inst_builder.push_inst(inst); - Ok(()) - }) - } - - /// Translate a Wasm `i32.eq` instruction. - pub fn visit_i32_eq(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32Eq) - } - - /// Translate a Wasm `i32.ne` instruction. - pub fn visit_i32_ne(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32Ne) - } - - /// Translate a Wasm `i32.lt` instruction. - pub fn visit_i32_lt_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32LtS) - } - - /// Translate a Wasm `u32.lt` instruction. - pub fn visit_i32_lt_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32LtU) - } - - /// Translate a Wasm `i32.gt` instruction. - pub fn visit_i32_gt_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32GtS) - } - - /// Translate a Wasm `u32.gt` instruction. - pub fn visit_i32_gt_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32GtU) - } - - /// Translate a Wasm `i32.le` instruction. - pub fn visit_i32_le_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32LeS) - } - - /// Translate a Wasm `u32.le` instruction. - pub fn visit_i32_le_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32LeU) - } - - /// Translate a Wasm `i32.ge` instruction. - pub fn visit_i32_ge_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32GeS) - } - - /// Translate a Wasm `u32.ge` instruction. - pub fn visit_i32_ge_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I32, Instruction::I32GeU) - } - - /// Translate a Wasm `i64.eqz` instruction. - pub fn visit_i64_eqz(&mut self) -> Result<(), TranslationError> { - self.translate_unary_cmp(ValueType::I64, Instruction::I64Eqz) - } - - /// Translate a Wasm `i64.eq` instruction. - pub fn visit_i64_eq(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64Eq) - } - - /// Translate a Wasm `i64.ne` instruction. - pub fn visit_i64_ne(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64Ne) - } - - /// Translate a Wasm `i64.lt` instruction. - pub fn visit_i64_lt_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64LtS) - } - - /// Translate a Wasm `u64.lt` instruction. - pub fn visit_i64_lt_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64LtU) - } - - /// Translate a Wasm `i64.gt` instruction. - pub fn visit_i64_gt_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64GtS) - } - - /// Translate a Wasm `u64.gt` instruction. - pub fn visit_i64_gt_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64GtU) - } - - /// Translate a Wasm `i64.le` instruction. - pub fn visit_i64_le_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64LeS) - } - - /// Translate a Wasm `u64.le` instruction. - pub fn visit_i64_le_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64LeU) - } - - /// Translate a Wasm `i64.ge` instruction. - pub fn visit_i64_ge_s(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64GeS) - } - - /// Translate a Wasm `u64.ge` instruction. - pub fn visit_i64_ge_u(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::I64, Instruction::I64GeU) - } - - /// Translate a Wasm `f32.eq` instruction. - pub fn visit_f32_eq(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F32, Instruction::F32Eq) - } - - /// Translate a Wasm `f32.ne` instruction. - pub fn visit_f32_ne(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F32, Instruction::F32Ne) - } - - /// Translate a Wasm `f32.lt` instruction. - pub fn visit_f32_lt(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F32, Instruction::F32Lt) - } - - /// Translate a Wasm `f32.gt` instruction. - pub fn visit_f32_gt(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F32, Instruction::F32Gt) - } - - /// Translate a Wasm `f32.le` instruction. - pub fn visit_f32_le(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F32, Instruction::F32Le) - } - - /// Translate a Wasm `f32.ge` instruction. - pub fn visit_f32_ge(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F32, Instruction::F32Ge) - } - - /// Translate a Wasm `f64.eq` instruction. - pub fn visit_f64_eq(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F64, Instruction::F64Eq) - } - - /// Translate a Wasm `f64.ne` instruction. - pub fn visit_f64_ne(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F64, Instruction::F64Ne) - } - - /// Translate a Wasm `f64.lt` instruction. - pub fn visit_f64_lt(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F64, Instruction::F64Lt) - } - - /// Translate a Wasm `f64.gt` instruction. - pub fn visit_f64_gt(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F64, Instruction::F64Gt) - } - - /// Translate a Wasm `f64.le` instruction. - pub fn visit_f64_le(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F64, Instruction::F64Le) - } - - /// Translate a Wasm `f64.ge` instruction. - pub fn visit_f64_ge(&mut self) -> Result<(), TranslationError> { - self.translate_binary_cmp(ValueType::F64, Instruction::F64Ge) - } - - /// Translate a unary Wasm instruction. - /// - /// # Note - /// - /// This is used to translate the following Wasm instructions: - /// - /// - `i32.clz` - /// - `i32.ctz` - /// - `i32.popcnt` - /// - `{f32, f64}.abs` - /// - `{f32, f64}.neg` - /// - `{f32, f64}.ceil` - /// - `{f32, f64}.floor` - /// - `{f32, f64}.trunc` - /// - `{f32, f64}.nearest` - /// - `{f32, f64}.sqrt` - pub fn visit_unary_operation( - &mut self, - _value_type: ValueType, - inst: Instruction, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.alloc.inst_builder.push_inst(inst); - Ok(()) - }) - } - - /// Translate a Wasm `i32.clz` instruction. - pub fn visit_i32_clz(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I32, Instruction::I32Clz) - } - - /// Translate a Wasm `i32.ctz` instruction. - pub fn visit_i32_ctz(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I32, Instruction::I32Ctz) - } - - /// Translate a Wasm `i32.popcnt` instruction. - pub fn visit_i32_popcnt(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I32, Instruction::I32Popcnt) - } - - /// Translate a binary Wasm instruction. - /// - /// - `{i32, i64}.add` - /// - `{i32, i64}.sub` - /// - `{i32, i64}.mul` - /// - `{i32, u32, i64, u64}.div` - /// - `{i32, u32, i64, u64}.rem` - /// - `{i32, i64}.and` - /// - `{i32, i64}.or` - /// - `{i32, i64}.xor` - /// - `{i32, i64}.shl` - /// - `{i32, u32, i64, u64}.shr` - /// - `{i32, i64}.rotl` - /// - `{i32, i64}.rotr` - /// - `{f32, f64}.add` - /// - `{f32, f64}.sub` - /// - `{f32, f64}.mul` - /// - `{f32, f64}.div` - /// - `{f32, f64}.min` - /// - `{f32, f64}.max` - /// - `{f32, f64}.copysign` - pub fn visit_binary_operation( - &mut self, - _value_type: ValueType, - inst: Instruction, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop2(); - builder.stack_height.push(); - builder.alloc.inst_builder.push_inst(inst); - Ok(()) - }) - } - - /// Translate a Wasm `i32.add` instruction. - pub fn visit_i32_add(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Add) - } - - /// Translate a Wasm `i32.sub` instruction. - pub fn visit_i32_sub(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Sub) - } - - /// Translate a Wasm `i32.mul` instruction. - pub fn visit_i32_mul(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Mul) - } - - /// Translate a Wasm `i32.div` instruction. - pub fn visit_i32_div_s(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32DivS) - } - - /// Translate a Wasm `u32.div` instruction. - pub fn visit_i32_div_u(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32DivU) - } - - /// Translate a Wasm `i32.rem` instruction. - pub fn visit_i32_rem_s(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32RemS) - } - - /// Translate a Wasm `u32.rem` instruction. - pub fn visit_i32_rem_u(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32RemU) - } - - /// Translate a Wasm `i32.and` instruction. - pub fn visit_i32_and(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32And) - } - - /// Translate a Wasm `i32.or` instruction. - pub fn visit_i32_or(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Or) - } - - /// Translate a Wasm `i32.xor` instruction. - pub fn visit_i32_xor(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Xor) - } - - /// Translate a Wasm `i32.shl` instruction. - pub fn visit_i32_shl(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Shl) - } - - /// Translate a Wasm `i32.shr` instruction. - pub fn visit_i32_shr_s(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32ShrS) - } - - /// Translate a Wasm `u32.shr` instruction. - pub fn visit_i32_shr_u(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32ShrU) - } - - /// Translate a Wasm `i32.rotl` instruction. - pub fn visit_i32_rotl(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Rotl) - } - - /// Translate a Wasm `i32.rotr` instruction. - pub fn visit_i32_rotr(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I32, Instruction::I32Rotr) - } - - /// Translate a Wasm `i64.clz` instruction. - pub fn visit_i64_clz(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I64, Instruction::I64Clz) - } - - /// Translate a Wasm `i64.ctz` instruction. - pub fn visit_i64_ctz(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I64, Instruction::I64Ctz) - } - - /// Translate a Wasm `i64.popcnt` instruction. - pub fn visit_i64_popcnt(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I64, Instruction::I64Popcnt) - } - - /// Translate a Wasm `i64.add` instruction. - pub fn visit_i64_add(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Add) - } - - /// Translate a Wasm `i64.sub` instruction. - pub fn visit_i64_sub(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Sub) - } - - /// Translate a Wasm `i64.mul` instruction. - pub fn visit_i64_mul(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Mul) - } - - /// Translate a Wasm `i64.div` instruction. - pub fn visit_i64_div_s(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64DivS) - } - - /// Translate a Wasm `u64.div` instruction. - pub fn visit_i64_div_u(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64DivU) - } - - /// Translate a Wasm `i64.rem` instruction. - pub fn visit_i64_rem_s(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64RemS) - } - - /// Translate a Wasm `u64.rem` instruction. - pub fn visit_i64_rem_u(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64RemU) - } - - /// Translate a Wasm `i64.and` instruction. - pub fn visit_i64_and(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64And) - } - - /// Translate a Wasm `i64.or` instruction. - pub fn visit_i64_or(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Or) - } - - /// Translate a Wasm `i64.xor` instruction. - pub fn visit_i64_xor(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Xor) - } - - /// Translate a Wasm `i64.shl` instruction. - pub fn visit_i64_shl(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Shl) - } - - /// Translate a Wasm `i64.shr` instruction. - pub fn visit_i64_shr_s(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64ShrS) - } - - /// Translate a Wasm `u64.shr` instruction. - pub fn visit_i64_shr_u(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64ShrU) - } - - /// Translate a Wasm `i64.rotl` instruction. - pub fn visit_i64_rotl(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Rotl) - } - - /// Translate a Wasm `i64.rotr` instruction. - pub fn visit_i64_rotr(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::I64, Instruction::I64Rotr) - } - - /// Translate a Wasm `f32.abs` instruction. - pub fn visit_f32_abs(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F32, Instruction::F32Abs) - } - - /// Translate a Wasm `f32.neg` instruction. - pub fn visit_f32_neg(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F32, Instruction::F32Neg) - } - - /// Translate a Wasm `f32.ceil` instruction. - pub fn visit_f32_ceil(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F32, Instruction::F32Ceil) - } - - /// Translate a Wasm `f32.floor` instruction. - pub fn visit_f32_floor(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F32, Instruction::F32Floor) - } - - /// Translate a Wasm `f32.trunc` instruction. - pub fn visit_f32_trunc(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F32, Instruction::F32Trunc) - } - - /// Translate a Wasm `f32.nearest` instruction. - pub fn visit_f32_nearest(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F32, Instruction::F32Nearest) - } - - /// Translate a Wasm `f32.sqrt` instruction. - pub fn visit_f32_sqrt(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F32, Instruction::F32Sqrt) - } - - /// Translate a Wasm `f32.add` instruction. - pub fn visit_f32_add(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F32, Instruction::F32Add) - } - - /// Translate a Wasm `f32.sub` instruction. - pub fn visit_f32_sub(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F32, Instruction::F32Sub) - } - - /// Translate a Wasm `f32.mul` instruction. - pub fn visit_f32_mul(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F32, Instruction::F32Mul) - } - - /// Translate a Wasm `f32.div` instruction. - pub fn visit_f32_div(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F32, Instruction::F32Div) - } - - /// Translate a Wasm `f32.min` instruction. - pub fn visit_f32_min(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F32, Instruction::F32Min) - } - - /// Translate a Wasm `f32.max` instruction. - pub fn visit_f32_max(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F32, Instruction::F32Max) - } - - /// Translate a Wasm `f32.copysign` instruction. - pub fn visit_f32_copysign(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F32, Instruction::F32Copysign) - } - - /// Translate a Wasm `f64.abs` instruction. - pub fn visit_f64_abs(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F64, Instruction::F64Abs) - } - - /// Translate a Wasm `f64.neg` instruction. - pub fn visit_f64_neg(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F64, Instruction::F64Neg) - } - - /// Translate a Wasm `f64.ceil` instruction. - pub fn visit_f64_ceil(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F64, Instruction::F64Ceil) - } - - /// Translate a Wasm `f64.floor` instruction. - pub fn visit_f64_floor(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F64, Instruction::F64Floor) - } - - /// Translate a Wasm `f64.trunc` instruction. - pub fn visit_f64_trunc(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F64, Instruction::F64Trunc) - } - - /// Translate a Wasm `f64.nearest` instruction. - pub fn visit_f64_nearest(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F64, Instruction::F64Nearest) - } - - /// Translate a Wasm `f64.sqrt` instruction. - pub fn visit_f64_sqrt(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::F64, Instruction::F64Sqrt) - } - - /// Translate a Wasm `f64.add` instruction. - pub fn visit_f64_add(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F64, Instruction::F64Add) - } - - /// Translate a Wasm `f64.sub` instruction. - pub fn visit_f64_sub(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F64, Instruction::F64Sub) - } - - /// Translate a Wasm `f64.mul` instruction. - pub fn visit_f64_mul(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F64, Instruction::F64Mul) - } - - /// Translate a Wasm `f64.div` instruction. - pub fn visit_f64_div(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F64, Instruction::F64Div) - } - - /// Translate a Wasm `f64.min` instruction. - pub fn visit_f64_min(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F64, Instruction::F64Min) - } - - /// Translate a Wasm `f64.max` instruction. - pub fn visit_f64_max(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F64, Instruction::F64Max) - } - - /// Translate a Wasm `f64.copysign` instruction. - pub fn visit_f64_copysign(&mut self) -> Result<(), TranslationError> { - self.visit_binary_operation(ValueType::F64, Instruction::F64Copysign) - } - - /// Translate a Wasm conversion instruction. - /// - /// - `i32.wrap_i64` - /// - `{i32, u32}.trunc_f32 - /// - `{i32, u32}.trunc_f64` - /// - `{i64, u64}.extend_i32` - /// - `{i64, u64}.trunc_f32` - /// - `{i64, u64}.trunc_f64` - /// - `f32.convert_{i32, u32, i64, u64}` - /// - `f32.demote_f64` - /// - `f64.convert_{i32, u32, i64, u64}` - /// - `f64.promote_f32` - /// - `i32.reinterpret_f32` - /// - `i64.reinterpret_f64` - /// - `f32.reinterpret_i32` - /// - `f64.reinterpret_i64` - pub fn visit_conversion( - &mut self, - _input_type: ValueType, - _output_type: ValueType, - inst: Instruction, - ) -> Result<(), TranslationError> { - self.translate_if_reachable(|builder| { - builder.stack_height.pop1(); - builder.stack_height.push(); - builder.alloc.inst_builder.push_inst(inst); - Ok(()) - }) - } - - /// Translate a Wasm `i32.wrap_i64` instruction. - pub fn visit_i32_wrap_i64(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I64, ValueType::I32, Instruction::I32WrapI64) - } - - /// Translate a Wasm `i32.trunc_f32` instruction. - pub fn visit_i32_trunc_f32_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncF32S) - } - - /// Translate a Wasm `u32.trunc_f32` instruction. - pub fn visit_i32_trunc_f32_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncF32U) - } - - /// Translate a Wasm `i32.trunc_f64` instruction. - pub fn visit_i32_trunc_f64_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncF64S) - } - - /// Translate a Wasm `u32.trunc_f64` instruction. - pub fn visit_i32_trunc_f64_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncF64U) - } - - /// Translate a Wasm `i64.extend_i32` instruction. - pub fn visit_i64_extend_i32_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I32, ValueType::I64, Instruction::I64ExtendI32S) - } - - /// Translate a Wasm `u64.extend_i32` instruction. - pub fn visit_i64_extend_i32_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I32, ValueType::I64, Instruction::I64ExtendI32U) - } - - /// Translate a Wasm `i64.trunc_f32` instruction. - pub fn visit_i64_trunc_f32_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncF32S) - } - - /// Translate a Wasm `u64.trunc_f32` instruction. - pub fn visit_i64_trunc_f32_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncF32U) - } - - /// Translate a Wasm `i64.trunc_f64` instruction. - pub fn visit_i64_trunc_f64_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncF64S) - } - - /// Translate a Wasm `u64.trunc_f64` instruction. - pub fn visit_i64_trunc_f64_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncF64U) - } - - /// Translate a Wasm `f32.convert_i32` instruction. - pub fn visit_f32_convert_i32_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I32, ValueType::F32, Instruction::F32ConvertI32S) - } - - /// Translate a Wasm `f32.convert_u32` instruction. - pub fn visit_f32_convert_i32_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I32, ValueType::F32, Instruction::F32ConvertI32U) - } - - /// Translate a Wasm `f32.convert_i64` instruction. - pub fn visit_f32_convert_i64_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I64, ValueType::F32, Instruction::F32ConvertI64S) - } - - /// Translate a Wasm `f32.convert_u64` instruction. - pub fn visit_f32_convert_i64_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I64, ValueType::F32, Instruction::F32ConvertI64U) - } - - /// Translate a Wasm `f32.demote_f64` instruction. - pub fn visit_f32_demote_f64(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::F32, Instruction::F32DemoteF64) - } - - /// Translate a Wasm `f64.convert_i32` instruction. - pub fn visit_f64_convert_i32_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I32, ValueType::F64, Instruction::F64ConvertI32S) - } - - /// Translate a Wasm `f64.convert_u32` instruction. - pub fn visit_f64_convert_i32_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I32, ValueType::F64, Instruction::F64ConvertI32U) - } - - /// Translate a Wasm `f64.convert_i64` instruction. - pub fn visit_f64_convert_i64_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I64, ValueType::F64, Instruction::F64ConvertI64S) - } - - /// Translate a Wasm `f64.convert_u64` instruction. - pub fn visit_f64_convert_i64_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::I64, ValueType::F64, Instruction::F64ConvertI64U) - } - - /// Translate a Wasm `f64.promote_f32` instruction. - pub fn visit_f64_promote_f32(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::F64, Instruction::F64PromoteF32) - } - - /// Translate a Wasm reinterpret instruction. - /// - /// Since `wasmi` bytecode is untyped those reinterpret - /// instructions from Wasm are simply dropped from codegen. - /// - /// - `i32.reinterpret_f32` - /// - `i64.reinterpret_f64` - /// - `f32.reinterpret_i32` - /// - `f64.reinterpret_i64` - pub fn visit_reinterpret( - &mut self, - _input_type: ValueType, - _output_type: ValueType, - ) -> Result<(), TranslationError> { - Ok(()) - } - - /// Translate a Wasm `i32.reinterpret_f32` instruction. - pub fn visit_i32_reinterpret_f32(&mut self) -> Result<(), TranslationError> { - self.visit_reinterpret(ValueType::F32, ValueType::I32) - } - - /// Translate a Wasm `i64.reinterpret_f64` instruction. - pub fn visit_i64_reinterpret_f64(&mut self) -> Result<(), TranslationError> { - self.visit_reinterpret(ValueType::F64, ValueType::I64) - } - - /// Translate a Wasm `f32.reinterpret_i32` instruction. - pub fn visit_f32_reinterpret_i32(&mut self) -> Result<(), TranslationError> { - self.visit_reinterpret(ValueType::I32, ValueType::F32) - } - - /// Translate a Wasm `f64.reinterpret_i64` instruction. - pub fn visit_f64_reinterpret_i64(&mut self) -> Result<(), TranslationError> { - self.visit_reinterpret(ValueType::I64, ValueType::F64) - } - - /// Translate a Wasm `i32.extend_8s` instruction. - pub fn visit_i32_extend8_s(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I32, Instruction::I32Extend8S) - } - - /// Translate a Wasm `i32.extend_16s` instruction. - pub fn visit_i32_extend16_s(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I32, Instruction::I32Extend16S) - } - - /// Translate a Wasm `i64.extend_8s` instruction. - pub fn visit_i64_extend8_s(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I64, Instruction::I64Extend8S) - } - - /// Translate a Wasm `i64.extend_16s` instruction. - pub fn visit_i64_extend16_s(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I64, Instruction::I64Extend16S) - } - - /// Translate a Wasm `i64.extend_32s` instruction. - pub fn visit_i64_extend32_s(&mut self) -> Result<(), TranslationError> { - self.visit_unary_operation(ValueType::I64, Instruction::I64Extend32S) - } - - /// Translate a Wasm `i32.trunc_sat_f32` instruction. - pub fn visit_i32_trunc_sat_f32_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32S) - } - - /// Translate a Wasm `u32.trunc_sat_f32` instruction. - pub fn visit_i32_trunc_sat_f32_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32U) - } - - /// Translate a Wasm `i32.trunc_sat_f64` instruction. - pub fn visit_i32_trunc_sat_f64_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64S) - } - - /// Translate a Wasm `u32.trunc_sat_f64` instruction. - pub fn visit_i32_trunc_sat_f64_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64U) - } - - /// Translate a Wasm `i64.trunc_sat_f32` instruction. - pub fn visit_i64_trunc_sat_f32_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncSatF32S) - } - - /// Translate a Wasm `u64.trunc_sat_f32` instruction. - pub fn visit_i64_trunc_sat_f32_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncSatF32U) - } - - /// Translate a Wasm `i64.trunc_sat_f64` instruction. - pub fn visit_i64_trunc_sat_f64_s(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncSatF64S) - } - - /// Translate a Wasm `u64.trunc_sat_f64` instruction. - pub fn visit_i64_trunc_sat_f64_u(&mut self) -> Result<(), TranslationError> { - self.visit_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncSatF64U) - } + wasmparser::for_each_operator!(impl_visit_operator); } diff --git a/crates/wasmi/src/engine/func_builder/translator.rs b/crates/wasmi/src/engine/func_builder/translator.rs new file mode 100644 index 0000000000..7c4ecfef06 --- /dev/null +++ b/crates/wasmi/src/engine/func_builder/translator.rs @@ -0,0 +1,1948 @@ +use super::{ + control_frame::{ + BlockControlFrame, + ControlFrame, + IfControlFrame, + LoopControlFrame, + UnreachableControlFrame, + }, + labels::LabelRef, + locals_registry::LocalsRegistry, + value_stack::ValueStackHeight, + FunctionBuilderAllocations, + TranslationError, +}; +use crate::{ + engine::{ + bytecode::{ + self, + BranchParams, + DataSegmentIdx, + ElementSegmentIdx, + Instruction, + Offset, + SignatureIdx, + TableIdx, + }, + func_builder::control_frame::ControlFrameKind, + DropKeep, + FuncBody, + Instr, + RelativeDepth, + }, + module::{ + BlockType, + FuncIdx, + FuncTypeIdx, + GlobalIdx, + InitExpr, + MemoryIdx, + ModuleResources, + DEFAULT_MEMORY_INDEX, + }, + Engine, + FuncType, + GlobalType, + Mutability, + Value, +}; +use wasmi_core::{ValueType, F32, F64}; +use wasmparser::VisitOperator; + +/// Type concerned with translating from Wasm bytecode to `wasmi` bytecode. +pub struct FuncTranslator<'parser> { + /// The [`Engine`] for which the function is translated. + engine: Engine, + /// The function under construction. + func: FuncIdx, + /// The immutable `wasmi` module resources. + res: ModuleResources<'parser>, + /// This represents the reachability of the currently translated code. + /// + /// - `true`: The currently translated code is reachable. + /// - `false`: The currently translated code is unreachable and can be skipped. + /// + /// # Note + /// + /// Visiting the Wasm `Else` or `End` control flow operator resets + /// reachability to `true` again. + reachable: bool, + /// The height of the emulated value stack. + stack_height: ValueStackHeight, + /// Stores and resolves local variable types. + locals: LocalsRegistry, + /// The reusable data structures of the [`FuncTranslator`]. + alloc: FunctionBuilderAllocations, +} + +impl<'parser> FuncTranslator<'parser> { + /// Creates a new [`FuncTranslator`]. + pub fn new( + engine: &Engine, + func: FuncIdx, + res: ModuleResources<'parser>, + allocations: FunctionBuilderAllocations, + ) -> Self { + let mut allocations = allocations; + let mut locals = LocalsRegistry::default(); + Self::register_func_body_block(func, res, &mut allocations); + Self::register_func_params(func, res, &mut locals); + Self { + engine: engine.clone(), + func, + res, + reachable: true, + stack_height: ValueStackHeight::default(), + locals, + alloc: allocations, + } + } + + /// Registers an `amount` of local variables. + /// + /// # Panics + /// + /// If too many local variables have been registered. + pub fn register_locals(&mut self, amount: u32) { + self.locals.register_locals(amount); + } + + /// Finishes constructing the function and returns its [`FuncBody`]. + pub fn finish(&mut self) -> Result { + let func_body = self.alloc.inst_builder.finish( + &self.engine, + self.len_locals(), + self.stack_height.max_stack_height() as usize, + ); + Ok(func_body) + } + + /// Consumes `self` and returns the underlying reusable [`FunctionBuilderAllocations`]. + pub fn into_allocations(self) -> FunctionBuilderAllocations { + self.alloc + } + + /// Returns the [`FuncType`] of the function that is currently translated. + fn func_type(&self) -> FuncType { + let dedup_func_type = self.res.get_type_of_func(self.func); + self.engine.resolve_func_type(dedup_func_type, Clone::clone) + } + + /// Resolves the [`FuncType`] of the given [`FuncTypeIdx`]. + fn func_type_at(&self, func_type_index: SignatureIdx) -> FuncType { + let func_type_index = FuncTypeIdx(func_type_index.into_inner()); // TODO: use the same type + let dedup_func_type = self.res.get_func_type(func_type_index); + self.res + .engine() + .resolve_func_type(dedup_func_type, Clone::clone) + } + + /// Resolves the [`FuncType`] of the given [`FuncIdx`]. + fn func_type_of(&self, func_index: FuncIdx) -> FuncType { + let dedup_func_type = self.res.get_type_of_func(func_index); + self.res + .engine() + .resolve_func_type(dedup_func_type, Clone::clone) + } + + /// Registers the `block` control frame surrounding the entire function body. + fn register_func_body_block( + func: FuncIdx, + res: ModuleResources<'parser>, + allocations: &mut FunctionBuilderAllocations, + ) { + allocations.reset(); + let func_type = res.get_type_of_func(func); + let block_type = BlockType::func_type(func_type); + let end_label = allocations.inst_builder.new_label(); + let block_frame = BlockControlFrame::new(block_type, end_label, 0); + allocations.control_frames.push_frame(block_frame); + } + + /// Registers the function parameters in the emulated value stack. + fn register_func_params( + func: FuncIdx, + res: ModuleResources<'parser>, + locals: &mut LocalsRegistry, + ) -> usize { + let dedup_func_type = res.get_type_of_func(func); + let func_type = res + .engine() + .resolve_func_type(dedup_func_type, Clone::clone); + let params = func_type.params(); + for _param_type in params { + locals.register_locals(1); + } + params.len() + } + + /// Returns the number of local variables of the function under construction. + fn len_locals(&self) -> usize { + let len_params_locals = self.locals.len_registered() as usize; + let len_params = self.func_type().params().len(); + debug_assert!(len_params_locals >= len_params); + len_params_locals - len_params + } + + /// Returns `true` if the code at the current translation position is reachable. + fn is_reachable(&self) -> bool { + self.reachable + } + + /// Translates into `wasmi` bytecode if the current code path is reachable. + /// + /// # Note + /// + /// Ignores the `translator` closure if the current code path is unreachable. + fn translate_if_reachable(&mut self, translator: F) -> Result<(), TranslationError> + where + F: FnOnce(&mut Self) -> Result<(), TranslationError>, + { + if self.is_reachable() { + translator(self)?; + } + Ok(()) + } + + /// Computes how many values should be dropped and kept for the specific branch. + /// + /// # Panics + /// + /// If underflow of the value stack is detected. + fn compute_drop_keep(&self, depth: u32) -> Result { + debug_assert!(self.is_reachable()); + let frame = self.alloc.control_frames.nth_back(depth); + // Find out how many values we need to keep (copy to the new stack location after the drop). + let keep = match frame.kind() { + ControlFrameKind::Block | ControlFrameKind::If => { + frame.block_type().len_results(&self.engine) + } + ControlFrameKind::Loop => frame.block_type().len_params(&self.engine), + }; + // Find out how many values we need to drop. + let current_height = self.stack_height.height(); + let origin_height = frame.stack_height().expect("frame is reachable"); + assert!( + origin_height <= current_height, + "encountered value stack underflow: \ + current height {current_height}, original height {origin_height}", + ); + let height_diff = current_height - origin_height; + assert!( + keep <= height_diff, + "tried to keep {keep} values while having \ + only {height_diff} values available on the frame", + ); + let drop = height_diff - keep; + DropKeep::new(drop as usize, keep as usize).map_err(Into::into) + } + + /// Compute [`DropKeep`] for the return statement. + /// + /// # Panics + /// + /// - If the control flow frame stack is empty. + /// - If the value stack is underflown. + fn drop_keep_return(&self) -> Result { + debug_assert!(self.is_reachable()); + assert!( + !self.alloc.control_frames.is_empty(), + "drop_keep_return cannot be called with the frame stack empty" + ); + let max_depth = self + .alloc + .control_frames + .len() + .checked_sub(1) + .expect("control flow frame stack must not be empty") as u32; + let drop_keep = self.compute_drop_keep(max_depth)?; + let len_params_locals = self.locals.len_registered() as usize; + DropKeep::new( + // Drop all local variables and parameters upon exit. + drop_keep.drop() + len_params_locals, + drop_keep.keep(), + ) + .map_err(Into::into) + } + + /// Returns the relative depth on the stack of the local variable. + fn relative_local_depth(&self, local_idx: u32) -> usize { + debug_assert!(self.is_reachable()); + let stack_height = self.stack_height.height() as usize; + let len_params_locals = self.locals.len_registered() as usize; + stack_height + .checked_add(len_params_locals) + .and_then(|x| x.checked_sub(local_idx as usize)) + .unwrap_or_else(|| panic!("cannot convert local index into local depth: {local_idx}")) + } + + /// Creates [`BranchParams`] to `target` using `drop_keep` for the current instruction. + fn branch_params(&mut self, target: LabelRef, drop_keep: DropKeep) -> BranchParams { + BranchParams::new(self.alloc.inst_builder.try_resolve_label(target), drop_keep) + } + + /// Calculates the stack height upon entering a control flow frame. + /// + /// # Note + /// + /// This does not include the parameters of the control flow frame + /// so that when shrinking the emulated value stack to the control flow + /// frame's original stack height the control flow frame parameters are + /// no longer on the emulated value stack. + /// + /// # Panics + /// + /// When the emulated value stack underflows. This should not happen + /// since we have already validated the input Wasm prior. + fn frame_stack_height(&self, block_type: BlockType) -> u32 { + let len_params = block_type.len_params(&self.engine); + let stack_height = self.stack_height.height(); + stack_height.checked_sub(len_params).unwrap_or_else(|| { + panic!( + "encountered emulated value stack underflow with \ + stack height {stack_height} and {len_params} block parameters", + ) + }) + } + + /// Adjusts the emulated value stack given the [`FuncType`] of the call. + fn adjust_value_stack_for_call(&mut self, func_type: &FuncType) { + let (params, results) = func_type.params_results(); + self.stack_height.pop_n(params.len() as u32); + self.stack_height.push_n(results.len() as u32); + } + + /// Returns `Some` equivalent instruction if the `global.get` can be optimzied. + /// + /// # Note + /// + /// Only internal (non-imported) and constant (non-mutable) globals + /// have a chance to be optimized to more efficient instructions. + fn optimize_global_get( + global_type: &GlobalType, + init_value: Option<&InitExpr>, + ) -> Option { + let content_type = global_type.content(); + if let (Mutability::Const, Some(init_expr)) = (global_type.mutability(), init_value) { + if let Some(value) = init_expr.to_const(content_type) { + // We can optimize `global.get` to the constant value. + return Some(Instruction::constant(value)); + } + if let Some(func_index) = init_expr.func_ref() { + // We can optimize `global.get` to the equivalent `ref.func x` instruction. + let func_index = bytecode::FuncIdx::from(func_index.into_u32()); + return Some(Instruction::RefFunc { func_index }); + } + } + None + } + + /// Decompose a [`wasmparser::MemArg`] into its raw parts. + fn decompose_memarg(memarg: wasmparser::MemArg) -> (MemoryIdx, u32) { + let memory_idx = MemoryIdx(memarg.memory); + let offset = memarg.offset as u32; + (memory_idx, offset) + } + + /// Translate a Wasm `.load` instruction. + /// + /// # Note + /// + /// This is used as the translation backend of the following Wasm instructions: + /// + /// - `i32.load` + /// - `i64.load` + /// - `f32.load` + /// - `f64.load` + /// - `i32.load_i8` + /// - `i32.load_u8` + /// - `i32.load_i16` + /// - `i32.load_u16` + /// - `i64.load_i8` + /// - `i64.load_u8` + /// - `i64.load_i16` + /// - `i64.load_u16` + /// - `i64.load_i32` + /// - `i64.load_u32` + fn translate_load( + &mut self, + memarg: wasmparser::MemArg, + _loaded_type: ValueType, + make_inst: fn(Offset) -> Instruction, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let (memory_idx, offset) = Self::decompose_memarg(memarg); + debug_assert_eq!(memory_idx.into_u32(), DEFAULT_MEMORY_INDEX); + builder.stack_height.pop1(); + builder.stack_height.push(); + let offset = Offset::from(offset); + builder.alloc.inst_builder.push_inst(make_inst(offset)); + Ok(()) + }) + } + + /// Translate a Wasm `.store` instruction. + /// + /// # Note + /// + /// This is used as the translation backend of the following Wasm instructions: + /// + /// - `i32.store` + /// - `i64.store` + /// - `f32.store` + /// - `f64.store` + /// - `i32.store_i8` + /// - `i32.store_i16` + /// - `i64.store_i8` + /// - `i64.store_i16` + /// - `i64.store_i32` + fn translate_store( + &mut self, + memarg: wasmparser::MemArg, + _stored_value: ValueType, + make_inst: fn(Offset) -> Instruction, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let (memory_idx, offset) = Self::decompose_memarg(memarg); + debug_assert_eq!(memory_idx.into_u32(), DEFAULT_MEMORY_INDEX); + builder.stack_height.pop2(); + let offset = Offset::from(offset); + builder.alloc.inst_builder.push_inst(make_inst(offset)); + Ok(()) + }) + } + + /// Translate a Wasm `.const` instruction. + /// + /// # Note + /// + /// This is used as the translation backend of the following Wasm instructions: + /// + /// - `i32.const` + /// - `i64.const` + /// - `f32.const` + /// - `f64.const` + fn translate_const(&mut self, value: T) -> Result<(), TranslationError> + where + T: Into, + { + self.translate_if_reachable(|builder| { + let value = value.into(); + builder.stack_height.push(); + builder + .alloc + .inst_builder + .push_inst(Instruction::constant(value)); + Ok(()) + }) + } + + /// Translate a Wasm unary comparison instruction. + /// + /// # Note + /// + /// This is used to translate the following Wasm instructions: + /// + /// - `i32.eqz` + /// - `i64.eqz` + fn translate_unary_cmp( + &mut self, + _input_type: ValueType, + inst: Instruction, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop1(); + builder.stack_height.push(); + builder.alloc.inst_builder.push_inst(inst); + Ok(()) + }) + } + + /// Translate a Wasm binary comparison instruction. + /// + /// # Note + /// + /// This is used to translate the following Wasm instructions: + /// + /// - `{i32, i64, f32, f64}.eq` + /// - `{i32, i64, f32, f64}.ne` + /// - `{i32, u32, i64, u64, f32, f64}.lt` + /// - `{i32, u32, i64, u64, f32, f64}.le` + /// - `{i32, u32, i64, u64, f32, f64}.gt` + /// - `{i32, u32, i64, u64, f32, f64}.ge` + fn translate_binary_cmp( + &mut self, + _input_type: ValueType, + inst: Instruction, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop2(); + builder.stack_height.push(); + builder.alloc.inst_builder.push_inst(inst); + Ok(()) + }) + } + + /// Translate a unary Wasm instruction. + /// + /// # Note + /// + /// This is used to translate the following Wasm instructions: + /// + /// - `i32.clz` + /// - `i32.ctz` + /// - `i32.popcnt` + /// - `{f32, f64}.abs` + /// - `{f32, f64}.neg` + /// - `{f32, f64}.ceil` + /// - `{f32, f64}.floor` + /// - `{f32, f64}.trunc` + /// - `{f32, f64}.nearest` + /// - `{f32, f64}.sqrt` + fn translate_unary_operation( + &mut self, + _value_type: ValueType, + inst: Instruction, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.alloc.inst_builder.push_inst(inst); + Ok(()) + }) + } + + /// Translate a binary Wasm instruction. + /// + /// - `{i32, i64}.add` + /// - `{i32, i64}.sub` + /// - `{i32, i64}.mul` + /// - `{i32, u32, i64, u64}.div` + /// - `{i32, u32, i64, u64}.rem` + /// - `{i32, i64}.and` + /// - `{i32, i64}.or` + /// - `{i32, i64}.xor` + /// - `{i32, i64}.shl` + /// - `{i32, u32, i64, u64}.shr` + /// - `{i32, i64}.rotl` + /// - `{i32, i64}.rotr` + /// - `{f32, f64}.add` + /// - `{f32, f64}.sub` + /// - `{f32, f64}.mul` + /// - `{f32, f64}.div` + /// - `{f32, f64}.min` + /// - `{f32, f64}.max` + /// - `{f32, f64}.copysign` + fn translate_binary_operation( + &mut self, + _value_type: ValueType, + inst: Instruction, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop2(); + builder.stack_height.push(); + builder.alloc.inst_builder.push_inst(inst); + Ok(()) + }) + } + + /// Translate a Wasm conversion instruction. + /// + /// - `i32.wrap_i64` + /// - `{i32, u32}.trunc_f32 + /// - `{i32, u32}.trunc_f64` + /// - `{i64, u64}.extend_i32` + /// - `{i64, u64}.trunc_f32` + /// - `{i64, u64}.trunc_f64` + /// - `f32.convert_{i32, u32, i64, u64}` + /// - `f32.demote_f64` + /// - `f64.convert_{i32, u32, i64, u64}` + /// - `f64.promote_f32` + /// - `i32.reinterpret_f32` + /// - `i64.reinterpret_f64` + /// - `f32.reinterpret_i32` + /// - `f64.reinterpret_i64` + fn translate_conversion( + &mut self, + _input_type: ValueType, + _output_type: ValueType, + inst: Instruction, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop1(); + builder.stack_height.push(); + builder.alloc.inst_builder.push_inst(inst); + Ok(()) + }) + } + + /// Returns the target at the given `depth` together with its [`DropKeep`]. + /// + /// # Panics + /// + /// - If the `depth` is greater than the current height of the control frame stack. + /// - If the value stack underflowed. + fn acquire_target(&self, relative_depth: u32) -> Result { + debug_assert!(self.is_reachable()); + if self.alloc.control_frames.is_root(relative_depth) { + let drop_keep = self.drop_keep_return()?; + Ok(AcquiredTarget::Return(drop_keep)) + } else { + let label = self + .alloc + .control_frames + .nth_back(relative_depth) + .branch_destination(); + let drop_keep = self.compute_drop_keep(relative_depth)?; + Ok(AcquiredTarget::Branch(label, drop_keep)) + } + } + + /// Translates a Wasm reinterpret instruction. + /// + /// # Note + /// + /// The `wasmi` translation simply ignores reinterpret instructions since + /// `wasmi` bytecode in itself it untyped. + fn visit_reinterpret( + &mut self, + _input_type: ValueType, + _output_type: ValueType, + ) -> Result<(), TranslationError> { + Ok(()) + } + + /// Called when translating an unsupported Wasm operator. + /// + /// # Note + /// + /// We panic instead of returning an error because unsupported Wasm + /// errors should have been filtered out by the validation procedure + /// already, therefore encountering an unsupported Wasm operator + /// in the function translation procedure can be considered a bug. + fn unsupported_operator(&self, name: &str) -> Result<(), TranslationError> { + panic!("tried to translate an unsupported Wasm operator: {name}") + } +} + +/// An acquired target. +/// +/// Returned by [`FuncTranslator::acquire_target`]. +#[derive(Debug)] +pub enum AcquiredTarget { + /// The branch jumps to the label. + Branch(LabelRef, DropKeep), + /// The branch returns to the caller. + /// + /// # Note + /// + /// This is returned if the `relative_depth` points to the outmost + /// function body `block`. WebAssembly defines branches to this control + /// flow frame as equivalent to returning from the function. + Return(DropKeep), +} + +macro_rules! impl_visit_operator { + ( @mvp $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @sign_extension $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @saturating_float_to_int $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @bulk_memory $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @reference_types $($rest:tt)* ) => { + impl_visit_operator!(@@skipped $($rest)*); + }; + ( @@skipped $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + // We skip Wasm operators that we already implement manually. + impl_visit_operator!($($rest)*); + }; + ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + // Wildcard match arm for all the other (yet) unsupported Wasm proposals. + fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output { + self.unsupported_operator(stringify!($op)) + } + impl_visit_operator!($($rest)*); + }; + () => {}; +} + +impl<'a> VisitOperator<'a> for FuncTranslator<'a> { + type Output = Result<(), TranslationError>; + + wasmparser::for_each_operator!(impl_visit_operator); + + fn visit_nop(&mut self) -> Result<(), TranslationError> { + Ok(()) + } + + fn visit_unreachable(&mut self) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder + .alloc + .inst_builder + .push_inst(Instruction::Unreachable); + builder.reachable = false; + Ok(()) + }) + } + + fn visit_block(&mut self, block_type: wasmparser::BlockType) -> Result<(), TranslationError> { + let block_type = BlockType::try_from_wasmparser(block_type, self.res)?; + if self.is_reachable() { + let stack_height = self.frame_stack_height(block_type); + let end_label = self.alloc.inst_builder.new_label(); + self.alloc.control_frames.push_frame(BlockControlFrame::new( + block_type, + end_label, + stack_height, + )); + } else { + self.alloc + .control_frames + .push_frame(UnreachableControlFrame::new( + ControlFrameKind::Block, + block_type, + )); + } + Ok(()) + } + + fn visit_loop(&mut self, block_type: wasmparser::BlockType) -> Result<(), TranslationError> { + let block_type = BlockType::try_from_wasmparser(block_type, self.res)?; + if self.is_reachable() { + let stack_height = self.frame_stack_height(block_type); + let header = self.alloc.inst_builder.new_label(); + self.alloc.inst_builder.pin_label(header); + self.alloc.control_frames.push_frame(LoopControlFrame::new( + block_type, + header, + stack_height, + )); + } else { + self.alloc + .control_frames + .push_frame(UnreachableControlFrame::new( + ControlFrameKind::Loop, + block_type, + )); + } + Ok(()) + } + + fn visit_if(&mut self, block_type: wasmparser::BlockType) -> Result<(), TranslationError> { + let block_type = BlockType::try_from_wasmparser(block_type, self.res)?; + if self.is_reachable() { + self.stack_height.pop1(); + let stack_height = self.frame_stack_height(block_type); + let else_label = self.alloc.inst_builder.new_label(); + let end_label = self.alloc.inst_builder.new_label(); + self.alloc.control_frames.push_frame(IfControlFrame::new( + block_type, + end_label, + else_label, + stack_height, + )); + let branch_params = self.branch_params(else_label, DropKeep::none()); + self.alloc + .inst_builder + .push_inst(Instruction::BrIfEqz(branch_params)); + } else { + self.alloc + .control_frames + .push_frame(UnreachableControlFrame::new( + ControlFrameKind::If, + block_type, + )); + } + Ok(()) + } + + fn visit_else(&mut self) -> Result<(), TranslationError> { + let mut if_frame = match self.alloc.control_frames.pop_frame() { + ControlFrame::If(if_frame) => if_frame, + ControlFrame::Unreachable(frame) if matches!(frame.kind(), ControlFrameKind::If) => { + // Encountered `Else` block for unreachable `If` block. + // + // In this case we can simply ignore the entire `Else` block + // since it is unreachable anyways. + self.alloc.control_frames.push_frame(frame); + return Ok(()); + } + unexpected => panic!( + "expected `if` control flow frame on top \ + for `else` but found: {unexpected:?}", + ), + }; + let reachable = self.is_reachable(); + // At this point we know if the end of the `then` block of the paren + // `if` block is reachable so we update the parent `if` frame. + // + // Note: This information is important to decide whether code is + // reachable after the `if` block (including `else`) ends. + if_frame.update_end_of_then_reachability(reachable); + // Create the jump from the end of the `then` block to the `if` + // block's end label in case the end of `then` is reachable. + if reachable { + let params = self.branch_params(if_frame.end_label(), DropKeep::none()); + self.alloc.inst_builder.push_inst(Instruction::Br(params)); + } + // Now resolve labels for the instructions of the `else` block + self.alloc.inst_builder.pin_label(if_frame.else_label()); + // We need to reset the value stack to exactly how it has been + // when entering the `if` in the first place so that the `else` + // block has the same parameters on top of the stack. + self.stack_height.shrink_to(if_frame.stack_height()); + if_frame.block_type().foreach_param(&self.engine, |_param| { + self.stack_height.push(); + }); + self.alloc.control_frames.push_frame(if_frame); + // We can reset reachability now since the parent `if` block was reachable. + self.reachable = true; + Ok(()) + } + + fn visit_end(&mut self) -> Result<(), TranslationError> { + let frame = self.alloc.control_frames.last(); + if let ControlFrame::If(if_frame) = &frame { + // At this point we can resolve the `Else` label. + // + // Note: The `Else` label might have already been resolved + // in case there was an `Else` block. + self.alloc + .inst_builder + .pin_label_if_unpinned(if_frame.else_label()); + } + if frame.is_reachable() && !matches!(frame.kind(), ControlFrameKind::Loop) { + // At this point we can resolve the `End` labels. + // Note that `loop` control frames do not have an `End` label. + self.alloc.inst_builder.pin_label(frame.end_label()); + } + // These bindings are required because of borrowing issues. + let frame_reachable = frame.is_reachable(); + let frame_stack_height = frame.stack_height(); + if self.alloc.control_frames.len() == 1 { + // If the control flow frames stack is empty after this point + // we know that we are ending the function body `block` + // frame and therefore we have to return from the function. + self.visit_return()?; + } else { + // The following code is only reachable if the ended control flow + // frame was reachable upon entering to begin with. + self.reachable = frame_reachable; + } + if let Some(frame_stack_height) = frame_stack_height { + self.stack_height.shrink_to(frame_stack_height); + } + let frame = self.alloc.control_frames.pop_frame(); + frame + .block_type() + .foreach_result(&self.engine, |_result| self.stack_height.push()); + Ok(()) + } + + fn visit_br(&mut self, relative_depth: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + match builder.acquire_target(relative_depth)? { + AcquiredTarget::Branch(end_label, drop_keep) => { + let params = builder.branch_params(end_label, drop_keep); + builder + .alloc + .inst_builder + .push_inst(Instruction::Br(params)); + } + AcquiredTarget::Return(_) => { + // In this case the `br` can be directly translated as `return`. + builder.visit_return()?; + } + } + builder.reachable = false; + Ok(()) + }) + } + + fn visit_br_if(&mut self, relative_depth: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop1(); + match builder.acquire_target(relative_depth)? { + AcquiredTarget::Branch(end_label, drop_keep) => { + let params = builder.branch_params(end_label, drop_keep); + builder + .alloc + .inst_builder + .push_inst(Instruction::BrIfNez(params)); + } + AcquiredTarget::Return(drop_keep) => { + builder + .alloc + .inst_builder + .push_inst(Instruction::ReturnIfNez(drop_keep)); + } + } + Ok(()) + }) + } + + fn visit_br_table(&mut self, table: wasmparser::BrTable<'a>) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + fn offset_instr(base: Instr, offset: usize) -> Instr { + Instr::from_u32(base.into_u32() + offset as u32) + } + + fn compute_instr( + builder: &mut FuncTranslator, + n: usize, + depth: RelativeDepth, + ) -> Result { + match builder.acquire_target(depth.into_u32())? { + AcquiredTarget::Branch(label, drop_keep) => { + let base = builder.alloc.inst_builder.current_pc(); + let instr = offset_instr(base, n + 1); + let offset = builder + .alloc + .inst_builder + .try_resolve_label_for(label, instr); + let params = BranchParams::new(offset, drop_keep); + Ok(Instruction::Br(params)) + } + AcquiredTarget::Return(drop_keep) => Ok(Instruction::Return(drop_keep)), + } + } + + let default = RelativeDepth::from_u32(table.default()); + let targets = table + .targets() + .map(|relative_depth| { + relative_depth.unwrap_or_else(|error| { + panic!( + "encountered unexpected invalid relative depth \ + for `br_table` target: {error}", + ) + }) + }) + .map(RelativeDepth::from_u32); + + builder.stack_height.pop1(); + builder.alloc.br_table_branches.clear(); + for (n, depth) in targets.into_iter().enumerate() { + let relative_depth = compute_instr(builder, n, depth)?; + builder.alloc.br_table_branches.push(relative_depth); + } + + // We include the default target in `len_branches`. + let len_branches = builder.alloc.br_table_branches.len(); + let default_branch = compute_instr(builder, len_branches, default)?; + builder.alloc.inst_builder.push_inst(Instruction::BrTable { + len_targets: len_branches + 1, + }); + for branch in builder.alloc.br_table_branches.drain(..) { + builder.alloc.inst_builder.push_inst(branch); + } + builder.alloc.inst_builder.push_inst(default_branch); + builder.reachable = false; + Ok(()) + }) + } + + fn visit_return(&mut self) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let drop_keep = builder.drop_keep_return()?; + builder + .alloc + .inst_builder + .push_inst(Instruction::Return(drop_keep)); + builder.reachable = false; + Ok(()) + }) + } + + fn visit_call(&mut self, func_idx: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let func_idx = FuncIdx(func_idx); + let func_type = builder.func_type_of(func_idx); + builder.adjust_value_stack_for_call(&func_type); + let func_idx = func_idx.into_u32().into(); + builder + .alloc + .inst_builder + .push_inst(Instruction::Call(func_idx)); + Ok(()) + }) + } + + fn visit_call_indirect( + &mut self, + func_type_index: u32, + table_index: u32, + _table_byte: u8, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let func_type_index = SignatureIdx::from(func_type_index); + let table = TableIdx::from(table_index); + builder.stack_height.pop1(); + let func_type = builder.func_type_at(func_type_index); + builder.adjust_value_stack_for_call(&func_type); + builder + .alloc + .inst_builder + .push_inst(Instruction::CallIndirect { + table, + func_type: func_type_index, + }); + Ok(()) + }) + } + + fn visit_drop(&mut self) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop1(); + builder.alloc.inst_builder.push_inst(Instruction::Drop); + Ok(()) + }) + } + + fn visit_select(&mut self) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop3(); + builder.stack_height.push(); + builder.alloc.inst_builder.push_inst(Instruction::Select); + Ok(()) + }) + } + + fn visit_typed_select(&mut self, _ty: wasmparser::ValType) -> Result<(), TranslationError> { + // The `ty` parameter is only important for Wasm validation. + // Since `wasmi` bytecode is untyped we are not interested in this additional information. + self.visit_select() + } + + fn visit_ref_null(&mut self, _ty: wasmparser::ValType) -> Result<(), TranslationError> { + // Since `wasmi` bytecode is untyped we have no special `null` instructions + // but simply reuse the `constant` instruction with an immediate value of 0. + // Note that `FuncRef` and `ExternRef` are encoded as 64-bit values in `wasmi`. + self.translate_const(0i64) + } + + fn visit_ref_is_null(&mut self) -> Result<(), TranslationError> { + // Since `wasmi` bytecode is untyped we have no special `null` instructions + // but simply reuse the `i64.eqz` instruction with an immediate value of 0. + // Note that `FuncRef` and `ExternRef` are encoded as 64-bit values in `wasmi`. + self.visit_i64_eqz() + } + + fn visit_ref_func(&mut self, func_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let func_index = bytecode::FuncIdx::from(func_index); + builder + .alloc + .inst_builder + .push_inst(Instruction::RefFunc { func_index }); + builder.stack_height.push(); + Ok(()) + }) + } + + fn visit_local_get(&mut self, local_idx: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let local_depth = builder.relative_local_depth(local_idx); + builder + .alloc + .inst_builder + .push_inst(Instruction::local_get(local_depth)); + builder.stack_height.push(); + Ok(()) + }) + } + + fn visit_local_set(&mut self, local_idx: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop1(); + let local_depth = builder.relative_local_depth(local_idx); + builder + .alloc + .inst_builder + .push_inst(Instruction::local_set(local_depth)); + Ok(()) + }) + } + + fn visit_local_tee(&mut self, local_idx: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let local_depth = builder.relative_local_depth(local_idx); + builder + .alloc + .inst_builder + .push_inst(Instruction::local_tee(local_depth)); + Ok(()) + }) + } + + fn visit_global_get(&mut self, global_idx: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let global_idx = GlobalIdx(global_idx); + builder.stack_height.push(); + let (global_type, init_value) = builder.res.get_global(global_idx); + let instr = Self::optimize_global_get(&global_type, init_value).unwrap_or_else(|| { + // No optimization took place in this case. + Instruction::GlobalGet(global_idx.into_u32().into()) + }); + builder.alloc.inst_builder.push_inst(instr); + Ok(()) + }) + } + + fn visit_global_set(&mut self, global_idx: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let global_idx = GlobalIdx(global_idx); + let global_type = builder.res.get_type_of_global(global_idx); + debug_assert_eq!(global_type.mutability(), Mutability::Var); + builder.stack_height.pop1(); + let global_idx = global_idx.into_u32().into(); + builder + .alloc + .inst_builder + .push_inst(Instruction::GlobalSet(global_idx)); + Ok(()) + }) + } + + fn visit_i32_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I32, Instruction::I32Load) + } + + fn visit_i64_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I64, Instruction::I64Load) + } + + fn visit_f32_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::F32, Instruction::F32Load) + } + + fn visit_f64_load(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::F64, Instruction::F64Load) + } + + fn visit_i32_load8_s(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I32, Instruction::I32Load8S) + } + + fn visit_i32_load8_u(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I32, Instruction::I32Load8U) + } + + fn visit_i32_load16_s(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I32, Instruction::I32Load16S) + } + + fn visit_i32_load16_u(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I32, Instruction::I32Load16U) + } + + fn visit_i64_load8_s(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I64, Instruction::I64Load8S) + } + + fn visit_i64_load8_u(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I64, Instruction::I64Load8U) + } + + fn visit_i64_load16_s(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I64, Instruction::I64Load16S) + } + + fn visit_i64_load16_u(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I64, Instruction::I64Load16U) + } + + fn visit_i64_load32_s(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I64, Instruction::I64Load32S) + } + + fn visit_i64_load32_u(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_load(memarg, ValueType::I64, Instruction::I64Load32U) + } + + fn visit_i32_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::I32, Instruction::I32Store) + } + + fn visit_i64_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::I64, Instruction::I64Store) + } + + fn visit_f32_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::F32, Instruction::F32Store) + } + + fn visit_f64_store(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::F64, Instruction::F64Store) + } + + fn visit_i32_store8(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::I32, Instruction::I32Store8) + } + + fn visit_i32_store16(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::I32, Instruction::I32Store16) + } + + fn visit_i64_store8(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::I64, Instruction::I64Store8) + } + + fn visit_i64_store16(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::I64, Instruction::I64Store16) + } + + fn visit_i64_store32(&mut self, memarg: wasmparser::MemArg) -> Result<(), TranslationError> { + self.translate_store(memarg, ValueType::I64, Instruction::I64Store32) + } + + fn visit_memory_size( + &mut self, + memory_idx: u32, + _mem_byte: u8, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let memory_idx = MemoryIdx(memory_idx); + debug_assert_eq!(memory_idx.into_u32(), DEFAULT_MEMORY_INDEX); + builder.stack_height.push(); + builder + .alloc + .inst_builder + .push_inst(Instruction::MemorySize); + Ok(()) + }) + } + + fn visit_memory_grow( + &mut self, + memory_index: u32, + _mem_byte: u8, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + debug_assert_eq!(memory_index, DEFAULT_MEMORY_INDEX); + builder + .alloc + .inst_builder + .push_inst(Instruction::MemoryGrow); + Ok(()) + }) + } + + fn visit_memory_init( + &mut self, + segment_index: u32, + memory_index: u32, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + debug_assert_eq!(memory_index, DEFAULT_MEMORY_INDEX); + builder.stack_height.pop3(); + builder + .alloc + .inst_builder + .push_inst(Instruction::MemoryInit(DataSegmentIdx::from(segment_index))); + Ok(()) + }) + } + + fn visit_memory_fill(&mut self, memory_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + debug_assert_eq!(memory_index, DEFAULT_MEMORY_INDEX); + builder.stack_height.pop3(); + builder + .alloc + .inst_builder + .push_inst(Instruction::MemoryFill); + Ok(()) + }) + } + + fn visit_memory_copy(&mut self, dst_mem: u32, src_mem: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + debug_assert_eq!(dst_mem, DEFAULT_MEMORY_INDEX); + debug_assert_eq!(src_mem, DEFAULT_MEMORY_INDEX); + builder.stack_height.pop3(); + builder + .alloc + .inst_builder + .push_inst(Instruction::MemoryCopy); + Ok(()) + }) + } + + fn visit_data_drop(&mut self, segment_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let segment_index = DataSegmentIdx::from(segment_index); + builder + .alloc + .inst_builder + .push_inst(Instruction::DataDrop(segment_index)); + Ok(()) + }) + } + + fn visit_table_size(&mut self, table_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let table = TableIdx::from(table_index); + builder.stack_height.push(); + builder + .alloc + .inst_builder + .push_inst(Instruction::TableSize { table }); + Ok(()) + }) + } + + fn visit_table_grow(&mut self, table_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let table = TableIdx::from(table_index); + builder.stack_height.pop1(); + builder + .alloc + .inst_builder + .push_inst(Instruction::TableGrow { table }); + Ok(()) + }) + } + + fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let dst = TableIdx::from(dst_table); + let src = TableIdx::from(src_table); + builder.stack_height.pop3(); + builder + .alloc + .inst_builder + .push_inst(Instruction::TableCopy { dst, src }); + Ok(()) + }) + } + + fn visit_table_fill(&mut self, table_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let table = TableIdx::from(table_index); + builder.stack_height.pop3(); + builder + .alloc + .inst_builder + .push_inst(Instruction::TableFill { table }); + Ok(()) + }) + } + + fn visit_table_get(&mut self, table_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let table = TableIdx::from(table_index); + builder + .alloc + .inst_builder + .push_inst(Instruction::TableGet { table }); + Ok(()) + }) + } + + fn visit_table_set(&mut self, table_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + let table = TableIdx::from(table_index); + builder.stack_height.pop2(); + builder + .alloc + .inst_builder + .push_inst(Instruction::TableSet { table }); + Ok(()) + }) + } + + fn visit_table_init( + &mut self, + segment_index: u32, + table_index: u32, + ) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder.stack_height.pop3(); + let table = TableIdx::from(table_index); + let elem = ElementSegmentIdx::from(segment_index); + builder + .alloc + .inst_builder + .push_inst(Instruction::TableInit { table, elem }); + Ok(()) + }) + } + + fn visit_elem_drop(&mut self, segment_index: u32) -> Result<(), TranslationError> { + self.translate_if_reachable(|builder| { + builder + .alloc + .inst_builder + .push_inst(Instruction::ElemDrop(ElementSegmentIdx::from( + segment_index, + ))); + Ok(()) + }) + } + + fn visit_i32_const(&mut self, value: i32) -> Result<(), TranslationError> { + self.translate_const(value) + } + + fn visit_i64_const(&mut self, value: i64) -> Result<(), TranslationError> { + self.translate_const(value) + } + + fn visit_f32_const(&mut self, value: wasmparser::Ieee32) -> Result<(), TranslationError> { + self.translate_const(F32::from_bits(value.bits())) + } + + fn visit_f64_const(&mut self, value: wasmparser::Ieee64) -> Result<(), TranslationError> { + self.translate_const(F64::from_bits(value.bits())) + } + + fn visit_i32_eqz(&mut self) -> Result<(), TranslationError> { + self.translate_unary_cmp(ValueType::I32, Instruction::I32Eqz) + } + + fn visit_i32_eq(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32Eq) + } + + fn visit_i32_ne(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32Ne) + } + + fn visit_i32_lt_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32LtS) + } + + fn visit_i32_lt_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32LtU) + } + + fn visit_i32_gt_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32GtS) + } + + fn visit_i32_gt_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32GtU) + } + + fn visit_i32_le_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32LeS) + } + + fn visit_i32_le_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32LeU) + } + + fn visit_i32_ge_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32GeS) + } + + fn visit_i32_ge_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I32, Instruction::I32GeU) + } + + fn visit_i64_eqz(&mut self) -> Result<(), TranslationError> { + self.translate_unary_cmp(ValueType::I64, Instruction::I64Eqz) + } + + fn visit_i64_eq(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64Eq) + } + + fn visit_i64_ne(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64Ne) + } + + fn visit_i64_lt_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64LtS) + } + + fn visit_i64_lt_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64LtU) + } + + fn visit_i64_gt_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64GtS) + } + + fn visit_i64_gt_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64GtU) + } + + fn visit_i64_le_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64LeS) + } + + fn visit_i64_le_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64LeU) + } + + fn visit_i64_ge_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64GeS) + } + + fn visit_i64_ge_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::I64, Instruction::I64GeU) + } + + fn visit_f32_eq(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F32, Instruction::F32Eq) + } + + fn visit_f32_ne(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F32, Instruction::F32Ne) + } + + fn visit_f32_lt(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F32, Instruction::F32Lt) + } + + fn visit_f32_gt(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F32, Instruction::F32Gt) + } + + fn visit_f32_le(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F32, Instruction::F32Le) + } + + fn visit_f32_ge(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F32, Instruction::F32Ge) + } + + fn visit_f64_eq(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F64, Instruction::F64Eq) + } + + fn visit_f64_ne(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F64, Instruction::F64Ne) + } + + fn visit_f64_lt(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F64, Instruction::F64Lt) + } + + fn visit_f64_gt(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F64, Instruction::F64Gt) + } + + fn visit_f64_le(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F64, Instruction::F64Le) + } + + fn visit_f64_ge(&mut self) -> Result<(), TranslationError> { + self.translate_binary_cmp(ValueType::F64, Instruction::F64Ge) + } + + fn visit_i32_clz(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I32, Instruction::I32Clz) + } + + fn visit_i32_ctz(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I32, Instruction::I32Ctz) + } + + fn visit_i32_popcnt(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I32, Instruction::I32Popcnt) + } + + fn visit_i32_add(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Add) + } + + fn visit_i32_sub(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Sub) + } + + fn visit_i32_mul(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Mul) + } + + fn visit_i32_div_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32DivS) + } + + fn visit_i32_div_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32DivU) + } + + fn visit_i32_rem_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32RemS) + } + + fn visit_i32_rem_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32RemU) + } + + fn visit_i32_and(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32And) + } + + fn visit_i32_or(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Or) + } + + fn visit_i32_xor(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Xor) + } + + fn visit_i32_shl(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Shl) + } + + fn visit_i32_shr_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32ShrS) + } + + fn visit_i32_shr_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32ShrU) + } + + fn visit_i32_rotl(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Rotl) + } + + fn visit_i32_rotr(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I32, Instruction::I32Rotr) + } + + fn visit_i64_clz(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I64, Instruction::I64Clz) + } + + fn visit_i64_ctz(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I64, Instruction::I64Ctz) + } + + fn visit_i64_popcnt(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I64, Instruction::I64Popcnt) + } + + fn visit_i64_add(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Add) + } + + fn visit_i64_sub(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Sub) + } + + fn visit_i64_mul(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Mul) + } + + fn visit_i64_div_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64DivS) + } + + fn visit_i64_div_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64DivU) + } + + fn visit_i64_rem_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64RemS) + } + + fn visit_i64_rem_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64RemU) + } + + fn visit_i64_and(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64And) + } + + fn visit_i64_or(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Or) + } + + fn visit_i64_xor(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Xor) + } + + fn visit_i64_shl(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Shl) + } + + fn visit_i64_shr_s(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64ShrS) + } + + fn visit_i64_shr_u(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64ShrU) + } + + fn visit_i64_rotl(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Rotl) + } + + fn visit_i64_rotr(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::I64, Instruction::I64Rotr) + } + + fn visit_f32_abs(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F32, Instruction::F32Abs) + } + + fn visit_f32_neg(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F32, Instruction::F32Neg) + } + + fn visit_f32_ceil(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F32, Instruction::F32Ceil) + } + + fn visit_f32_floor(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F32, Instruction::F32Floor) + } + + fn visit_f32_trunc(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F32, Instruction::F32Trunc) + } + + fn visit_f32_nearest(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F32, Instruction::F32Nearest) + } + + fn visit_f32_sqrt(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F32, Instruction::F32Sqrt) + } + + fn visit_f32_add(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F32, Instruction::F32Add) + } + + fn visit_f32_sub(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F32, Instruction::F32Sub) + } + + fn visit_f32_mul(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F32, Instruction::F32Mul) + } + + fn visit_f32_div(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F32, Instruction::F32Div) + } + + fn visit_f32_min(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F32, Instruction::F32Min) + } + + fn visit_f32_max(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F32, Instruction::F32Max) + } + + fn visit_f32_copysign(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F32, Instruction::F32Copysign) + } + + fn visit_f64_abs(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F64, Instruction::F64Abs) + } + + fn visit_f64_neg(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F64, Instruction::F64Neg) + } + + fn visit_f64_ceil(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F64, Instruction::F64Ceil) + } + + fn visit_f64_floor(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F64, Instruction::F64Floor) + } + + fn visit_f64_trunc(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F64, Instruction::F64Trunc) + } + + fn visit_f64_nearest(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F64, Instruction::F64Nearest) + } + + fn visit_f64_sqrt(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::F64, Instruction::F64Sqrt) + } + + fn visit_f64_add(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F64, Instruction::F64Add) + } + + fn visit_f64_sub(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F64, Instruction::F64Sub) + } + + fn visit_f64_mul(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F64, Instruction::F64Mul) + } + + fn visit_f64_div(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F64, Instruction::F64Div) + } + + fn visit_f64_min(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F64, Instruction::F64Min) + } + + fn visit_f64_max(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F64, Instruction::F64Max) + } + + fn visit_f64_copysign(&mut self) -> Result<(), TranslationError> { + self.translate_binary_operation(ValueType::F64, Instruction::F64Copysign) + } + + fn visit_i32_wrap_i64(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I64, ValueType::I32, Instruction::I32WrapI64) + } + + fn visit_i32_trunc_f32_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncF32S) + } + + fn visit_i32_trunc_f32_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncF32U) + } + + fn visit_i32_trunc_f64_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncF64S) + } + + fn visit_i32_trunc_f64_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncF64U) + } + + fn visit_i64_extend_i32_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I32, ValueType::I64, Instruction::I64ExtendI32S) + } + + fn visit_i64_extend_i32_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I32, ValueType::I64, Instruction::I64ExtendI32U) + } + + fn visit_i64_trunc_f32_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncF32S) + } + + fn visit_i64_trunc_f32_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncF32U) + } + + fn visit_i64_trunc_f64_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncF64S) + } + + fn visit_i64_trunc_f64_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncF64U) + } + + fn visit_f32_convert_i32_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I32, ValueType::F32, Instruction::F32ConvertI32S) + } + + fn visit_f32_convert_i32_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I32, ValueType::F32, Instruction::F32ConvertI32U) + } + + fn visit_f32_convert_i64_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I64, ValueType::F32, Instruction::F32ConvertI64S) + } + + fn visit_f32_convert_i64_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I64, ValueType::F32, Instruction::F32ConvertI64U) + } + + fn visit_f32_demote_f64(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::F32, Instruction::F32DemoteF64) + } + + fn visit_f64_convert_i32_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I32, ValueType::F64, Instruction::F64ConvertI32S) + } + + fn visit_f64_convert_i32_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I32, ValueType::F64, Instruction::F64ConvertI32U) + } + + fn visit_f64_convert_i64_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I64, ValueType::F64, Instruction::F64ConvertI64S) + } + + fn visit_f64_convert_i64_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::I64, ValueType::F64, Instruction::F64ConvertI64U) + } + + fn visit_f64_promote_f32(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::F64, Instruction::F64PromoteF32) + } + + fn visit_i32_reinterpret_f32(&mut self) -> Result<(), TranslationError> { + self.visit_reinterpret(ValueType::F32, ValueType::I32) + } + + fn visit_i64_reinterpret_f64(&mut self) -> Result<(), TranslationError> { + self.visit_reinterpret(ValueType::F64, ValueType::I64) + } + + fn visit_f32_reinterpret_i32(&mut self) -> Result<(), TranslationError> { + self.visit_reinterpret(ValueType::I32, ValueType::F32) + } + + fn visit_f64_reinterpret_i64(&mut self) -> Result<(), TranslationError> { + self.visit_reinterpret(ValueType::I64, ValueType::F64) + } + + fn visit_i32_extend8_s(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I32, Instruction::I32Extend8S) + } + + fn visit_i32_extend16_s(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I32, Instruction::I32Extend16S) + } + + fn visit_i64_extend8_s(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I64, Instruction::I64Extend8S) + } + + fn visit_i64_extend16_s(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I64, Instruction::I64Extend16S) + } + + fn visit_i64_extend32_s(&mut self) -> Result<(), TranslationError> { + self.translate_unary_operation(ValueType::I64, Instruction::I64Extend32S) + } + + fn visit_i32_trunc_sat_f32_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32S) + } + + fn visit_i32_trunc_sat_f32_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32U) + } + + fn visit_i32_trunc_sat_f64_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64S) + } + + fn visit_i32_trunc_sat_f64_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64U) + } + + fn visit_i64_trunc_sat_f32_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncSatF32S) + } + + fn visit_i64_trunc_sat_f32_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncSatF32U) + } + + fn visit_i64_trunc_sat_f64_s(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncSatF64S) + } + + fn visit_i64_trunc_sat_f64_u(&mut self) -> Result<(), TranslationError> { + self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncSatF64U) + } +} diff --git a/crates/wasmi/src/engine/func_builder/visit.rs b/crates/wasmi/src/engine/func_builder/visit.rs deleted file mode 100644 index a7c4fa5708..0000000000 --- a/crates/wasmi/src/engine/func_builder/visit.rs +++ /dev/null @@ -1,78 +0,0 @@ -use super::{FuncBuilder, FuncValidator, TranslationError}; -use wasmparser::{BinaryReaderError, VisitOperator}; - -impl<'parser> FuncBuilder<'parser> { - /// Translates into `wasmi` bytecode if the current code path is reachable. - fn validate_then_translate( - &mut self, - validate: V, - translator: F, - ) -> Result<(), TranslationError> - where - V: FnOnce(&mut FuncValidator) -> Result<(), BinaryReaderError>, - F: FnOnce(&mut Self) -> Result<(), TranslationError>, - { - validate(&mut self.validator)?; - translator(self)?; - Ok(()) - } -} - -macro_rules! impl_visit_operator { - ( @mvp BrTable { $arg:ident: $argty:ty } => $visit:ident $($rest:tt)* ) => { - // We need to special case the `BrTable` operand since its - // arguments (a.k.a. `BrTable<'a>`) are not `Copy` which all - // the other impls make use of. - fn $visit(&mut self, $arg: $argty) -> Self::Output { - let offset = self.current_pos(); - let arg_cloned = $arg.clone(); - self.validate_then_translate( - |v| v.visitor(offset).$visit(arg_cloned), - |this| this.$visit($arg), - ) - } - impl_visit_operator!($($rest)*); - }; - ( @mvp $($rest:tt)* ) => { - impl_visit_operator!(@@supported $($rest)*); - }; - ( @sign_extension $($rest:tt)* ) => { - impl_visit_operator!(@@supported $($rest)*); - }; - ( @saturating_float_to_int $($rest:tt)* ) => { - impl_visit_operator!(@@supported $($rest)*); - }; - ( @bulk_memory $($rest:tt)* ) => { - impl_visit_operator!(@@supported $($rest)*); - }; - ( @reference_types $($rest:tt)* ) => { - impl_visit_operator!(@@supported $($rest)*); - }; - ( @@supported $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { - fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { - let offset = self.current_pos(); - self.validate_then_translate( - |v| v.visitor(offset).$visit($($($arg),*)?), - |this| { - this.$visit($($($arg),*)?) - }, - ) - } - impl_visit_operator!($($rest)*); - }; - ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { - // Wildcard match arm for all the other (yet) unsupported Wasm proposals. - fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output { - let offset = self.current_pos(); - self.validator.visitor(offset).$visit($($($arg),*)?).map_err(::core::convert::Into::into) - } - impl_visit_operator!($($rest)*); - }; - () => {}; -} - -impl<'a> VisitOperator<'a> for FuncBuilder<'a> { - type Output = Result<(), TranslationError>; - - wasmparser::for_each_operator!(impl_visit_operator); -}