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