zingen/codegen/
function.rsuse crate::{
backtrace::Backtrace,
control::ControlStack,
jump::JumpTable,
local::{LocalSlot, LocalSlotType, Locals},
masm::MacroAssembler,
validator::ValidateThenVisit,
wasm::Env,
Buffer, Error, Result,
};
use opcodes::ShangHai as OpCode;
use wasmparser::{FuncType, FuncValidator, LocalsReader, OperatorsReader, ValidatorResources};
use zabi::Abi;
pub struct Function {
pub abi: Option<Abi>,
pub backtrace: Backtrace,
pub control: ControlStack,
pub env: Env,
pub locals: Locals,
pub masm: MacroAssembler,
pub table: JumpTable,
pub ty: FuncType,
pub is_main: bool,
}
impl Function {
pub fn new(env: Env, ty: FuncType, abi: Option<Abi>, is_main: bool) -> Result<Self> {
let is_external = abi.is_some();
let mut codegen = Self {
abi,
backtrace: Backtrace::default(),
control: ControlStack::default(),
env,
ty,
locals: Default::default(),
masm: Default::default(),
table: Default::default(),
is_main,
};
if is_main {
return Ok(codegen);
}
if is_external {
tracing::debug!("<External function>");
codegen.masm._jumpdest()?;
} else {
tracing::debug!("<Internal function>");
codegen.masm.increment_sp(1)?;
codegen.masm._jumpdest()?;
}
Ok(codegen)
}
pub fn emit_locals(
&mut self,
locals: &mut LocalsReader<'_>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<()> {
let mut sp = if self.is_main { 0 } else { 1 };
for param in self.ty.params() {
self.locals
.push(LocalSlot::new(*param, LocalSlotType::Parameter, sp));
sp += 1;
}
while let Ok((count, val)) = locals.read() {
for _ in 0..count {
self.locals
.push(LocalSlot::new(val, LocalSlotType::Variable, sp));
sp += 1;
}
let validation_offset = locals.original_position();
validator.define_locals(validation_offset, count, val)?;
}
tracing::trace!("{:?}", self.locals);
Ok(())
}
pub fn emit_operators(
&mut self,
ops: &mut OperatorsReader<'_>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<()> {
while !ops.eof() {
let offset = ops.original_position();
let mut validate_then_visit = ValidateThenVisit(validator.visitor(offset), self);
ops.visit_operator(&mut validate_then_visit)???;
}
if (self.abi.is_some() || self.is_main)
&& self.masm.buffer().last() != Some(&OpCode::RETURN.into())
{
self._end()?;
}
Ok(())
}
pub fn finish(self, jump_table: &mut JumpTable, pc: u16) -> Result<Buffer> {
let sp = self.masm.sp();
if !self.is_main && self.abi.is_none() && self.masm.sp() != self.ty.results().len() as u8 {
return Err(Error::StackNotBalanced(sp));
}
jump_table.merge(self.table, pc)?;
Ok(self.masm.buffer().into())
}
}