Rust · 5712 bytes Raw Blame History
1 //! AAPCS64 argument classification and stack layout.
2 //!
3 //! Apple's AArch64 procedure-call standard, as implemented for armfortas's
4 //! lowering layer. Covers integer (general-purpose) registers `x0-x7`,
5 //! floating-point `s0-s7` / `d0-d7`, NEON vector `v0-v7`, the i128 register
6 //! pair lane (`x{N}, x{N+1}`), and stack-overflow assignment with proper
7 //! per-type alignment.
8 //!
9 //! Extracted from `isel.rs` so the ABI assumption lives in one place. Any
10 //! future ABI variant (e.g. Linux AArch64, with its different va_list shape)
11 //! goes here, not in instruction selection.
12
13 use crate::ir::types::{FloatWidth, IntWidth, IrType};
14
15 /// Where a single argument lives after AAPCS64 classification.
16 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
17 pub enum AbiArgLoc {
18 Gp(u8),
19 Gp32(u8),
20 Fp(u8),
21 Fp32(u8),
22 GpPair(u8),
23 /// 128-bit NEON vector via `v0-v7`. Per AAPCS64, vectors share
24 /// the same physical bank as floats; the V form is the 128-bit
25 /// view of the same register.
26 V128(u8),
27 Stack(i64),
28 }
29
30 /// Running tally of consumed argument slots, used while classifying a
31 /// signature one parameter at a time.
32 #[derive(Debug, Default, Clone, Copy)]
33 pub struct AbiArgState {
34 pub gp_idx: u8,
35 pub fp_idx: u8,
36 pub stack_offset: i64,
37 }
38
39 /// Round `value` up to the next multiple of `align` (which must be a
40 /// power of two).
41 pub fn align_to(value: i64, align: i64) -> i64 {
42 debug_assert!(align > 0 && (align & (align - 1)) == 0);
43 (value + align - 1) & !(align - 1)
44 }
45
46 /// `(size, align)` of a type when passed on the stack overflow area
47 /// per AAPCS64 §6.4. Mostly the natural ABI size; vectors are 16-byte
48 /// aligned, i128 takes a 16-byte slot.
49 pub fn abi_stack_layout(ty: &IrType) -> (i64, i64) {
50 match ty {
51 IrType::Int(IntWidth::I128) => (16, 16),
52 IrType::Int(IntWidth::I64) | IrType::Ptr(_) | IrType::FuncPtr(_) => (8, 8),
53 IrType::Float(FloatWidth::F64) => (8, 8),
54 // TODO: integer(c_short), value actuals still appear widened before
55 // reaching call lowering, so end-to-end 16-bit VALUE ABI parity needs
56 // a follow-up beyond this stack-packing fix.
57 IrType::Float(FloatWidth::F32) => (4, 4),
58 IrType::Int(IntWidth::I32) => (4, 4),
59 IrType::Int(IntWidth::I16) => (2, 2),
60 IrType::Int(IntWidth::I8) | IrType::Bool => (1, 1),
61 IrType::Vector { .. } => (16, 16),
62 _ => (8, 8),
63 }
64 }
65
66 /// Place one argument according to AAPCS64. Updates `state` in-place
67 /// to reflect the consumed slot.
68 pub fn classify_abi_arg(ty: &IrType, state: &mut AbiArgState) -> AbiArgLoc {
69 match ty {
70 IrType::Int(IntWidth::I128) => {
71 if state.gp_idx + 2 <= 8 {
72 let reg = state.gp_idx;
73 state.gp_idx += 2;
74 AbiArgLoc::GpPair(reg)
75 } else {
76 state.gp_idx = 8;
77 let (size, align) = abi_stack_layout(ty);
78 let offset = align_to(state.stack_offset, align);
79 state.stack_offset = offset + size;
80 AbiArgLoc::Stack(offset)
81 }
82 }
83 IrType::Float(FloatWidth::F64) => {
84 if state.fp_idx < 8 {
85 let reg = state.fp_idx;
86 state.fp_idx += 1;
87 AbiArgLoc::Fp(reg)
88 } else {
89 let (size, align) = abi_stack_layout(ty);
90 let offset = align_to(state.stack_offset, align);
91 state.stack_offset = offset + size;
92 AbiArgLoc::Stack(offset)
93 }
94 }
95 IrType::Float(FloatWidth::F32) => {
96 if state.fp_idx < 8 {
97 let reg = state.fp_idx;
98 state.fp_idx += 1;
99 AbiArgLoc::Fp32(reg)
100 } else {
101 let (size, align) = abi_stack_layout(ty);
102 let offset = align_to(state.stack_offset, align);
103 state.stack_offset = offset + size;
104 AbiArgLoc::Stack(offset)
105 }
106 }
107 IrType::Int(IntWidth::I8)
108 | IrType::Int(IntWidth::I16)
109 | IrType::Int(IntWidth::I32)
110 | IrType::Bool => {
111 if state.gp_idx < 8 {
112 let reg = state.gp_idx;
113 state.gp_idx += 1;
114 AbiArgLoc::Gp32(reg)
115 } else {
116 state.gp_idx = 8;
117 let (size, align) = abi_stack_layout(ty);
118 let offset = align_to(state.stack_offset, align);
119 state.stack_offset = offset + size;
120 AbiArgLoc::Stack(offset)
121 }
122 }
123 IrType::Vector { .. } => {
124 // AAPCS64 §6.4.2: vector args pass in v0-v7, sharing the
125 // same idx counter as float args (the V registers ARE the
126 // 128-bit form of the same physical regs).
127 if state.fp_idx < 8 {
128 let reg = state.fp_idx;
129 state.fp_idx += 1;
130 AbiArgLoc::V128(reg)
131 } else {
132 let (size, align) = abi_stack_layout(ty);
133 let offset = align_to(state.stack_offset, align);
134 state.stack_offset = offset + size;
135 AbiArgLoc::Stack(offset)
136 }
137 }
138 _ => {
139 if state.gp_idx < 8 {
140 let reg = state.gp_idx;
141 state.gp_idx += 1;
142 AbiArgLoc::Gp(reg)
143 } else {
144 state.gp_idx = 8;
145 let (size, align) = abi_stack_layout(ty);
146 let offset = align_to(state.stack_offset, align);
147 state.stack_offset = offset + size;
148 AbiArgLoc::Stack(offset)
149 }
150 }
151 }
152 }
153