use super::code_translator::translate_operator;
use super::func_state::FuncTranslationState;
use super::translation_utils::get_vmctx_value_label;
use crate::func_environ::FuncEnvironment;
use crate::translator::EXN_REF_TYPE;
use core::convert::TryFrom;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
use cranelift_codegen::timing;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use wasmer_compiler::wasmparser::RefType;
use wasmer_compiler::{FunctionBinaryReader, ModuleTranslationState, wptype_to_type};
use wasmer_compiler::{wasm_unsupported, wasmparser};
use wasmer_types::{LocalFunctionIndex, WasmResult};
pub struct FuncTranslator {
func_ctx: FunctionBuilderContext,
state: FuncTranslationState,
allow_unaligned_memory_accesses: bool,
}
impl wasmer_compiler::FuncTranslator for FuncTranslator {}
impl FuncTranslator {
pub fn new(allow_unaligned_memory_accesses: bool) -> Self {
Self {
func_ctx: FunctionBuilderContext::new(),
state: FuncTranslationState::new(),
allow_unaligned_memory_accesses,
}
}
pub fn translate(
&mut self,
module_translation_state: &ModuleTranslationState,
reader: &mut dyn FunctionBinaryReader,
func: &mut ir::Function,
environ: &mut FuncEnvironment<'_>,
local_function_index: LocalFunctionIndex,
) -> WasmResult<()> {
environ.push_params_on_stack(local_function_index);
self.translate_from_reader(module_translation_state, reader, func, environ)
}
pub fn translate_from_reader(
&mut self,
module_translation_state: &ModuleTranslationState,
reader: &mut dyn FunctionBinaryReader,
func: &mut ir::Function,
environ: &mut FuncEnvironment<'_>,
) -> WasmResult<()> {
let _tt = timing::wasm_translate_function();
tracing::trace!(
"translate({} bytes, {}{})",
reader.bytes_remaining(),
func.name,
func.signature
);
debug_assert_eq!(func.dfg.num_blocks(), 0, "Function must be empty");
debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
builder.set_srcloc(cur_srcloc(reader));
let entry_block = builder.create_block();
builder.append_block_params_for_function_params(entry_block);
builder.switch_to_block(entry_block); builder.seal_block(entry_block);
builder.ensure_inserted_block();
let num_params = declare_wasm_parameters(&mut builder, entry_block, environ);
let exit_block = builder.create_block();
for &ty in environ.return_types() {
builder.append_block_param(
exit_block,
crate::translator::type_to_irtype(ty, environ.target_config())?,
);
}
self.state.initialize(
&builder.func.signature,
exit_block,
environ.return_types().len(),
);
parse_local_decls(reader, &mut builder, num_params, environ)?;
parse_function_body(
module_translation_state,
reader,
&mut builder,
&mut self.state,
environ,
self.allow_unaligned_memory_accesses,
)?;
builder.finalize(environ.target_config());
Ok(())
}
}
fn declare_wasm_parameters(
builder: &mut FunctionBuilder,
entry_block: Block,
environ: &FuncEnvironment<'_>,
) -> usize {
let sig_len = builder.func.signature.params.len();
let mut next_local = 0;
for i in 0..sig_len {
let param_type = builder.func.signature.params[i];
if environ.is_wasm_parameter(&builder.func.signature, i) {
let local = builder.declare_var(param_type.value_type);
let local_index = local.index();
debug_assert_eq!(local_index, next_local);
debug_assert!(u32::try_from(local_index).is_ok());
next_local += 1;
let param_value = builder.block_params(entry_block)[i];
builder.def_var(local, param_value);
}
if param_type.purpose == ir::ArgumentPurpose::VMContext {
let param_value = builder.block_params(entry_block)[i];
builder.set_val_label(param_value, get_vmctx_value_label());
}
}
next_local
}
fn parse_local_decls(
reader: &mut dyn FunctionBinaryReader,
builder: &mut FunctionBuilder,
num_params: usize,
environ: &mut FuncEnvironment<'_>,
) -> WasmResult<()> {
let mut next_local = num_params;
let local_count = reader.read_local_count()?;
for _ in 0..local_count {
builder.set_srcloc(cur_srcloc(reader));
let (count, ty) = reader.read_local_decl()?;
declare_locals(builder, count, ty, &mut next_local, environ)?;
}
Ok(())
}
fn declare_locals(
builder: &mut FunctionBuilder,
count: u32,
wasm_type: wasmparser::ValType,
next_local: &mut usize,
environ: &mut FuncEnvironment<'_>,
) -> WasmResult<()> {
use wasmparser::ValType::*;
let zeroval = match wasm_type {
I32 => builder.ins().iconst(ir::types::I32, 0),
I64 => builder.ins().iconst(ir::types::I64, 0),
F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
V128 => {
let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
builder.ins().vconst(ir::types::I8X16, constant_handle)
}
Ref(ty) => {
if ty.is_func_ref() || ty.is_extern_ref() {
builder.ins().iconst(environ.reference_type(), 0)
} else if ty == RefType::EXNREF || ty == RefType::EXN {
builder.ins().iconst(EXN_REF_TYPE, 0)
} else {
return Err(wasm_unsupported!("unsupported reference type: {:?}", ty));
}
}
};
let wasmer_ty = wptype_to_type(wasm_type).unwrap();
let ty = builder.func.dfg.value_type(zeroval);
for _ in 0..count {
let local = builder.declare_var(ty);
let local_index = local.index();
debug_assert_eq!(local_index, *next_local);
debug_assert!(u32::try_from(local_index).is_ok());
builder.def_var(local, zeroval);
builder.set_val_label(zeroval, ValueLabel::new(*next_local));
environ.push_local_decl_on_stack(wasmer_ty);
*next_local += 1;
}
Ok(())
}
fn parse_function_body(
module_translation_state: &ModuleTranslationState,
reader: &mut dyn FunctionBinaryReader,
builder: &mut FunctionBuilder,
state: &mut FuncTranslationState,
environ: &mut FuncEnvironment<'_>,
allow_unaligned_memory_accesses: bool,
) -> WasmResult<()> {
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
while !state.control_stack.is_empty() {
builder.set_srcloc(cur_srcloc(reader));
let op = reader.read_operator()?;
translate_operator(
module_translation_state,
&op,
builder,
state,
environ,
allow_unaligned_memory_accesses,
)?;
}
if state.reachable {
if !builder.is_unreachable() {
environ.emit_wasm_return(builder, &state.stack);
}
}
state.stack.clear();
debug_assert!(reader.eof());
Ok(())
}
fn cur_srcloc(reader: &dyn FunctionBinaryReader) -> ir::SourceLoc {
ir::SourceLoc::new(reader.original_position() as u32)
}