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