Files
wasmtime/winch/codegen/src/codegen.rs
Alex Crichton 3b9668558f winch: Prepare for an update to the wasm-tools crates (#5238)
This commit prepares the `winch` crate for updating `wasm-tools`,
notably changing a bit about how the visitation of operators works. This
moves the function body and wasm validator out of the `CodeGen`
structure and into parameters threaded into the emission of the actual
function.

Additionally the `VisitOperator` implementation was updated to remove
the explicit calls to the validator, favoring instead a macro-generated
solution to guarantee that all validation happens before any translation
proceeds. This means that the `VisitOperator for CodeGen` impl is now
infallible and the various methods have been inlined into the trait
methods as well as removing the `Result<_>`.

Finally this commit updates translation to call `validator.finish(..)`
which is required to perform the final validation steps of the function
body.
2022-11-10 14:01:42 -06: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,
);
let mut visitor = ValidateThenVisit(&mut validator, self);
while !body.eof() {
body.visit_operator(&mut visitor)??;
}
validator.finish(body.original_position())?;
return Ok(());
struct ValidateThenVisit<'a, T, U>(&'a mut T, &'a mut U);
macro_rules! validate_then_visit {
($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
$(
fn $visit(&mut self, offset: usize $($(,$arg: $argty)*)?) -> Self::Output {
self.0.$visit(offset, $($($arg.clone()),*)?)?;
Ok(self.1.$visit(offset, $($($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);
}
}