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