use crate::{
Result,
abi::{ABI, ABIOperand, ABISig, LocalSlot, align_to},
codegen::{CodeGenPhase, Emission, Prologue},
masm::MacroAssembler,
};
use smallvec::SmallVec;
use std::marker::PhantomData;
use std::ops::Range;
use wasmparser::{BinaryReader, FuncValidator, ValidatorResources};
use wasmtime_environ::{TypeConvert, WasmValType};
pub(crate) type WasmLocals = SmallVec<[LocalSlot; 16]>;
pub(crate) type SpecialLocals = [LocalSlot; 2];
pub(crate) struct DefinedLocalsRange(Range<u32>);
impl DefinedLocalsRange {
pub fn as_range(&self) -> &Range<u32> {
&self.0
}
}
#[derive(Default)]
pub(crate) struct DefinedLocals {
pub defined_locals: WasmLocals,
pub stack_size: u32,
}
impl DefinedLocals {
pub fn new<A: ABI>(
types: &impl TypeConvert,
reader: &mut BinaryReader<'_>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<Self> {
let mut next_stack: u32 = 0;
let local_count = reader.read_var_u32()?;
let mut slots: WasmLocals = Default::default();
for _ in 0..local_count {
let position = reader.original_position();
let count = reader.read_var_u32()?;
let ty = reader.read()?;
validator.define_locals(position, count, ty)?;
let ty = types.convert_valtype(ty)?;
for _ in 0..count {
let ty_size = <A as ABI>::sizeof(&ty);
next_stack = align_to(next_stack, ty_size as u32) + (ty_size as u32);
slots.push(LocalSlot::new(ty, next_stack));
}
}
Ok(Self {
defined_locals: slots,
stack_size: next_stack,
})
}
}
pub(crate) struct Frame<P: CodeGenPhase> {
pub locals_size: u32,
pub defined_locals_range: DefinedLocalsRange,
wasm_locals: WasmLocals,
special_locals: SpecialLocals,
pub results_base_slot: Option<LocalSlot>,
marker: PhantomData<P>,
}
impl Frame<Prologue> {
pub fn new<A: ABI>(sig: &ABISig, defined_locals: &DefinedLocals) -> Result<Frame<Prologue>> {
let (special_locals, mut wasm_locals, defined_locals_start) =
Self::compute_arg_slots::<A>(sig)?;
wasm_locals.extend(
defined_locals
.defined_locals
.iter()
.map(|l| LocalSlot::new(l.ty, l.offset + defined_locals_start)),
);
let stack_align = <A as ABI>::stack_align();
let defined_locals_end = align_to(
defined_locals_start + defined_locals.stack_size,
stack_align as u32,
);
let (results_base_slot, locals_size) = if sig.params.has_retptr() {
match sig.params.unwrap_results_area_operand() {
ABIOperand::Stack { ty, offset, .. } => (
Some(LocalSlot::stack_arg(
*ty,
*offset + (<A as ABI>::arg_base_offset() as u32),
)),
defined_locals_end,
),
ABIOperand::Reg { ty, size, .. } => {
let offs = align_to(defined_locals_end, *size) + *size;
(
Some(LocalSlot::new(*ty, offs)),
align_to(offs, <A as ABI>::stack_align().into()),
)
}
}
} else {
(None, defined_locals_end)
};
Ok(Self {
wasm_locals,
special_locals,
locals_size,
defined_locals_range: DefinedLocalsRange(
defined_locals_start..(defined_locals_start + defined_locals.stack_size),
),
results_base_slot,
marker: PhantomData,
})
}
pub fn locals(&self) -> impl Iterator<Item = &LocalSlot> {
self.special_locals.iter().chain(self.wasm_locals.iter())
}
pub fn for_emission(self) -> Frame<Emission> {
Frame {
wasm_locals: self.wasm_locals,
special_locals: self.special_locals,
locals_size: self.locals_size,
defined_locals_range: self.defined_locals_range,
results_base_slot: self.results_base_slot,
marker: PhantomData,
}
}
fn compute_arg_slots<A: ABI>(sig: &ABISig) -> Result<(SpecialLocals, WasmLocals, u32)> {
let arg_base_offset = <A as ABI>::arg_base_offset().into();
let mut next_stack = 0u32;
let mut params_iter = sig.params_without_retptr().into_iter();
let callee_vmctx = params_iter
.next()
.map(|arg| Self::abi_arg_slot(&arg, &mut next_stack, arg_base_offset))
.expect("Slot for VMContext");
let caller_vmctx = params_iter
.next()
.map(|arg| Self::abi_arg_slot(&arg, &mut next_stack, arg_base_offset))
.expect("Slot for VMContext");
let slots: WasmLocals = params_iter
.map(|arg| Self::abi_arg_slot(&arg, &mut next_stack, arg_base_offset))
.collect();
Ok(([callee_vmctx, caller_vmctx], slots, next_stack))
}
fn abi_arg_slot(arg: &ABIOperand, next_stack: &mut u32, arg_base_offset: u32) -> LocalSlot {
match arg {
ABIOperand::Reg { ty, size, .. } => {
*next_stack = align_to(*next_stack, *size) + *size;
LocalSlot::new(*ty, *next_stack)
}
ABIOperand::Stack { ty, offset, .. } => {
LocalSlot::stack_arg(*ty, offset + arg_base_offset)
}
}
}
}
impl Frame<Emission> {
pub fn get_wasm_local(&self, index: u32) -> &LocalSlot {
self.wasm_locals
.get(index as usize)
.unwrap_or_else(|| panic!(" Expected WebAssembly local at slot: {index}"))
}
pub fn get_special_local(&self, index: usize) -> &LocalSlot {
self.special_locals
.get(index)
.unwrap_or_else(|| panic!(" Expected special local at slot: {index}"))
}
pub fn vmctx_slot(&self) -> &LocalSlot {
self.get_special_local(0)
}
pub fn get_local_address<M: MacroAssembler>(
&self,
index: u32,
masm: &mut M,
) -> Result<(WasmValType, M::Address)> {
let slot = self.get_wasm_local(index);
Ok((slot.ty, masm.local_address(&slot)?))
}
}