rucc_abi/shape.rs
1//! What a classifier is asked about, and what it answers.
2//!
3//! Design: `spec/cross-compile/06-abis.md` sections 6.2 and 6.7.
4//!
5//! Everything in this module is deliberately not C. A psABI does not read a C type, it reads a
6//! size, an alignment, and where the scalars inside are and whether each one is an integer or a
7//! floating point value. Flattening a C type down to that is the compiler's job, because that
8//! is where the C type system lives, and every rule after it is the target's.
9//!
10//! Keeping the boundary there is what lets the descriptions in [`crate::abis`] be data. A rule
11//! written over "the members of the struct" would have to know about unions, arrays, bit-fields
12//! and anonymous members. A rule written over a flat list of scalars at offsets does not, and
13//! every psABI on `spec/cross-compile/06-abis.md` section 6.1's list turns out to be expressible over the flat
14//! list.
15
16/// A floating point format.
17///
18/// The compiler's own [`rucc_base::float::Format`], rather than a copy of it. A format is one
19/// fact and it has to be the same fact in a data layout, in a psABI rule and in a constant the
20/// front end folded, because those three meet: the layout says a target's `long double` is x87,
21/// the ABI rule says an x87 value goes on the stack, and the constant evaluator has to produce
22/// eighty bits for it. Two enums with the same variants let those drift apart one variant at a
23/// time and the drift shows up as a wrong number rather than as a build error.
24///
25/// The width and the format are separate facts, which is the trap in `spec/cross-compile/06-abis.md` section
26/// 6.2 item 1. An x87 `long double` is eighty bits of value stored in twelve bytes on i386 and
27/// sixteen on x86-64, and a `long double` on AArch64 Linux is a different format entirely at the
28/// same sixteen bytes. A rule written over the width alone gets both wrong.
29#[doc(inline)]
30pub use rucc_base::float::Format;
31
32/// What a scalar is, once the ABI is the one asking.
33///
34/// Signedness is not here. Every ABI in `spec/cross-compile/06-abis.md` section 6.1 passes a value of a given
35/// width the same way whichever end of the range it sits at, and the widening a narrow argument
36/// gets on the way into a register is a property of the call rather than of the type.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum Kind {
39 /// An integer, an enumeration, a `bool` or a pointer.
40 Integer,
41 /// A floating point value in this format.
42 Float(Format),
43}
44
45/// One scalar, with the three facts a psABI reads about it.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct Scalar {
48 /// Whether it is an integer or a floating point value.
49 pub kind: Kind,
50 /// How many bytes it takes in memory, which is the target's answer and not the format's.
51 pub size: u64,
52 /// What it is aligned to, in bytes.
53 ///
54 /// One for a bit-field, which may start anywhere and is an integer wherever it starts. That
55 /// distinction earns its keep on SysV, where an ordinary member away from its natural
56 /// alignment sends the whole aggregate to memory and a bit-field straddling an eightbyte
57 /// does not.
58 pub align: u64,
59}
60
61impl Scalar {
62 /// An integer of this size, aligned to itself.
63 #[must_use]
64 pub const fn integer(size: u64) -> Self {
65 Self { kind: Kind::Integer, size, align: size }
66 }
67
68 /// A floating point value in this format, of this size, aligned to itself.
69 #[must_use]
70 pub const fn float(format: Format, size: u64) -> Self {
71 Self { kind: Kind::Float(format), size, align: size }
72 }
73
74 /// Whether it is a floating point value.
75 #[must_use]
76 pub const fn is_float(self) -> bool {
77 matches!(self.kind, Kind::Float(_))
78 }
79}
80
81/// One scalar inside an aggregate, at the offset the layout gave it.
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub struct Piece {
84 /// Where it starts, in bytes from the start of the aggregate.
85 pub offset: u64,
86 /// What it is.
87 pub scalar: Scalar,
88}
89
90impl Piece {
91 /// One past the last byte it covers.
92 #[must_use]
93 pub const fn end(&self) -> u64 {
94 self.offset + if self.scalar.size == 0 { 1 } else { self.scalar.size }
95 }
96}
97
98/// An aggregate, as much of it as an ABI cares about.
99///
100/// The pieces are every scalar in it with arrays and nested records flattened out, in offset
101/// order. Padding is not a piece: a hole is described by the offsets on either side of it, which
102/// is the form every classification rule is written in.
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub struct Shape<'a> {
105 /// The size of the whole thing in bytes, padding included.
106 pub size: u64,
107 /// What it is aligned to, in bytes.
108 pub align: u64,
109 /// The scalars in it.
110 pub pieces: &'a [Piece],
111 /// Whether it is a `_Complex` rather than a `struct` or a `union` of the same shape.
112 ///
113 /// Exactly one rule reads this, and it is on SysV AMD64, where `_Complex long double` comes
114 /// back on the x87 stack and `struct { long double a, b; }`, which is the same thirty two
115 /// bytes with the same two members in the same places, comes back in memory. Without the
116 /// flag there is no way to tell those apart from the shape, and they are passed differently.
117 pub complex: bool,
118}
119
120impl Shape<'_> {
121 /// Whether it holds at least one scalar and every one of them is this format.
122 #[must_use]
123 pub fn is_all_of(&self, format: Format) -> bool {
124 !self.pieces.is_empty()
125 && self.pieces.iter().all(|piece| piece.scalar.kind == Kind::Float(format))
126 }
127}
128
129/// One argument, or one return value, as much of it as an ABI cares about.
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub enum Arg<'a> {
132 /// `void`, which is a return type and never an argument.
133 Void,
134 /// A scalar.
135 Scalar(Scalar),
136 /// A `struct`, a `union`, an array or a `_Complex`.
137 Aggregate(Shape<'a>),
138}
139
140/// One register's worth of an aggregate that travels in registers, and which of the object's
141/// bytes go in it.
142///
143/// A slot says what the object's bytes are read as rather than what the program wrote into them.
144/// An eightbyte holding two `float`s is a [`Slot::Float`] of [`Format::Double`], because eight
145/// bytes of floating point data arrive in one vector register whichever way the program divided
146/// them up and the bits are the same either way.
147///
148/// The offset is carried rather than derived because it cannot be worked out from the run of
149/// slots. Two eightbytes are at zero and eight, four `float`s of a homogeneous aggregate are
150/// four bytes apart, and `struct { double value; int tag; }` on RISC-V travels in one floating
151/// point and one integer register whose bytes are at zero and eight, where the second is not
152/// where the first one ended.
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum Slot {
155 /// An integer this many bytes wide, which is one general purpose register.
156 ///
157 /// The last slot of an aggregate is only as wide as what is left of it, so a twelve byte
158 /// structure is eight bytes and then four and nothing reads a byte past the object.
159 Integer {
160 /// Where its bytes start in the object.
161 offset: u64,
162 /// How many of them there are.
163 size: u32,
164 },
165 /// A floating point value in this format, which is one vector register.
166 Float {
167 /// Where its bytes start in the object.
168 offset: u64,
169 /// What is read out of them.
170 format: Format,
171 },
172}
173
174impl Slot {
175 /// Where its bytes start in the object.
176 #[must_use]
177 pub const fn offset(self) -> u64 {
178 match self {
179 Self::Integer { offset, .. } | Self::Float { offset, .. } => offset,
180 }
181 }
182
183 /// Whether it is a vector register rather than a general purpose one.
184 #[must_use]
185 pub const fn is_float(self) -> bool {
186 matches!(self, Self::Float { .. })
187 }
188}
189
190/// How one value travels.
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub enum Pass {
193 /// Nothing travels, which is `void` and an aggregate of no size.
194 Ignore,
195 /// The value itself. Every scalar is this.
196 Direct,
197 /// The object's bytes, in these slots, which is what passing an aggregate in registers means
198 /// once the object has been taken apart.
199 Pieces(Vec<Slot>),
200 /// The address of a copy, in the place the value itself would have gone.
201 ///
202 /// For an argument the caller makes the copy. For a return value the caller passes the
203 /// address of somewhere to put it, which is the hidden first argument.
204 Reference,
205 /// The object's own bytes in the argument area, with no address anywhere.
206 ///
207 /// SysV's MEMORY class and AAPCS's aggregate that ran out of registers. Never a return
208 /// value: a return value that does not fit in registers is [`Pass::Reference`].
209 Memory,
210}