Skip to main content

rucc_abi/
lib.rs

1//! The psABIs, as descriptions read by one classifier rather than as one function per ABI.
2//!
3//! Design: `spec/cross-compile/06-abis.md`, and section 6.7 for the argument this crate exists to settle.
4//!
5//! # The problem
6//!
7//! An ABI is the only part of a target where being subtly wrong produces a program that works
8//! for months and then does not. Everything else fails loudly: a bad encoding traps, a missing
9//! relocation is a link error. A classification that is right for the first eight arguments and
10//! wrong for the ninth produces garbage, on one target, in a function nobody changed.
11//!
12//! `spec/cross-compile/06-abis.md` section 6.1 lists fifteen of them. The compiler implements four by hand at
13//! about a thousand lines, and fifteen at that rate is six to ten thousand lines of exactly that
14//! kind of code, which is what `spec/cross-compile/02-the-goal.md` claim 3 is written against.
15//!
16//! # The shape of the answer
17//!
18//! An ABI is a [`AbiDescription`]: register banks, how a scalar spends them, and two ordered
19//! lists of [`Rule`]s saying what an aggregate has to look like and how it travels if it does.
20//! [`Call`] reads a description and answers, once per return value and once per argument in
21//! order.
22//!
23//! The rules are not an interpreter for the psABI. The parts that are real algorithms stay as
24//! code in [`classify`], and there are four of them across the five ABIs described here. What is
25//! data is which of those an ABI uses, in what order, with what limits, and what happens when the
26//! registers run out. [`describe`] argues for the split at length, including the case against it.
27//!
28//! # What is in here and what is not
29//!
30//! Argument classification and type layout. Not prologue emission, not register allocation
31//! constraints, not unwind information, because section 6.7 keeps those as per architecture code
32//! with per ABI parameters and says why: an abstraction over them costs more than the
33//! duplication it removes.
34//!
35//! ```
36//! use rucc_abi::{Arg, Pass, Scalar, Shape, Slot, abis};
37//! use rucc_tuple::TargetTuple;
38//!
39//! let linux: TargetTuple = "x86_64-linux-gnu".parse().unwrap();
40//! let windows: TargetTuple = "x86_64-pc-windows-msvc".parse().unwrap();
41//!
42//! // Two eight byte integers: two registers on SysV, a hidden pointer on Windows. Same C, same
43//! // architecture, different answer, which is the whole reason this is a property of the target
44//! // rather than a rule about C.
45//! let pieces = rucc_abi::pieces(&[Scalar::integer(8), Scalar::integer(8)]);
46//! let shape = Arg::Aggregate(Shape { size: 16, align: 8, pieces: &pieces, complex: false });
47//!
48//! let mut call = abis::for_target(linux).unwrap().call();
49//! assert_eq!(
50//!     call.argument(&shape),
51//!     Pass::Pieces(vec![
52//!         Slot::Integer { offset: 0, size: 8 },
53//!         Slot::Integer { offset: 8, size: 8 },
54//!     ])
55//! );
56//!
57//! let mut call = abis::for_target(windows).unwrap().call();
58//! assert_eq!(call.argument(&shape), Pass::Reference);
59//! ```
60
61#![doc(html_root_url = "https://docs.rs/rucc-abi/0.10.23")]
62// Every public item here is read by somebody bringing up a target who has the psABI document
63// open beside it, so an undocumented one is a question they have to answer by reading the body.
64#![deny(missing_docs)]
65
66pub mod abis;
67pub mod classify;
68pub mod describe;
69pub mod layout;
70pub mod shape;
71
72pub use classify::Call;
73pub use describe::{
74    AbiDescription, Banks, ReturnPointer, Rule, Scalars, Short, StackArgs, Test, Travel, Variadic,
75};
76pub use layout::{BitfieldOrder, DataLayout, FloatType};
77pub use shape::{Arg, Format, Kind, Pass, Piece, Scalar, Shape, Slot};
78
79/// The pieces of a record whose members are these, each at the next offset it fits.
80///
81/// A convenience for building a shape in a test or a tool, and it is here rather than in a test
82/// module because the command line tool needs the same thing and two copies of a layout rule is
83/// two chances to write one of them differently.
84#[must_use]
85pub fn pieces(scalars: &[Scalar]) -> Vec<Piece> {
86    let mut pieces = Vec::new();
87    let mut at: u64 = 0;
88    for &scalar in scalars {
89        at = at.next_multiple_of(scalar.align.max(1));
90        pieces.push(Piece { offset: at, scalar });
91        at += scalar.size;
92    }
93    pieces
94}
95
96/// The shape of a record whose members are these, sized and aligned the way C would.
97#[must_use]
98pub fn record(pieces: &[Piece]) -> Shape<'_> {
99    let align = pieces.iter().map(|piece| piece.scalar.align).max().unwrap_or(1);
100    let size = pieces.iter().map(Piece::end).max().unwrap_or(0).next_multiple_of(align);
101    Shape { size, align, pieces, complex: false }
102}