mod control_frame;
mod control_stack;
mod error;
mod inst_builder;
mod labels;
mod locals_registry;
mod value_stack;
mod visit;
use self::{
control_frame::{
BlockControlFrame,
ControlFrame,
ControlFrameKind,
IfControlFrame,
LoopControlFrame,
UnreachableControlFrame,
},
control_stack::ControlFlowStack,
labels::LabelRef,
locals_registry::LocalsRegistry,
value_stack::ValueStackHeight,
};
pub use self::{
error::TranslationError,
inst_builder::{Instr, InstructionsBuilder, RelativeDepth},
};
use super::{bytecode, DropKeep, FuncBody, Instruction};
use crate::{
engine::bytecode::{
BranchParams,
DataSegmentIdx,
ElementSegmentIdx,
Offset,
SignatureIdx,
TableIdx,
},
module::{
BlockType,
FuncIdx,
FuncTypeIdx,
GlobalIdx,
InitExpr,
MemoryIdx,
ModuleResources,
ReusableAllocations,
DEFAULT_MEMORY_INDEX,
},
Engine,
FuncType,
GlobalType,
Mutability,
Value,
};
use alloc::vec::Vec;
use wasmi_core::{ValueType, F32, F64};
type FuncValidator = wasmparser::FuncValidator<wasmparser::ValidatorResources>;
pub struct FuncBuilder<'parser> {
engine: Engine,
func: FuncIdx,
res: ModuleResources<'parser>,
reachable: bool,
validator: FuncValidator,
stack_height: ValueStackHeight,
locals: LocalsRegistry,
alloc: FunctionBuilderAllocations,
}
#[derive(Debug, Default)]
pub struct FunctionBuilderAllocations {
control_frames: ControlFlowStack,
inst_builder: InstructionsBuilder,
br_table_branches: Vec<Instruction>,
}
impl FunctionBuilderAllocations {
fn reset(&mut self) {
self.control_frames.reset();
self.inst_builder.reset();
self.br_table_branches.clear();
}
}
impl<'parser> FuncBuilder<'parser> {
pub fn new(
engine: &Engine,
func: FuncIdx,
res: ModuleResources<'parser>,
validator: FuncValidator,
mut allocations: FunctionBuilderAllocations,
) -> Self {
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,
}
}
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)
}
fn func_type_at(&self, func_type_index: SignatureIdx) -> FuncType {
let func_type_index = FuncTypeIdx(func_type_index.into_inner()); let dedup_func_type = self.res.get_func_type(func_type_index);
self.res
.engine()
.resolve_func_type(dedup_func_type, Clone::clone)
}
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)
}
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);
}
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()
}
pub fn translate_locals(
&mut self,
offset: usize,
amount: u32,
value_type: wasmparser::ValType,
) -> Result<(), TranslationError> {
self.validator.define_locals(offset, amount, value_type)?;
self.locals.register_locals(amount);
Ok(())
}
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
}
pub fn finish(
mut self,
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 allocations = ReusableAllocations {
translation: self.alloc,
validation: self.validator.into_allocations(),
};
Ok((func_body, allocations))
}
fn is_reachable(&self) -> bool {
self.reachable
}
fn translate_if_reachable<F>(&mut self, translator: F) -> Result<(), TranslationError>
where
F: FnOnce(&mut Self) -> Result<(), TranslationError>,
{
if self.is_reachable() {
translator(self)?;
}
Ok(())
}
fn compute_drop_keep(&self, depth: u32) -> Result<DropKeep, TranslationError> {
debug_assert!(self.is_reachable());
let frame = self.alloc.control_frames.nth_back(depth);
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),
};
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)
}
fn drop_keep_return(&self) -> Result<DropKeep, TranslationError> {
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_keep.drop() + len_params_locals,
drop_keep.keep(),
)
.map_err(Into::into)
}
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}"))
}
fn acquire_target(&self, relative_depth: u32) -> Result<AcquiredTarget, TranslationError> {
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))
}
}
fn branch_params(&mut self, target: LabelRef, drop_keep: DropKeep) -> BranchParams {
BranchParams::new(self.alloc.inst_builder.try_resolve_label(target), drop_keep)
}
}
#[derive(Debug)]
pub enum AcquiredTarget {
Branch(LabelRef, DropKeep),
Return(DropKeep),
}
impl<'parser> FuncBuilder<'parser> {
pub fn translate_nop(&mut self) -> Result<(), TranslationError> {
Ok(())
}
pub fn translate_unreachable(&mut self) -> Result<(), TranslationError> {
self.translate_if_reachable(|builder| {
builder
.alloc
.inst_builder
.push_inst(Instruction::Unreachable);
builder.reachable = false;
Ok(())
})
}
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",
)
})
}
pub fn translate_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(())
}
pub fn translate_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(())
}
pub fn translate_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(())
}
pub fn translate_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) => {
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();
if_frame.update_end_of_then_reachability(reachable);
if reachable {
let params = self.branch_params(if_frame.end_label(), DropKeep::none());
self.alloc.inst_builder.push_inst(Instruction::Br(params));
}
self.alloc.inst_builder.pin_label(if_frame.else_label());
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);
self.reachable = true;
Ok(())
}
pub fn translate_end(&mut self) -> Result<(), TranslationError> {
let frame = self.alloc.control_frames.last();
if let ControlFrame::If(if_frame) = &frame {
self.alloc
.inst_builder
.pin_label_if_unpinned(if_frame.else_label());
}
if frame.is_reachable() && !matches!(frame.kind(), ControlFrameKind::Loop) {
self.alloc.inst_builder.pin_label(frame.end_label());
}
let frame_reachable = frame.is_reachable();
let frame_stack_height = frame.stack_height();
if self.alloc.control_frames.len() == 1 {
self.translate_return()?;
} else {
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(())
}
pub fn translate_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(_) => {
builder.translate_return()?;
}
}
builder.reachable = false;
Ok(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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<Instruction, TranslationError> {
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);
}
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(())
})
}
pub fn translate_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 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);
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_drop(&mut self) -> Result<(), TranslationError> {
self.translate_if_reachable(|builder| {
builder.stack_height.pop1();
builder.alloc.inst_builder.push_inst(Instruction::Drop);
Ok(())
})
}
pub fn translate_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 translate_typed_select(
&mut self,
_ty: wasmparser::ValType,
) -> Result<(), TranslationError> {
self.translate_select()
}
pub fn translate_ref_null(&mut self, _ty: wasmparser::ValType) -> Result<(), TranslationError> {
self.translate_const(0i64)
}
pub fn translate_ref_is_null(&mut self) -> Result<(), TranslationError> {
self.translate_i64_eqz()
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(|| {
Instruction::GlobalGet(global_idx.into_u32().into())
});
builder.alloc.inst_builder.push_inst(instr);
Ok(())
})
}
fn optimize_global_get(
global_type: &GlobalType,
init_value: Option<&InitExpr>,
) -> Option<Instruction> {
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) {
return Some(Instruction::constant(value));
}
if let Some(func_index) = init_expr.func_ref() {
let func_index = bytecode::FuncIdx::from(func_index.into_u32());
return Some(Instruction::RefFunc { func_index });
}
}
None
}
pub fn translate_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 decompose_memarg(memarg: wasmparser::MemArg) -> (MemoryIdx, u32) {
let memory_idx = MemoryIdx(memarg.memory);
let offset = memarg.offset as u32;
(memory_idx, offset)
}
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(())
})
}
pub fn translate_i32_load(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I32, Instruction::I32Load)
}
pub fn translate_i64_load(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I64, Instruction::I64Load)
}
pub fn translate_f32_load(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::F32, Instruction::F32Load)
}
pub fn translate_f64_load(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::F64, Instruction::F64Load)
}
pub fn translate_i32_load8_s(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I32, Instruction::I32Load8S)
}
pub fn translate_i32_load8_u(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I32, Instruction::I32Load8U)
}
pub fn translate_i32_load16_s(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I32, Instruction::I32Load16S)
}
pub fn translate_i32_load16_u(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I32, Instruction::I32Load16U)
}
pub fn translate_i64_load8_s(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I64, Instruction::I64Load8S)
}
pub fn translate_i64_load8_u(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I64, Instruction::I64Load8U)
}
pub fn translate_i64_load16_s(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I64, Instruction::I64Load16S)
}
pub fn translate_i64_load16_u(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I64, Instruction::I64Load16U)
}
pub fn translate_i64_load32_s(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I64, Instruction::I64Load32S)
}
pub fn translate_i64_load32_u(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_load(memarg, ValueType::I64, Instruction::I64Load32U)
}
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(())
})
}
pub fn translate_i32_store(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::I32, Instruction::I32Store)
}
pub fn translate_i64_store(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::I64, Instruction::I64Store)
}
pub fn translate_f32_store(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::F32, Instruction::F32Store)
}
pub fn translate_f64_store(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::F64, Instruction::F64Store)
}
pub fn translate_i32_store8(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::I32, Instruction::I32Store8)
}
pub fn translate_i32_store16(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::I32, Instruction::I32Store16)
}
pub fn translate_i64_store8(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::I64, Instruction::I64Store8)
}
pub fn translate_i64_store16(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::I64, Instruction::I64Store16)
}
pub fn translate_i64_store32(
&mut self,
memarg: wasmparser::MemArg,
) -> Result<(), TranslationError> {
self.translate_store(memarg, ValueType::I64, Instruction::I64Store32)
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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(())
})
}
pub fn translate_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 translate_const<T>(&mut self, value: T) -> Result<(), TranslationError>
where
T: Into<Value>,
{
self.translate_if_reachable(|builder| {
let value = value.into();
builder.stack_height.push();
builder
.alloc
.inst_builder
.push_inst(Instruction::constant(value));
Ok(())
})
}
pub fn translate_i32_const(&mut self, value: i32) -> Result<(), TranslationError> {
self.translate_const(value)
}
pub fn translate_i64_const(&mut self, value: i64) -> Result<(), TranslationError> {
self.translate_const(value)
}
pub fn translate_f32_const(
&mut self,
value: wasmparser::Ieee32,
) -> Result<(), TranslationError> {
self.translate_const(F32::from_bits(value.bits()))
}
pub fn translate_f64_const(
&mut self,
value: wasmparser::Ieee64,
) -> Result<(), TranslationError> {
self.translate_const(F64::from_bits(value.bits()))
}
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(())
})
}
pub fn translate_i32_eqz(&mut self) -> Result<(), TranslationError> {
self.translate_unary_cmp(ValueType::I32, Instruction::I32Eqz)
}
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(())
})
}
pub fn translate_i32_eq(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32Eq)
}
pub fn translate_i32_ne(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32Ne)
}
pub fn translate_i32_lt_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32LtS)
}
pub fn translate_i32_lt_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32LtU)
}
pub fn translate_i32_gt_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32GtS)
}
pub fn translate_i32_gt_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32GtU)
}
pub fn translate_i32_le_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32LeS)
}
pub fn translate_i32_le_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32LeU)
}
pub fn translate_i32_ge_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32GeS)
}
pub fn translate_i32_ge_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I32, Instruction::I32GeU)
}
pub fn translate_i64_eqz(&mut self) -> Result<(), TranslationError> {
self.translate_unary_cmp(ValueType::I64, Instruction::I64Eqz)
}
pub fn translate_i64_eq(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64Eq)
}
pub fn translate_i64_ne(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64Ne)
}
pub fn translate_i64_lt_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64LtS)
}
pub fn translate_i64_lt_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64LtU)
}
pub fn translate_i64_gt_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64GtS)
}
pub fn translate_i64_gt_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64GtU)
}
pub fn translate_i64_le_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64LeS)
}
pub fn translate_i64_le_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64LeU)
}
pub fn translate_i64_ge_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64GeS)
}
pub fn translate_i64_ge_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::I64, Instruction::I64GeU)
}
pub fn translate_f32_eq(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F32, Instruction::F32Eq)
}
pub fn translate_f32_ne(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F32, Instruction::F32Ne)
}
pub fn translate_f32_lt(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F32, Instruction::F32Lt)
}
pub fn translate_f32_gt(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F32, Instruction::F32Gt)
}
pub fn translate_f32_le(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F32, Instruction::F32Le)
}
pub fn translate_f32_ge(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F32, Instruction::F32Ge)
}
pub fn translate_f64_eq(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F64, Instruction::F64Eq)
}
pub fn translate_f64_ne(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F64, Instruction::F64Ne)
}
pub fn translate_f64_lt(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F64, Instruction::F64Lt)
}
pub fn translate_f64_gt(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F64, Instruction::F64Gt)
}
pub fn translate_f64_le(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F64, Instruction::F64Le)
}
pub fn translate_f64_ge(&mut self) -> Result<(), TranslationError> {
self.translate_binary_cmp(ValueType::F64, Instruction::F64Ge)
}
pub 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(())
})
}
pub fn translate_i32_clz(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I32, Instruction::I32Clz)
}
pub fn translate_i32_ctz(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I32, Instruction::I32Ctz)
}
pub fn translate_i32_popcnt(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I32, Instruction::I32Popcnt)
}
pub 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(())
})
}
pub fn translate_i32_add(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Add)
}
pub fn translate_i32_sub(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Sub)
}
pub fn translate_i32_mul(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Mul)
}
pub fn translate_i32_div_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32DivS)
}
pub fn translate_i32_div_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32DivU)
}
pub fn translate_i32_rem_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32RemS)
}
pub fn translate_i32_rem_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32RemU)
}
pub fn translate_i32_and(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32And)
}
pub fn translate_i32_or(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Or)
}
pub fn translate_i32_xor(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Xor)
}
pub fn translate_i32_shl(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Shl)
}
pub fn translate_i32_shr_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32ShrS)
}
pub fn translate_i32_shr_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32ShrU)
}
pub fn translate_i32_rotl(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Rotl)
}
pub fn translate_i32_rotr(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I32, Instruction::I32Rotr)
}
pub fn translate_i64_clz(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I64, Instruction::I64Clz)
}
pub fn translate_i64_ctz(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I64, Instruction::I64Ctz)
}
pub fn translate_i64_popcnt(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I64, Instruction::I64Popcnt)
}
pub fn translate_i64_add(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Add)
}
pub fn translate_i64_sub(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Sub)
}
pub fn translate_i64_mul(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Mul)
}
pub fn translate_i64_div_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64DivS)
}
pub fn translate_i64_div_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64DivU)
}
pub fn translate_i64_rem_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64RemS)
}
pub fn translate_i64_rem_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64RemU)
}
pub fn translate_i64_and(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64And)
}
pub fn translate_i64_or(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Or)
}
pub fn translate_i64_xor(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Xor)
}
pub fn translate_i64_shl(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Shl)
}
pub fn translate_i64_shr_s(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64ShrS)
}
pub fn translate_i64_shr_u(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64ShrU)
}
pub fn translate_i64_rotl(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Rotl)
}
pub fn translate_i64_rotr(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::I64, Instruction::I64Rotr)
}
pub fn translate_f32_abs(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F32, Instruction::F32Abs)
}
pub fn translate_f32_neg(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F32, Instruction::F32Neg)
}
pub fn translate_f32_ceil(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F32, Instruction::F32Ceil)
}
pub fn translate_f32_floor(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F32, Instruction::F32Floor)
}
pub fn translate_f32_trunc(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F32, Instruction::F32Trunc)
}
pub fn translate_f32_nearest(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F32, Instruction::F32Nearest)
}
pub fn translate_f32_sqrt(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F32, Instruction::F32Sqrt)
}
pub fn translate_f32_add(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F32, Instruction::F32Add)
}
pub fn translate_f32_sub(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F32, Instruction::F32Sub)
}
pub fn translate_f32_mul(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F32, Instruction::F32Mul)
}
pub fn translate_f32_div(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F32, Instruction::F32Div)
}
pub fn translate_f32_min(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F32, Instruction::F32Min)
}
pub fn translate_f32_max(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F32, Instruction::F32Max)
}
pub fn translate_f32_copysign(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F32, Instruction::F32Copysign)
}
pub fn translate_f64_abs(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F64, Instruction::F64Abs)
}
pub fn translate_f64_neg(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F64, Instruction::F64Neg)
}
pub fn translate_f64_ceil(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F64, Instruction::F64Ceil)
}
pub fn translate_f64_floor(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F64, Instruction::F64Floor)
}
pub fn translate_f64_trunc(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F64, Instruction::F64Trunc)
}
pub fn translate_f64_nearest(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F64, Instruction::F64Nearest)
}
pub fn translate_f64_sqrt(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::F64, Instruction::F64Sqrt)
}
pub fn translate_f64_add(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F64, Instruction::F64Add)
}
pub fn translate_f64_sub(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F64, Instruction::F64Sub)
}
pub fn translate_f64_mul(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F64, Instruction::F64Mul)
}
pub fn translate_f64_div(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F64, Instruction::F64Div)
}
pub fn translate_f64_min(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F64, Instruction::F64Min)
}
pub fn translate_f64_max(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F64, Instruction::F64Max)
}
pub fn translate_f64_copysign(&mut self) -> Result<(), TranslationError> {
self.translate_binary_operation(ValueType::F64, Instruction::F64Copysign)
}
pub 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(())
})
}
pub fn translate_i32_wrap_i64(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I64, ValueType::I32, Instruction::I32WrapI64)
}
pub fn translate_i32_trunc_f32_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncF32S)
}
pub fn translate_i32_trunc_f32_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncF32U)
}
pub fn translate_i32_trunc_f64_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncF64S)
}
pub fn translate_i32_trunc_f64_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncF64U)
}
pub fn translate_i64_extend_i32_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I32, ValueType::I64, Instruction::I64ExtendI32S)
}
pub fn translate_i64_extend_i32_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I32, ValueType::I64, Instruction::I64ExtendI32U)
}
pub fn translate_i64_trunc_f32_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncF32S)
}
pub fn translate_i64_trunc_f32_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncF32U)
}
pub fn translate_i64_trunc_f64_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncF64S)
}
pub fn translate_i64_trunc_f64_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncF64U)
}
pub fn translate_f32_convert_i32_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I32, ValueType::F32, Instruction::F32ConvertI32S)
}
pub fn translate_f32_convert_i32_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I32, ValueType::F32, Instruction::F32ConvertI32U)
}
pub fn translate_f32_convert_i64_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I64, ValueType::F32, Instruction::F32ConvertI64S)
}
pub fn translate_f32_convert_i64_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I64, ValueType::F32, Instruction::F32ConvertI64U)
}
pub fn translate_f32_demote_f64(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::F32, Instruction::F32DemoteF64)
}
pub fn translate_f64_convert_i32_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I32, ValueType::F64, Instruction::F64ConvertI32S)
}
pub fn translate_f64_convert_i32_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I32, ValueType::F64, Instruction::F64ConvertI32U)
}
pub fn translate_f64_convert_i64_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I64, ValueType::F64, Instruction::F64ConvertI64S)
}
pub fn translate_f64_convert_i64_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::I64, ValueType::F64, Instruction::F64ConvertI64U)
}
pub fn translate_f64_promote_f32(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::F64, Instruction::F64PromoteF32)
}
pub fn translate_reinterpret(
&mut self,
_input_type: ValueType,
_output_type: ValueType,
) -> Result<(), TranslationError> {
Ok(())
}
pub fn translate_i32_reinterpret_f32(&mut self) -> Result<(), TranslationError> {
self.translate_reinterpret(ValueType::F32, ValueType::I32)
}
pub fn translate_i64_reinterpret_f64(&mut self) -> Result<(), TranslationError> {
self.translate_reinterpret(ValueType::F64, ValueType::I64)
}
pub fn translate_f32_reinterpret_i32(&mut self) -> Result<(), TranslationError> {
self.translate_reinterpret(ValueType::I32, ValueType::F32)
}
pub fn translate_f64_reinterpret_i64(&mut self) -> Result<(), TranslationError> {
self.translate_reinterpret(ValueType::I64, ValueType::F64)
}
pub fn translate_i32_extend8_s(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I32, Instruction::I32Extend8S)
}
pub fn translate_i32_extend16_s(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I32, Instruction::I32Extend16S)
}
pub fn translate_i64_extend8_s(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I64, Instruction::I64Extend8S)
}
pub fn translate_i64_extend16_s(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I64, Instruction::I64Extend16S)
}
pub fn translate_i64_extend32_s(&mut self) -> Result<(), TranslationError> {
self.translate_unary_operation(ValueType::I64, Instruction::I64Extend32S)
}
pub fn translate_i32_trunc_sat_f32_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32S)
}
pub fn translate_i32_trunc_sat_f32_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32U)
}
pub fn translate_i32_trunc_sat_f64_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64S)
}
pub fn translate_i32_trunc_sat_f64_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64U)
}
pub fn translate_i64_trunc_sat_f32_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncSatF32S)
}
pub fn translate_i64_trunc_sat_f32_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncSatF32U)
}
pub fn translate_i64_trunc_sat_f64_s(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncSatF64S)
}
pub fn translate_i64_trunc_sat_f64_u(&mut self) -> Result<(), TranslationError> {
self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncSatF64U)
}
}