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, StackArgs, 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. Unless no
73 // register holds it, where the caller passes somewhere to put it and the callee
74 // writes it there, the same as for an aggregate of that size.
75 Arg::Void => return Pass::Ignore,
76 Arg::Scalar(scalar) if self.by_reference(*scalar) => {
77 // Except for the one the ABI brings back in a vector register anyway, which is
78 // an `__int128` on Windows: it goes out as an address and comes back in xmm0.
79 let vector = self.abi.scalars.wide_integer_returns_in;
80 if let (Kind::Integer, Some(format)) = (scalar.kind, vector) {
81 return Pass::Pieces(vec![Slot::Float { offset: 0, format }]);
82 }
83 if self.abi.return_pointer == ReturnPointer::FirstArgument {
84 self.integer = self.integer.saturating_sub(1);
85 }
86 return Pass::Reference;
87 }
88 Arg::Scalar(_) => return Pass::Direct,
89 Arg::Aggregate(shape) => *shape,
90 };
91 self.apply(self.abi.returns, &shape, true)
92 }
93
94 /// How the next fixed argument travels, which spends whatever registers it takes.
95 #[must_use]
96 pub fn argument(&mut self, arg: &Arg<'_>) -> Pass {
97 let shape = match arg {
98 Arg::Void => return Pass::Ignore,
99 Arg::Scalar(scalar) => return self.scalar(*scalar),
100 Arg::Aggregate(shape) => *shape,
101 };
102 self.apply(self.abi.arguments, &shape, false)
103 }
104
105 /// How the next argument past the `...` travels.
106 ///
107 /// Only one of the three policies changes the answer this crate gives. Under
108 /// [`Variadic::AlwaysMemory`] the argument is classified as though no argument registers were
109 /// left, which is Darwin arm64's rule stated in the one form that needs no new mechanism.
110 /// [`Variadic::BothBanks`] is a fact about which registers the backend has to write, not
111 /// about the form the value travels in, so the answer here is the same as for a fixed
112 /// argument and the description carries the flag for the backend to read.
113 #[must_use]
114 pub fn variadic_argument(&mut self, arg: &Arg<'_>) -> Pass {
115 match self.abi.variadic {
116 Variadic::SameAsFixed | Variadic::BothBanks => self.argument(arg),
117 Variadic::AlwaysMemory => {
118 let shape = match arg {
119 Arg::Void => return Pass::Ignore,
120 // On the stack, in the same form, spending nothing.
121 Arg::Scalar(_) => return Pass::Direct,
122 Arg::Aggregate(shape) => *shape,
123 };
124 // A scratch call with nothing left. Every rule that wanted a register runs
125 // short, which is exactly what "always on the stack" means, and the real banks
126 // are untouched because a variadic argument does not spend one.
127 let mut empty = Self { abi: self.abi, integer: 0, float: 0 };
128 empty.apply(self.abi.arguments, &shape, false)
129 }
130 }
131 }
132
133 /// What a fixed argument that travels as [`Pass::Memory`] is aligned to in the argument area.
134 ///
135 /// Its own alignment on every ABI but the one that packs the area, and the backend rounds that
136 /// to a word. Darwin arm64 packs, and there the answer depends on what the aggregate is. A
137 /// homogeneous floating point aggregate keeps the alignment of its members, so three `float`s
138 /// are twelve bytes on a four byte boundary. Anything else is on a word boundary, which is what
139 /// clang makes of it by passing it as an array of `i64`, and the backend takes the size rounded
140 /// up to the alignment, so a record of three `char`s is one word. An argument past the `...`
141 /// is not packed, so this is not asked about one.
142 #[must_use]
143 pub fn in_memory(&self, shape: &Shape<'_>) -> u64 {
144 if self.abi.stack_args != StackArgs::Packed {
145 return shape.align;
146 }
147 let limit = self.abi.arguments.iter().find_map(|rule| match rule.when {
148 Test::Homogeneous { limit } => Some(limit),
149 _ => None,
150 });
151 if limit.is_some_and(|limit| homogeneous(shape, limit).is_some()) {
152 return shape.align;
153 }
154 shape.align.max(self.abi.banks.integer_width)
155 }
156
157 /// Whether a scalar travels as the address of a copy rather than as itself.
158 ///
159 /// The size rule of the one ABI that does this is written over the size of the object and
160 /// says nothing about what is in it, so it is asked here the same way and of the same sizes
161 /// the aggregate rules in [`crate::abis`] are written with. That is also why the rule itself
162 /// is on the description rather than here: a back end pass writing a call to a runtime routine
163 /// has to ask the same question with a width and no C type behind it.
164 fn by_reference(&self, scalar: Scalar) -> bool {
165 self.abi.scalar_is_by_reference(scalar.size)
166 }
167
168 /// How a scalar argument travels, which is as itself wherever a register holds it, and what it
169 /// costs.
170 fn scalar(&mut self, scalar: Scalar) -> Pass {
171 let Banks { shared, integer_width, float_width, .. } = self.abi.banks;
172 let Scalars { in_memory, wide_integer_is_all_or_nothing, .. } = self.abi.scalars;
173 // A scalar no register holds is the address of a copy the caller made, which costs the
174 // one position that address travels in and nothing else.
175 if self.by_reference(scalar) {
176 self.integer = self.integer.saturating_sub(1);
177 return Pass::Reference;
178 }
179 // A `long double` argument on SysV is in the argument area and there is no register file
180 // it could have gone in, so it costs nothing and leaves the banks alone.
181 if matches!(scalar.kind, Kind::Float(format) if Some(format) == in_memory) {
182 return Pass::Direct;
183 }
184 let want = registers(scalar.size, integer_width);
185 match scalar.kind {
186 // Shared banks mean there is one sequence of positions and every value takes the
187 // next one, whichever kind of register it ends up in.
188 _ if shared => self.integer = self.integer.saturating_sub(1),
189 Kind::Float(_) if scalar.size <= float_width => {
190 self.float = self.float.saturating_sub(1);
191 }
192 // Wider than a vector register holds, which is a `long double` on RISC-V LP64D. It
193 // travels in general purpose registers like an integer of the same size.
194 Kind::Float(_) => self.integer = self.integer.saturating_sub(want),
195 Kind::Integer if wide_integer_is_all_or_nothing => {
196 if want <= self.integer {
197 self.integer -= want;
198 }
199 }
200 Kind::Integer => self.integer = self.integer.saturating_sub(want),
201 }
202 Pass::Direct
203 }
204
205 /// The first rule whose test matches, with what it costs applied.
206 fn apply(&mut self, rules: &'static [Rule], shape: &Shape<'_>, returning: bool) -> Pass {
207 for rule in rules {
208 let Some(found) = self.matches(rule.when, shape) else { continue };
209 match self.travel(rule, &found, shape, returning) {
210 Some(pass) => return pass,
211 // The rule ran short of registers and said to try the next one.
212 None => continue,
213 }
214 }
215 // A description whose last rule is not `Test::Anything` has a hole in it, and the test
216 // in `abis.rs` is what stops one being written. Reaching here means that test is gone.
217 unreachable!("every rule list ends with a rule that matches anything")
218 }
219
220 /// Whether a test matches, and the slots it found if it is one of the tests that looks
221 /// inside.
222 fn matches(&self, test: Test, shape: &Shape<'_>) -> Option<Vec<Slot>> {
223 let Banks { integer_width, float_width, .. } = self.abi.banks;
224 match test {
225 Test::Anything => Some(Vec::new()),
226 Test::Empty => (shape.size == 0).then(Vec::new),
227 Test::SizeOneOf(sizes) => sizes.contains(&shape.size).then(Vec::new),
228 Test::SizeAtMost(limit) => (shape.size <= limit).then(Vec::new),
229 Test::Homogeneous { limit } => homogeneous(shape, limit),
230 Test::FloatPair => float_pair(shape, integer_width, float_width),
231 Test::X87Stack => x87_stack(shape),
232 Test::Eightbytes { limit } => eightbytes(shape, limit),
233 }
234 }
235
236 /// The pass a matched rule produces, and [`None`] if it ran short and said to try the next
237 /// rule.
238 fn travel(
239 &mut self,
240 rule: &Rule,
241 found: &[Slot],
242 shape: &Shape<'_>,
243 returning: bool,
244 ) -> Option<Pass> {
245 let width = self.abi.banks.integer_width;
246 let slots = match rule.then {
247 Travel::Ignore => return Some(Pass::Ignore),
248 Travel::InMemory => return Some(Pass::Memory),
249 Travel::ByReference => {
250 // As an argument the address is one more argument. As a return value it is
251 // whichever register this ABI reserves for the purpose, and on AAPCS64 that is
252 // not an argument register at all.
253 if !returning || self.abi.return_pointer == ReturnPointer::FirstArgument {
254 self.integer = self.integer.saturating_sub(1);
255 }
256 return Some(Pass::Reference);
257 }
258 Travel::AsFound => found.to_vec(),
259 Travel::AsIntegers => integer_slots(shape.size, width),
260 Travel::AsOneInteger => {
261 vec![Slot::Integer { offset: 0, size: u32::try_from(shape.size).unwrap_or(8) }]
262 }
263 };
264 // A return value in registers spends nothing: the registers a value comes back in are
265 // not the ones arguments go out in.
266 if returning {
267 return Some(Pass::Pieces(slots));
268 }
269 let (integer, float) = self.cost(&slots);
270 if integer <= self.integer && float <= self.float {
271 self.integer -= integer;
272 self.float -= float;
273 return Some(Pass::Pieces(slots));
274 }
275 match rule.short {
276 Short::Unchanged => {
277 self.integer = self.integer.saturating_sub(integer);
278 self.float = self.float.saturating_sub(float);
279 Some(Pass::Pieces(slots))
280 }
281 Short::Memory => Some(Pass::Memory),
282 Short::MemoryAndDrain => {
283 // Whichever bank it could not be served from is spent, so that nothing after it
284 // gets a register the ABI would have had to skip over.
285 if integer > self.integer {
286 self.integer = 0;
287 }
288 if float > self.float {
289 self.float = 0;
290 }
291 Some(Pass::Memory)
292 }
293 Short::TryNextRule => None,
294 }
295 }
296
297 /// What a run of slots costs, as general purpose registers and then vector registers.
298 fn cost(&self, slots: &[Slot]) -> (u32, u32) {
299 let count = u32::try_from(slots.len()).unwrap_or(u32::MAX);
300 if self.abi.banks.shared {
301 // One position per register's worth, whichever bank it lands in.
302 return (count, 0);
303 }
304 let float =
305 u32::try_from(slots.iter().filter(|slot| slot.is_float()).count()).unwrap_or(u32::MAX);
306 (count - float, float)
307 }
308}
309
310/// How many registers of this width a value of this size takes, which is at least one.
311fn registers(size: u64, width: u64) -> u32 {
312 u32::try_from(size.div_ceil(width.max(1))).unwrap_or(1).max(1)
313}
314
315/// An object of this size as a run of integer registers, the last one holding only what is left.
316///
317/// The last slot being narrow is not tidiness. A twelve byte structure at the end of a page is
318/// twelve readable bytes followed by four that are not, and a load of the full register width
319/// there faults on a program that is correct.
320fn integer_slots(size: u64, width: u64) -> Vec<Slot> {
321 let width = width.max(1);
322 (0..size.div_ceil(width))
323 .map(|index| Slot::Integer {
324 offset: index * width,
325 size: u32::try_from((size - index * width).min(width)).unwrap_or(8),
326 })
327 .collect()
328}
329
330/// The vector registers of a homogeneous floating point aggregate, and [`None`] for anything
331/// else.
332///
333/// Homogeneous means every scalar is the same floating point type once arrays and nested records
334/// are flattened out, and that they fill the aggregate. The second half is what rules out
335/// `struct { float a; char pad[8]; }`, which has one floating point member and is not an HFA,
336/// and anything a zero width bit-field has stretched.
337fn homogeneous(shape: &Shape<'_>, limit: usize) -> Option<Vec<Slot>> {
338 let first = shape.pieces.first()?;
339 let Kind::Float(format) = first.scalar.kind else { return None };
340 let count = shape.pieces.len();
341 if count > limit || shape.pieces.iter().any(|piece| piece.scalar != first.scalar) {
342 return None;
343 }
344 let fills = first.scalar.size.checked_mul(count as u64) == Some(shape.size);
345 fills.then(|| {
346 shape.pieces.iter().map(|piece| Slot::Float { offset: piece.offset, format }).collect()
347 })
348}
349
350/// The registers a one or two member aggregate travels in under the RISC-V floating point rule,
351/// and [`None`] for one the rule does not reach.
352///
353/// A member wider than a floating point register is not a floating point member for this
354/// purpose, which is why a `long double` on LP64D makes the aggregate holding it an ordinary
355/// integer pair.
356fn float_pair(shape: &Shape<'_>, integer_width: u64, float_width: u64) -> Option<Vec<Slot>> {
357 let slot = |piece: &crate::shape::Piece| match piece.scalar.kind {
358 Kind::Float(format) if piece.scalar.size <= float_width => {
359 Some(Slot::Float { offset: piece.offset, format })
360 }
361 Kind::Integer if piece.scalar.size <= integer_width => Some(Slot::Integer {
362 offset: piece.offset,
363 size: u32::try_from(piece.scalar.size).ok()?,
364 }),
365 _ => None,
366 };
367 let floats = shape.pieces.iter().filter(|piece| piece.scalar.is_float()).count();
368 match shape.pieces {
369 // One floating point member, in the register the member itself would have used.
370 [only] if floats == 1 => Some(vec![slot(only)?]),
371 // Two members with at least one floating point member between them. Two integers are not
372 // this: they are the ordinary size rule, and the ordinary size rule gives them the same
373 // two registers anyway.
374 [first, second] if floats > 0 => Some(vec![slot(first)?, slot(second)?]),
375 _ => None,
376 }
377}
378
379/// The x87 stack registers a `long double` or a `_Complex long double` comes back in, and
380/// [`None`] for anything else.
381fn x87_stack(shape: &Shape<'_>) -> Option<Vec<Slot>> {
382 let one_value = shape.pieces.len() == 1 || (shape.pieces.len() == 2 && shape.complex);
383 let all_x87 = shape.is_all_of(Format::X87Extended);
384 (all_x87 && one_value).then(|| {
385 shape
386 .pieces
387 .iter()
388 .map(|piece| Slot::Float { offset: piece.offset, format: Format::X87Extended })
389 .collect()
390 })
391}
392
393/// The class of one eightbyte, section 3.2.3 of the SysV psABI.
394///
395/// X87UP is not here. It means "the second eightbyte of the `long double` before this one", and
396/// an x87 value in an aggregate goes to memory on every path through here anyway, so the two
397/// classes would have the same answer.
398#[derive(Debug, Clone, Copy, PartialEq, Eq)]
399enum Class {
400 /// Nothing reaches into it, which takes padding or an empty member.
401 None,
402 /// A general purpose register.
403 Integer,
404 /// A vector register.
405 Sse,
406 /// The rest of the value whose first eightbyte was [`Class::Sse`], which is what the upper
407 /// half of a `_Float128` is. The pair travels in one vector register rather than two.
408 SseUp,
409 /// The x87 stack.
410 X87,
411 /// Memory, which takes the whole argument with it.
412 Memory,
413}
414
415/// Two classes over one eightbyte, section 3.2.3's merge rule.
416fn merge(left: Class, right: Class) -> Class {
417 match (left, right) {
418 (a, b) if a == b => a,
419 (Class::None, other) | (other, Class::None) => other,
420 (Class::Memory, _) | (_, Class::Memory) => Class::Memory,
421 // An x87 value shares an eightbyte with something else only in a packed record, and
422 // there is no way to pass the two of them together.
423 (Class::X87, _) | (_, Class::X87) => Class::Memory,
424 // The rule that surprises people: one `int` in an eightbyte sends the `float` beside it
425 // into a general purpose register.
426 (Class::Integer, _) | (_, Class::Integer) => Class::Integer,
427 // An upper half sharing its eightbyte with anything else is no longer an upper half, so
428 // the two of them are an ordinary vector register between them.
429 _ => Class::Sse,
430 }
431}
432
433/// The slots the SysV classification produces, and [`None`] when the answer is memory.
434///
435/// x87 counts as memory here. As an argument that is the right answer directly, and as a return
436/// value the x87 stack rule is a separate rule earlier in the list, so by the time this runs an
437/// x87 class means the value goes back in memory either way.
438fn eightbytes(shape: &Shape<'_>, limit: u64) -> Option<Vec<Slot>> {
439 if shape.size > limit {
440 return None;
441 }
442 let mut classes = vec![Class::None; usize::try_from(shape.size.div_ceil(8)).ok()?];
443 for piece in shape.pieces {
444 // A member away from its natural alignment is what `packed` makes, and it is the second
445 // of the two things section 3.2.3 sends straight to memory.
446 if piece.scalar.align > 1 && piece.offset % piece.scalar.align != 0 {
447 return None;
448 }
449 let class = match piece.scalar.kind {
450 Kind::Integer => Class::Integer,
451 Kind::Float(Format::X87Extended) => Class::X87,
452 Kind::Float(_) => Class::Sse,
453 };
454 let first = piece.offset / 8;
455 for at in first..=(piece.end() - 1) / 8 {
456 let slot = classes.get_mut(usize::try_from(at).ok()?)?;
457 // A member wider than an eightbyte is one value and not two. Its first eightbyte
458 // carries the class and every later one says "the same value again", which is what
459 // sends a `_Float128` into one vector register instead of two. An integer member is
460 // not this: `__int128` is two general purpose registers and the psABI classifies
461 // both of its eightbytes as INTEGER.
462 let class = if at > first && class == Class::Sse { Class::SseUp } else { class };
463 *slot = merge(*slot, class);
464 }
465 }
466 if classes.iter().any(|class| matches!(class, Class::Memory | Class::X87)) {
467 return None;
468 }
469 // Post merge rule (d). An upper half whose lower half was classified as something else is
470 // not the continuation of anything, so it stands on its own as an ordinary vector register.
471 // `union { _Float128 q; long a; }` is the shape that gets here: the first eightbyte is
472 // INTEGER because of the `long` and the second is the top of the `_Float128` with nothing
473 // above it any more.
474 for index in 0..classes.len() {
475 let above = index > 0 && matches!(classes[index - 1], Class::Sse | Class::SseUp);
476 if classes[index] == Class::SseUp && !above {
477 classes[index] = Class::Sse;
478 }
479 }
480 let mut slots = Vec::with_capacity(classes.len());
481 for (index, class) in classes.iter().enumerate() {
482 // An upper half is already part of the slot the eightbyte below it pushed.
483 if *class == Class::SseUp {
484 continue;
485 }
486 let offset = index as u64 * 8;
487 let bytes = (shape.size - offset).min(8);
488 slots.push(match class {
489 // A lower half with its upper half above it is the whole sixteen byte value in one
490 // register, and the only member that makes that shape is a `_Float128`.
491 Class::Sse if classes.get(index + 1) == Some(&Class::SseUp) => {
492 Slot::Float { offset, format: Format::Quad }
493 }
494 // Four bytes or fewer of floating point is one `float`. More than that is a
495 // `double` or two `float`s, which arrive in the same register either way.
496 Class::Sse if bytes <= 4 => Slot::Float { offset, format: Format::Single },
497 Class::Sse => Slot::Float { offset, format: Format::Double },
498 // An eightbyte nothing reaches into still travels, and it travels in a general
499 // purpose register, because an ABI does not leave a hole in the middle of an
500 // argument.
501 _ => Slot::Integer { offset, size: u32::try_from(bytes).unwrap_or(8) },
502 });
503 }
504 Some(slots)
505}