Skip to main content

rucc_abi/
classify.rs

1//! The one classifier every ABI is run through, and the four mechanisms it is built from.
2//!
3//! Design: `spec/cross-compile/06-abis.md` section 6.7.
4//!
5//! [`crate::describe`] argues for the split between mechanism and policy. This is the mechanism
6//! half. There are four things in here that look inside an aggregate, and every ABI in
7//! [`crate::abis`] is one of them plus a size rule plus an order.
8//!
9//! # Ask about the return value first
10//!
11//! On three of the five ABIs described here, a return value that comes back in memory takes an
12//! argument register with it on the way past, so a function returning a large structure has one
13//! argument register fewer than the same function returning `int`. Classifying the arguments
14//! before the return value gives a different answer for the last argument, and it is a different
15//! answer rather than an error, which is the worst kind.
16//!
17//! [`Call::returns`] therefore comes first and [`Call::argument`] is asked once per argument in
18//! source order. Asking out of order answers for a different program.
19
20use crate::describe::{
21    AbiDescription, Banks, ReturnPointer, Rule, Scalars, Short, Test, Travel, Variadic,
22};
23use crate::shape::{Arg, Format, Kind, Pass, Scalar, Shape, Slot};
24
25/// The registers one call has left.
26///
27/// Made by [`AbiDescription::call`], asked about the return value first and then about each
28/// argument in order.
29#[derive(Debug, Clone)]
30pub struct Call {
31    /// The ABI being followed.
32    abi: &'static AbiDescription,
33    /// General purpose argument registers left. On an ABI whose banks are shared this is the
34    /// argument positions left, since there both kinds of register share them.
35    integer: u32,
36    /// Floating point argument registers left.
37    float: u32,
38}
39
40impl AbiDescription {
41    /// The start of one call, with every argument register still to spend.
42    #[must_use]
43    pub const fn call(&'static self) -> Call {
44        Call { abi: self, integer: self.banks.integer, float: self.banks.float }
45    }
46}
47
48impl Call {
49    /// The ABI this call follows.
50    #[must_use]
51    pub const fn abi(&self) -> &'static AbiDescription {
52        self.abi
53    }
54
55    /// General purpose argument registers left, which is what a test asserts about draining.
56    #[must_use]
57    pub const fn integer_left(&self) -> u32 {
58        self.integer
59    }
60
61    /// Floating point argument registers left.
62    #[must_use]
63    pub const fn float_left(&self) -> u32 {
64        self.float
65    }
66
67    /// How the return value comes back, which is asked before anything else.
68    #[must_use]
69    pub fn returns(&mut self, arg: &Arg<'_>) -> Pass {
70        let shape = match arg {
71            // A returned scalar comes back in the first register of its bank and spends nothing,
72            // because the registers a return value uses are not the ones arguments use.
73            Arg::Void => return Pass::Ignore,
74            Arg::Scalar(_) => return Pass::Direct,
75            Arg::Aggregate(shape) => *shape,
76        };
77        self.apply(self.abi.returns, &shape, true)
78    }
79
80    /// How the next fixed argument travels, which spends whatever registers it takes.
81    #[must_use]
82    pub fn argument(&mut self, arg: &Arg<'_>) -> Pass {
83        let shape = match arg {
84            Arg::Void => return Pass::Ignore,
85            Arg::Scalar(scalar) => return self.scalar(*scalar),
86            Arg::Aggregate(shape) => *shape,
87        };
88        self.apply(self.abi.arguments, &shape, false)
89    }
90
91    /// How the next argument past the `...` travels.
92    ///
93    /// Only one of the three policies changes the answer this crate gives. Under
94    /// [`Variadic::AlwaysMemory`] the argument is classified as though no argument registers were
95    /// left, which is Darwin arm64's rule stated in the one form that needs no new mechanism.
96    /// [`Variadic::BothBanks`] is a fact about which registers the backend has to write, not
97    /// about the form the value travels in, so the answer here is the same as for a fixed
98    /// argument and the description carries the flag for the backend to read.
99    #[must_use]
100    pub fn variadic_argument(&mut self, arg: &Arg<'_>) -> Pass {
101        match self.abi.variadic {
102            Variadic::SameAsFixed | Variadic::BothBanks => self.argument(arg),
103            Variadic::AlwaysMemory => {
104                let shape = match arg {
105                    Arg::Void => return Pass::Ignore,
106                    // On the stack, in the same form, spending nothing.
107                    Arg::Scalar(_) => return Pass::Direct,
108                    Arg::Aggregate(shape) => *shape,
109                };
110                // A scratch call with nothing left. Every rule that wanted a register runs
111                // short, which is exactly what "always on the stack" means, and the real banks
112                // are untouched because a variadic argument does not spend one.
113                let mut empty = Self { abi: self.abi, integer: 0, float: 0 };
114                empty.apply(self.abi.arguments, &shape, false)
115            }
116        }
117    }
118
119    /// How a scalar argument travels, which is always as itself, and what it costs.
120    fn scalar(&mut self, scalar: Scalar) -> Pass {
121        let Banks { shared, integer_width, float_width, .. } = self.abi.banks;
122        let Scalars { in_memory, wide_integer_is_all_or_nothing } = self.abi.scalars;
123        // A `long double` argument on SysV is in the argument area and there is no register file
124        // it could have gone in, so it costs nothing and leaves the banks alone.
125        if matches!(scalar.kind, Kind::Float(format) if Some(format) == in_memory) {
126            return Pass::Direct;
127        }
128        let want = registers(scalar.size, integer_width);
129        match scalar.kind {
130            // Shared banks mean there is one sequence of positions and every value takes the
131            // next one, whichever kind of register it ends up in.
132            _ if shared => self.integer = self.integer.saturating_sub(1),
133            Kind::Float(_) if scalar.size <= float_width => {
134                self.float = self.float.saturating_sub(1);
135            }
136            // Wider than a vector register holds, which is a `long double` on RISC-V LP64D. It
137            // travels in general purpose registers like an integer of the same size.
138            Kind::Float(_) => self.integer = self.integer.saturating_sub(want),
139            Kind::Integer if wide_integer_is_all_or_nothing => {
140                if want <= self.integer {
141                    self.integer -= want;
142                }
143            }
144            Kind::Integer => self.integer = self.integer.saturating_sub(want),
145        }
146        Pass::Direct
147    }
148
149    /// The first rule whose test matches, with what it costs applied.
150    fn apply(&mut self, rules: &'static [Rule], shape: &Shape<'_>, returning: bool) -> Pass {
151        for rule in rules {
152            let Some(found) = self.matches(rule.when, shape) else { continue };
153            match self.travel(rule, &found, shape, returning) {
154                Some(pass) => return pass,
155                // The rule ran short of registers and said to try the next one.
156                None => continue,
157            }
158        }
159        // A description whose last rule is not `Test::Anything` has a hole in it, and the test
160        // in `abis.rs` is what stops one being written. Reaching here means that test is gone.
161        unreachable!("every rule list ends with a rule that matches anything")
162    }
163
164    /// Whether a test matches, and the slots it found if it is one of the tests that looks
165    /// inside.
166    fn matches(&self, test: Test, shape: &Shape<'_>) -> Option<Vec<Slot>> {
167        let Banks { integer_width, float_width, .. } = self.abi.banks;
168        match test {
169            Test::Anything => Some(Vec::new()),
170            Test::Empty => (shape.size == 0).then(Vec::new),
171            Test::SizeOneOf(sizes) => sizes.contains(&shape.size).then(Vec::new),
172            Test::SizeAtMost(limit) => (shape.size <= limit).then(Vec::new),
173            Test::Homogeneous { limit } => homogeneous(shape, limit),
174            Test::FloatPair => float_pair(shape, integer_width, float_width),
175            Test::X87Stack => x87_stack(shape),
176            Test::Eightbytes { limit } => eightbytes(shape, limit),
177        }
178    }
179
180    /// The pass a matched rule produces, and [`None`] if it ran short and said to try the next
181    /// rule.
182    fn travel(
183        &mut self,
184        rule: &Rule,
185        found: &[Slot],
186        shape: &Shape<'_>,
187        returning: bool,
188    ) -> Option<Pass> {
189        let width = self.abi.banks.integer_width;
190        let slots = match rule.then {
191            Travel::Ignore => return Some(Pass::Ignore),
192            Travel::InMemory => return Some(Pass::Memory),
193            Travel::ByReference => {
194                // As an argument the address is one more argument. As a return value it is
195                // whichever register this ABI reserves for the purpose, and on AAPCS64 that is
196                // not an argument register at all.
197                if !returning || self.abi.return_pointer == ReturnPointer::FirstArgument {
198                    self.integer = self.integer.saturating_sub(1);
199                }
200                return Some(Pass::Reference);
201            }
202            Travel::AsFound => found.to_vec(),
203            Travel::AsIntegers => integer_slots(shape.size, width),
204            Travel::AsOneInteger => {
205                vec![Slot::Integer { offset: 0, size: u32::try_from(shape.size).unwrap_or(8) }]
206            }
207        };
208        // A return value in registers spends nothing: the registers a value comes back in are
209        // not the ones arguments go out in.
210        if returning {
211            return Some(Pass::Pieces(slots));
212        }
213        let (integer, float) = self.cost(&slots);
214        if integer <= self.integer && float <= self.float {
215            self.integer -= integer;
216            self.float -= float;
217            return Some(Pass::Pieces(slots));
218        }
219        match rule.short {
220            Short::Unchanged => {
221                self.integer = self.integer.saturating_sub(integer);
222                self.float = self.float.saturating_sub(float);
223                Some(Pass::Pieces(slots))
224            }
225            Short::Memory => Some(Pass::Memory),
226            Short::MemoryAndDrain => {
227                // Whichever bank it could not be served from is spent, so that nothing after it
228                // gets a register the ABI would have had to skip over.
229                if integer > self.integer {
230                    self.integer = 0;
231                }
232                if float > self.float {
233                    self.float = 0;
234                }
235                Some(Pass::Memory)
236            }
237            Short::TryNextRule => None,
238        }
239    }
240
241    /// What a run of slots costs, as general purpose registers and then vector registers.
242    fn cost(&self, slots: &[Slot]) -> (u32, u32) {
243        let count = u32::try_from(slots.len()).unwrap_or(u32::MAX);
244        if self.abi.banks.shared {
245            // One position per register's worth, whichever bank it lands in.
246            return (count, 0);
247        }
248        let float =
249            u32::try_from(slots.iter().filter(|slot| slot.is_float()).count()).unwrap_or(u32::MAX);
250        (count - float, float)
251    }
252}
253
254/// How many registers of this width a value of this size takes, which is at least one.
255fn registers(size: u64, width: u64) -> u32 {
256    u32::try_from(size.div_ceil(width.max(1))).unwrap_or(1).max(1)
257}
258
259/// An object of this size as a run of integer registers, the last one holding only what is left.
260///
261/// The last slot being narrow is not tidiness. A twelve byte structure at the end of a page is
262/// twelve readable bytes followed by four that are not, and a load of the full register width
263/// there faults on a program that is correct.
264fn integer_slots(size: u64, width: u64) -> Vec<Slot> {
265    let width = width.max(1);
266    (0..size.div_ceil(width))
267        .map(|index| Slot::Integer {
268            offset: index * width,
269            size: u32::try_from((size - index * width).min(width)).unwrap_or(8),
270        })
271        .collect()
272}
273
274/// The vector registers of a homogeneous floating point aggregate, and [`None`] for anything
275/// else.
276///
277/// Homogeneous means every scalar is the same floating point type once arrays and nested records
278/// are flattened out, and that they fill the aggregate. The second half is what rules out
279/// `struct { float a; char pad[8]; }`, which has one floating point member and is not an HFA,
280/// and anything a zero width bit-field has stretched.
281fn homogeneous(shape: &Shape<'_>, limit: usize) -> Option<Vec<Slot>> {
282    let first = shape.pieces.first()?;
283    let Kind::Float(format) = first.scalar.kind else { return None };
284    let count = shape.pieces.len();
285    if count > limit || shape.pieces.iter().any(|piece| piece.scalar != first.scalar) {
286        return None;
287    }
288    let fills = first.scalar.size.checked_mul(count as u64) == Some(shape.size);
289    fills.then(|| {
290        shape.pieces.iter().map(|piece| Slot::Float { offset: piece.offset, format }).collect()
291    })
292}
293
294/// The registers a one or two member aggregate travels in under the RISC-V floating point rule,
295/// and [`None`] for one the rule does not reach.
296///
297/// A member wider than a floating point register is not a floating point member for this
298/// purpose, which is why a `long double` on LP64D makes the aggregate holding it an ordinary
299/// integer pair.
300fn float_pair(shape: &Shape<'_>, integer_width: u64, float_width: u64) -> Option<Vec<Slot>> {
301    let slot = |piece: &crate::shape::Piece| match piece.scalar.kind {
302        Kind::Float(format) if piece.scalar.size <= float_width => {
303            Some(Slot::Float { offset: piece.offset, format })
304        }
305        Kind::Integer if piece.scalar.size <= integer_width => Some(Slot::Integer {
306            offset: piece.offset,
307            size: u32::try_from(piece.scalar.size).ok()?,
308        }),
309        _ => None,
310    };
311    let floats = shape.pieces.iter().filter(|piece| piece.scalar.is_float()).count();
312    match shape.pieces {
313        // One floating point member, in the register the member itself would have used.
314        [only] if floats == 1 => Some(vec![slot(only)?]),
315        // Two members with at least one floating point member between them. Two integers are not
316        // this: they are the ordinary size rule, and the ordinary size rule gives them the same
317        // two registers anyway.
318        [first, second] if floats > 0 => Some(vec![slot(first)?, slot(second)?]),
319        _ => None,
320    }
321}
322
323/// The x87 stack registers a `long double` or a `_Complex long double` comes back in, and
324/// [`None`] for anything else.
325fn x87_stack(shape: &Shape<'_>) -> Option<Vec<Slot>> {
326    let one_value = shape.pieces.len() == 1 || (shape.pieces.len() == 2 && shape.complex);
327    let all_x87 = shape.is_all_of(Format::X87Extended);
328    (all_x87 && one_value).then(|| {
329        shape
330            .pieces
331            .iter()
332            .map(|piece| Slot::Float { offset: piece.offset, format: Format::X87Extended })
333            .collect()
334    })
335}
336
337/// The class of one eightbyte, section 3.2.3 of the SysV psABI.
338///
339/// SSEUP and X87UP are not here. Both mean "the continuation of the eightbyte before this one",
340/// and the only two things that produce them are a vector wider than eight bytes, which is not
341/// an aggregate and does not come through here, and a `long double`, whose two eightbytes are
342/// treated as the one value they are.
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344enum Class {
345    /// Nothing reaches into it, which takes padding or an empty member.
346    None,
347    /// A general purpose register.
348    Integer,
349    /// A vector register.
350    Sse,
351    /// The x87 stack.
352    X87,
353    /// Memory, which takes the whole argument with it.
354    Memory,
355}
356
357/// Two classes over one eightbyte, section 3.2.3's merge rule.
358fn merge(left: Class, right: Class) -> Class {
359    match (left, right) {
360        (a, b) if a == b => a,
361        (Class::None, other) | (other, Class::None) => other,
362        (Class::Memory, _) | (_, Class::Memory) => Class::Memory,
363        // An x87 value shares an eightbyte with something else only in a packed record, and
364        // there is no way to pass the two of them together.
365        (Class::X87, _) | (_, Class::X87) => Class::Memory,
366        // The rule that surprises people: one `int` in an eightbyte sends the `float` beside it
367        // into a general purpose register.
368        (Class::Integer, _) | (_, Class::Integer) => Class::Integer,
369        _ => Class::Sse,
370    }
371}
372
373/// The slots the SysV classification produces, and [`None`] when the answer is memory.
374///
375/// x87 counts as memory here. As an argument that is the right answer directly, and as a return
376/// value the x87 stack rule is a separate rule earlier in the list, so by the time this runs an
377/// x87 class means the value goes back in memory either way.
378fn eightbytes(shape: &Shape<'_>, limit: u64) -> Option<Vec<Slot>> {
379    if shape.size > limit {
380        return None;
381    }
382    let mut classes = vec![Class::None; usize::try_from(shape.size.div_ceil(8)).ok()?];
383    for piece in shape.pieces {
384        // A member away from its natural alignment is what `packed` makes, and it is the second
385        // of the two things section 3.2.3 sends straight to memory.
386        if piece.scalar.align > 1 && piece.offset % piece.scalar.align != 0 {
387            return None;
388        }
389        let class = match piece.scalar.kind {
390            Kind::Integer => Class::Integer,
391            Kind::Float(Format::X87Extended) => Class::X87,
392            Kind::Float(_) => Class::Sse,
393        };
394        for at in piece.offset / 8..=(piece.end() - 1) / 8 {
395            let slot = classes.get_mut(usize::try_from(at).ok()?)?;
396            *slot = merge(*slot, class);
397        }
398    }
399    if classes.iter().any(|class| matches!(class, Class::Memory | Class::X87)) {
400        return None;
401    }
402    Some(
403        classes
404            .iter()
405            .enumerate()
406            .map(|(index, class)| {
407                let offset = index as u64 * 8;
408                let bytes = (shape.size - offset).min(8);
409                match class {
410                    // Four bytes or fewer of floating point is one `float`. More than that is a
411                    // `double` or two `float`s, which arrive in the same register either way.
412                    Class::Sse if bytes <= 4 => Slot::Float { offset, format: Format::Single },
413                    Class::Sse => Slot::Float { offset, format: Format::Double },
414                    // An eightbyte nothing reaches into still travels, and it travels in a
415                    // general purpose register, because an ABI does not leave a hole in the
416                    // middle of an argument.
417                    _ => Slot::Integer { offset, size: u32::try_from(bytes).unwrap_or(8) },
418                }
419            })
420            .collect(),
421    )
422}