winch_codegen/abi/
mod.rs

1//!
2//! The Default ABI
3//!
4//! Winch uses a default ABI, for all internal functions. This allows
5//! us to push the complexity of system ABI compliance to the trampolines.  The
6//! default ABI treats all allocatable registers as caller saved, which means
7//! that (i) all register values in the Wasm value stack (which are normally
8//! referred to as "live"), must be saved onto the machine stack (ii) function
9//! prologues and epilogues don't store/restore other registers more than the
10//! non-allocatable ones (e.g. rsp/rbp in x86_64).
11//!
12//! The calling convention in the default ABI, uses registers to a certain fixed
13//! count for arguments and return values, and then the stack is used for all
14//! additional arguments and return values. Aside from the parameters declared
15//! in each WebAssembly function, Winch's ABI declares two extra parameters, to
16//! hold the callee and caller `VMContext` pointers. A well-known `LocalSlot` is
17//! reserved for the callee VMContext pointer and also a particular pinned
18//! register is used to hold the value of the callee `VMContext`, which is
19//! available throughout the lifetime of the function.
20//!
21//!
22//! Generally the stack layout looks like:
23//! +-------------------------------+
24//! |                               |
25//! |                               |
26//! |         Stack Args            |
27//! |                               |
28//! |                               |
29//! +-------------------------------+----> SP @ function entry
30//! |         Ret addr              |
31//! +-------------------------------+
32//! |            SP                 |
33//! +-------------------------------+----> SP @ Function prologue
34//! |                               |
35//! +-------------------------------+----> VMContext slot
36//! |                               |
37//! |                               |
38//! |        Stack slots            |
39//! |        + dynamic space        |
40//! |                               |
41//! |                               |
42//! |                               |
43//! +-------------------------------+----> SP @ callsite (after)
44//! |        alignment              |
45//! |        + arguments            |
46//! |                               | ----> Space allocated for calls
47//! |                               |
48use crate::codegen::ptr_type_from_ptr_size;
49use crate::isa::{CallingConvention, reg::Reg};
50use crate::masm::SPOffset;
51use anyhow::Result;
52use smallvec::SmallVec;
53use std::collections::HashSet;
54use std::ops::{Add, BitAnd, Not, Sub};
55use wasmtime_environ::{WasmFuncType, WasmValType};
56
57pub(crate) mod local;
58pub(crate) use local::*;
59
60/// Internal classification for params or returns,
61/// mainly used for params and return register assignment.
62#[derive(Clone, Copy, Eq, PartialEq, Debug)]
63pub(super) enum ParamsOrReturns {
64    Params,
65    Returns,
66}
67
68/// Macro to get the pinned register holding the [VMContext].
69macro_rules! vmctx {
70    ($m:ident) => {
71        <$m::ABI as $crate::abi::ABI>::vmctx_reg()
72    };
73}
74
75pub(crate) use vmctx;
76
77/// Constructs an [ABISig] using Winch's ABI.
78pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> Result<ABISig> {
79    // 6 is used semi-arbitrarily here, we can modify as we see fit.
80    let mut params: SmallVec<[WasmValType; 6]> = SmallVec::new();
81    params.extend_from_slice(&vmctx_types::<A>());
82    params.extend_from_slice(ty.params());
83
84    A::sig_from(&params, ty.returns(), &CallingConvention::Default)
85}
86
87/// Returns the callee and caller [VMContext] types.
88pub(crate) fn vmctx_types<A: ABI>() -> [WasmValType; 2] {
89    [A::ptr_type(), A::ptr_type()]
90}
91
92/// Trait implemented by a specific ISA and used to provide
93/// information about alignment, parameter passing, usage of
94/// specific registers, etc.
95pub(crate) trait ABI {
96    /// The required stack alignment.
97    fn stack_align() -> u8;
98
99    /// The required stack alignment for calls.
100    fn call_stack_align() -> u8;
101
102    /// The offset to the argument base, relative to the frame pointer.
103    fn arg_base_offset() -> u8;
104
105    /// The initial size in bytes of the function's frame.
106    ///
107    /// This amount is constant and accounts for all the stack space allocated
108    /// at the frame setup.
109    fn initial_frame_size() -> u8;
110
111    /// Construct the ABI-specific signature from a WebAssembly
112    /// function type.
113    #[cfg(test)]
114    fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> Result<ABISig> {
115        Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv)
116    }
117
118    /// Construct an ABI signature from WasmType params and returns.
119    fn sig_from(
120        params: &[WasmValType],
121        returns: &[WasmValType],
122        call_conv: &CallingConvention,
123    ) -> Result<ABISig>;
124
125    /// Construct [`ABIResults`] from a slice of [`WasmType`].
126    fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults>;
127
128    /// Returns the number of bits in a word.
129    fn word_bits() -> u8;
130
131    /// Returns the number of bytes in a word.
132    fn word_bytes() -> u8 {
133        Self::word_bits() / 8
134    }
135
136    /// Returns the pinned register used to hold
137    /// the `VMContext`.
138    fn vmctx_reg() -> Reg;
139
140    /// The size, in bytes, of each stack slot used for stack parameter passing.
141    fn stack_slot_size() -> u8;
142
143    /// Returns the size in bytes of the given [`WasmType`].
144    fn sizeof(ty: &WasmValType) -> u8;
145
146    /// The target pointer size represented as [WasmValType].
147    fn ptr_type() -> WasmValType {
148        // Defaulting to 64, since we currently only support 64-bit
149        // architectures.
150        WasmValType::I64
151    }
152}
153
154/// ABI-specific representation of function argument or result.
155#[derive(Clone, Debug)]
156pub enum ABIOperand {
157    /// A register [`ABIOperand`].
158    Reg {
159        /// The type of the [`ABIOperand`].
160        ty: WasmValType,
161        /// Register holding the [`ABIOperand`].
162        reg: Reg,
163        /// The size of the [`ABIOperand`], in bytes.
164        size: u32,
165    },
166    /// A stack [`ABIOperand`].
167    Stack {
168        /// The type of the [`ABIOperand`].
169        ty: WasmValType,
170        /// Offset of the operand referenced through FP by the callee and
171        /// through SP by the caller.
172        offset: u32,
173        /// The size of the [`ABIOperand`], in bytes.
174        size: u32,
175    },
176}
177
178impl ABIOperand {
179    /// Allocate a new register [`ABIOperand`].
180    pub fn reg(reg: Reg, ty: WasmValType, size: u32) -> Self {
181        Self::Reg { reg, ty, size }
182    }
183
184    /// Allocate a new stack [`ABIOperand`].
185    pub fn stack_offset(offset: u32, ty: WasmValType, size: u32) -> Self {
186        Self::Stack { ty, offset, size }
187    }
188
189    /// Is this [`ABIOperand`] in a register.
190    pub fn is_reg(&self) -> bool {
191        match *self {
192            ABIOperand::Reg { .. } => true,
193            _ => false,
194        }
195    }
196
197    /// Unwraps the underlying register if it is one.
198    ///
199    /// # Panics
200    /// This function panics if the [`ABIOperand`] is not a register.
201    pub fn unwrap_reg(&self) -> Reg {
202        match self {
203            ABIOperand::Reg { reg, .. } => *reg,
204            _ => unreachable!(),
205        }
206    }
207}
208
209/// Information about the [`ABIOperand`] information used in [`ABISig`].
210#[derive(Clone, Debug)]
211pub(crate) struct ABIOperands {
212    /// All the operands.
213    pub inner: SmallVec<[ABIOperand; 6]>,
214    /// All the registers used as operands.
215    pub regs: HashSet<Reg>,
216    /// Stack bytes used by the operands.
217    pub bytes: u32,
218}
219
220impl Default for ABIOperands {
221    fn default() -> Self {
222        Self {
223            inner: Default::default(),
224            regs: HashSet::with_capacity(0),
225            bytes: 0,
226        }
227    }
228}
229
230/// Machine stack location of the stack results.
231#[derive(Debug, Copy, Clone)]
232pub(crate) enum RetArea {
233    /// Addressed from the stack pointer at the given offset.
234    SP(SPOffset),
235    /// The address of the results base is stored at a particular,
236    /// well known [LocalSlot].
237    Slot(LocalSlot),
238    /// The return area cannot be fully resolved ahead-of-time.
239    /// If there are results on the stack, this is the default state to which
240    /// all return areas get initialized to until they can be fully resolved to
241    /// either a [RetArea::SP] or [RetArea::Slot].
242    ///
243    /// This allows a more explicit differentiation between the existence of
244    /// a return area versus no return area at all.
245    Uninit,
246}
247
248impl Default for RetArea {
249    fn default() -> Self {
250        Self::Uninit
251    }
252}
253
254impl RetArea {
255    /// Create a [RetArea] addressed from SP at the given offset.
256    pub fn sp(offs: SPOffset) -> Self {
257        Self::SP(offs)
258    }
259
260    /// Create a [RetArea] addressed stored at the given [LocalSlot].
261    pub fn slot(local: LocalSlot) -> Self {
262        Self::Slot(local)
263    }
264
265    /// Returns the [SPOffset] used as the base of the return area.
266    ///
267    /// # Panics
268    /// This function panics if the return area doesn't hold a [SPOffset].
269    pub fn unwrap_sp(&self) -> SPOffset {
270        match self {
271            Self::SP(offs) => *offs,
272            _ => unreachable!(),
273        }
274    }
275
276    /// Returns true if the return area is addressed via the stack pointer.
277    pub fn is_sp(&self) -> bool {
278        match self {
279            Self::SP(_) => true,
280            _ => false,
281        }
282    }
283
284    /// Returns true if the return area is uninitialized.
285    pub fn is_uninit(&self) -> bool {
286        match self {
287            Self::Uninit => true,
288            _ => false,
289        }
290    }
291}
292
293/// ABI-specific representation of an [`ABISig`].
294#[derive(Clone, Debug, Default)]
295pub(crate) struct ABIResults {
296    /// The result operands.
297    operands: ABIOperands,
298    /// The return area, if there are results on the stack.
299    ret_area: Option<RetArea>,
300}
301
302impl ABIResults {
303    /// Creates [`ABIResults`] from a slice of `WasmType`.
304    /// This function maps the given return types to their ABI specific
305    /// representation. It does so, by iterating over them and applying the
306    /// given `map` closure. The map closure takes a [WasmValType], maps its ABI
307    /// representation, according to the calling convention. In the case of
308    /// results, one result is stored in registers and the rest at particular
309    /// offsets in the stack.
310    pub fn from<F>(
311        returns: &[WasmValType],
312        call_conv: &CallingConvention,
313        mut map: F,
314    ) -> Result<Self>
315    where
316        F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
317    {
318        if returns.len() == 0 {
319            return Ok(Self::default());
320        }
321
322        type FoldTuple = (SmallVec<[ABIOperand; 6]>, HashSet<Reg>, u32);
323        type FoldTupleResult = Result<FoldTuple>;
324
325        let fold_impl =
326            |(mut operands, mut regs, stack_bytes): FoldTuple, arg| -> FoldTupleResult {
327                let (operand, bytes) = map(arg, stack_bytes)?;
328                if operand.is_reg() {
329                    regs.insert(operand.unwrap_reg());
330                }
331                operands.push(operand);
332                Ok((operands, regs, bytes))
333            };
334
335        // When dealing with multiple results, Winch's calling convention stores the
336        // last return value in a register rather than the first one. In that
337        // sense, Winch's return values in the ABI signature are "reversed" in
338        // terms of storage. This technique is particularly helpful to ensure that
339        // the following invariants are maintained:
340        // * Spilled memory values always precede register values
341        // * Spilled values are stored from oldest to newest, matching their
342        //   respective locations on the machine stack.
343        let (mut operands, regs, bytes) = if call_conv.is_default() {
344            returns
345                .iter()
346                .rev()
347                .try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
348        } else {
349            returns
350                .iter()
351                .try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
352        };
353
354        // Similar to above, we reverse the result of the operands calculation
355        // to ensure that they match the declared order.
356        if call_conv.is_default() {
357            operands.reverse();
358        }
359
360        Ok(Self::new(ABIOperands {
361            inner: operands,
362            regs,
363            bytes,
364        }))
365    }
366
367    /// Create a new [`ABIResults`] from [`ABIOperands`].
368    pub fn new(operands: ABIOperands) -> Self {
369        let ret_area = (operands.bytes > 0).then(|| RetArea::default());
370        Self { operands, ret_area }
371    }
372
373    /// Returns a reference to a [HashSet<Reg>], which includes
374    /// all the registers used to hold function results.
375    pub fn regs(&self) -> &HashSet<Reg> {
376        &self.operands.regs
377    }
378
379    /// Get a slice over all the result [`ABIOperand`]s.
380    pub fn operands(&self) -> &[ABIOperand] {
381        &self.operands.inner
382    }
383
384    /// Returns the length of the result.
385    pub fn len(&self) -> usize {
386        self.operands.inner.len()
387    }
388
389    /// Returns the length of results on the stack.
390    pub fn stack_operands_len(&self) -> usize {
391        self.operands().len() - self.regs().len()
392    }
393
394    /// Get the [`ABIOperand`] result in the nth position.
395    #[cfg(test)]
396    pub fn get(&self, n: usize) -> Option<&ABIOperand> {
397        self.operands.inner.get(n)
398    }
399
400    /// Returns the first [`ABIOperand`].
401    /// Useful in situations where the function signature is known to
402    /// have a single return.
403    ///
404    /// # Panics
405    /// This function panics if the function signature contains more
406    pub fn unwrap_singleton(&self) -> &ABIOperand {
407        debug_assert_eq!(self.len(), 1);
408        &self.operands.inner[0]
409    }
410
411    /// Returns the size, in bytes of all the [`ABIOperand`]s in the stack.
412    pub fn size(&self) -> u32 {
413        self.operands.bytes
414    }
415
416    /// Returns true if the [`ABIResults`] require space on the machine stack
417    /// for results.
418    pub fn on_stack(&self) -> bool {
419        self.operands.bytes > 0
420    }
421
422    /// Set the return area of the signature.
423    ///
424    /// # Panics
425    ///
426    /// This function will panic if trying to set a return area if there are
427    /// no results on the stack or if trying to set an uninitialize return area.
428    /// This method must only be used when the return area can be fully
429    /// materialized.
430    pub fn set_ret_area(&mut self, area: RetArea) {
431        debug_assert!(self.on_stack());
432        debug_assert!(!area.is_uninit());
433        self.ret_area = Some(area);
434    }
435
436    /// Returns a reference to the return area, if any.
437    pub fn ret_area(&self) -> Option<&RetArea> {
438        self.ret_area.as_ref()
439    }
440}
441
442/// ABI-specific representation of an [`ABISig`].
443#[derive(Debug, Clone, Default)]
444pub(crate) struct ABIParams {
445    /// The param operands.
446    operands: ABIOperands,
447    /// Whether [`ABIParams`] contains an extra parameter for the stack
448    /// result area.
449    has_retptr: bool,
450}
451
452impl ABIParams {
453    /// Creates [`ABIParams`] from a slice of `WasmType`.
454    /// This function maps the given param types to their ABI specific
455    /// representation. It does so, by iterating over them and applying the
456    /// given `map` closure. The map closure takes a [WasmType], maps its ABI
457    /// representation, according to the calling convention. In the case of
458    /// params, multiple params may be passed in registers and the rest on the
459    /// stack depending on the calling convention.
460    pub fn from<F, A: ABI>(
461        params: &[WasmValType],
462        initial_bytes: u32,
463        needs_stack_results: bool,
464        mut map: F,
465    ) -> Result<Self>
466    where
467        F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
468    {
469        if params.len() == 0 && !needs_stack_results {
470            return Ok(Self::with_bytes(initial_bytes));
471        }
472
473        let register_capacity = params.len().min(6);
474        let mut operands = SmallVec::new();
475        let mut regs = HashSet::with_capacity(register_capacity);
476        let mut stack_bytes = initial_bytes;
477
478        let ptr_type = ptr_type_from_ptr_size(<A as ABI>::word_bytes());
479        // Handle stack results by specifying an extra, implicit first argument.
480        let stack_results = if needs_stack_results {
481            let (operand, bytes) = map(&ptr_type, stack_bytes)?;
482            if operand.is_reg() {
483                regs.insert(operand.unwrap_reg());
484            }
485            stack_bytes = bytes;
486            Some(operand)
487        } else {
488            None
489        };
490
491        for arg in params.iter() {
492            let (operand, bytes) = map(arg, stack_bytes)?;
493            if operand.is_reg() {
494                regs.insert(operand.unwrap_reg());
495            }
496            operands.push(operand);
497            stack_bytes = bytes;
498        }
499
500        if let Some(operand) = stack_results {
501            // But still push the operand for stack results last as that is what
502            // the rest of the code expects.
503            operands.push(operand);
504        }
505
506        Ok(Self {
507            operands: ABIOperands {
508                inner: operands,
509                regs,
510                bytes: stack_bytes,
511            },
512            has_retptr: needs_stack_results,
513        })
514    }
515
516    /// Creates new [`ABIParams`], with the specified amount of stack bytes.
517    pub fn with_bytes(bytes: u32) -> Self {
518        let mut params = Self::default();
519        params.operands.bytes = bytes;
520        params
521    }
522
523    /// Get the [`ABIOperand`] param in the nth position.
524    #[cfg(test)]
525    pub fn get(&self, n: usize) -> Option<&ABIOperand> {
526        self.operands.inner.get(n)
527    }
528
529    /// Get a slice over all the parameter [`ABIOperand`]s.
530    pub fn operands(&self) -> &[ABIOperand] {
531        &self.operands.inner
532    }
533
534    /// Returns the length of the params, including the return pointer,
535    /// if any.
536    pub fn len(&self) -> usize {
537        self.operands.inner.len()
538    }
539
540    /// Returns the length of the params, excluding the return pointer,
541    /// if any.
542    pub fn len_without_retptr(&self) -> usize {
543        if self.has_retptr {
544            self.len() - 1
545        } else {
546            self.len()
547        }
548    }
549
550    /// Returns true if the [ABISig] has an extra parameter for stack results.
551    pub fn has_retptr(&self) -> bool {
552        self.has_retptr
553    }
554
555    /// Returns the last [ABIOperand] used as the pointer to the
556    /// stack results area.
557    ///
558    /// # Panics
559    /// This function panics if the [ABIParams] doesn't have a stack results
560    /// parameter.
561    pub fn unwrap_results_area_operand(&self) -> &ABIOperand {
562        debug_assert!(self.has_retptr);
563        self.operands.inner.last().unwrap()
564    }
565}
566
567/// An ABI-specific representation of a function signature.
568#[derive(Debug, Clone)]
569pub(crate) struct ABISig {
570    /// Function parameters.
571    pub params: ABIParams,
572    /// Function result.
573    pub results: ABIResults,
574    /// A unique set of registers used in the entire [`ABISig`].
575    pub regs: HashSet<Reg>,
576    /// Calling convention used.
577    pub call_conv: CallingConvention,
578}
579
580impl Default for ABISig {
581    fn default() -> Self {
582        Self {
583            params: Default::default(),
584            results: Default::default(),
585            regs: Default::default(),
586            call_conv: CallingConvention::Default,
587        }
588    }
589}
590
591impl ABISig {
592    /// Create a new ABI signature.
593    pub fn new(cc: CallingConvention, params: ABIParams, results: ABIResults) -> Self {
594        let regs = params
595            .operands
596            .regs
597            .union(&results.operands.regs)
598            .copied()
599            .collect();
600        Self {
601            params,
602            results,
603            regs,
604            call_conv: cc,
605        }
606    }
607
608    /// Returns an iterator over all the parameter operands.
609    pub fn params(&self) -> &[ABIOperand] {
610        self.params.operands()
611    }
612
613    /// Returns an iterator over all the result operands.
614    pub fn results(&self) -> &[ABIOperand] {
615        self.results.operands()
616    }
617
618    /// Returns a slice over the signature params, excluding the results
619    /// base parameter, if any.
620    pub fn params_without_retptr(&self) -> &[ABIOperand] {
621        if self.params.has_retptr() {
622            &self.params()[0..(self.params.len() - 1)]
623        } else {
624            self.params()
625        }
626    }
627
628    /// Returns the stack size, in bytes, needed for arguments on the stack.
629    pub fn params_stack_size(&self) -> u32 {
630        self.params.operands.bytes
631    }
632
633    /// Returns the stack size, in bytes, needed for results on the stack.
634    pub fn results_stack_size(&self) -> u32 {
635        self.results.operands.bytes
636    }
637
638    /// Returns true if the signature has results on the stack.
639    pub fn has_stack_results(&self) -> bool {
640        self.results.on_stack()
641    }
642}
643
644/// Align a value up to the given power-of-two-alignment.
645// See https://sites.google.com/site/theoryofoperatingsystems/labs/malloc/align8
646pub(crate) fn align_to<N>(value: N, alignment: N) -> N
647where
648    N: Not<Output = N>
649        + BitAnd<N, Output = N>
650        + Add<N, Output = N>
651        + Sub<N, Output = N>
652        + From<u8>
653        + Copy,
654{
655    let alignment_mask = alignment - 1.into();
656    (value + alignment_mask) & !alignment_mask
657}
658
659/// Calculates the delta needed to adjust a function's frame plus some
660/// addend to a given alignment.
661pub(crate) fn calculate_frame_adjustment(frame_size: u32, addend: u32, alignment: u32) -> u32 {
662    let total = frame_size + addend;
663    (alignment - (total % alignment)) % alignment
664}