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}