Skip to main content

backend_lang/target/
abi.rs

1//! Calling conventions: where arguments and return values go, which
2//! registers a call preserves, and the stack rules a frame must follow.
3//!
4//! An [`Abi`] is data, like a [`Target`]: argument and return registers per
5//! value class, the callee-saved set, stack alignment at calls, the stack
6//! space reserved for the callee (Win64 shadow space), the space below the
7//! stack pointer a leaf may use (SysV red zone), and the stack-probe
8//! interval. [`Abi::assign_args`] and [`Abi::assign_rets`] apply the rules to
9//! a signature; [`frame`](super::frame) lays out a function's frame by them.
10//!
11//! Scalar integer and floating-point values are covered. Aggregates passed
12//! by value (SysV classification, Win64 by-reference rules), variadic calls
13//! (SysV `al`, Win64 register duplication), and 128-bit integers come with
14//! call lowering from ir-lang 2.0 (backend-lang 0.5).
15
16use alloc::vec::Vec;
17use core::fmt;
18
19use super::Target;
20use crate::regs::{PReg, RegSet};
21
22/// The kind of a scalar argument or return value, which picks its register
23/// sequence.
24///
25/// # Examples
26///
27/// ```
28/// use backend_lang::target::abi::{ArgClass, ArgLoc};
29/// use backend_lang::x86_64::{SYSV, RDI, XMM0};
30///
31/// let mut locs = Vec::new();
32/// SYSV.assign_args(&[ArgClass::Int, ArgClass::Float], &mut locs);
33/// assert_eq!(locs, [ArgLoc::Reg(RDI), ArgLoc::Reg(XMM0)]);
34/// ```
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36#[non_exhaustive]
37pub enum ArgClass {
38    /// An integer or pointer of at most 64 bits.
39    Int,
40    /// A 32- or 64-bit floating-point value.
41    Float,
42}
43
44/// Where one argument or return value lives at the call.
45///
46/// # Examples
47///
48/// ```
49/// use backend_lang::target::abi::{ArgClass, ArgLoc};
50/// use backend_lang::x86_64::WIN64;
51///
52/// let mut locs = Vec::new();
53/// WIN64.assign_args(&[ArgClass::Int; 5], &mut locs);
54/// // The fifth argument goes above the 32 bytes of shadow space.
55/// assert_eq!(locs[4], ArgLoc::Stack(32));
56/// ```
57#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58pub enum ArgLoc {
59    /// In a register.
60    Reg(PReg),
61    /// On the stack, at this byte offset from the stack pointer at the call
62    /// instruction (before the return address is pushed). In the callee it
63    /// is the same offset from the canonical frame address; an incoming
64    /// stack slot ([`SlotKind::Incoming`]) names it.
65    ///
66    /// [`SlotKind::Incoming`]: crate::mir::SlotKind::Incoming
67    Stack(u32),
68}
69
70/// Why return values could not be assigned.
71///
72/// # Examples
73///
74/// ```
75/// use backend_lang::target::abi::{AbiError, ArgClass};
76/// use backend_lang::x86_64::WIN64;
77///
78/// let mut locs = Vec::new();
79/// let err = WIN64.assign_rets(&[ArgClass::Int, ArgClass::Int], &mut locs).unwrap_err();
80/// assert_eq!(err, AbiError::TooManyReturns { index: 1 });
81/// ```
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
83#[non_exhaustive]
84pub enum AbiError {
85    /// Return value `index` has no register left. Values that do not fit
86    /// the return registers are returned through memory the caller provides
87    /// (a hidden pointer argument), which is part of aggregate lowering
88    /// (backend-lang 0.5); return fewer scalars until then.
89    TooManyReturns {
90        /// The first value that did not fit.
91        index: usize,
92    },
93}
94
95impl fmt::Display for AbiError {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            AbiError::TooManyReturns { index } => write!(
99                f,
100                "return value {index} does not fit the return registers; return it through memory"
101            ),
102        }
103    }
104}
105
106impl core::error::Error for AbiError {}
107
108/// The integer and float register sequences of one direction (arguments or
109/// returns), and whether they are consumed positionally.
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
111pub struct RegSeq {
112    int: &'static [PReg],
113    float: &'static [PReg],
114    positional: bool,
115}
116
117impl RegSeq {
118    /// Integers take `int` in order and floats take `float` in order. With
119    /// `positional`, value `i` may only take entry `i` of its list (Win64),
120    /// so an integer in position 1 uses `rdx` even when position 0 was a
121    /// float; otherwise each list has its own counter (SysV).
122    ///
123    /// # Examples
124    ///
125    /// ```
126    /// use backend_lang::target::abi::RegSeq;
127    /// use backend_lang::x86_64::{RAX, XMM0};
128    ///
129    /// let rets = RegSeq::new(&[RAX], &[XMM0], true);
130    /// assert_eq!(rets.int(), [RAX]);
131    /// assert!(rets.positional());
132    /// ```
133    #[must_use]
134    pub const fn new(int: &'static [PReg], float: &'static [PReg], positional: bool) -> RegSeq {
135        RegSeq {
136            int,
137            float,
138            positional,
139        }
140    }
141
142    /// The integer registers, in order.
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// use backend_lang::x86_64::{SYSV, RDI};
148    ///
149    /// assert_eq!(SYSV.args().int()[0], RDI);
150    /// ```
151    #[inline]
152    #[must_use]
153    pub const fn int(&self) -> &'static [PReg] {
154        self.int
155    }
156
157    /// The floating-point registers, in order.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use backend_lang::x86_64::{SYSV, XMM7};
163    ///
164    /// assert_eq!(SYSV.args().float().last(), Some(&XMM7));
165    /// ```
166    #[inline]
167    #[must_use]
168    pub const fn float(&self) -> &'static [PReg] {
169        self.float
170    }
171
172    /// Whether value `i` may only take entry `i` of its list.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use backend_lang::x86_64::{SYSV, WIN64};
178    ///
179    /// assert!(WIN64.args().positional());
180    /// assert!(!SYSV.args().positional());
181    /// ```
182    #[inline]
183    #[must_use]
184    pub const fn positional(&self) -> bool {
185        self.positional
186    }
187
188    /// The register for value `index` of `class`, given how many values of
189    /// each list came before it; `None` when the list is used up.
190    fn pick(
191        &self,
192        class: ArgClass,
193        index: usize,
194        ints: &mut usize,
195        floats: &mut usize,
196    ) -> Option<PReg> {
197        let (list, counter) = match class {
198            ArgClass::Int => (self.int, ints),
199            ArgClass::Float => (self.float, floats),
200        };
201        let slot = if self.positional { index } else { *counter };
202        let reg = list.get(slot).copied();
203        if reg.is_some() {
204            *counter += 1;
205        }
206        reg
207    }
208}
209
210/// Stack rules: alignment at calls, the callee's reserved area, the red
211/// zone, the slot size of stack arguments, and the probe interval.
212///
213/// # Examples
214///
215/// ```
216/// use backend_lang::x86_64::{SYSV, WIN64};
217///
218/// assert_eq!(SYSV.stack().red_zone(), 128);
219/// assert_eq!(WIN64.stack().shadow_space(), 32);
220/// assert_eq!(WIN64.stack().align(), 16);
221/// ```
222#[derive(Clone, Copy, Debug, PartialEq, Eq)]
223pub struct StackRules {
224    align: u32,
225    arg_slot: u32,
226    shadow_space: u32,
227    red_zone: u32,
228    probe_interval: u32,
229}
230
231impl StackRules {
232    /// Stack rules: the stack pointer is a multiple of `align` at every call
233    /// instruction; each stack argument takes `arg_slot` bytes; every call
234    /// reserves `shadow_space` bytes for the callee below the stack
235    /// arguments; a leaf function may use `red_zone` bytes below the stack
236    /// pointer without moving it; and a frame of `probe_interval` bytes or
237    /// more is allocated one probed page at a time (`0` disables probing).
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// use backend_lang::target::abi::StackRules;
243    ///
244    /// let rules = StackRules::new(16, 8, 0, 0, 4096);
245    /// assert_eq!(rules.probe_interval(), 4096);
246    /// ```
247    #[must_use]
248    pub const fn new(
249        align: u32,
250        arg_slot: u32,
251        shadow_space: u32,
252        red_zone: u32,
253        probe_interval: u32,
254    ) -> StackRules {
255        StackRules {
256            align,
257            arg_slot,
258            shadow_space,
259            red_zone,
260            probe_interval,
261        }
262    }
263
264    /// The stack pointer's alignment at a call instruction, in bytes.
265    ///
266    /// # Examples
267    ///
268    /// ```
269    /// use backend_lang::x86_64::SYSV;
270    ///
271    /// assert_eq!(SYSV.stack().align(), 16);
272    /// ```
273    #[inline]
274    #[must_use]
275    pub const fn align(&self) -> u32 {
276        self.align
277    }
278
279    /// Bytes per stack argument.
280    ///
281    /// # Examples
282    ///
283    /// ```
284    /// use backend_lang::x86_64::SYSV;
285    ///
286    /// assert_eq!(SYSV.stack().arg_slot(), 8);
287    /// ```
288    #[inline]
289    #[must_use]
290    pub const fn arg_slot(&self) -> u32 {
291        self.arg_slot
292    }
293
294    /// Bytes every caller reserves at the bottom of its outgoing area for the
295    /// callee (Win64: 32, the home of the four register arguments).
296    ///
297    /// # Examples
298    ///
299    /// ```
300    /// use backend_lang::x86_64::SYSV;
301    ///
302    /// assert_eq!(SYSV.stack().shadow_space(), 0);
303    /// ```
304    #[inline]
305    #[must_use]
306    pub const fn shadow_space(&self) -> u32 {
307        self.shadow_space
308    }
309
310    /// Bytes below the stack pointer a function that makes no calls may use
311    /// without moving the stack pointer (SysV: 128).
312    ///
313    /// # Examples
314    ///
315    /// ```
316    /// use backend_lang::x86_64::WIN64;
317    ///
318    /// assert_eq!(WIN64.stack().red_zone(), 0);
319    /// ```
320    #[inline]
321    #[must_use]
322    pub const fn red_zone(&self) -> u32 {
323        self.red_zone
324    }
325
326    /// Frames of at least this many bytes touch each page as they allocate
327    /// it, so the guard page below the stack is never skipped; `0` means no
328    /// probing.
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use backend_lang::x86_64::WIN64;
334    ///
335    /// assert_eq!(WIN64.stack().probe_interval(), 4096);
336    /// ```
337    #[inline]
338    #[must_use]
339    pub const fn probe_interval(&self) -> u32 {
340        self.probe_interval
341    }
342}
343
344/// A calling convention on a target.
345///
346/// # Examples
347///
348/// ```
349/// use backend_lang::target::abi::{ArgClass, ArgLoc};
350/// use backend_lang::x86_64::{SYSV, RAX, RBX, RCX, RDX, RSI, RDI, R8, R9};
351///
352/// let mut locs = Vec::new();
353/// let stack = SYSV.assign_args(&[ArgClass::Int; 8], &mut locs);
354/// assert_eq!(&locs[..6], [RDI, RSI, RDX, RCX, R8, R9].map(ArgLoc::Reg));
355/// assert_eq!(&locs[6..], [ArgLoc::Stack(0), ArgLoc::Stack(8)]);
356/// assert_eq!(stack, 16);
357/// assert!(SYSV.callee_saved().contains(RBX));
358/// assert!(SYSV.call_clobbers().contains(RAX));
359/// ```
360#[derive(Debug)]
361pub struct Abi {
362    name: &'static str,
363    target: &'static Target,
364    args: RegSeq,
365    rets: RegSeq,
366    callee_saved: RegSet,
367    stack: StackRules,
368}
369
370impl Abi {
371    /// Describes a calling convention on `target`.
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// use backend_lang::target::abi::{Abi, RegSeq, StackRules};
377    /// use backend_lang::x86_64::{self, RAX, RDI, RBX};
378    /// use backend_lang::RegSet;
379    ///
380    /// static TINY: Abi = Abi::new(
381    ///     "tiny",
382    ///     &x86_64::TARGET,
383    ///     RegSeq::new(&[RDI], &[], false),
384    ///     RegSeq::new(&[RAX], &[], false),
385    ///     RegSet::of(&[RBX]),
386    ///     StackRules::new(16, 8, 0, 0, 0),
387    /// );
388    /// assert_eq!(TINY.name(), "tiny");
389    /// ```
390    #[must_use]
391    pub const fn new(
392        name: &'static str,
393        target: &'static Target,
394        args: RegSeq,
395        rets: RegSeq,
396        callee_saved: RegSet,
397        stack: StackRules,
398    ) -> Abi {
399        Abi {
400            name,
401            target,
402            args,
403            rets,
404            callee_saved,
405            stack,
406        }
407    }
408
409    /// The convention's name, as the MIR printer writes it.
410    ///
411    /// # Examples
412    ///
413    /// ```
414    /// use backend_lang::x86_64::WIN64;
415    ///
416    /// assert_eq!(WIN64.name(), "win64");
417    /// ```
418    #[inline]
419    #[must_use]
420    pub const fn name(&self) -> &'static str {
421        self.name
422    }
423
424    /// The target the convention belongs to.
425    ///
426    /// # Examples
427    ///
428    /// ```
429    /// use backend_lang::x86_64::SYSV;
430    ///
431    /// assert_eq!(SYSV.target().name(), "x86-64");
432    /// ```
433    #[inline]
434    #[must_use]
435    pub const fn target(&self) -> &'static Target {
436        self.target
437    }
438
439    /// The argument registers.
440    ///
441    /// # Examples
442    ///
443    /// ```
444    /// use backend_lang::x86_64::{WIN64, RCX};
445    ///
446    /// assert_eq!(WIN64.args().int()[0], RCX);
447    /// ```
448    #[inline]
449    #[must_use]
450    pub const fn args(&self) -> &RegSeq {
451        &self.args
452    }
453
454    /// The return registers.
455    ///
456    /// # Examples
457    ///
458    /// ```
459    /// use backend_lang::x86_64::{SYSV, RAX, RDX};
460    ///
461    /// assert_eq!(SYSV.rets().int(), [RAX, RDX]);
462    /// ```
463    #[inline]
464    #[must_use]
465    pub const fn rets(&self) -> &RegSeq {
466        &self.rets
467    }
468
469    /// The registers a callee must preserve (restore before returning).
470    ///
471    /// # Examples
472    ///
473    /// ```
474    /// use backend_lang::x86_64::{WIN64, RSI, XMM6};
475    ///
476    /// assert!(WIN64.callee_saved().contains(RSI));
477    /// assert!(WIN64.callee_saved().contains(XMM6));
478    /// ```
479    #[inline]
480    #[must_use]
481    pub const fn callee_saved(&self) -> RegSet {
482        self.callee_saved
483    }
484
485    /// The stack rules.
486    ///
487    /// # Examples
488    ///
489    /// ```
490    /// use backend_lang::x86_64::SYSV;
491    ///
492    /// assert_eq!(SYSV.stack().align(), 16);
493    /// ```
494    #[inline]
495    #[must_use]
496    pub const fn stack(&self) -> &StackRules {
497        &self.stack
498    }
499
500    /// The caller-saved registers: every allocatable register the convention
501    /// does not preserve. A value live across a call must not be in one (or
502    /// must be saved around the call).
503    ///
504    /// # Examples
505    ///
506    /// ```
507    /// use backend_lang::x86_64::{SYSV, WIN64, FLAGS, R11, RDI};
508    ///
509    /// assert!(SYSV.caller_saved().contains(RDI));
510    /// assert!(!WIN64.caller_saved().contains(RDI));
511    /// assert!(SYSV.caller_saved().contains(R11));
512    /// assert!(!SYSV.caller_saved().contains(FLAGS));
513    /// ```
514    #[must_use]
515    pub fn caller_saved(&self) -> RegSet {
516        self.target
517            .classes()
518            .iter()
519            .fold(RegSet::EMPTY, |acc, c| acc.union(c.allocatable()))
520            .difference(self.callee_saved)
521    }
522
523    /// The registers a call may change: every allocatable register the
524    /// convention does not preserve, plus the flags. This is the clobber set
525    /// call lowering attaches to a call instruction.
526    ///
527    /// # Examples
528    ///
529    /// ```
530    /// use backend_lang::x86_64::{SYSV, WIN64, FLAGS, R11, RSI, RBX, XMM6};
531    ///
532    /// assert!(SYSV.call_clobbers().contains(RSI));
533    /// assert!(!WIN64.call_clobbers().contains(RSI));
534    /// assert!(SYSV.call_clobbers().contains(XMM6));
535    /// assert!(!WIN64.call_clobbers().contains(XMM6));
536    /// assert!(SYSV.call_clobbers().contains(FLAGS) && SYSV.call_clobbers().contains(R11));
537    /// assert!(!SYSV.call_clobbers().contains(RBX));
538    /// ```
539    #[must_use]
540    pub fn call_clobbers(&self) -> RegSet {
541        match self.target.flags() {
542            Some(flags) => self.caller_saved().with(flags),
543            None => self.caller_saved(),
544        }
545    }
546
547    /// Assigns a location to each argument, in order, appending them to
548    /// `out`, and returns the bytes of outgoing stack area the call needs:
549    /// the shadow space (if the convention has one) plus the stack
550    /// arguments. A caller's frame reserves the largest such area over its
551    /// calls ([`Function::reserve_outgoing`]).
552    ///
553    /// # Examples
554    ///
555    /// ```
556    /// use backend_lang::target::abi::{ArgClass, ArgLoc};
557    /// use backend_lang::x86_64::{WIN64, RCX, XMM1, R8};
558    ///
559    /// let mut locs = Vec::new();
560    /// let area = WIN64.assign_args(&[ArgClass::Int, ArgClass::Float, ArgClass::Int], &mut locs);
561    /// // Positional: the float in slot 1 takes xmm1, the int in slot 2 takes r8.
562    /// assert_eq!(locs, [ArgLoc::Reg(RCX), ArgLoc::Reg(XMM1), ArgLoc::Reg(R8)]);
563    /// assert_eq!(area, 32);
564    /// ```
565    ///
566    /// [`Function::reserve_outgoing`]: crate::mir::Function::reserve_outgoing
567    pub fn assign_args(&self, args: &[ArgClass], out: &mut Vec<ArgLoc>) -> u32 {
568        out.reserve(args.len());
569        let (mut ints, mut floats) = (0, 0);
570        let mut offset = self.stack.shadow_space;
571        for (i, &class) in args.iter().enumerate() {
572            match self.args.pick(class, i, &mut ints, &mut floats) {
573                Some(reg) => out.push(ArgLoc::Reg(reg)),
574                None => {
575                    out.push(ArgLoc::Stack(offset));
576                    offset = offset.saturating_add(self.stack.arg_slot);
577                }
578            }
579        }
580        offset
581    }
582
583    /// Assigns a register to each return value, in order, appending them to
584    /// `out`.
585    ///
586    /// # Errors
587    ///
588    /// [`AbiError::TooManyReturns`] when a value has no register left;
589    /// nothing is appended in that case.
590    ///
591    /// # Examples
592    ///
593    /// ```
594    /// use backend_lang::target::abi::{ArgClass, ArgLoc};
595    /// use backend_lang::x86_64::{SYSV, RAX, XMM0};
596    ///
597    /// let mut locs = Vec::new();
598    /// SYSV.assign_rets(&[ArgClass::Int, ArgClass::Float], &mut locs)?;
599    /// assert_eq!(locs, [ArgLoc::Reg(RAX), ArgLoc::Reg(XMM0)]);
600    /// # Ok::<(), backend_lang::target::abi::AbiError>(())
601    /// ```
602    pub fn assign_rets(&self, rets: &[ArgClass], out: &mut Vec<ArgLoc>) -> Result<(), AbiError> {
603        let start = out.len();
604        let (mut ints, mut floats) = (0, 0);
605        for (i, &class) in rets.iter().enumerate() {
606            match self.rets.pick(class, i, &mut ints, &mut floats) {
607                Some(reg) => out.push(ArgLoc::Reg(reg)),
608                None => {
609                    out.truncate(start);
610                    return Err(AbiError::TooManyReturns { index: i });
611                }
612            }
613        }
614        Ok(())
615    }
616}
617
618#[cfg(test)]
619mod tests {
620    use alloc::string::ToString;
621    use alloc::vec;
622
623    use super::*;
624    use crate::x86_64::*;
625
626    fn args(abi: &Abi, classes: &[ArgClass]) -> (Vec<ArgLoc>, u32) {
627        let mut out = Vec::new();
628        let n = abi.assign_args(classes, &mut out);
629        (out, n)
630    }
631
632    #[test]
633    fn test_sysv_args_interleaved_int_and_float_counters_are_independent() {
634        use ArgClass::{Float as F, Int as I};
635        let (locs, stack) = args(&SYSV, &[F, I, F, I, I, I, I, I, I, F]);
636        let regs = |r: &[PReg]| r.iter().map(|&r| ArgLoc::Reg(r)).collect::<Vec<_>>();
637        assert_eq!(
638            locs,
639            [
640                regs(&[XMM0, RDI, XMM1, RSI, RDX, RCX, R8, R9]),
641                vec![ArgLoc::Stack(0), ArgLoc::Reg(XMM2)]
642            ]
643            .concat()
644        );
645        assert_eq!(stack, 8);
646    }
647
648    #[test]
649    fn test_sysv_float_args_spill_after_eight() {
650        let (locs, stack) = args(&SYSV, &[ArgClass::Float; 10]);
651        assert_eq!(locs[7], ArgLoc::Reg(XMM7));
652        assert_eq!(locs[8], ArgLoc::Stack(0));
653        assert_eq!(locs[9], ArgLoc::Stack(8));
654        assert_eq!(stack, 16);
655    }
656
657    #[test]
658    fn test_win64_positional_and_shadow_space() {
659        use ArgClass::{Float as F, Int as I};
660        let (locs, stack) = args(&WIN64, &[F, F, I, F, I, F]);
661        assert_eq!(
662            locs,
663            [
664                ArgLoc::Reg(XMM0),
665                ArgLoc::Reg(XMM1),
666                ArgLoc::Reg(R8),
667                ArgLoc::Reg(XMM3),
668                ArgLoc::Stack(32),
669                ArgLoc::Stack(40)
670            ]
671        );
672        assert_eq!(stack, 48);
673        // No arguments still reserves the shadow space.
674        assert_eq!(args(&WIN64, &[]).1, 32);
675        assert_eq!(args(&SYSV, &[]).1, 0);
676    }
677
678    #[test]
679    fn test_returns_per_convention() {
680        let mut out = Vec::new();
681        assert_eq!(SYSV.assign_rets(&[ArgClass::Int; 2], &mut out), Ok(()));
682        assert_eq!(out, [ArgLoc::Reg(RAX), ArgLoc::Reg(RDX)]);
683        out.clear();
684        assert_eq!(
685            SYSV.assign_rets(&[ArgClass::Int; 3], &mut out),
686            Err(AbiError::TooManyReturns { index: 2 })
687        );
688        assert!(out.is_empty());
689        assert_eq!(WIN64.assign_rets(&[ArgClass::Float], &mut out), Ok(()));
690        assert_eq!(out, [ArgLoc::Reg(XMM0)]);
691        out.clear();
692        assert_eq!(
693            WIN64.assign_rets(&[ArgClass::Float, ArgClass::Int], &mut out),
694            Err(AbiError::TooManyReturns { index: 1 })
695        );
696        assert_eq!(
697            AbiError::TooManyReturns { index: 1 }.to_string(),
698            "return value 1 does not fit the return registers; return it through memory"
699        );
700    }
701
702    #[test]
703    fn test_callee_saved_sets_match_the_published_conventions() {
704        let sysv: Vec<_> = SYSV.callee_saved().iter().collect();
705        assert_eq!(sysv, [RBX, RBP, R12, R13, R14, R15]);
706        let win: Vec<_> = WIN64.callee_saved().iter().collect();
707        assert_eq!(
708            win,
709            [
710                RBX, RBP, RSI, RDI, R12, R13, R14, R15, XMM6, XMM7, XMM8, XMM9, XMM10, XMM11,
711                XMM12, XMM13, XMM14, XMM15
712            ]
713        );
714        // Clobbers: everything allocatable and not preserved, plus flags; never
715        // the stack pointer.
716        for abi in [&SYSV, &WIN64] {
717            let c = abi.call_clobbers();
718            assert!(!c.contains(RSP));
719            assert!(c.contains(FLAGS));
720            assert!(c.intersect(abi.callee_saved()).is_empty());
721        }
722        assert_eq!(SYSV.call_clobbers().len(), 9 + 16 + 1);
723        assert_eq!(WIN64.call_clobbers().len(), 7 + 6 + 1);
724    }
725
726    #[test]
727    fn test_stack_offsets_saturate_instead_of_wrapping() {
728        // A hostile signature cannot wrap the offset arithmetic.
729        let many = vec![ArgClass::Int; 1 << 16];
730        let (locs, stack) = args(&SYSV, &many);
731        assert_eq!(locs.len(), 1 << 16);
732        assert_eq!(stack, 8 * ((1 << 16) - 6));
733    }
734}