Files
wasmtime/winch/codegen/src/codegen.rs
Alex Crichton 0548952319 Update wasm-tools crates (#5248)
No major updates, just keeping up-to-date.
2022-11-10 21:23:20 +00:00

176 lines
5.1 KiB
Rust

use crate::{
abi::{ABISig, ABI},
frame::Frame,
masm::{MacroAssembler, OperandSize},
regalloc::RegAlloc,
stack::Stack,
};
use anyhow::Result;
use wasmparser::{BinaryReader, FuncValidator, ValType, ValidatorResources, VisitOperator};
/// The code generation context.
pub(crate) struct CodeGenContext<'a, M>
where
M: MacroAssembler,
{
pub masm: M,
pub stack: Stack,
pub frame: &'a Frame,
}
impl<'a, M> CodeGenContext<'a, M>
where
M: MacroAssembler,
{
pub fn new(masm: M, stack: Stack, frame: &'a Frame) -> Self {
Self { masm, stack, frame }
}
}
/// The code generation abstraction.
pub(crate) struct CodeGen<'a, M>
where
M: MacroAssembler,
{
/// The word size in bytes, extracted from the current ABI.
word_size: u32,
/// The ABI-specific representation of the function signature, excluding results.
sig: ABISig,
/// The code generation context.
pub context: CodeGenContext<'a, M>,
/// The register allocator.
pub regalloc: RegAlloc,
}
impl<'a, M> CodeGen<'a, M>
where
M: MacroAssembler,
{
pub fn new<A: ABI>(context: CodeGenContext<'a, M>, sig: ABISig, regalloc: RegAlloc) -> Self {
Self {
word_size: <A as ABI>::word_bytes(),
sig,
context,
regalloc,
}
}
/// Emit the function body to machine code.
pub fn emit(
&mut self,
body: &mut BinaryReader<'a>,
validator: FuncValidator<ValidatorResources>,
) -> Result<Vec<String>> {
self.emit_start()
.and(self.emit_body(body, validator))
.and(self.emit_end())?;
let buf = self.context.masm.finalize();
let code = Vec::from(buf);
Ok(code)
}
// TODO stack checks
fn emit_start(&mut self) -> Result<()> {
self.context.masm.prologue();
self.context
.masm
.reserve_stack(self.context.frame.locals_size);
Ok(())
}
fn emit_body(
&mut self,
body: &mut BinaryReader<'a>,
mut validator: FuncValidator<ValidatorResources>,
) -> Result<()> {
self.spill_register_arguments();
let defined_locals_range = &self.context.frame.defined_locals_range;
self.context.masm.zero_mem_range(
defined_locals_range.as_range(),
self.word_size,
&mut self.regalloc,
);
while !body.eof() {
let offset = body.original_position();
body.visit_operator(&mut ValidateThenVisit(validator.visitor(offset), self))??;
}
validator.finish(body.original_position())?;
return Ok(());
struct ValidateThenVisit<'a, T, U>(T, &'a mut U);
macro_rules! validate_then_visit {
($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
$(
fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
self.0.$visit($($($arg.clone()),*)?)?;
Ok(self.1.$visit($($($arg),*)?))
}
)*
};
}
impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U>
where
T: VisitOperator<'a, Output = wasmparser::Result<()>>,
U: VisitOperator<'a>,
{
type Output = Result<U::Output>;
wasmparser::for_each_operator!(validate_then_visit);
}
}
// Emit the usual function end instruction sequence.
pub fn emit_end(&mut self) -> Result<()> {
self.handle_abi_result();
self.context.masm.epilogue(self.context.frame.locals_size);
Ok(())
}
fn spill_register_arguments(&mut self) {
// TODO
// Revisit this once the implicit VMContext argument is introduced;
// when that happens the mapping between local slots and abi args
// is not going to be symmetric.
self.sig
.params
.iter()
.enumerate()
.filter(|(_, a)| a.is_reg())
.for_each(|(index, arg)| {
let ty = arg.ty();
let local = self
.context
.frame
.get_local(index as u32)
.expect("valid local slot at location");
let addr = self.context.masm.local_address(local);
let src = arg
.get_reg()
.expect("arg should be associated to a register");
match &ty {
ValType::I32 => self.context.masm.store(src.into(), addr, OperandSize::S32),
ValType::I64 => self.context.masm.store(src.into(), addr, OperandSize::S64),
_ => panic!("Unsupported type {:?}", ty),
}
});
}
pub fn handle_abi_result(&mut self) {
if self.sig.result.is_void() {
return;
}
let named_reg = self.sig.result.result_reg();
let reg = self
.regalloc
.pop_to_named_reg(&mut self.context, named_reg, OperandSize::S64);
self.regalloc.free_gpr(reg);
}
}