xref: /wasmtime-44.0.1/winch/codegen/src/abi/mod.rs (revision d463cf4e)
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     /// 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)]
171 pub 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 
193 impl 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)]
226 pub(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 
235 impl 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)]
247 pub(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 
263 impl Default for RetArea {
264     fn default() -> Self {
265         Self::Uninit
266     }
267 }
268 
269 impl 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)]
310 pub(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 
317 impl 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)]
459 pub(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 
467 impl 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)]
584 pub(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 
595 impl 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 
606 impl 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
661 pub(crate) fn align_to<N>(value: N, alignment: N) -> N
662 where
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.
676 pub(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 }
680