use super::{ControlFrame, ControlFrameKind, FuncTranslator, LocalIdx};
use crate::{
core::{wasm, FuelCostsProvider, IndexType, TypedVal},
engine::{
translator::func::{
op,
stack::{AcquiredTarget, IfReachability},
ControlFrameBase,
Input,
Operand,
},
BlockType,
},
ir::{self, Const16, Op},
module::{self, FuncIdx, MemoryIdx, TableIdx, WasmiValueType},
Error,
ExternRef,
Func,
FuncType,
Mutability,
Ref,
TrapCode,
ValType,
F32,
F64,
};
use wasmparser::VisitOperator;
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)*);
};
( @tail_call $($rest:tt)* ) => {
impl_visit_operator!(@@skipped $($rest)*);
};
( @wide_arithmetic $($rest:tt)* ) => {
impl_visit_operator!(@@skipped $($rest)*);
};
( @@skipped $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $_ann:tt $($rest:tt)* ) => {
impl_visit_operator!($($rest)*);
};
( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $_ann:tt $($rest:tt)* ) => {
fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output {
$( $( let _ = $arg; )* )?
self.translate_unsupported_operator(stringify!($op))
}
impl_visit_operator!($($rest)*);
};
() => {};
}
impl FuncTranslator {
pub fn translate_unsupported_operator(&self, name: &str) -> Result<(), Error> {
panic!("tried to translate an unsupported Wasm operator: {name}")
}
}
impl<'a> VisitOperator<'a> for FuncTranslator {
type Output = Result<(), Error>;
#[cfg(feature = "simd")]
fn simd_visitor(
&mut self,
) -> Option<&mut dyn wasmparser::VisitSimdOperator<'a, Output = Self::Output>> {
Some(self)
}
wasmparser::for_each_visit_operator!(impl_visit_operator);
#[inline(never)]
fn visit_unreachable(&mut self) -> Self::Output {
bail_unreachable!(self);
self.translate_trap(TrapCode::UnreachableCodeReached)
}
#[inline(never)]
fn visit_nop(&mut self) -> Self::Output {
Ok(())
}
#[inline(never)]
fn visit_block(&mut self, block_ty: wasmparser::BlockType) -> Self::Output {
if !self.reachable {
self.stack.push_unreachable(ControlFrameKind::Block)?;
return Ok(());
}
self.preserve_all_locals()?;
let block_ty = BlockType::new(block_ty, &self.module);
let end_label = self.labels.new_label();
self.stack.push_block(block_ty, end_label)?;
Ok(())
}
#[inline(never)]
fn visit_loop(&mut self, block_ty: wasmparser::BlockType) -> Self::Output {
if !self.reachable {
self.stack.push_unreachable(ControlFrameKind::Loop)?;
return Ok(());
}
self.preserve_all_locals()?;
let block_ty = BlockType::new(block_ty, &self.module);
let len_params = block_ty.len_params(&self.engine);
let continue_label = self.labels.new_label();
let consume_fuel = self.stack.consume_fuel_instr();
self.move_operands_to_temp(usize::from(len_params), consume_fuel)?;
self.pin_label(continue_label);
let consume_fuel = self.instrs.push_consume_fuel_instr()?;
self.stack
.push_loop(block_ty, continue_label, consume_fuel)?;
self.instrs.reset_last_instr();
Ok(())
}
#[inline(never)]
fn visit_if(&mut self, block_ty: wasmparser::BlockType) -> Self::Output {
if !self.reachable {
self.stack.push_unreachable(ControlFrameKind::If)?;
return Ok(());
}
let end_label = self.labels.new_label();
let condition = self.stack.pop();
self.preserve_all_locals()?;
let (reachability, consume_fuel_instr) = match condition {
Operand::Immediate(operand) => {
let condition = i32::from(operand.val());
let reachability = match condition {
0 => {
self.reachable = false;
IfReachability::OnlyElse
}
_ => IfReachability::OnlyThen,
};
let consume_fuel_instr = self.stack.consume_fuel_instr();
(reachability, consume_fuel_instr)
}
_ => {
let else_label = self.labels.new_label();
self.encode_br_eqz(condition, else_label)?;
let reachability = IfReachability::Both { else_label };
let consume_fuel_instr = self.instrs.push_consume_fuel_instr()?;
(reachability, consume_fuel_instr)
}
};
let block_ty = BlockType::new(block_ty, &self.module);
self.stack
.push_if(block_ty, end_label, reachability, consume_fuel_instr)?;
Ok(())
}
#[inline(never)]
fn visit_else(&mut self) -> Self::Output {
let mut frame = match self.stack.pop_control() {
ControlFrame::If(frame) => frame,
ControlFrame::Unreachable(ControlFrameKind::If) => {
debug_assert!(!self.reachable);
self.stack.push_unreachable(ControlFrameKind::Else)?;
return Ok(());
}
unexpected => panic!("expected `if` control frame but found: {unexpected:?}"),
};
let is_end_of_then_reachable = self.reachable;
if let IfReachability::Both { else_label } = frame.reachability() {
if is_end_of_then_reachable {
let consume_fuel_instr = frame.consume_fuel_instr();
self.copy_branch_params(&frame, consume_fuel_instr)?;
frame.branch_to();
self.encode_br(frame.label())?;
}
self.labels
.pin_label(else_label, self.instrs.next_instr())
.unwrap();
self.instrs.reset_last_instr();
}
let consume_fuel_instr = self.instrs.push_consume_fuel_instr()?;
self.reachable = frame.is_else_reachable();
self.stack
.push_else(frame, is_end_of_then_reachable, consume_fuel_instr)?;
Ok(())
}
#[inline(never)]
fn visit_end(&mut self) -> Self::Output {
match self.stack.pop_control() {
ControlFrame::Block(frame) => self.translate_end_block(frame),
ControlFrame::Loop(frame) => self.translate_end_loop(frame),
ControlFrame::If(frame) => self.translate_end_if(frame),
ControlFrame::Else(frame) => self.translate_end_else(frame),
ControlFrame::Unreachable(frame) => self.translate_end_unreachable(frame),
}
}
#[inline(never)]
fn visit_br(&mut self, depth: u32) -> Self::Output {
bail_unreachable!(self);
let Ok(depth) = usize::try_from(depth) else {
panic!("out of bounds depth: {depth}")
};
let consume_fuel_instr = self.stack.consume_fuel_instr();
match self.stack.peek_control_mut(depth) {
AcquiredTarget::Return(_) => self.visit_return(),
AcquiredTarget::Branch(mut frame) => {
frame.branch_to();
let label = frame.label();
let len_params = frame.len_branch_params(&self.engine);
let branch_results = Self::frame_results_impl(&frame, &self.engine, &self.layout)?;
if let Some(branch_results) = branch_results {
self.encode_copies(branch_results, len_params, consume_fuel_instr)?;
}
self.encode_br(label)?;
self.reachable = false;
Ok(())
}
}
}
#[inline(never)]
fn visit_br_if(&mut self, depth: u32) -> Self::Output {
bail_unreachable!(self);
let condition = self.stack.pop();
if let Operand::Immediate(condition) = condition {
if i32::from(condition.val()) != 0 {
self.visit_br(depth)?;
}
return Ok(());
}
let Ok(depth) = usize::try_from(depth) else {
panic!("out of bounds depth: {depth}")
};
let mut frame = self.stack.peek_control_mut(depth).control_frame();
frame.branch_to();
let len_branch_params = frame.len_branch_params(&self.engine);
let branch_results = Self::frame_results_impl(&frame, &self.engine, &self.layout)?;
let label = frame.label();
if len_branch_params == 0 {
self.encode_br_nez(condition, label)?;
return Ok(());
}
if !self.requires_branch_param_copies(depth) {
self.encode_br_nez(condition, label)?;
return Ok(());
}
let consume_fuel_instr = self.stack.consume_fuel_instr();
let skip_label = self.labels.new_label();
self.encode_br_eqz(condition, skip_label)?;
if let Some(branch_results) = branch_results {
self.encode_copies(branch_results, len_branch_params, consume_fuel_instr)?;
}
self.encode_br(label)?;
self.labels
.pin_label(skip_label, self.instrs.next_instr())
.unwrap();
Ok(())
}
#[inline(never)]
fn visit_br_table(&mut self, table: wasmparser::BrTable<'a>) -> Self::Output {
bail_unreachable!(self);
let index = self.stack.pop();
let default_target = table.default();
if table.is_empty() {
return self.visit_br(default_target);
}
if let Operand::Immediate(index) = index {
let chosen_index = usize::try_from(u32::from(index.val())).unwrap_or(usize::MAX);
let chosen_target = table
.targets()
.nth(chosen_index)
.transpose()?
.unwrap_or(default_target);
return self.visit_br(chosen_target);
}
Self::copy_targets_from_br_table(&table, &mut self.immediates)?;
let targets = &self.immediates[..];
if targets
.iter()
.all(|&target| u32::from(target) == default_target)
{
return self.visit_br(default_target);
}
let Ok(default_target) = usize::try_from(default_target) else {
panic!("out of bounds `default_target` does not fit into `usize`: {default_target}");
};
let index = self.layout.operand_to_reg(index)?;
let len_branch_params = self
.stack
.peek_control(default_target)
.len_branch_params(&self.engine);
match len_branch_params {
0 => self.encode_br_table_0(table, index)?,
n => self.encode_br_table_n(table, index, n)?,
};
self.reachable = false;
Ok(())
}
#[inline(never)]
fn visit_return(&mut self) -> Self::Output {
bail_unreachable!(self);
let consume_fuel_instr = self.stack.consume_fuel_instr();
self.encode_return(consume_fuel_instr)?;
let len_results = self.func_type_with(FuncType::len_results);
for _ in 0..len_results {
self.stack.pop();
}
self.reachable = false;
Ok(())
}
#[inline(never)]
fn visit_call(&mut self, function_index: u32) -> Self::Output {
bail_unreachable!(self);
let func_idx = FuncIdx::from(function_index);
let func_type = self.resolve_func_type(func_idx);
let len_params = usize::from(func_type.len_params());
let results = self.call_regspan(len_params)?;
let instr = match self.module.get_engine_func(func_idx) {
Some(engine_func) => {
match len_params {
0 => Op::call_internal_0(results, engine_func),
_ => Op::call_internal(results, engine_func),
}
}
None => {
match len_params {
0 => Op::call_imported_0(results, function_index),
_ => Op::call_imported(results, function_index),
}
}
};
let call_instr = self.push_instr(instr, FuelCostsProvider::call)?;
self.stack.pop_n(len_params, &mut self.operands);
self.instrs
.encode_register_list(&self.operands, &mut self.layout)?;
if let Some(span) = self.push_results(call_instr, func_type.results())? {
debug_assert_eq!(span, results);
}
Ok(())
}
#[inline(never)]
fn visit_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output {
bail_unreachable!(self);
let func_type = self.resolve_type(type_index);
let index = self.stack.pop();
let indirect_params = self.call_indirect_params(index, table_index)?;
let len_params = usize::from(func_type.len_params());
let results = self.call_regspan(len_params)?;
let instr = match (len_params, indirect_params) {
(0, Op::CallIndirectParams { .. }) => Op::call_indirect_0(results, type_index),
(0, Op::CallIndirectParamsImm16 { .. }) => {
Op::call_indirect_0_imm16(results, type_index)
}
(_, Op::CallIndirectParams { .. }) => Op::call_indirect(results, type_index),
(_, Op::CallIndirectParamsImm16 { .. }) => Op::call_indirect_imm16(results, type_index),
_ => unreachable!(),
};
let call_instr = self.push_instr(instr, FuelCostsProvider::call)?;
self.push_param(indirect_params)?;
self.stack.pop_n(len_params, &mut self.operands);
self.instrs
.encode_register_list(&self.operands, &mut self.layout)?;
if let Some(span) = self.push_results(call_instr, func_type.results())? {
debug_assert_eq!(span, results);
}
Ok(())
}
#[inline(never)]
fn visit_drop(&mut self) -> Self::Output {
bail_unreachable!(self);
_ = self.stack.pop();
Ok(())
}
#[inline(never)]
fn visit_select(&mut self) -> Self::Output {
self.translate_select(None)
}
#[inline(never)]
fn visit_local_get(&mut self, local_index: u32) -> Self::Output {
bail_unreachable!(self);
let local_idx = LocalIdx::from(local_index);
let ty = self.locals.ty(local_idx);
self.stack.push_local(local_idx, ty)?;
Ok(())
}
#[inline(never)]
fn visit_local_set(&mut self, local_index: u32) -> Self::Output {
self.translate_local_set(local_index, false)
}
#[inline(never)]
fn visit_local_tee(&mut self, local_index: u32) -> Self::Output {
self.translate_local_set(local_index, true)
}
#[inline(never)]
fn visit_global_get(&mut self, global_index: u32) -> Self::Output {
bail_unreachable!(self);
let global_idx = module::GlobalIdx::from(global_index);
let (global_type, init_value) = self.module.get_global(global_idx);
let content = global_type.content();
if let (Mutability::Const, Some(init_expr)) = (global_type.mutability(), init_value) {
if let Some(value) = init_expr.eval_const() {
self.stack.push_immediate(TypedVal::new(content, value))?;
return Ok(());
}
if let Some(func_index) = init_expr.funcref() {
self.visit_ref_func(func_index.into_u32())?;
return Ok(());
}
}
let global_idx = ir::index::Global::from(global_index);
self.push_instr_with_result(
content,
|result| Op::global_get(result, global_idx),
FuelCostsProvider::instance,
)?;
Ok(())
}
#[inline(never)]
fn visit_global_set(&mut self, global_index: u32) -> Self::Output {
bail_unreachable!(self);
let global = ir::index::Global::from(global_index);
let input = match self.stack.pop() {
Operand::Immediate(input) => input.val(),
input => {
let input = self.layout.operand_to_reg(input)?;
self.push_instr(Op::global_set(input, global), FuelCostsProvider::instance)?;
return Ok(());
}
};
let (global_type, _init_value) = self
.module
.get_global(module::GlobalIdx::from(global_index));
debug_assert_eq!(global_type.content(), input.ty());
match global_type.content() {
ValType::I32 => {
if let Ok(value) = Const16::try_from(i32::from(input)) {
self.push_instr(
Op::global_set_i32imm16(value, global),
FuelCostsProvider::instance,
)?;
return Ok(());
}
}
ValType::I64 => {
if let Ok(value) = Const16::try_from(i64::from(input)) {
self.push_instr(
Op::global_set_i64imm16(value, global),
FuelCostsProvider::instance,
)?;
return Ok(());
}
}
_ => {}
};
let cref = self.layout.const_to_reg(input)?;
self.push_instr(Op::global_set(cref, global), FuelCostsProvider::instance)?;
Ok(())
}
#[inline(never)]
fn visit_i32_load(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I32,
Op::load32,
Op::load32_offset16,
Op::load32_at,
)
}
#[inline(never)]
fn visit_i64_load(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I64,
Op::load64,
Op::load64_offset16,
Op::load64_at,
)
}
#[inline(never)]
fn visit_f32_load(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::F32,
Op::load32,
Op::load32_offset16,
Op::load32_at,
)
}
#[inline(never)]
fn visit_f64_load(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::F64,
Op::load64,
Op::load64_offset16,
Op::load64_at,
)
}
#[inline(never)]
fn visit_i32_load8_s(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I32,
Op::i32_load8_s,
Op::i32_load8_s_offset16,
Op::i32_load8_s_at,
)
}
#[inline(never)]
fn visit_i32_load8_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I32,
Op::i32_load8_u,
Op::i32_load8_u_offset16,
Op::i32_load8_u_at,
)
}
#[inline(never)]
fn visit_i32_load16_s(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I32,
Op::i32_load16_s,
Op::i32_load16_s_offset16,
Op::i32_load16_s_at,
)
}
#[inline(never)]
fn visit_i32_load16_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I32,
Op::i32_load16_u,
Op::i32_load16_u_offset16,
Op::i32_load16_u_at,
)
}
#[inline(never)]
fn visit_i64_load8_s(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I64,
Op::i64_load8_s,
Op::i64_load8_s_offset16,
Op::i64_load8_s_at,
)
}
#[inline(never)]
fn visit_i64_load8_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I64,
Op::i64_load8_u,
Op::i64_load8_u_offset16,
Op::i64_load8_u_at,
)
}
#[inline(never)]
fn visit_i64_load16_s(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I64,
Op::i64_load16_s,
Op::i64_load16_s_offset16,
Op::i64_load16_s_at,
)
}
#[inline(never)]
fn visit_i64_load16_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I64,
Op::i64_load16_u,
Op::i64_load16_u_offset16,
Op::i64_load16_u_at,
)
}
#[inline(never)]
fn visit_i64_load32_s(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I64,
Op::i64_load32_s,
Op::i64_load32_s_offset16,
Op::i64_load32_s_at,
)
}
#[inline(never)]
fn visit_i64_load32_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_load(
memarg,
ValType::I64,
Op::i64_load32_u,
Op::i64_load32_u_offset16,
Op::i64_load32_u_at,
)
}
#[inline(never)]
fn visit_i32_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_istore_wrap::<op::I32Store>(memarg)
}
#[inline(never)]
fn visit_i64_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_istore_wrap::<op::I64Store>(memarg)
}
#[inline(never)]
fn visit_f32_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_store(memarg, Op::store32, Op::store32_offset16, Op::store32_at)
}
#[inline(never)]
fn visit_f64_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_store(memarg, Op::store64, Op::store64_offset16, Op::store64_at)
}
#[inline(never)]
fn visit_i32_store8(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_istore_wrap::<op::I32Store8>(memarg)
}
#[inline(never)]
fn visit_i32_store16(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_istore_wrap::<op::I32Store16>(memarg)
}
#[inline(never)]
fn visit_i64_store8(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_istore_wrap::<op::I64Store8>(memarg)
}
#[inline(never)]
fn visit_i64_store16(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_istore_wrap::<op::I64Store16>(memarg)
}
#[inline(never)]
fn visit_i64_store32(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
self.translate_istore_wrap::<op::I64Store32>(memarg)
}
#[inline(never)]
fn visit_memory_size(&mut self, mem: u32) -> Self::Output {
bail_unreachable!(self);
let index_ty = self
.module
.get_type_of_memory(MemoryIdx::from(mem))
.index_ty()
.ty();
self.push_instr_with_result(
index_ty,
|result| Op::memory_size(result, mem),
FuelCostsProvider::instance,
)?;
Ok(())
}
#[inline(never)]
fn visit_memory_grow(&mut self, mem: u32) -> Self::Output {
bail_unreachable!(self);
let index_ty = self
.module
.get_type_of_memory(MemoryIdx::from(mem))
.index_ty();
let delta = self.stack.pop();
if let Operand::Immediate(delta) = delta {
let delta = delta.val();
let delta = match index_ty {
IndexType::I32 => u64::from(u32::from(delta)),
IndexType::I64 => u64::from(delta),
};
if delta == 0 {
self.push_instr_with_result(
index_ty.ty(),
|result| Op::memory_size(result, mem),
FuelCostsProvider::instance,
)?;
return Ok(());
}
}
let delta = self.immediate_to_reg(delta)?;
self.push_instr_with_result(
index_ty.ty(),
|result| Op::memory_grow(result, delta),
FuelCostsProvider::instance,
)?;
self.push_param(Op::memory_index(mem))?;
Ok(())
}
#[inline(never)]
fn visit_i32_const(&mut self, value: i32) -> Self::Output {
bail_unreachable!(self);
self.stack.push_immediate(value)?;
Ok(())
}
#[inline(never)]
fn visit_i64_const(&mut self, value: i64) -> Self::Output {
bail_unreachable!(self);
self.stack.push_immediate(value)?;
Ok(())
}
#[inline(never)]
fn visit_f32_const(&mut self, value: wasmparser::Ieee32) -> Self::Output {
bail_unreachable!(self);
let value = F32::from_bits(value.bits());
self.stack.push_immediate(value)?;
Ok(())
}
#[inline(never)]
fn visit_f64_const(&mut self, value: wasmparser::Ieee64) -> Self::Output {
bail_unreachable!(self);
let value = F64::from_bits(value.bits());
self.stack.push_immediate(value)?;
Ok(())
}
#[inline(never)]
fn visit_i32_eqz(&mut self) -> Self::Output {
bail_unreachable!(self);
self.stack.push_immediate(0_i32)?;
self.visit_i32_eq()
}
#[inline(never)]
fn visit_i32_eq(&mut self) -> Self::Output {
self.translate_binary_commutative::<i32, bool>(
Op::i32_eq,
Op::i32_eq_imm16,
wasm::i32_eq,
FuncTranslator::fuse_eqz,
)
}
#[inline(never)]
fn visit_i32_ne(&mut self) -> Self::Output {
self.translate_binary_commutative::<i32, bool>(
Op::i32_ne,
Op::i32_ne_imm16,
wasm::i32_ne,
FuncTranslator::fuse_nez,
)
}
#[inline(never)]
fn visit_i32_lt_s(&mut self) -> Self::Output {
self.translate_binary::<i32, bool>(
Op::i32_lt_s,
Op::i32_lt_s_imm16_rhs,
Op::i32_lt_s_imm16_lhs,
wasm::i32_lt_s,
)
}
#[inline(never)]
fn visit_i32_lt_u(&mut self) -> Self::Output {
self.translate_binary::<u32, bool>(
Op::i32_lt_u,
Op::i32_lt_u_imm16_rhs,
Op::i32_lt_u_imm16_lhs,
wasm::i32_lt_u,
)
}
#[inline(never)]
fn visit_i32_gt_s(&mut self) -> Self::Output {
self.translate_binary::<i32, bool>(
swap_ops!(Op::i32_lt_s),
swap_ops!(Op::i32_lt_s_imm16_lhs),
swap_ops!(Op::i32_lt_s_imm16_rhs),
wasm::i32_gt_s,
)
}
#[inline(never)]
fn visit_i32_gt_u(&mut self) -> Self::Output {
self.translate_binary::<u32, bool>(
swap_ops!(Op::i32_lt_u),
swap_ops!(Op::i32_lt_u_imm16_lhs),
swap_ops!(Op::i32_lt_u_imm16_rhs),
wasm::i32_gt_u,
)
}
#[inline(never)]
fn visit_i32_le_s(&mut self) -> Self::Output {
self.translate_binary::<i32, bool>(
Op::i32_le_s,
Op::i32_le_s_imm16_rhs,
Op::i32_le_s_imm16_lhs,
wasm::i32_le_s,
)
}
#[inline(never)]
fn visit_i32_le_u(&mut self) -> Self::Output {
self.translate_binary::<u32, bool>(
Op::i32_le_u,
Op::i32_le_u_imm16_rhs,
Op::i32_le_u_imm16_lhs,
wasm::i32_le_u,
)
}
#[inline(never)]
fn visit_i32_ge_s(&mut self) -> Self::Output {
self.translate_binary::<i32, bool>(
swap_ops!(Op::i32_le_s),
swap_ops!(Op::i32_le_s_imm16_lhs),
swap_ops!(Op::i32_le_s_imm16_rhs),
wasm::i32_ge_s,
)
}
#[inline(never)]
fn visit_i32_ge_u(&mut self) -> Self::Output {
self.translate_binary::<u32, bool>(
swap_ops!(Op::i32_le_u),
swap_ops!(Op::i32_le_u_imm16_lhs),
swap_ops!(Op::i32_le_u_imm16_rhs),
wasm::i32_ge_u,
)
}
#[inline(never)]
fn visit_i64_eqz(&mut self) -> Self::Output {
bail_unreachable!(self);
self.stack.push_immediate(0_i64)?;
self.visit_i64_eq()
}
#[inline(never)]
fn visit_i64_eq(&mut self) -> Self::Output {
self.translate_binary_commutative::<i64, bool>(
Op::i64_eq,
Op::i64_eq_imm16,
wasm::i64_eq,
FuncTranslator::fuse_eqz,
)
}
#[inline(never)]
fn visit_i64_ne(&mut self) -> Self::Output {
self.translate_binary_commutative::<i64, bool>(
Op::i64_ne,
Op::i64_ne_imm16,
wasm::i64_ne,
FuncTranslator::fuse_nez,
)
}
#[inline(never)]
fn visit_i64_lt_s(&mut self) -> Self::Output {
self.translate_binary::<i64, bool>(
Op::i64_lt_s,
Op::i64_lt_s_imm16_rhs,
Op::i64_lt_s_imm16_lhs,
wasm::i64_lt_s,
)
}
#[inline(never)]
fn visit_i64_lt_u(&mut self) -> Self::Output {
self.translate_binary::<u64, bool>(
Op::i64_lt_u,
Op::i64_lt_u_imm16_rhs,
Op::i64_lt_u_imm16_lhs,
wasm::i64_lt_u,
)
}
#[inline(never)]
fn visit_i64_gt_s(&mut self) -> Self::Output {
self.translate_binary::<i64, bool>(
swap_ops!(Op::i64_lt_s),
swap_ops!(Op::i64_lt_s_imm16_lhs),
swap_ops!(Op::i64_lt_s_imm16_rhs),
wasm::i64_gt_s,
)
}
#[inline(never)]
fn visit_i64_gt_u(&mut self) -> Self::Output {
self.translate_binary::<u64, bool>(
swap_ops!(Op::i64_lt_u),
swap_ops!(Op::i64_lt_u_imm16_lhs),
swap_ops!(Op::i64_lt_u_imm16_rhs),
wasm::i64_gt_u,
)
}
#[inline(never)]
fn visit_i64_le_s(&mut self) -> Self::Output {
self.translate_binary::<i64, bool>(
Op::i64_le_s,
Op::i64_le_s_imm16_rhs,
Op::i64_le_s_imm16_lhs,
wasm::i64_le_s,
)
}
#[inline(never)]
fn visit_i64_le_u(&mut self) -> Self::Output {
self.translate_binary::<u64, bool>(
Op::i64_le_u,
Op::i64_le_u_imm16_rhs,
Op::i64_le_u_imm16_lhs,
wasm::i64_le_u,
)
}
#[inline(never)]
fn visit_i64_ge_s(&mut self) -> Self::Output {
self.translate_binary::<i64, bool>(
swap_ops!(Op::i64_le_s),
swap_ops!(Op::i64_le_s_imm16_lhs),
swap_ops!(Op::i64_le_s_imm16_rhs),
wasm::i64_ge_s,
)
}
#[inline(never)]
fn visit_i64_ge_u(&mut self) -> Self::Output {
self.translate_binary::<u64, bool>(
swap_ops!(Op::i64_le_u),
swap_ops!(Op::i64_le_u_imm16_lhs),
swap_ops!(Op::i64_le_u_imm16_rhs),
wasm::i64_ge_u,
)
}
#[inline(never)]
fn visit_f32_eq(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_eq, wasm::f32_eq)
}
#[inline(never)]
fn visit_f32_ne(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_ne, wasm::f32_ne)
}
#[inline(never)]
fn visit_f32_lt(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_lt, wasm::f32_lt)
}
#[inline(never)]
fn visit_f32_gt(&mut self) -> Self::Output {
self.translate_fbinary(swap_ops!(Op::f32_lt), wasm::f32_gt)
}
#[inline(never)]
fn visit_f32_le(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_le, wasm::f32_le)
}
#[inline(never)]
fn visit_f32_ge(&mut self) -> Self::Output {
self.translate_fbinary(swap_ops!(Op::f32_le), wasm::f32_ge)
}
#[inline(never)]
fn visit_f64_eq(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_eq, wasm::f64_eq)
}
#[inline(never)]
fn visit_f64_ne(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_ne, wasm::f64_ne)
}
#[inline(never)]
fn visit_f64_lt(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_lt, wasm::f64_lt)
}
#[inline(never)]
fn visit_f64_gt(&mut self) -> Self::Output {
self.translate_fbinary(swap_ops!(Op::f64_lt), wasm::f64_gt)
}
#[inline(never)]
fn visit_f64_le(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_le, wasm::f64_le)
}
#[inline(never)]
fn visit_f64_ge(&mut self) -> Self::Output {
self.translate_fbinary(swap_ops!(Op::f64_le), wasm::f64_ge)
}
#[inline(never)]
fn visit_i32_clz(&mut self) -> Self::Output {
self.translate_unary::<i32, i32>(Op::i32_clz, wasm::i32_clz)
}
#[inline(never)]
fn visit_i32_ctz(&mut self) -> Self::Output {
self.translate_unary::<i32, i32>(Op::i32_ctz, wasm::i32_ctz)
}
#[inline(never)]
fn visit_i32_popcnt(&mut self) -> Self::Output {
self.translate_unary::<i32, i32>(Op::i32_popcnt, wasm::i32_popcnt)
}
#[inline(never)]
fn visit_i32_add(&mut self) -> Self::Output {
self.translate_binary_commutative::<i32, i32>(
Op::i32_add,
Op::i32_add_imm16,
wasm::i32_add,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i32_sub(&mut self) -> Self::Output {
self.translate_isub(
Op::i32_sub,
Op::i32_add_imm16,
Op::i32_sub_imm16_lhs,
wasm::i32_sub,
)
}
#[inline(never)]
fn visit_i32_mul(&mut self) -> Self::Output {
self.translate_binary_commutative::<i32, i32>(
Op::i32_mul,
Op::i32_mul_imm16,
wasm::i32_mul,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i32_div_s(&mut self) -> Self::Output {
self.translate_divrem::<i32>(
Op::i32_div_s,
Op::i32_div_s_imm16_rhs,
Op::i32_div_s_imm16_lhs,
wasm::i32_div_s,
)
}
#[inline(never)]
fn visit_i32_div_u(&mut self) -> Self::Output {
self.translate_divrem::<u32>(
Op::i32_div_u,
Op::i32_div_u_imm16_rhs,
Op::i32_div_u_imm16_lhs,
wasm::i32_div_u,
)
}
#[inline(never)]
fn visit_i32_rem_s(&mut self) -> Self::Output {
self.translate_divrem::<i32>(
Op::i32_rem_s,
Op::i32_rem_s_imm16_rhs,
Op::i32_rem_s_imm16_lhs,
wasm::i32_rem_s,
)
}
#[inline(never)]
fn visit_i32_rem_u(&mut self) -> Self::Output {
self.translate_divrem::<u32>(
Op::i32_rem_u,
Op::i32_rem_u_imm16_rhs,
Op::i32_rem_u_imm16_lhs,
wasm::i32_rem_u,
)
}
#[inline(never)]
fn visit_i32_and(&mut self) -> Self::Output {
self.translate_binary_commutative::<i32, i32>(
Op::i32_bitand,
Op::i32_bitand_imm16,
wasm::i32_bitand,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i32_or(&mut self) -> Self::Output {
self.translate_binary_commutative::<i32, i32>(
Op::i32_bitor,
Op::i32_bitor_imm16,
wasm::i32_bitor,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i32_xor(&mut self) -> Self::Output {
self.translate_binary_commutative::<i32, i32>(
Op::i32_bitxor,
Op::i32_bitxor_imm16,
wasm::i32_bitxor,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i32_shl(&mut self) -> Self::Output {
self.translate_shift::<i32>(
Op::i32_shl,
Op::i32_shl_by,
Op::i32_shl_imm16,
wasm::i32_shl,
)
}
#[inline(never)]
fn visit_i32_shr_s(&mut self) -> Self::Output {
self.translate_shift::<i32>(
Op::i32_shr_s,
Op::i32_shr_s_by,
Op::i32_shr_s_imm16,
wasm::i32_shr_s,
)
}
#[inline(never)]
fn visit_i32_shr_u(&mut self) -> Self::Output {
self.translate_shift::<i32>(
Op::i32_shr_u,
Op::i32_shr_u_by,
Op::i32_shr_u_imm16,
wasm::i32_shr_u,
)
}
#[inline(never)]
fn visit_i32_rotl(&mut self) -> Self::Output {
self.translate_shift::<i32>(
Op::i32_rotl,
Op::i32_rotl_by,
Op::i32_rotl_imm16,
wasm::i32_rotl,
)
}
#[inline(never)]
fn visit_i32_rotr(&mut self) -> Self::Output {
self.translate_shift::<i32>(
Op::i32_rotr,
Op::i32_rotr_by,
Op::i32_rotr_imm16,
wasm::i32_rotr,
)
}
#[inline(never)]
fn visit_i64_clz(&mut self) -> Self::Output {
self.translate_unary::<i64, i64>(Op::i64_clz, wasm::i64_clz)
}
#[inline(never)]
fn visit_i64_ctz(&mut self) -> Self::Output {
self.translate_unary::<i64, i64>(Op::i64_ctz, wasm::i64_ctz)
}
#[inline(never)]
fn visit_i64_popcnt(&mut self) -> Self::Output {
self.translate_unary::<i64, i64>(Op::i64_popcnt, wasm::i64_popcnt)
}
#[inline(never)]
fn visit_i64_add(&mut self) -> Self::Output {
self.translate_binary_commutative::<i64, i64>(
Op::i64_add,
Op::i64_add_imm16,
wasm::i64_add,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i64_sub(&mut self) -> Self::Output {
self.translate_isub(
Op::i64_sub,
Op::i64_add_imm16,
Op::i64_sub_imm16_lhs,
wasm::i64_sub,
)
}
#[inline(never)]
fn visit_i64_mul(&mut self) -> Self::Output {
self.translate_binary_commutative::<i64, i64>(
Op::i64_mul,
Op::i64_mul_imm16,
wasm::i64_mul,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i64_div_s(&mut self) -> Self::Output {
self.translate_divrem::<i64>(
Op::i64_div_s,
Op::i64_div_s_imm16_rhs,
Op::i64_div_s_imm16_lhs,
wasm::i64_div_s,
)
}
#[inline(never)]
fn visit_i64_div_u(&mut self) -> Self::Output {
self.translate_divrem::<u64>(
Op::i64_div_u,
Op::i64_div_u_imm16_rhs,
Op::i64_div_u_imm16_lhs,
wasm::i64_div_u,
)
}
#[inline(never)]
fn visit_i64_rem_s(&mut self) -> Self::Output {
self.translate_divrem::<i64>(
Op::i64_rem_s,
Op::i64_rem_s_imm16_rhs,
Op::i64_rem_s_imm16_lhs,
wasm::i64_rem_s,
)
}
#[inline(never)]
fn visit_i64_rem_u(&mut self) -> Self::Output {
self.translate_divrem::<u64>(
Op::i64_rem_u,
Op::i64_rem_u_imm16_rhs,
Op::i64_rem_u_imm16_lhs,
wasm::i64_rem_u,
)
}
#[inline(never)]
fn visit_i64_and(&mut self) -> Self::Output {
self.translate_binary_commutative::<i64, i64>(
Op::i64_bitand,
Op::i64_bitand_imm16,
wasm::i64_bitand,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i64_or(&mut self) -> Self::Output {
self.translate_binary_commutative::<i64, i64>(
Op::i64_bitor,
Op::i64_bitor_imm16,
wasm::i64_bitor,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i64_xor(&mut self) -> Self::Output {
self.translate_binary_commutative::<i64, i64>(
Op::i64_bitxor,
Op::i64_bitxor_imm16,
wasm::i64_bitxor,
FuncTranslator::no_opt_ri,
)
}
#[inline(never)]
fn visit_i64_shl(&mut self) -> Self::Output {
self.translate_shift::<i64>(
Op::i64_shl,
Op::i64_shl_by,
Op::i64_shl_imm16,
wasm::i64_shl,
)
}
#[inline(never)]
fn visit_i64_shr_s(&mut self) -> Self::Output {
self.translate_shift::<i64>(
Op::i64_shr_s,
Op::i64_shr_s_by,
Op::i64_shr_s_imm16,
wasm::i64_shr_s,
)
}
#[inline(never)]
fn visit_i64_shr_u(&mut self) -> Self::Output {
self.translate_shift::<i64>(
Op::i64_shr_u,
Op::i64_shr_u_by,
Op::i64_shr_u_imm16,
wasm::i64_shr_u,
)
}
#[inline(never)]
fn visit_i64_rotl(&mut self) -> Self::Output {
self.translate_shift::<i64>(
Op::i64_rotl,
Op::i64_rotl_by,
Op::i64_rotl_imm16,
wasm::i64_rotl,
)
}
#[inline(never)]
fn visit_i64_rotr(&mut self) -> Self::Output {
self.translate_shift::<i64>(
Op::i64_rotr,
Op::i64_rotr_by,
Op::i64_rotr_imm16,
wasm::i64_rotr,
)
}
#[inline(never)]
fn visit_f32_abs(&mut self) -> Self::Output {
self.translate_unary(Op::f32_abs, wasm::f32_abs)
}
#[inline(never)]
fn visit_f32_neg(&mut self) -> Self::Output {
self.translate_unary(Op::f32_neg, wasm::f32_neg)
}
#[inline(never)]
fn visit_f32_ceil(&mut self) -> Self::Output {
self.translate_unary(Op::f32_ceil, wasm::f32_ceil)
}
#[inline(never)]
fn visit_f32_floor(&mut self) -> Self::Output {
self.translate_unary(Op::f32_floor, wasm::f32_floor)
}
#[inline(never)]
fn visit_f32_trunc(&mut self) -> Self::Output {
self.translate_unary(Op::f32_trunc, wasm::f32_trunc)
}
#[inline(never)]
fn visit_f32_nearest(&mut self) -> Self::Output {
self.translate_unary(Op::f32_nearest, wasm::f32_nearest)
}
#[inline(never)]
fn visit_f32_sqrt(&mut self) -> Self::Output {
self.translate_unary(Op::f32_sqrt, wasm::f32_sqrt)
}
#[inline(never)]
fn visit_f32_add(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_add, wasm::f32_add)
}
#[inline(never)]
fn visit_f32_sub(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_sub, wasm::f32_sub)
}
#[inline(never)]
fn visit_f32_mul(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_mul, wasm::f32_mul)
}
#[inline(never)]
fn visit_f32_div(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_div, wasm::f32_div)
}
#[inline(never)]
fn visit_f32_min(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_min, wasm::f32_min)
}
#[inline(never)]
fn visit_f32_max(&mut self) -> Self::Output {
self.translate_fbinary(Op::f32_max, wasm::f32_max)
}
#[inline(never)]
fn visit_f32_copysign(&mut self) -> Self::Output {
self.translate_fcopysign::<f32>(Op::f32_copysign, Op::f32_copysign_imm, wasm::f32_copysign)
}
#[inline(never)]
fn visit_f64_abs(&mut self) -> Self::Output {
self.translate_unary(Op::f64_abs, wasm::f64_abs)
}
#[inline(never)]
fn visit_f64_neg(&mut self) -> Self::Output {
self.translate_unary(Op::f64_neg, wasm::f64_neg)
}
#[inline(never)]
fn visit_f64_ceil(&mut self) -> Self::Output {
self.translate_unary(Op::f64_ceil, wasm::f64_ceil)
}
#[inline(never)]
fn visit_f64_floor(&mut self) -> Self::Output {
self.translate_unary(Op::f64_floor, wasm::f64_floor)
}
#[inline(never)]
fn visit_f64_trunc(&mut self) -> Self::Output {
self.translate_unary(Op::f64_trunc, wasm::f64_trunc)
}
#[inline(never)]
fn visit_f64_nearest(&mut self) -> Self::Output {
self.translate_unary(Op::f64_nearest, wasm::f64_nearest)
}
#[inline(never)]
fn visit_f64_sqrt(&mut self) -> Self::Output {
self.translate_unary(Op::f64_sqrt, wasm::f64_sqrt)
}
#[inline(never)]
fn visit_f64_add(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_add, wasm::f64_add)
}
#[inline(never)]
fn visit_f64_sub(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_sub, wasm::f64_sub)
}
#[inline(never)]
fn visit_f64_mul(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_mul, wasm::f64_mul)
}
#[inline(never)]
fn visit_f64_div(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_div, wasm::f64_div)
}
#[inline(never)]
fn visit_f64_min(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_min, wasm::f64_min)
}
#[inline(never)]
fn visit_f64_max(&mut self) -> Self::Output {
self.translate_fbinary(Op::f64_max, wasm::f64_max)
}
#[inline(never)]
fn visit_f64_copysign(&mut self) -> Self::Output {
self.translate_fcopysign::<f64>(Op::f64_copysign, Op::f64_copysign_imm, wasm::f64_copysign)
}
#[inline(never)]
fn visit_i32_wrap_i64(&mut self) -> Self::Output {
self.translate_unary(Op::i32_wrap_i64, wasm::i32_wrap_i64)
}
#[inline(never)]
fn visit_i32_trunc_f32_s(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i32_trunc_f32_s, wasm::i32_trunc_f32_s)
}
#[inline(never)]
fn visit_i32_trunc_f32_u(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i32_trunc_f32_u, wasm::i32_trunc_f32_u)
}
#[inline(never)]
fn visit_i32_trunc_f64_s(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i32_trunc_f64_s, wasm::i32_trunc_f64_s)
}
#[inline(never)]
fn visit_i32_trunc_f64_u(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i32_trunc_f64_u, wasm::i32_trunc_f64_u)
}
#[inline(never)]
fn visit_i64_extend_i32_s(&mut self) -> Self::Output {
self.translate_unary::<i32, i64>(Op::i64_extend32_s, wasm::i64_extend_i32_s)
}
#[inline(never)]
fn visit_i64_extend_i32_u(&mut self) -> Self::Output {
self.translate_reinterpret(wasm::i64_extend_i32_u)
}
#[inline(never)]
fn visit_i64_trunc_f32_s(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i64_trunc_f32_s, wasm::i64_trunc_f32_s)
}
#[inline(never)]
fn visit_i64_trunc_f32_u(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i64_trunc_f32_u, wasm::i64_trunc_f32_u)
}
#[inline(never)]
fn visit_i64_trunc_f64_s(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i64_trunc_f64_s, wasm::i64_trunc_f64_s)
}
#[inline(never)]
fn visit_i64_trunc_f64_u(&mut self) -> Self::Output {
self.translate_unary_fallible(Op::i64_trunc_f64_u, wasm::i64_trunc_f64_u)
}
#[inline(never)]
fn visit_f32_convert_i32_s(&mut self) -> Self::Output {
self.translate_unary(Op::f32_convert_i32_s, wasm::f32_convert_i32_s)
}
#[inline(never)]
fn visit_f32_convert_i32_u(&mut self) -> Self::Output {
self.translate_unary(Op::f32_convert_i32_u, wasm::f32_convert_i32_u)
}
#[inline(never)]
fn visit_f32_convert_i64_s(&mut self) -> Self::Output {
self.translate_unary(Op::f32_convert_i64_s, wasm::f32_convert_i64_s)
}
#[inline(never)]
fn visit_f32_convert_i64_u(&mut self) -> Self::Output {
self.translate_unary(Op::f32_convert_i64_u, wasm::f32_convert_i64_u)
}
#[inline(never)]
fn visit_f32_demote_f64(&mut self) -> Self::Output {
self.translate_unary(Op::f32_demote_f64, wasm::f32_demote_f64)
}
#[inline(never)]
fn visit_f64_convert_i32_s(&mut self) -> Self::Output {
self.translate_unary(Op::f64_convert_i32_s, wasm::f64_convert_i32_s)
}
#[inline(never)]
fn visit_f64_convert_i32_u(&mut self) -> Self::Output {
self.translate_unary(Op::f64_convert_i32_u, wasm::f64_convert_i32_u)
}
#[inline(never)]
fn visit_f64_convert_i64_s(&mut self) -> Self::Output {
self.translate_unary(Op::f64_convert_i64_s, wasm::f64_convert_i64_s)
}
#[inline(never)]
fn visit_f64_convert_i64_u(&mut self) -> Self::Output {
self.translate_unary(Op::f64_convert_i64_u, wasm::f64_convert_i64_u)
}
#[inline(never)]
fn visit_f64_promote_f32(&mut self) -> Self::Output {
self.translate_unary(Op::f64_promote_f32, wasm::f64_promote_f32)
}
#[inline(never)]
fn visit_i32_reinterpret_f32(&mut self) -> Self::Output {
self.translate_reinterpret(wasm::i32_reinterpret_f32)
}
#[inline(never)]
fn visit_i64_reinterpret_f64(&mut self) -> Self::Output {
self.translate_reinterpret(wasm::i64_reinterpret_f64)
}
#[inline(never)]
fn visit_f32_reinterpret_i32(&mut self) -> Self::Output {
self.translate_reinterpret(wasm::f32_reinterpret_i32)
}
#[inline(never)]
fn visit_f64_reinterpret_i64(&mut self) -> Self::Output {
self.translate_reinterpret(wasm::f64_reinterpret_i64)
}
#[inline(never)]
fn visit_i32_extend8_s(&mut self) -> Self::Output {
self.translate_unary(Op::i32_extend8_s, wasm::i32_extend8_s)
}
#[inline(never)]
fn visit_i32_extend16_s(&mut self) -> Self::Output {
self.translate_unary(Op::i32_extend16_s, wasm::i32_extend16_s)
}
#[inline(never)]
fn visit_i64_extend8_s(&mut self) -> Self::Output {
self.translate_unary(Op::i64_extend8_s, wasm::i64_extend8_s)
}
#[inline(never)]
fn visit_i64_extend16_s(&mut self) -> Self::Output {
self.translate_unary(Op::i64_extend16_s, wasm::i64_extend16_s)
}
#[inline(never)]
fn visit_i64_extend32_s(&mut self) -> Self::Output {
self.translate_unary(Op::i64_extend32_s, wasm::i64_extend32_s)
}
#[inline(never)]
fn visit_i32_trunc_sat_f32_s(&mut self) -> Self::Output {
self.translate_unary(Op::i32_trunc_sat_f32_s, wasm::i32_trunc_sat_f32_s)
}
#[inline(never)]
fn visit_i32_trunc_sat_f32_u(&mut self) -> Self::Output {
self.translate_unary(Op::i32_trunc_sat_f32_u, wasm::i32_trunc_sat_f32_u)
}
#[inline(never)]
fn visit_i32_trunc_sat_f64_s(&mut self) -> Self::Output {
self.translate_unary(Op::i32_trunc_sat_f64_s, wasm::i32_trunc_sat_f64_s)
}
#[inline(never)]
fn visit_i32_trunc_sat_f64_u(&mut self) -> Self::Output {
self.translate_unary(Op::i32_trunc_sat_f64_u, wasm::i32_trunc_sat_f64_u)
}
#[inline(never)]
fn visit_i64_trunc_sat_f32_s(&mut self) -> Self::Output {
self.translate_unary(Op::i64_trunc_sat_f32_s, wasm::i64_trunc_sat_f32_s)
}
#[inline(never)]
fn visit_i64_trunc_sat_f32_u(&mut self) -> Self::Output {
self.translate_unary(Op::i64_trunc_sat_f32_u, wasm::i64_trunc_sat_f32_u)
}
#[inline(never)]
fn visit_i64_trunc_sat_f64_s(&mut self) -> Self::Output {
self.translate_unary(Op::i64_trunc_sat_f64_s, wasm::i64_trunc_sat_f64_s)
}
#[inline(never)]
fn visit_i64_trunc_sat_f64_u(&mut self) -> Self::Output {
self.translate_unary(Op::i64_trunc_sat_f64_u, wasm::i64_trunc_sat_f64_u)
}
#[inline(never)]
fn visit_memory_init(&mut self, data_index: u32, mem: u32) -> Self::Output {
bail_unreachable!(self);
let (dst, src, len) = self.stack.pop3();
let dst = self.immediate_to_reg(dst)?;
let src = self.immediate_to_reg(src)?;
let len = self.immediate_to_reg(len)?;
self.push_instr(Op::memory_init(dst, src, len), FuelCostsProvider::instance)?;
self.push_param(Op::memory_index(mem))?;
self.push_param(Op::data_index(data_index))?;
Ok(())
}
#[inline(never)]
fn visit_data_drop(&mut self, data_index: u32) -> Self::Output {
bail_unreachable!(self);
self.push_instr(Op::data_drop(data_index), FuelCostsProvider::instance)?;
Ok(())
}
#[inline(never)]
fn visit_memory_copy(&mut self, dst_mem: u32, src_mem: u32) -> Self::Output {
bail_unreachable!(self);
let (dst, src, len) = self.stack.pop3();
let dst = self.immediate_to_reg(dst)?;
let src = self.immediate_to_reg(src)?;
let len = self.immediate_to_reg(len)?;
self.push_instr(Op::memory_copy(dst, src, len), FuelCostsProvider::instance)?;
self.push_param(Op::memory_index(dst_mem))?;
self.push_param(Op::memory_index(src_mem))?;
Ok(())
}
#[inline(never)]
fn visit_memory_fill(&mut self, mem: u32) -> Self::Output {
bail_unreachable!(self);
let (dst, value, len) = self.stack.pop3();
let dst = self.immediate_to_reg(dst)?;
let value = self.make_input(value, |_, value| {
let byte = u32::from(value) as u8;
Ok(Input::Immediate(byte))
})?;
let len = self.immediate_to_reg(len)?;
let instr: Op = match value {
Input::Slot(value) => Op::memory_fill(dst, value, len),
Input::Immediate(value) => Op::memory_fill_imm(dst, value, len),
};
self.push_instr(instr, FuelCostsProvider::instance)?;
self.push_param(Op::memory_index(mem))?;
Ok(())
}
#[inline(never)]
fn visit_table_init(&mut self, elem_index: u32, table: u32) -> Self::Output {
bail_unreachable!(self);
let (dst, src, len) = self.stack.pop3();
let dst = self.immediate_to_reg(dst)?;
let src = self.immediate_to_reg(src)?;
let len = self.immediate_to_reg(len)?;
self.push_instr(Op::table_init(dst, src, len), FuelCostsProvider::instance)?;
self.push_param(Op::table_index(table))?;
self.push_param(Op::elem_index(elem_index))?;
Ok(())
}
#[inline(never)]
fn visit_elem_drop(&mut self, elem_index: u32) -> Self::Output {
bail_unreachable!(self);
self.push_instr(Op::elem_drop(elem_index), FuelCostsProvider::instance)?;
Ok(())
}
#[inline(never)]
fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output {
bail_unreachable!(self);
let (dst, src, len) = self.stack.pop3();
let dst = self.immediate_to_reg(dst)?;
let src = self.immediate_to_reg(src)?;
let len = self.immediate_to_reg(len)?;
self.push_instr(Op::table_copy(dst, src, len), FuelCostsProvider::instance)?;
self.push_param(Op::table_index(dst_table))?;
self.push_param(Op::table_index(src_table))?;
Ok(())
}
#[inline(never)]
fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output {
let type_hint = WasmiValueType::from(ty).into_inner();
self.translate_select(Some(type_hint))
}
#[inline(never)]
fn visit_ref_null(&mut self, ty: wasmparser::HeapType) -> Self::Output {
bail_unreachable!(self);
let type_hint = WasmiValueType::from(ty).into_inner();
let null = match type_hint {
ValType::FuncRef => TypedVal::from(<Ref<Func>>::Null),
ValType::ExternRef => TypedVal::from(<Ref<ExternRef>>::Null),
ty => panic!("expected a Wasm `reftype` but found: {ty:?}"),
};
self.stack.push_immediate(null)?;
Ok(())
}
#[inline(never)]
fn visit_ref_is_null(&mut self) -> Self::Output {
bail_unreachable!(self);
match self.stack.pop() {
Operand::Local(input) => {
self.stack.push_local(input.local_index(), ValType::I64)?;
self.visit_i64_eqz()
}
Operand::Temp(input) => {
self.stack.push_temp(ValType::I64, input.instr())?;
self.visit_i64_eqz()
}
Operand::Immediate(input) => {
let untyped = input.val().untyped();
let is_null = match input.ty() {
ValType::FuncRef => <Ref<Func>>::from(untyped).is_null(),
ValType::ExternRef => <Ref<ExternRef>>::from(untyped).is_null(),
invalid => panic!("`ref.is_null`: encountered invalid input type: {invalid:?}"),
};
self.stack.push_immediate(i32::from(is_null))?;
Ok(())
}
}
}
#[inline(never)]
fn visit_ref_func(&mut self, function_index: u32) -> Self::Output {
bail_unreachable!(self);
self.push_instr_with_result(
ValType::FuncRef,
|result| Op::ref_func(result, function_index),
FuelCostsProvider::instance,
)?;
Ok(())
}
#[inline(never)]
fn visit_table_fill(&mut self, table: u32) -> Self::Output {
bail_unreachable!(self);
let (dst, value, len) = self.stack.pop3();
let dst = self.immediate_to_reg(dst)?;
let value = self.immediate_to_reg(value)?;
let len = self.immediate_to_reg(len)?;
self.push_instr(Op::table_fill(dst, len, value), FuelCostsProvider::instance)?;
self.push_param(Op::table_index(table))?;
Ok(())
}
#[inline(never)]
fn visit_table_get(&mut self, table: u32) -> Self::Output {
bail_unreachable!(self);
let table_type = *self.module.get_type_of_table(TableIdx::from(table));
let index = self.stack.pop();
let item_ty = table_type.element();
let index_ty = table_type.index_ty();
let index = self.make_index32(index, index_ty)?;
self.push_instr_with_result(
item_ty,
|result| match index {
Input::Slot(index) => Op::table_get(result, index),
Input::Immediate(index) => Op::table_get_imm(result, index),
},
FuelCostsProvider::instance,
)?;
self.push_param(Op::table_index(table))?;
Ok(())
}
#[inline(never)]
fn visit_table_set(&mut self, table: u32) -> Self::Output {
bail_unreachable!(self);
let table_type = *self.module.get_type_of_table(TableIdx::from(table));
let index_ty = table_type.index_ty();
let (index, value) = self.stack.pop2();
let index = self.make_index32(index, index_ty)?;
let value = self.layout.operand_to_reg(value)?;
let instr = match index {
Input::Slot(index) => Op::table_set(index, value),
Input::Immediate(index) => Op::table_set_at(value, index),
};
self.push_instr(instr, FuelCostsProvider::instance)?;
self.push_param(Op::table_index(table))?;
Ok(())
}
#[inline(never)]
fn visit_table_grow(&mut self, table: u32) -> Self::Output {
bail_unreachable!(self);
let table_type = *self.module.get_type_of_table(TableIdx::from(table));
let index_ty = table_type.index_ty();
let (value, delta) = self.stack.pop2();
if let Operand::Immediate(delta) = delta {
let delta = delta.val();
let delta = match index_ty {
IndexType::I32 => u64::from(u32::from(delta)),
IndexType::I64 => u64::from(delta),
};
if delta == 0 {
self.push_instr_with_result(
index_ty.ty(),
|result| Op::table_size(result, table),
FuelCostsProvider::instance,
)?;
return Ok(());
}
}
let value = self.immediate_to_reg(value)?;
let delta = self.immediate_to_reg(delta)?;
self.push_instr_with_result(
index_ty.ty(),
|result| Op::table_grow(result, delta, value),
FuelCostsProvider::instance,
)?;
self.push_param(Op::table_index(table))?;
Ok(())
}
#[inline(never)]
fn visit_table_size(&mut self, table: u32) -> Self::Output {
bail_unreachable!(self);
let table_type = *self.module.get_type_of_table(TableIdx::from(table));
let index_ty = table_type.index_ty();
self.push_instr_with_result(
index_ty.ty(),
|result| Op::table_size(result, table),
FuelCostsProvider::instance,
)?;
Ok(())
}
#[inline(never)]
fn visit_return_call(&mut self, function_index: u32) -> Self::Output {
bail_unreachable!(self);
let func_idx = FuncIdx::from(function_index);
let func_type = self.resolve_func_type(func_idx);
let len_params = usize::from(func_type.len_params());
let instr = match self.module.get_engine_func(func_idx) {
Some(engine_func) => {
match len_params {
0 => Op::return_call_internal_0(engine_func),
_ => Op::return_call_internal(engine_func),
}
}
None => {
match len_params {
0 => Op::return_call_imported_0(function_index),
_ => Op::return_call_imported(function_index),
}
}
};
self.push_instr(instr, FuelCostsProvider::call)?;
self.stack.pop_n(len_params, &mut self.operands);
self.instrs
.encode_register_list(&self.operands, &mut self.layout)?;
self.reachable = false;
Ok(())
}
#[inline(never)]
fn visit_return_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output {
bail_unreachable!(self);
let func_type = self.resolve_type(type_index);
let index = self.stack.pop();
let indirect_params = self.call_indirect_params(index, table_index)?;
let len_params = usize::from(func_type.len_params());
let instr = match (len_params, indirect_params) {
(0, Op::CallIndirectParams { .. }) => Op::return_call_indirect_0(type_index),
(0, Op::CallIndirectParamsImm16 { .. }) => Op::return_call_indirect_0_imm16(type_index),
(_, Op::CallIndirectParams { .. }) => Op::return_call_indirect(type_index),
(_, Op::CallIndirectParamsImm16 { .. }) => Op::return_call_indirect_imm16(type_index),
_ => unreachable!(),
};
self.push_instr(instr, FuelCostsProvider::call)?;
self.push_param(indirect_params)?;
self.stack.pop_n(len_params, &mut self.operands);
self.instrs
.encode_register_list(&self.operands, &mut self.layout)?;
self.reachable = false;
Ok(())
}
#[inline(never)]
fn visit_i64_add128(&mut self) -> Self::Output {
self.translate_i64_binop128(Op::i64_add128, wasm::i64_add128)
}
#[inline(never)]
fn visit_i64_sub128(&mut self) -> Self::Output {
self.translate_i64_binop128(Op::i64_sub128, wasm::i64_sub128)
}
#[inline(never)]
fn visit_i64_mul_wide_s(&mut self) -> Self::Output {
self.translate_i64_mul_wide_sx(Op::i64_mul_wide_s, wasm::i64_mul_wide_s, true)
}
#[inline(never)]
fn visit_i64_mul_wide_u(&mut self) -> Self::Output {
self.translate_i64_mul_wide_sx(Op::i64_mul_wide_u, wasm::i64_mul_wide_u, false)
}
}