//!1//! The Default ABI2//!3//! Winch uses a default ABI, for all internal functions. This allows4//! us to push the complexity of system ABI compliance to the trampolines. The5//! default ABI treats all allocatable registers as caller saved, which means6//! that (i) all register values in the Wasm value stack (which are normally7//! referred to as "live"), must be saved onto the machine stack (ii) function8//! prologues and epilogues don't store/restore other registers more than the9//! non-allocatable ones (e.g. rsp/rbp in x86_64).10//!11//! The calling convention in the default ABI, uses registers to a certain fixed12//! count for arguments and return values, and then the stack is used for all13//! additional arguments and return values. Aside from the parameters declared14//! in each WebAssembly function, Winch's ABI declares two extra parameters, to15//! hold the callee and caller `VMContext` pointers. A well-known `LocalSlot` is16//! reserved for the callee VMContext pointer and also a particular pinned17//! register is used to hold the value of the callee `VMContext`, which is18//! available throughout the lifetime of the function.19//!20//!21//! Generally the stack layout looks like:22//! +-------------------------------+23//! | |24//! | |25//! | Stack Args |26//! | |27//! | |28//! +-------------------------------+----> SP @ function entry29//! | Ret addr |30//! +-------------------------------+31//! | SP |32//! +-------------------------------+----> SP @ Function prologue33//! | |34//! +-------------------------------+----> VMContext slot35//! | |36//! | |37//! | Stack slots |38//! | + dynamic space |39//! | |40//! | |41//! | |42//! +-------------------------------+----> SP @ callsite (after)43//! | alignment |44//! | + arguments |45//! | | ----> Space allocated for calls46//! | |47use crate::codegen::ptr_type_from_ptr_size;48use crate::isa::{CallingConvention, reg::Reg};49use crate::masm::SPOffset;50use anyhow::Result;51use smallvec::SmallVec;52use std::collections::HashSet;53use std::ops::{Add, BitAnd, Not, Sub};54use wasmtime_environ::{WasmFuncType, WasmValType};5556pub(crate) mod local;57pub(crate) use local::*;5859/// Internal classification for params or returns,60/// mainly used for params and return register assignment.61#[derive(Clone, Copy, Eq, PartialEq, Debug)]62pub(super) enum ParamsOrReturns {63Params,64Returns,65}6667/// Macro to get the pinned register holding the [VMContext].68macro_rules! vmctx {69($m:ident) => {70<$m::ABI as $crate::abi::ABI>::vmctx_reg()71};72}7374pub(crate) use vmctx;7576/// Constructs an [ABISig] using Winch's ABI.77pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> Result<ABISig> {78// 6 is used semi-arbitrarily here, we can modify as we see fit.79let mut params: SmallVec<[WasmValType; 6]> = SmallVec::new();80params.extend_from_slice(&vmctx_types::<A>());81params.extend_from_slice(ty.params());8283A::sig_from(¶ms, ty.returns(), &CallingConvention::Default)84}8586/// Returns the callee and caller [VMContext] types.87pub(crate) fn vmctx_types<A: ABI>() -> [WasmValType; 2] {88[A::ptr_type(), A::ptr_type()]89}9091/// Trait implemented by a specific ISA and used to provide92/// information about alignment, parameter passing, usage of93/// specific registers, etc.94pub(crate) trait ABI {95/// The required stack alignment.96fn stack_align() -> u8;9798/// The required stack alignment for calls.99fn call_stack_align() -> u8;100101/// The offset to the argument base, relative to the frame pointer.102fn arg_base_offset() -> u8;103104/// The initial size in bytes of the function's frame.105///106/// This amount is constant and accounts for all the stack space allocated107/// at the frame setup.108fn initial_frame_size() -> u8;109110/// Construct the ABI-specific signature from a WebAssembly111/// function type.112#[cfg(test)]113fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> Result<ABISig> {114Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv)115}116117/// Construct an ABI signature from WasmType params and returns.118fn sig_from(119params: &[WasmValType],120returns: &[WasmValType],121call_conv: &CallingConvention,122) -> Result<ABISig>;123124/// Construct [`ABIResults`] from a slice of [`WasmType`].125fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults>;126127/// Returns the number of bits in a word.128fn word_bits() -> u8;129130/// Returns the number of bytes in a word.131fn word_bytes() -> u8 {132Self::word_bits() / 8133}134135/// Returns the pinned register used to hold136/// the `VMContext`.137fn vmctx_reg() -> Reg;138139/// The size, in bytes, of each stack slot used for stack parameter passing.140fn stack_slot_size() -> u8;141142/// Returns the size in bytes of the given [`WasmType`].143fn sizeof(ty: &WasmValType) -> u8;144145/// The target pointer size represented as [WasmValType].146fn ptr_type() -> WasmValType {147// Defaulting to 64, since we currently only support 64-bit148// architectures.149WasmValType::I64150}151}152153/// ABI-specific representation of function argument or result.154#[derive(Clone, Debug)]155pub enum ABIOperand {156/// A register [`ABIOperand`].157Reg {158/// The type of the [`ABIOperand`].159ty: WasmValType,160/// Register holding the [`ABIOperand`].161reg: Reg,162/// The size of the [`ABIOperand`], in bytes.163size: u32,164},165/// A stack [`ABIOperand`].166Stack {167/// The type of the [`ABIOperand`].168ty: WasmValType,169/// Offset of the operand referenced through FP by the callee and170/// through SP by the caller.171offset: u32,172/// The size of the [`ABIOperand`], in bytes.173size: u32,174},175}176177impl ABIOperand {178/// Allocate a new register [`ABIOperand`].179pub fn reg(reg: Reg, ty: WasmValType, size: u32) -> Self {180Self::Reg { reg, ty, size }181}182183/// Allocate a new stack [`ABIOperand`].184pub fn stack_offset(offset: u32, ty: WasmValType, size: u32) -> Self {185Self::Stack { ty, offset, size }186}187188/// Is this [`ABIOperand`] in a register.189pub fn is_reg(&self) -> bool {190match *self {191ABIOperand::Reg { .. } => true,192_ => false,193}194}195196/// Unwraps the underlying register if it is one.197///198/// # Panics199/// This function panics if the [`ABIOperand`] is not a register.200pub fn unwrap_reg(&self) -> Reg {201match self {202ABIOperand::Reg { reg, .. } => *reg,203_ => unreachable!(),204}205}206}207208/// Information about the [`ABIOperand`] information used in [`ABISig`].209#[derive(Clone, Debug)]210pub(crate) struct ABIOperands {211/// All the operands.212pub inner: SmallVec<[ABIOperand; 6]>,213/// All the registers used as operands.214pub regs: HashSet<Reg>,215/// Stack bytes used by the operands.216pub bytes: u32,217}218219impl Default for ABIOperands {220fn default() -> Self {221Self {222inner: Default::default(),223regs: HashSet::with_capacity(0),224bytes: 0,225}226}227}228229/// Machine stack location of the stack results.230#[derive(Debug, Copy, Clone)]231pub(crate) enum RetArea {232/// Addressed from the stack pointer at the given offset.233SP(SPOffset),234/// The address of the results base is stored at a particular,235/// well known [LocalSlot].236Slot(LocalSlot),237/// The return area cannot be fully resolved ahead-of-time.238/// If there are results on the stack, this is the default state to which239/// all return areas get initialized to until they can be fully resolved to240/// either a [RetArea::SP] or [RetArea::Slot].241///242/// This allows a more explicit differentiation between the existence of243/// a return area versus no return area at all.244Uninit,245}246247impl Default for RetArea {248fn default() -> Self {249Self::Uninit250}251}252253impl RetArea {254/// Create a [RetArea] addressed from SP at the given offset.255pub fn sp(offs: SPOffset) -> Self {256Self::SP(offs)257}258259/// Create a [RetArea] addressed stored at the given [LocalSlot].260pub fn slot(local: LocalSlot) -> Self {261Self::Slot(local)262}263264/// Returns the [SPOffset] used as the base of the return area.265///266/// # Panics267/// This function panics if the return area doesn't hold a [SPOffset].268pub fn unwrap_sp(&self) -> SPOffset {269match self {270Self::SP(offs) => *offs,271_ => unreachable!(),272}273}274275/// Returns true if the return area is addressed via the stack pointer.276pub fn is_sp(&self) -> bool {277match self {278Self::SP(_) => true,279_ => false,280}281}282283/// Returns true if the return area is uninitialized.284pub fn is_uninit(&self) -> bool {285match self {286Self::Uninit => true,287_ => false,288}289}290}291292/// ABI-specific representation of an [`ABISig`].293#[derive(Clone, Debug, Default)]294pub(crate) struct ABIResults {295/// The result operands.296operands: ABIOperands,297/// The return area, if there are results on the stack.298ret_area: Option<RetArea>,299}300301impl ABIResults {302/// Creates [`ABIResults`] from a slice of `WasmType`.303/// This function maps the given return types to their ABI specific304/// representation. It does so, by iterating over them and applying the305/// given `map` closure. The map closure takes a [WasmValType], maps its ABI306/// representation, according to the calling convention. In the case of307/// results, one result is stored in registers and the rest at particular308/// offsets in the stack.309pub fn from<F>(310returns: &[WasmValType],311call_conv: &CallingConvention,312mut map: F,313) -> Result<Self>314where315F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,316{317if returns.len() == 0 {318return Ok(Self::default());319}320321type FoldTuple = (SmallVec<[ABIOperand; 6]>, HashSet<Reg>, u32);322type FoldTupleResult = Result<FoldTuple>;323324let fold_impl =325|(mut operands, mut regs, stack_bytes): FoldTuple, arg| -> FoldTupleResult {326let (operand, bytes) = map(arg, stack_bytes)?;327if operand.is_reg() {328regs.insert(operand.unwrap_reg());329}330operands.push(operand);331Ok((operands, regs, bytes))332};333334// When dealing with multiple results, Winch's calling convention stores the335// last return value in a register rather than the first one. In that336// sense, Winch's return values in the ABI signature are "reversed" in337// terms of storage. This technique is particularly helpful to ensure that338// the following invariants are maintained:339// * Spilled memory values always precede register values340// * Spilled values are stored from oldest to newest, matching their341// respective locations on the machine stack.342let (mut operands, regs, bytes) = if call_conv.is_default() {343returns344.iter()345.rev()346.try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?347} else {348returns349.iter()350.try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?351};352353// Similar to above, we reverse the result of the operands calculation354// to ensure that they match the declared order.355if call_conv.is_default() {356operands.reverse();357}358359Ok(Self::new(ABIOperands {360inner: operands,361regs,362bytes,363}))364}365366/// Create a new [`ABIResults`] from [`ABIOperands`].367pub fn new(operands: ABIOperands) -> Self {368let ret_area = (operands.bytes > 0).then(|| RetArea::default());369Self { operands, ret_area }370}371372/// Returns a reference to a [HashSet<Reg>], which includes373/// all the registers used to hold function results.374pub fn regs(&self) -> &HashSet<Reg> {375&self.operands.regs376}377378/// Get a slice over all the result [`ABIOperand`]s.379pub fn operands(&self) -> &[ABIOperand] {380&self.operands.inner381}382383/// Returns the length of the result.384pub fn len(&self) -> usize {385self.operands.inner.len()386}387388/// Returns the length of results on the stack.389pub fn stack_operands_len(&self) -> usize {390self.operands().len() - self.regs().len()391}392393/// Get the [`ABIOperand`] result in the nth position.394#[cfg(test)]395pub fn get(&self, n: usize) -> Option<&ABIOperand> {396self.operands.inner.get(n)397}398399/// Returns the first [`ABIOperand`].400/// Useful in situations where the function signature is known to401/// have a single return.402///403/// # Panics404/// This function panics if the function signature contains more405pub fn unwrap_singleton(&self) -> &ABIOperand {406debug_assert_eq!(self.len(), 1);407&self.operands.inner[0]408}409410/// Returns the size, in bytes of all the [`ABIOperand`]s in the stack.411pub fn size(&self) -> u32 {412self.operands.bytes413}414415/// Returns true if the [`ABIResults`] require space on the machine stack416/// for results.417pub fn on_stack(&self) -> bool {418self.operands.bytes > 0419}420421/// Set the return area of the signature.422///423/// # Panics424///425/// This function will panic if trying to set a return area if there are426/// no results on the stack or if trying to set an uninitialize return area.427/// This method must only be used when the return area can be fully428/// materialized.429pub fn set_ret_area(&mut self, area: RetArea) {430debug_assert!(self.on_stack());431debug_assert!(!area.is_uninit());432self.ret_area = Some(area);433}434435/// Returns a reference to the return area, if any.436pub fn ret_area(&self) -> Option<&RetArea> {437self.ret_area.as_ref()438}439}440441/// ABI-specific representation of an [`ABISig`].442#[derive(Debug, Clone, Default)]443pub(crate) struct ABIParams {444/// The param operands.445operands: ABIOperands,446/// Whether [`ABIParams`] contains an extra parameter for the stack447/// result area.448has_retptr: bool,449}450451impl ABIParams {452/// Creates [`ABIParams`] from a slice of `WasmType`.453/// This function maps the given param types to their ABI specific454/// representation. It does so, by iterating over them and applying the455/// given `map` closure. The map closure takes a [WasmType], maps its ABI456/// representation, according to the calling convention. In the case of457/// params, multiple params may be passed in registers and the rest on the458/// stack depending on the calling convention.459pub fn from<F, A: ABI>(460params: &[WasmValType],461initial_bytes: u32,462needs_stack_results: bool,463mut map: F,464) -> Result<Self>465where466F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,467{468if params.len() == 0 && !needs_stack_results {469return Ok(Self::with_bytes(initial_bytes));470}471472let register_capacity = params.len().min(6);473let mut operands = SmallVec::new();474let mut regs = HashSet::with_capacity(register_capacity);475let mut stack_bytes = initial_bytes;476477let ptr_type = ptr_type_from_ptr_size(<A as ABI>::word_bytes());478// Handle stack results by specifying an extra, implicit first argument.479let stack_results = if needs_stack_results {480let (operand, bytes) = map(&ptr_type, stack_bytes)?;481if operand.is_reg() {482regs.insert(operand.unwrap_reg());483}484stack_bytes = bytes;485Some(operand)486} else {487None488};489490for arg in params.iter() {491let (operand, bytes) = map(arg, stack_bytes)?;492if operand.is_reg() {493regs.insert(operand.unwrap_reg());494}495operands.push(operand);496stack_bytes = bytes;497}498499if let Some(operand) = stack_results {500// But still push the operand for stack results last as that is what501// the rest of the code expects.502operands.push(operand);503}504505Ok(Self {506operands: ABIOperands {507inner: operands,508regs,509bytes: stack_bytes,510},511has_retptr: needs_stack_results,512})513}514515/// Creates new [`ABIParams`], with the specified amount of stack bytes.516pub fn with_bytes(bytes: u32) -> Self {517let mut params = Self::default();518params.operands.bytes = bytes;519params520}521522/// Get the [`ABIOperand`] param in the nth position.523#[cfg(test)]524pub fn get(&self, n: usize) -> Option<&ABIOperand> {525self.operands.inner.get(n)526}527528/// Get a slice over all the parameter [`ABIOperand`]s.529pub fn operands(&self) -> &[ABIOperand] {530&self.operands.inner531}532533/// Returns the length of the params, including the return pointer,534/// if any.535pub fn len(&self) -> usize {536self.operands.inner.len()537}538539/// Returns the length of the params, excluding the return pointer,540/// if any.541pub fn len_without_retptr(&self) -> usize {542if self.has_retptr {543self.len() - 1544} else {545self.len()546}547}548549/// Returns true if the [ABISig] has an extra parameter for stack results.550pub fn has_retptr(&self) -> bool {551self.has_retptr552}553554/// Returns the last [ABIOperand] used as the pointer to the555/// stack results area.556///557/// # Panics558/// This function panics if the [ABIParams] doesn't have a stack results559/// parameter.560pub fn unwrap_results_area_operand(&self) -> &ABIOperand {561debug_assert!(self.has_retptr);562self.operands.inner.last().unwrap()563}564}565566/// An ABI-specific representation of a function signature.567#[derive(Debug, Clone)]568pub(crate) struct ABISig {569/// Function parameters.570pub params: ABIParams,571/// Function result.572pub results: ABIResults,573/// A unique set of registers used in the entire [`ABISig`].574pub regs: HashSet<Reg>,575/// Calling convention used.576pub call_conv: CallingConvention,577}578579impl Default for ABISig {580fn default() -> Self {581Self {582params: Default::default(),583results: Default::default(),584regs: Default::default(),585call_conv: CallingConvention::Default,586}587}588}589590impl ABISig {591/// Create a new ABI signature.592pub fn new(cc: CallingConvention, params: ABIParams, results: ABIResults) -> Self {593let regs = params594.operands595.regs596.union(&results.operands.regs)597.copied()598.collect();599Self {600params,601results,602regs,603call_conv: cc,604}605}606607/// Returns an iterator over all the parameter operands.608pub fn params(&self) -> &[ABIOperand] {609self.params.operands()610}611612/// Returns an iterator over all the result operands.613pub fn results(&self) -> &[ABIOperand] {614self.results.operands()615}616617/// Returns a slice over the signature params, excluding the results618/// base parameter, if any.619pub fn params_without_retptr(&self) -> &[ABIOperand] {620if self.params.has_retptr() {621&self.params()[0..(self.params.len() - 1)]622} else {623self.params()624}625}626627/// Returns the stack size, in bytes, needed for arguments on the stack.628pub fn params_stack_size(&self) -> u32 {629self.params.operands.bytes630}631632/// Returns the stack size, in bytes, needed for results on the stack.633pub fn results_stack_size(&self) -> u32 {634self.results.operands.bytes635}636637/// Returns true if the signature has results on the stack.638pub fn has_stack_results(&self) -> bool {639self.results.on_stack()640}641}642643/// Align a value up to the given power-of-two-alignment.644// See https://sites.google.com/site/theoryofoperatingsystems/labs/malloc/align8645pub(crate) fn align_to<N>(value: N, alignment: N) -> N646where647N: Not<Output = N>648+ BitAnd<N, Output = N>649+ Add<N, Output = N>650+ Sub<N, Output = N>651+ From<u8>652+ Copy,653{654let alignment_mask = alignment - 1.into();655(value + alignment_mask) & !alignment_mask656}657658/// Calculates the delta needed to adjust a function's frame plus some659/// addend to a given alignment.660pub(crate) fn calculate_frame_adjustment(frame_size: u32, addend: u32, alignment: u32) -> u32 {661let total = frame_size + addend;662(alignment - (total % alignment)) % alignment663}664665666