use crate::code_translator::{bitcast_wasm_returns, translate_operator};
use crate::environ::FuncEnvironment;
use crate::state::FuncTranslationState;
use crate::translation_utils::get_vmctx_value_label;
use crate::WasmResult;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
use cranelift_codegen::timing;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
use wasmparser::{BinaryReader, FuncValidator, FunctionBody, WasmModuleResources};
pub struct FuncTranslator {
func_ctx: FunctionBuilderContext,
state: FuncTranslationState,
}
impl FuncTranslator {
pub fn new() -> Self {
Self {
func_ctx: FunctionBuilderContext::new(),
state: FuncTranslationState::new(),
}
}
pub fn context(&mut self) -> &mut FunctionBuilderContext {
&mut self.func_ctx
}
pub fn translate_body<FE: FuncEnvironment + ?Sized>(
&mut self,
validator: &mut FuncValidator<impl WasmModuleResources>,
body: FunctionBody<'_>,
func: &mut ir::Function,
environ: &mut FE,
) -> WasmResult<()> {
let _tt = timing::wasm_translate_function();
let mut reader = body.get_binary_reader();
log::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();
builder.append_block_params_for_function_returns(exit_block);
self.state.initialize(&builder.func.signature, exit_block);
parse_local_decls(&mut reader, &mut builder, num_params, environ, validator)?;
parse_function_body(validator, reader, &mut builder, &mut self.state, environ)?;
builder.finalize();
log::trace!("translated Wasm to CLIF:\n{}", func.display());
Ok(())
}
}
fn declare_wasm_parameters<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder,
entry_block: Block,
environ: &FE,
) -> 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 = Variable::new(next_local);
builder.declare_var(local, param_type.value_type);
next_local += 1;
if environ.param_needs_stack_map(&builder.func.signature, i) {
builder.declare_var_needs_stack_map(local);
}
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<FE: FuncEnvironment + ?Sized>(
reader: &mut BinaryReader,
builder: &mut FunctionBuilder,
num_params: usize,
environ: &mut FE,
validator: &mut FuncValidator<impl WasmModuleResources>,
) -> WasmResult<()> {
let mut next_local = num_params;
let local_count = reader.read_var_u32()?;
for _ in 0..local_count {
builder.set_srcloc(cur_srcloc(reader));
let pos = reader.original_position();
let count = reader.read_var_u32()?;
let ty = reader.read()?;
validator.define_locals(pos, count, ty)?;
declare_locals(builder, count, ty, &mut next_local, environ)?;
}
environ.after_locals(next_local);
Ok(())
}
fn declare_locals<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder,
count: u32,
wasm_type: wasmparser::ValType,
next_local: &mut usize,
environ: &mut FE,
) -> WasmResult<()> {
use wasmparser::ValType::*;
let (ty, init, needs_stack_map) = match wasm_type {
I32 => (
ir::types::I32,
Some(builder.ins().iconst(ir::types::I32, 0)),
false,
),
I64 => (
ir::types::I64,
Some(builder.ins().iconst(ir::types::I64, 0)),
false,
),
F32 => (
ir::types::F32,
Some(builder.ins().f32const(ir::immediates::Ieee32::with_bits(0))),
false,
),
F64 => (
ir::types::F64,
Some(builder.ins().f64const(ir::immediates::Ieee64::with_bits(0))),
false,
),
V128 => {
let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
(
ir::types::I8X16,
Some(builder.ins().vconst(ir::types::I8X16, constant_handle)),
false,
)
}
Ref(rt) => {
let hty = environ.convert_heap_type(rt.heap_type());
let (ty, needs_stack_map) = environ.reference_type(hty);
let init = if rt.is_nullable() {
Some(environ.translate_ref_null(builder.cursor(), hty)?)
} else {
None
};
(ty, init, needs_stack_map)
}
};
for _ in 0..count {
let local = Variable::new(*next_local);
builder.declare_var(local, ty);
if needs_stack_map {
builder.declare_var_needs_stack_map(local);
}
if let Some(init) = init {
builder.def_var(local, init);
builder.set_val_label(init, ValueLabel::new(*next_local));
}
*next_local += 1;
}
Ok(())
}
fn parse_function_body<FE: FuncEnvironment + ?Sized>(
validator: &mut FuncValidator<impl WasmModuleResources>,
mut reader: BinaryReader,
builder: &mut FunctionBuilder,
state: &mut FuncTranslationState,
environ: &mut FE,
) -> WasmResult<()> {
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
environ.before_translate_function(builder, state)?;
while !reader.eof() {
let pos = reader.original_position();
builder.set_srcloc(cur_srcloc(&reader));
let op = reader.read_operator()?;
validator.op(pos, &op)?;
environ.before_translate_operator(&op, builder, state)?;
translate_operator(validator, &op, builder, state, environ)?;
environ.after_translate_operator(&op, builder, state)?;
}
environ.after_translate_function(builder, state)?;
let pos = reader.original_position();
validator.finish(pos)?;
if state.reachable {
if !builder.is_unreachable() {
environ.handle_before_return(&state.stack, builder);
bitcast_wasm_returns(environ, &mut state.stack, builder);
builder.ins().return_(&state.stack);
}
}
state.stack.clear();
Ok(())
}
fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
ir::SourceLoc::new(reader.original_position() as u32)
}