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