use crate::code_translator::{bitcast_arguments, translate_operator, wasm_param_types};
use crate::environ::{FuncEnvironment, ReturnMode, WasmResult};
use crate::state::{FuncTranslationState, ModuleTranslationState};
use crate::translation_utils::get_vmctx_value_label;
use crate::wasm_unsupported;
use core::convert::TryInto;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
use cranelift_codegen::timing;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
use log::info;
use wasmparser::{self, BinaryReader};
pub struct FuncTranslator {
func_ctx: FunctionBuilderContext,
state: FuncTranslationState,
}
impl FuncTranslator {
pub fn new() -> Self {
Self {
func_ctx: FunctionBuilderContext::new(),
state: FuncTranslationState::new(),
}
}
pub fn translate<FE: FuncEnvironment + ?Sized>(
&mut self,
module_translation_state: &ModuleTranslationState,
code: &[u8],
code_offset: usize,
func: &mut ir::Function,
environ: &mut FE,
) -> WasmResult<()> {
self.translate_from_reader(
module_translation_state,
BinaryReader::new_with_offset(code, code_offset),
func,
environ,
)
}
pub fn translate_from_reader<FE: FuncEnvironment + ?Sized>(
&mut self,
module_translation_state: &ModuleTranslationState,
mut reader: BinaryReader,
func: &mut ir::Function,
environ: &mut FE,
) -> WasmResult<()> {
let _tt = timing::wasm_translate_function();
info!(
"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)?;
parse_function_body(
module_translation_state,
reader,
&mut builder,
&mut self.state,
environ,
)?;
builder.finalize();
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;
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,
) -> WasmResult<()> {
let mut next_local = num_params;
let local_count = reader.read_local_count()?;
let mut locals_total = 0;
for _ in 0..local_count {
builder.set_srcloc(cur_srcloc(reader));
let (count, ty) = reader.read_local_decl(&mut locals_total)?;
declare_locals(builder, count, ty, &mut next_local, environ)?;
}
Ok(())
}
fn declare_locals<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder,
count: u32,
wasm_type: wasmparser::Type,
next_local: &mut usize,
environ: &mut FE,
) -> WasmResult<()> {
use wasmparser::Type::*;
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)
}
ExternRef | FuncRef => {
environ.translate_ref_null(builder.cursor(), wasm_type.try_into()?)?
}
ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)),
};
let ty = builder.func.dfg.value_type(zeroval);
for _ in 0..count {
let local = Variable::new(*next_local);
builder.declare_var(local, ty);
builder.def_var(local, zeroval);
builder.set_val_label(zeroval, ValueLabel::new(*next_local));
*next_local += 1;
}
Ok(())
}
fn parse_function_body<FE: FuncEnvironment + ?Sized>(
module_translation_state: &ModuleTranslationState,
mut reader: BinaryReader,
builder: &mut FunctionBuilder,
state: &mut FuncTranslationState,
environ: &mut FE,
) -> 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()?;
environ.before_translate_operator(&op, builder, state)?;
translate_operator(module_translation_state, &op, builder, state, environ)?;
environ.after_translate_operator(&op, builder, state)?;
}
if state.reachable {
debug_assert!(builder.is_pristine());
if !builder.is_unreachable() {
match environ.return_mode() {
ReturnMode::NormalReturns => {
let return_types = wasm_param_types(&builder.func.signature.returns, |i| {
environ.is_wasm_return(&builder.func.signature, i)
});
bitcast_arguments(&mut state.stack, &return_types, builder);
builder.ins().return_(&state.stack)
}
ReturnMode::FallthroughReturn => builder.ins().fallthrough_return(&state.stack),
};
}
}
state.stack.clear();
debug_assert!(reader.eof());
Ok(())
}
fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
ir::SourceLoc::new(reader.original_position() as u32)
}
#[cfg(test)]
mod tests {
use super::{FuncTranslator, ReturnMode};
use crate::environ::DummyEnvironment;
use crate::ModuleTranslationState;
use cranelift_codegen::ir::types::I32;
use cranelift_codegen::{ir, isa, settings, Context};
use log::debug;
use target_lexicon::PointerWidth;
#[test]
fn small1() {
const BODY: [u8; 7] = [
0x00,
0x20, 0x00,
0x41, 0x01,
0x6a,
0x0b,
];
let mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder());
let runtime = DummyEnvironment::new(
isa::TargetFrontendConfig {
default_call_conv: isa::CallConv::Fast,
pointer_width: PointerWidth::U64,
},
ReturnMode::NormalReturns,
false,
);
let module_translation_state = ModuleTranslationState::new();
let mut ctx = Context::new();
ctx.func.name = ir::ExternalName::testcase("small1");
ctx.func.signature.params.push(ir::AbiParam::new(I32));
ctx.func.signature.returns.push(ir::AbiParam::new(I32));
trans
.translate(
&module_translation_state,
&BODY,
0,
&mut ctx.func,
&mut runtime.func_env(),
)
.unwrap();
debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap();
}
#[test]
fn small2() {
const BODY: [u8; 8] = [
0x00,
0x20, 0x00,
0x41, 0x01,
0x6a,
0x0f,
0x0b,
];
let mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder());
let runtime = DummyEnvironment::new(
isa::TargetFrontendConfig {
default_call_conv: isa::CallConv::Fast,
pointer_width: PointerWidth::U64,
},
ReturnMode::NormalReturns,
false,
);
let module_translation_state = ModuleTranslationState::new();
let mut ctx = Context::new();
ctx.func.name = ir::ExternalName::testcase("small2");
ctx.func.signature.params.push(ir::AbiParam::new(I32));
ctx.func.signature.returns.push(ir::AbiParam::new(I32));
trans
.translate(
&module_translation_state,
&BODY,
0,
&mut ctx.func,
&mut runtime.func_env(),
)
.unwrap();
debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap();
}
#[test]
fn infloop() {
const BODY: [u8; 16] = [
0x01,
0x01, 0x7f,
0x03, 0x7f,
0x20, 0x00,
0x41, 0x01,
0x6a,
0x21, 0x00,
0x0c, 0x00,
0x0b,
0x0b,
];
let mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder());
let runtime = DummyEnvironment::new(
isa::TargetFrontendConfig {
default_call_conv: isa::CallConv::Fast,
pointer_width: PointerWidth::U64,
},
ReturnMode::NormalReturns,
false,
);
let module_translation_state = ModuleTranslationState::new();
let mut ctx = Context::new();
ctx.func.name = ir::ExternalName::testcase("infloop");
ctx.func.signature.returns.push(ir::AbiParam::new(I32));
trans
.translate(
&module_translation_state,
&BODY,
0,
&mut ctx.func,
&mut runtime.func_env(),
)
.unwrap();
debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap();
}
}