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