Skip to main content

rucc_target/
lib.rs

1//! Target descriptions: triples, and the facts about a target that the rest of the
2//! compiler reads rather than hard-codes.
3//!
4//! Design: `spec/12-abi-and-runtime.md`. Layer rank 2, see `spec/18-package-layout.md`.
5//!
6//! The rule from `spec/18-package-layout.md` section 18.2 is that there is no
7//! target-specific code outside this crate, `rucc-tuple`, `rucc-abi`, `rucc-sysroot` and the
8//! per-target rule sets. Those four are one group rather than four exceptions: the tuple names
9//! a machine, `rucc-abi` says what its types look like and how its calls are made,
10//! `rucc-sysroot` says where its headers and libraries are, and this crate is what the rest of
11//! the compiler reads all of it through. Everything a pass
12//! needs to know about a target is a field it can read here. That rule is what makes the
13//! claim in `spec/10-backend.md` testable, namely that a new target is a rule set and a few
14//! data files, and `M10` brings up a fourth target specifically to put a number on it.
15//!
16//! [`TargetInfo::call`] is the other half of that rule and the one with teeth. How a structure
17//! travels between a caller and a callee is the target's answer rather than C's, so the walk to
18//! the IR flattens a C type into a [`Shape`] and asks here what form it takes. Every psABI rule
19//! is behind [`Call`] and nothing outside this crate matches on an architecture to find one.
20//! The rules themselves are `rucc-abi`'s, as data rather than as code, and this crate hands the
21//! question over to them. It answers [`None`] on a target whose ABI is not written down yet,
22//! which today is AArch64 on Windows and nothing else.
23//!
24//! # Status
25//!
26//! Triple parsing and the basic data model are real, which is what `rucc --print-config`
27//! reports, and so is the argument classification of every psABI in
28//! `spec/12-abi-and-runtime.md` sections 12.2 to 12.5, which `rucc-abi` describes as data and
29//! this crate selects between. x86-64's register file is written down,
30//! in [`x86_64`], along with what each of the two conventions over it does with each register,
31//! what each of its machine instructions does with its operands, and which instructions a frame
32//! is made of, which is [`FrameInsts`]. AArch64's register file and the two conventions over it,
33//! AAPCS64 and Apple's, are in [`aarch64`], and its instructions arrive with its backend in `M6`.
34//! RISC-V's arrive with its own. Machine models land in `M6`.
35//!
36//! This crate is tier 3 in `spec/18-package-layout.md` section 18.5: its Rust API is
37//! explicitly unstable and will change without a major version bump.
38
39#![doc(html_root_url = "https://docs.rs/rucc-target/0.18.10")]
40
41use std::fmt;
42use std::str::FromStr;
43
44use rucc_abi::DataLayout;
45use rucc_base::float::Format;
46use rucc_tuple::{self as tuple, TargetTuple};
47
48pub mod aarch64;
49mod abi;
50mod bits;
51mod branch;
52mod flags;
53mod frame;
54pub mod isa;
55mod machine;
56mod named;
57mod operand;
58mod regs;
59mod short;
60pub mod template;
61mod timing;
62mod typenames;
63pub mod x86;
64pub mod x86_64;
65
66pub use crate::abi::{
67    AbiDescription, Arg, BitInts, Call, Cleanup, Convention, Kind, Narrow, Pass, Piece, Scalar,
68    Shape, Slot, Variadic,
69};
70pub use crate::bits::BitInsts;
71pub use crate::branch::{BranchInsts, Fusion, Move};
72pub use crate::flags::{Compare, FlagInsts, Reader, Reads, Zeroing};
73pub use crate::frame::{ClassMoves, FrameInsts, Kept, Pair, Probe, Thunks};
74pub use crate::isa::{Choices, Feature, Isa, Target, TargetRefusal};
75pub use crate::machine::{Address, MachineInsts};
76pub use crate::operand::{Constraint, OperandDesc, Role};
77pub use crate::regs::{
78    CallRegs, Chkstk, ClassInfo, Conventions, Guard, PhysReg, Places, RegClass, RegFile, Segment,
79    Trace, Where,
80};
81pub use crate::short::{Copied, Narrowed, ShortInsts, Stepped, Tested, Zeroed};
82pub use crate::timing::{Timing, TimingInsts, Unit};
83pub use crate::typenames::{Lane, TypeName};
84
85/// A target architecture.
86#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
87// Deliberately not `#[non_exhaustive]`. Adding a variant here has to break every
88// match that needs to change, in this workspace and in anyone else's code. That is
89// the property `spec/10-backend.md` section 10.8 is claiming when it says adding a
90// target is a data change: the compiler tells you every place the data is read.
91pub enum Arch {
92    /// x86-64, the first target and the one `M3` brings up.
93    X86_64,
94    /// AArch64, the second target, `M6`.
95    Aarch64,
96    /// 64-bit RISC-V. `spec/10-backend.md` calls this the middle-end canary, because it has
97    /// no condition codes and no complex addressing modes, so anything the middle end got
98    /// away with on x86-64 shows up here.
99    Riscv64,
100    /// 32-bit x86, the i386 of the psABI and the i686 of a triple, which issue #2247 brings up.
101    X86,
102}
103
104impl Arch {
105    /// Pointer width in bits.
106    pub const fn pointer_width(self) -> u32 {
107        match self {
108            Arch::X86_64 | Arch::Aarch64 | Arch::Riscv64 => 64,
109            Arch::X86 => 32,
110        }
111    }
112
113    /// Whether the target is little-endian.
114    pub const fn is_little_endian(self) -> bool {
115        match self {
116            Arch::X86_64 | Arch::Aarch64 | Arch::Riscv64 | Arch::X86 => true,
117        }
118    }
119
120    /// The name as it appears in a triple.
121    pub const fn as_str(self) -> &'static str {
122        match self {
123            Arch::X86_64 => "x86_64",
124            Arch::Aarch64 => "aarch64",
125            Arch::Riscv64 => "riscv64",
126            Arch::X86 => "i686",
127        }
128    }
129}
130
131/// The operating system a target runs on.
132#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
133// Deliberately not `#[non_exhaustive]`. Adding a variant here has to break every
134// match that needs to change, in this workspace and in anyone else's code. That is
135// the property `spec/10-backend.md` section 10.8 is claiming when it says adding a
136// target is a data change: the compiler tells you every place the data is read.
137pub enum Os {
138    /// Linux, hosted or freestanding.
139    Linux,
140    /// Apple platforms. `spec/12-abi-and-runtime.md` section 12.3 lists the four places
141    /// Apple diverges from AAPCS64, and every one of them is a real bug if missed.
142    Darwin,
143    /// Windows.
144    Windows,
145    /// No operating system, which is what `-ffreestanding` kernel work looks like.
146    None,
147}
148
149impl Os {
150    /// The name as it appears in a triple.
151    pub const fn as_str(self) -> &'static str {
152        match self {
153            Os::Linux => "linux",
154            Os::Darwin => "darwin",
155            Os::Windows => "windows",
156            Os::None => "none",
157        }
158    }
159
160    /// The object file format this operating system uses.
161    pub const fn object_format(self) -> ObjectFormat {
162        match self {
163            Os::Linux | Os::None => ObjectFormat::Elf,
164            Os::Darwin => ObjectFormat::MachO,
165            Os::Windows => ObjectFormat::Coff,
166        }
167    }
168}
169
170/// The C runtime and ABI variant.
171#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
172// Deliberately not `#[non_exhaustive]`. Adding a variant here has to break every
173// match that needs to change, in this workspace and in anyone else's code. That is
174// the property `spec/10-backend.md` section 10.8 is claiming when it says adding a
175// target is a data change: the compiler tells you every place the data is read.
176pub enum Env {
177    /// The default for the operating system.
178    None,
179    /// glibc.
180    Gnu,
181    /// musl.
182    Musl,
183    /// The MSVC ABI.
184    Msvc,
185}
186
187impl Env {
188    /// The name as it appears in a triple, if it appears at all.
189    pub const fn as_str(self) -> &'static str {
190        match self {
191            Env::None => "none",
192            Env::Gnu => "gnu",
193            Env::Musl => "musl",
194            Env::Msvc => "msvc",
195        }
196    }
197}
198
199/// The object file format to emit.
200#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
201// Deliberately not `#[non_exhaustive]`. Adding a variant here has to break every
202// match that needs to change, in this workspace and in anyone else's code. That is
203// the property `spec/10-backend.md` section 10.8 is claiming when it says adding a
204// target is a data change: the compiler tells you every place the data is read.
205pub enum ObjectFormat {
206    /// ELF.
207    Elf,
208    /// Mach-O.
209    MachO,
210    /// COFF.
211    Coff,
212    /// WebAssembly, which is a format for a module rather than for a machine's object file and
213    /// is in this list because the target table has two rows that emit one.
214    Wasm,
215}
216
217impl ObjectFormat {
218    /// The name used in diagnostics and in `--print-config`.
219    pub const fn as_str(self) -> &'static str {
220        match self {
221            ObjectFormat::Elf => "elf",
222            ObjectFormat::MachO => "macho",
223            ObjectFormat::Coff => "coff",
224            ObjectFormat::Wasm => "wasm",
225        }
226    }
227
228    /// The same format as [`rucc_tuple::ObjectFormat`] names it.
229    ///
230    /// The two enumerations exist because the tuple describes forty two targets and this crate
231    /// describes what the compiler emits for one, and they will stay separate for as long as that
232    /// is true. This is the one place they are put side by side.
233    #[must_use]
234    pub const fn from_tuple(format: tuple::ObjectFormat) -> Self {
235        match format {
236            tuple::ObjectFormat::Elf => ObjectFormat::Elf,
237            tuple::ObjectFormat::MachO => ObjectFormat::MachO,
238            tuple::ObjectFormat::Coff => ObjectFormat::Coff,
239            tuple::ObjectFormat::Wasm => ObjectFormat::Wasm,
240        }
241    }
242}
243
244/// Where the program's code and static data are promised to be, which is `-mcmodel=`.
245///
246/// The model is a promise about addresses that the code generator is allowed to believe. It says
247/// nothing about which link is coming, which is `-fPIC` and friends, and it decides only how an
248/// address is written into an instruction. x86-64 is the only machine with more than one here.
249///
250/// Design: `spec/11-asm-objects-debug.md` section 11.3.
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
252// Deliberately not `#[non_exhaustive]`, for the reason [`Arch`] is not: a new model is a new set of
253// addressing forms and every place that picks one should stop compiling until it says which.
254pub enum CodeModel {
255    /// Everything within 2 GiB of everything else, reached from the instruction pointer. The
256    /// default on every target and every hosted program's model.
257    #[default]
258    Small,
259    /// The top 2 GiB of the address space, from `0xffffffff80000000` up, which is where every
260    /// x86-64 Linux kernel is linked. An address there is a 32 bit number sign extended, so an
261    /// instruction may carry it as an immediate (`movq $sym, %rax`) or as the displacement of an
262    /// indexed address (`sym(,%rdi,8)`), both with `R_X86_64_32S`.
263    Kernel,
264}
265
266impl CodeModel {
267    /// The spelling `-mcmodel=` takes, and the one `__code_model_*__` is named after.
268    #[must_use]
269    pub const fn as_str(self) -> &'static str {
270        match self {
271            CodeModel::Small => "small",
272            CodeModel::Kernel => "kernel",
273        }
274    }
275}
276
277/// What the x86 speculation hardening flags ask of indirect branches and returns.
278///
279/// Each field is one flag's request and all of them are off by default, which is gcc's default.
280/// The kernel turns them on for `MITIGATION_RETPOLINE`, `MITIGATION_RETHUNK` and `MITIGATION_SLS`,
281/// and objtool then checks that every branch it asked about was written the way gcc writes it.
282/// Nothing here is written by the compiler into a section of its own: `.retpoline_sites` and
283/// `.return_sites` are objtool's, built from the calls and jumps it finds.
284///
285/// Design: `spec/04-driver-and-cli.md` section 4.12.
286#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
287pub struct Speculation {
288    /// `-mindirect-branch=`: where a call or jump through a register goes instead, which for
289    /// `thunk-extern` is `call __x86_indirect_thunk_rax` for `call *%rax`.
290    pub indirect: Thunk,
291    /// `-mindirect-branch-cs-prefix`: a call or jump to the thunk for `r8` to `r15` has a code
292    /// segment override in front of it.
293    pub padded: bool,
294    /// `-mfunction-return=`: where a return goes instead, which for `thunk-extern` is
295    /// `jmp __x86_return_thunk`.
296    pub returns: Thunk,
297    /// `-mharden-sls=return` or `all`: an `int3` after every return.
298    pub after_return: bool,
299    /// `-mharden-sls=indirect-jmp` or `all`: an `int3` after every jump through a register, and
300    /// after the jump to a thunk that takes its place.
301    pub after_jump: bool,
302}
303
304impl Speculation {
305    /// Whether anything at all is asked for.
306    #[must_use]
307    pub const fn any(self) -> bool {
308        self.indirect.taken() || self.returns.taken() || self.after_return || self.after_jump
309    }
310}
311
312/// The four answers gcc takes for `-mindirect-branch=` and `-mfunction-return=`.
313///
314/// The three that are not `keep` all send the branch through the same few instructions, a call
315/// that pushes a return address, a loop that catches a processor guessing where the return goes,
316/// and a return to the real address. What differs is where those instructions are.
317#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
318pub enum Thunk {
319    /// `keep`: the branch is left alone.
320    #[default]
321    Keep,
322    /// `thunk-extern`: the branch goes to a thunk the program links in from somewhere else,
323    /// which is how the kernel builds.
324    Extern,
325    /// `thunk`: the branch goes to the same thunk, and the unit carries its own copy of it in a
326    /// COMDAT group, so the linker keeps one.
327    Comdat,
328    /// `thunk-inline`: the thunk's instructions are written where the branch was, which is how
329    /// the kernel builds its vDSO.
330    Inline,
331}
332
333impl Thunk {
334    /// Whether the branch is rewritten at all.
335    #[must_use]
336    pub const fn taken(self) -> bool {
337        !matches!(self, Self::Keep)
338    }
339
340    /// Whether the branch goes to a thunk by name, which is every answer but `keep` and
341    /// `thunk-inline`.
342    #[must_use]
343    pub const fn named(self) -> bool {
344        matches!(self, Self::Extern | Self::Comdat)
345    }
346}
347
348/// A target triple.
349///
350/// We accept the LLVM-style `arch-vendor-os-env` form because that is what build systems
351/// pass, and we normalise it to the three fields we actually branch on. The vendor field is
352/// parsed and discarded: no decision in the compiler depends on it, and keeping it would
353/// invite one.
354#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
355pub struct Triple {
356    /// The architecture.
357    pub arch: Arch,
358    /// The operating system.
359    pub os: Os,
360    /// The runtime and ABI variant.
361    pub env: Env,
362}
363
364impl Triple {
365    /// A triple from its three parts.
366    pub const fn new(arch: Arch, os: Os, env: Env) -> Self {
367        Self { arch, os, env }
368    }
369
370    /// The same machine as a [`TargetTuple`], which is what the layout and ABI descriptions are
371    /// written over.
372    ///
373    /// The tuple carries ten fields and this carries three, so this fills the other seven in from
374    /// their defaults, and every one of those defaults is the answer for the targets this type can
375    /// spell. There is no `x32` here and no big-endian AArch64, so the data model and the byte
376    /// order follow the architecture, and the sub-architecture, the versions and the float ABI have
377    /// nothing to say about any of the combinations.
378    ///
379    /// The environment is narrowed rather than copied across. This type will hold
380    /// `Triple { os: Darwin, env: Gnu }`, because its parser takes the fields by content and
381    /// `aarch64-apple-darwin-gnu` is a string somebody can type, and that is not a machine: a
382    /// Darwin target has one libc and it is not glibc. A tuple refuses to describe one, so the
383    /// pairs that are not machines are mapped to the environment the operating system actually
384    /// has.
385    ///
386    /// # Panics
387    ///
388    /// Never, for a triple this type can hold, which `every_triple_describes_a_machine` checks by
389    /// building all sixty four of them.
390    #[must_use]
391    pub fn tuple(self) -> TargetTuple {
392        let arch = match self.arch {
393            Arch::X86_64 => tuple::Arch::X86_64,
394            Arch::Aarch64 => tuple::Arch::Aarch64,
395            Arch::Riscv64 => tuple::Arch::Riscv64,
396            Arch::X86 => tuple::Arch::X86,
397        };
398        let os = match self.os {
399            Os::Linux => tuple::Os::Linux,
400            // macOS rather than iOS, because the three field triple cannot tell them apart and
401            // this compiler is hosted on the one and not on the other.
402            Os::Darwin => tuple::Os::MacOs,
403            Os::Windows => tuple::Os::Windows,
404            Os::None => tuple::Os::None,
405        };
406        let env = match (self.os, self.env) {
407            (Os::Linux, Env::Musl) => tuple::Env::Musl,
408            (Os::Linux, _) => tuple::Env::Gnu,
409            // mingw-w64 is a real Windows environment and the one place `gnu` survives the
410            // narrowing, because it has a different `long double` from MSVC on the same OS.
411            (Os::Windows, Env::Gnu) => tuple::Env::Gnu,
412            (Os::Windows, _) => tuple::Env::Msvc,
413            // Darwin and freestanding have no libc to name.
414            (Os::Darwin | Os::None, _) => tuple::Env::None,
415        };
416        TargetTuple::builder(arch, os)
417            .env(env)
418            .build()
419            .expect("every triple this type can hold describes a machine")
420    }
421
422    /// The triple that describes the same machine as `target`, if this type can spell it.
423    ///
424    /// The inverse of [`Triple::tuple`], and computed by running that function over every triple
425    /// there is rather than by writing the narrowing out a second time. A second table would be a
426    /// second thing to keep in step, and the failure it invites is not a compile error: it is one
427    /// row of the matrix quietly answering as a neighbour.
428    ///
429    /// It returns `None` for most of the target table, and that is the honest answer rather than a
430    /// gap to be papered over. `rucc-abi` describes the scalar layout of all forty two rows, and
431    /// this type holds three fields with four architectures in the first, so only the rows on
432    /// those four have a [`TargetInfo`] and the rest do not. Anything that needs to lay a
433    /// record out for `s390x-linux-gnu` needs that gap closed rather than an approximation of it.
434    ///
435    /// The environment of the answer is the narrowed one, so the triple this gives back is the
436    /// canonical spelling of that machine: `Env::None` on Darwin and on a freestanding target,
437    /// never the `Env::Gnu` that a parser will accept from a string somebody typed. A deployment
438    /// target or a glibc release does not change which triple a tuple narrows to, so
439    /// `aarch64-macos.13` is the Darwin triple rather than a miss.
440    #[must_use]
441    pub fn from_tuple(target: TargetTuple) -> Option<Triple> {
442        // Four triples narrow onto `x86_64-linux-gnu`, because a Darwin triple claiming glibc is
443        // a string somebody can type and not a machine. So a match is not enough on its own: the
444        // answer is the candidate whose environment came through the narrowing unchanged, and
445        // anything else is only a fallback for the day a narrowing loses a spelling entirely.
446        let mut fallback = None;
447        for arch in [Arch::X86_64, Arch::Aarch64, Arch::Riscv64, Arch::X86] {
448            for os in [Os::Linux, Os::Darwin, Os::Windows, Os::None] {
449                for env in [Env::None, Env::Gnu, Env::Musl, Env::Msvc] {
450                    let candidate = Triple::new(arch, os, env);
451                    if candidate.tuple() != target.without_versions() {
452                        continue;
453                    }
454                    // By name rather than by a match on the pair, so that an environment added to
455                    // either enumeration does not need a line here. The one name the two spell
456                    // differently is the absent one, which the tuple writes as nothing.
457                    let survived = match env {
458                        Env::None => target.env() == tuple::Env::None,
459                        _ => env.as_str() == target.env().as_str(),
460                    };
461                    if survived {
462                        return Some(candidate);
463                    }
464                    fallback.get_or_insert(candidate);
465                }
466            }
467        }
468        fallback
469    }
470
471    /// The triple of the machine this compiler is running on.
472    ///
473    /// Used as the default target, which is what makes `rucc hello.c` work with no flags.
474    /// Unknown host combinations are not an error here: they are reported by the driver,
475    /// where there is somewhere to report them to.
476    pub fn host() -> Option<Self> {
477        let arch = match std::env::consts::ARCH {
478            "x86_64" => Arch::X86_64,
479            "aarch64" => Arch::Aarch64,
480            "riscv64" => Arch::Riscv64,
481            "x86" => Arch::X86,
482            _ => return None,
483        };
484        // Which libc this is matters, and `std::env::consts` does not say. A compiler built on
485        // Alpine and defaulting to `x86_64-unknown-linux-gnu` describes a machine it is not
486        // running on: musl and glibc disagree about `int_fast16_t` among other things, and a
487        // header that is written out of the predefined type names picks the disagreement up.
488        // The libc rucc itself was linked against is the best evidence available about the one
489        // the code it compiles will be linked against, and it is right on every machine where
490        // rucc was built for the machine it runs on.
491        let linux = if cfg!(target_env = "musl") { Env::Musl } else { Env::Gnu };
492        // Windows is gnu whichever ABI rucc itself was built for. An `rucc.exe` built with MSVC
493        // still has no Windows SDK to link against on a fresh machine, and it can fetch the
494        // mingw-w64 sysroot, so `rucc hello.c` works there only if the default is the one it can
495        // fetch. `--target=x86_64-windows-msvc` is still there for somebody who has the SDK.
496        let (os, env) = match std::env::consts::OS {
497            "linux" => (Os::Linux, linux),
498            "macos" => (Os::Darwin, Env::None),
499            "windows" => (Os::Windows, Env::Gnu),
500            _ => return None,
501        };
502        Some(Self::new(arch, os, env))
503    }
504}
505
506impl fmt::Display for Triple {
507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        // Always four fields, always the same spelling, because this string ends up in
509        // `--print-config` output that people diff.
510        write!(f, "{}-unknown-{}-{}", self.arch.as_str(), self.os.as_str(), self.env.as_str())
511    }
512}
513
514/// Why a triple failed to parse.
515#[derive(Debug, Clone, PartialEq, Eq)]
516pub struct ParseTripleError {
517    /// The triple as given.
518    pub input: String,
519    /// What specifically was not recognised.
520    pub reason: &'static str,
521}
522
523impl fmt::Display for ParseTripleError {
524    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525        write!(f, "unsupported target triple `{}`: {}", self.input, self.reason)
526    }
527}
528
529impl std::error::Error for ParseTripleError {}
530
531impl FromStr for Triple {
532    type Err = ParseTripleError;
533
534    fn from_str(s: &str) -> Result<Self, Self::Err> {
535        let err = |reason| ParseTripleError { input: s.to_owned(), reason };
536        let mut parts = s.split('-');
537
538        let arch = match parts.next() {
539            Some("x86_64" | "amd64") => Arch::X86_64,
540            Some("aarch64" | "arm64") => Arch::Aarch64,
541            Some("riscv64") => Arch::Riscv64,
542            Some("i386" | "i486" | "i586" | "i686" | "x86") => Arch::X86,
543            _ => return Err(err("unknown architecture")),
544        };
545
546        // The vendor field is optional in practice. `x86_64-linux-gnu` and
547        // `x86_64-unknown-linux-gnu` both occur in the wild and mean the same thing, so the
548        // remaining fields are matched by content rather than by position.
549        let rest: Vec<&str> = parts.collect();
550        let mut os = None;
551        let mut env = None;
552        for part in &rest {
553            match *part {
554                "linux" => os = Some(Os::Linux),
555                "darwin" | "macos" | "macosx" | "ios" => os = Some(Os::Darwin),
556                "windows" | "win32" => os = Some(Os::Windows),
557                // `none` is the one token that means different things in the two positions.
558                // In `x86_64-unknown-none-elf` it is the operating system; in
559                // `aarch64-apple-darwin-none` it is the environment. Which one it is depends
560                // on whether an operating system has already been seen, and that rule is what
561                // makes `Display` round-trip through `FromStr`.
562                "none" if os.is_none() => os = Some(Os::None),
563                "none" => env = Some(Env::None),
564                "elf" => os = os.or(Some(Os::None)),
565                "gnu" | "gnueabi" | "gnueabihf" => env = Some(Env::Gnu),
566                "musl" | "musleabi" | "musleabihf" => env = Some(Env::Musl),
567                "msvc" => env = Some(Env::Msvc),
568                _ => {}
569            }
570        }
571
572        let os = os.ok_or_else(|| err("unknown operating system"))?;
573        // The same defaults as `rucc_tuple::Os::default_env`, so that `x86_64-pc-windows` means
574        // one target whichever of the two parsers read it, and it means the mingw-w64 one because
575        // that is the one a fresh machine can link for.
576        let env = env.unwrap_or(match os {
577            Os::Linux | Os::Windows => Env::Gnu,
578            Os::Darwin | Os::None => Env::None,
579        });
580        Ok(Self::new(arch, os, env))
581    }
582}
583
584/// The facts about a target that the compiler reads instead of hard-coding.
585///
586/// This is the whole of what a pass is allowed to know about where its output will run.
587/// It grows, and every field added here is one fewer `#[cfg]` somewhere it should not be.
588#[derive(Debug, Clone, PartialEq, Eq)]
589#[non_exhaustive]
590pub struct TargetInfo {
591    /// The machine this describes, as the ten field tuple rather than as a three field triple.
592    ///
593    /// It is the tuple because a record layout is a question every row of the target table has an
594    /// answer to, and a triple can spell fifteen of the forty two. Nothing else in this type had
595    /// to change to widen it: every field below is already derived from `rucc-abi`'s description
596    /// of this tuple, and the ones that were not were the bugs.
597    pub tuple: TargetTuple,
598    /// The sizes, the alignments and the signedness this target's headers were written against.
599    ///
600    /// The widths below are views of this and the alignments are not, which is the reason it is
601    /// kept whole. A `long long` is eight bytes on every row of the table and is aligned to four
602    /// on System V i386 and to eight everywhere else, and no width can say that.
603    pub scalars: DataLayout,
604    /// Width of a pointer in bits.
605    pub pointer_width: u32,
606    /// Whether bytes are ordered little end first.
607    pub little_endian: bool,
608    /// Whether a bare `char` is signed.
609    ///
610    /// Signed on x86-64 and unsigned on AArch64 Linux, which is the classic source of code
611    /// that works on one and not the other, so it is data rather than an assumption.
612    pub char_is_signed: bool,
613    /// Width of `long` in bits. This is the field that separates the LP64 world from
614    /// Windows LLP64.
615    pub long_width: u32,
616    /// Width of `long double` in bits: 80 bits of x87 stored in 128 on every x86-64 target but
617    /// MSVC, 128 of true quad precision on AArch64 Linux and RISC-V, and 64 on Apple's AArch64 and
618    /// under MSVC.
619    ///
620    /// Apple's x86-64 is not one of the 64-bit ones, which is the trap. The change to a `double`
621    /// came with AArch64 and the Intel answer stayed as it was, so `x86_64-apple-darwin` and
622    /// `x86_64-unknown-linux-gnu` agree here and `aarch64-apple-darwin` is the odd one.
623    pub long_double_width: u32,
624    /// The format `long double` actually is, which the width does not say.
625    ///
626    /// It is 128 bits wide on SysV x86-64 and on AArch64 Linux and the two are not the same
627    /// type: one is the x87 eighty bit format padded out to sixteen bytes and the other is
628    /// true quad precision with a hundred and thirteen bits of significand. Anything that
629    /// converts a constant or folds one has to know which, and the width alone cannot say.
630    pub long_double_format: Format,
631    /// The format `_Float64x` is, which is the widest format the target has short of a software
632    /// one.
633    ///
634    /// It follows the architecture and not the operating system, which is what makes it worth a
635    /// field of its own next to `long double`. Apple and Windows define `long double` as a
636    /// `double` and neither of them takes `_Float64x` down with it: the type has to be wider
637    /// than a `_Float64`, so it is the x87 eighty bit format on x86-64 and quad precision on
638    /// AArch64 and RISC-V wherever it is written.
639    ///
640    /// [`None`] on a machine whose widest format is a `double`, which is 32-bit ARM and wasm32.
641    /// The type does not exist there and neither reference defines the macros that describe it,
642    /// so the honest answer is that there is no format rather than a `double` in its place.
643    pub float64x_format: Option<Format>,
644    /// Whether the target has `_Float16`.
645    ///
646    /// The named types are not all universal the way `_Float32` and `_Float64` are. gcc 13 has
647    /// this one on x86-64, AArch64 and RISC-V and does not have it on i686, armv7, ppc64le or
648    /// s390x, which was measured by compiling a declaration of it with each of those cross
649    /// compilers. The `__FLT16_*__` macros and the `f16` suffix are defined on exactly the rows
650    /// where the type is, so all three ask this one field.
651    ///
652    /// i686 is the row worth explaining. gcc aims at the baseline of the target rather than at
653    /// whatever chip is under it, and half precision on x86 needs SSE2, which is in the baseline
654    /// of x86-64 and not in the baseline of i686. So the two x86 rows disagree, and a `-msse2`
655    /// on the command line would move the 32-bit one, which is a thing this compiler has no
656    /// place to say yet.
657    pub has_float16: bool,
658    /// Whether the target has `_Float128`.
659    ///
660    /// Every row but 32-bit ARM among the seven measured against gcc 13. x86-64 and i686 have it
661    /// in software, and AArch64, RISC-V, s390x and ppc64le have it because quad precision is
662    /// already the format of something on those machines. armv7 has no format wider than a
663    /// `double` at all, so the type is not there and gcc says so.
664    ///
665    /// This is the ISO spelling. gcc's `__float128` is a narrower thing and is not this field:
666    /// that name exists on x86 and PowerPC only, and on AArch64, RISC-V and s390x gcc offers
667    /// `_Float128` in its place when a program writes it. `__SIZEOF_FLOAT128__` follows the
668    /// vendor name rather than the type, which is why it is missing on rows where the type is
669    /// there.
670    pub has_float128: bool,
671    /// Whether the target has `_Decimal32`, `_Decimal64` and `_Decimal128`.
672    ///
673    /// Only x86-64 Linux today. gcc has the three types on more rows than that, but a decimal is
674    /// a call into libgcc for everything but a move, and the only encoding the back end names
675    /// routines for is the binary integer one x86 uses. PowerPC and s390x use the densely packed
676    /// encoding and are a different set of routines, and the other rows are untested, so a
677    /// program that writes one there is told the type is not available rather than handed code
678    /// nobody has run.
679    pub has_decimal_float: bool,
680    /// Width of `wchar_t` in bits, which decides what a wide literal is encoded in.
681    ///
682    /// It is 16 on Windows, so a wide string there is UTF-16 and a character outside the basic
683    /// plane takes two elements, and 32 everywhere else, where a wide string is UTF-32 and no
684    /// character takes more than one.
685    pub wchar_width: u32,
686    /// Whether `wchar_t` is signed.
687    ///
688    /// x86-64 Linux makes it a signed `int` and AArch64 Linux makes it an `unsigned int`,
689    /// following the psABI's rule for plain `char`, so `L'\xffffffff'` is minus one on one of
690    /// them and four billion on the other.
691    pub wchar_is_signed: bool,
692    /// The granule a `_BitInt` wider than 64 bits is laid out in, in bits.
693    ///
694    /// Above 64 bits the psABIs stop treating a `_BitInt` like a standard integer type and
695    /// start treating it like an array of these, so its size is rounded up to a multiple of
696    /// this and its alignment is this. It is 64 on x86-64 and RISC-V and 128 on AArch64, which
697    /// is why `_BitInt(65)` is sixteen bytes aligned to eight on one and sixteen bytes aligned
698    /// to sixteen on the other. Measured with clang 18 on x86-64 Linux and clang on AArch64
699    /// Darwin rather than read off the documents.
700    pub bit_int_granule: u32,
701    /// The widest access, in bits, this machine performs atomically without taking a lock.
702    ///
703    /// It is what `__atomic_always_lock_free` and `__atomic_is_lock_free` answer from, and it is
704    /// a claim about what this compiler emits rather than about what the processor is capable of.
705    /// Sixty four on every target here. x86-64 does sixteen bytes atomically with `cmpxchg16b`,
706    /// which is not in the baseline the psABI names and which nothing in this compiler writes, and
707    /// AArch64 does the same with its pair instructions, which nothing writes either. A target
708    /// that answered yes for sixteen bytes and then called a library that has to take a lock for
709    /// them would have two answers to one question, and the wrong one is the one in the header.
710    pub lock_free_width: u32,
711    /// The object format to emit.
712    pub object_format: ObjectFormat,
713    /// Whether the output says where an unwind lands, which is the language specific data area
714    /// beside a function's call frame information that a cleanup under `-fexceptions` needs.
715    ///
716    /// ELF on x86-64 and AArch64 and nothing else yet. It is a claim about what this compiler
717    /// writes rather than about what the platform can do, so lowering turns down a cleanup it could
718    /// not honour on the others instead of emitting one the unwinder would skip.
719    pub landing_pads: bool,
720    /// Whether a table in read only data may hold how far a label is from the table, as a four
721    /// byte relocation measured from where it is written.
722    ///
723    /// x86-64 ELF, which is the one output whose writer has been taught that relocation for data.
724    /// Everywhere else a jump table holds whole addresses.
725    pub relative_tables: bool,
726    /// How bit-fields are allocated into storage, which is the one record layout question where
727    /// two targets in this table run different algorithms rather than the same one over different
728    /// numbers.
729    pub bit_field_style: BitFieldStyle,
730    /// Whether an unnamed bit-field raises the record's alignment the way a named one does.
731    ///
732    /// Almost everywhere it does not, which is why `struct { char c; int :20; }` is four bytes
733    /// aligned to one on x86-64 and four aligned to four with the field named. AAPCS64 says
734    /// otherwise and says it for the zero width member too, so `struct { unsigned :0; }` is
735    /// aligned to four on AArch64 Linux and to one on Apple's AArch64, on Windows on AArch64, on
736    /// x86-64 and on RISC-V. Measured with the pinned reference across every row that has one,
737    /// because it is neither an architecture rule nor an operating system rule: it is the ABI, and
738    /// Apple and Microsoft each dropped it.
739    ///
740    /// Windows says yes as well, and there it is not AAPCS64 but Microsoft's own rule, which is
741    /// why the two facts are separate fields rather than one. In a `union` the Microsoft rule goes
742    /// further and no bit-field contributes alignment at all, named or not, so this field is only
743    /// half the answer there and [`BitFieldStyle`] carries the other half.
744    pub unnamed_bit_field_aligns: bool,
745    /// How large a record with no storage in it is, in bytes, before its alignment is applied.
746    ///
747    /// Zero everywhere but MSVC, where it is four. A `struct` with no members is not C at all, it
748    /// is a GNU extension, and C++ gives it a size of one, so there is no standard to read the
749    /// answer out of and the number has to come from whatever else compiles for the target. On
750    /// mingw that is GCC and the answer is zero. On MSVC it is clang, because MSVC itself rejects
751    /// the declaration outright, and clang's Microsoft record layout gives it four bytes and gives
752    /// an array of three of them twelve. So this is a fact about the environment and not about the
753    /// operating system, which is the one place in this type where those two come apart in that
754    /// direction.
755    ///
756    /// It covers a record with no members and a record whose only members occupy nothing, which is
757    /// the zero width bit-field, the zero length array and the flexible array member. All four
758    /// were measured and all four agree.
759    pub empty_record_size: u64,
760    /// What `__builtin_va_list` is, which is the type every `va_list` in every header is a
761    /// typedef of.
762    ///
763    /// [`None`] on a target whose answer is a type this crate does not build yet. 32-bit ARM's is
764    /// a structure of one pointer and s390x's is a structure of four members, and neither is any
765    /// of the four below. A target with no backend cannot compile a call to `va_arg` in any case,
766    /// so saying so beats naming a neighbour's type and having a header believe it.
767    pub va_list: Option<VaList>,
768    /// The registers the machine has, which is [`RegFile::EMPTY`] for an architecture nothing
769    /// has described yet.
770    pub regs: &'static RegFile,
771    /// Which registers the calling convention gives which job, or `None` while the
772    /// architecture has no register file to name them out of.
773    pub call_regs: Option<&'static CallRegs>,
774    /// How many words of arguments a function of this unit's own convention takes in registers,
775    /// which is what `-mregparm=` says on 32 bit x86 and is zero everywhere else.
776    ///
777    /// [`TargetInfo::call_regs`] is the registers that go with it, and
778    /// [`TargetInfo::convention_for`] is what a function type's convention comes to under it.
779    pub regparm: u8,
780    /// Whether a small structure comes back in registers, which is what `-freg-struct-return` says
781    /// on 32 bit x86 and what the kernel builds with there. See [`TargetInfo::with_reg_struct_return`].
782    pub reg_struct_return: bool,
783    /// How long this machine's instructions take, or `None` for an architecture with no backend.
784    ///
785    /// [`None`] rather than a model of a machine nobody measured, for the reason the two fields
786    /// above are: a scheduler told made up numbers about a processor has no way to find out they
787    /// were made up. `--print-config` prints [`TimingInsts::model`] off this, which is the first
788    /// thing anybody comparing two runs of a benchmark wants to know.
789    pub timing: Option<&'static TimingInsts>,
790}
791
792/// The type a target's `__builtin_va_list` is.
793///
794/// A variable argument list is the one place a psABI dictates a C type rather than how a type
795/// travels, and the four answers below are not four spellings of one thing: `sizeof(va_list)` is
796/// eight bytes on Apple's AArch64 and thirty two on Linux's, and on SysV x86-64 a `va_list` is an
797/// array, so a `va_list` passed to a function is passed as a pointer and one assigned to another
798/// is a constraint violation rather than a copy. Code in the wild depends on all of that.
799#[derive(Debug, Clone, Copy, PartialEq, Eq)]
800// Deliberately not `#[non_exhaustive]`, for the reason [`Arch`] is not: a fifth answer here is
801// a fifth type to build, and every place that builds one should stop compiling until it does.
802pub enum VaList {
803    /// `char *`, which is what a target whose arguments are all passed in one place needs: the
804    /// address of the next argument and nothing else. Apple's AArch64 and both Windows targets.
805    CharPointer,
806    /// `void *`, which is the RISC-V psABI's spelling of the same thing.
807    VoidPointer,
808    /// `struct __va_list_tag { unsigned gp_offset, fp_offset; void *overflow_arg_area,
809    /// *reg_save_area; } [1]`, the SysV x86-64 one. Arguments arrive in two register files and
810    /// on the stack, so the list is a cursor into each, and the array of one is what makes
811    /// passing it to `vfprintf` pass its address.
812    SysV,
813    /// `struct __va_list { void *__stack, *__gr_top, *__vr_top; int __gr_offs, __vr_offs; }`,
814    /// the AAPCS64 one. The same idea as SysV's, counting down from the top of each save area
815    /// rather than up from the bottom, and not an array.
816    Aapcs,
817}
818
819impl VaList {
820    /// The name used in `--print-config`.
821    #[must_use]
822    pub const fn as_str(self) -> &'static str {
823        match self {
824            VaList::CharPointer => "char-pointer",
825            VaList::VoidPointer => "void-pointer",
826            VaList::SysV => "sysv",
827            VaList::Aapcs => "aapcs",
828        }
829    }
830}
831
832/// How a target allocates bit-fields into storage.
833///
834/// Everything else about laying a record out is one algorithm reading different sizes and
835/// alignments per target. This is not: the two answers below place the same members at different
836/// offsets and give the same struct different sizes, and no amount of changing what an `int` is
837/// turns one into the other. `struct { unsigned m:3; char c; }` is four bytes with the `char` at
838/// offset one under the first and eight bytes with it at offset four under the second.
839#[derive(Debug, Clone, Copy, PartialEq, Eq)]
840// Deliberately not `#[non_exhaustive]`, for the reason [`Arch`] is not: a third answer here is a
841// third algorithm to write, and every place that chooses between them should stop compiling until
842// it does.
843pub enum BitFieldStyle {
844    /// The Itanium C++ ABI's rule, which every psABI in this table except Windows follows. A
845    /// bit-field goes at the next free bit unless that would make it span more storage than its
846    /// own type occupies, in which case it starts at the next boundary of its alignment. Storage
847    /// is shared between members of different types freely, so `struct { char a:3; unsigned b:3; }`
848    /// is four bytes with both fields in the first one.
849    Itanium,
850    /// Microsoft's rule, which both Windows environments follow and not only MSVC. A run of
851    /// bit-fields is allocated into a unit the size and alignment of the declared type, and the
852    /// unit is closed both when the next member's declared type has a different size and when the
853    /// field does not fit in what is left. An ordinary member closes a unit too, and the closed
854    /// unit occupies its whole declared size whether or not the bits were used. So the same struct
855    /// is eight bytes: a one byte unit for the `char` and a four byte one for the `unsigned`,
856    /// aligned to four.
857    Microsoft,
858}
859
860impl BitFieldStyle {
861    /// The name used in `--print-config`.
862    #[must_use]
863    pub const fn as_str(self) -> &'static str {
864        match self {
865            BitFieldStyle::Itanium => "itanium",
866            BitFieldStyle::Microsoft => "microsoft",
867        }
868    }
869}
870
871/// A width in bits, from a size in bytes.
872///
873/// The fields here are widths because that is what a predefined macro and a diagnostic say, and a
874/// layout is sizes because that is what `sizeof` says. The conversion belongs at the one boundary
875/// between them rather than at every reader of one of these fields.
876/// Whether `target` is the one output the unwind tables and relative jump tables are written for.
877fn x86_64_elf(target: TargetTuple, pointer_size: u64) -> bool {
878    target.arch() == tuple::Arch::X86_64
879        && target.object_format() == tuple::ObjectFormat::Elf
880        && pointer_size == 8
881}
882
883fn bits(bytes: u64) -> u32 {
884    u32::try_from(bytes * 8).expect("no standard type is four billion bits wide")
885}
886
887impl TargetInfo {
888    /// The description of `triple`.
889    ///
890    /// The three field triple spells fifteen of the forty two rows of the target table, which is
891    /// every row with a backend and every row a driver will be handed today, so this is what the
892    /// compiler proper calls. [`TargetInfo::for_tuple`] is the one that answers for the whole
893    /// table.
894    #[must_use]
895    pub fn new(triple: Triple) -> Self {
896        Self::for_tuple(triple.tuple())
897    }
898
899    /// The type names this target's compiler has before any header is read, and what each is.
900    ///
901    /// Empty everywhere but AArch64, where gcc has the Advanced SIMD and SVE types and glibc's
902    /// `<math.h>` names them.
903    #[must_use]
904    pub fn type_names(&self) -> &'static [(&'static str, TypeName)] {
905        typenames::type_names(self.tuple.arch())
906    }
907
908    /// Whether a file-scope `register T x asm ("name")` on this target can be kept as what it says.
909    ///
910    /// Such a variable is the register for the whole program, so it can only be honoured for a
911    /// register the code generator never hands out and nothing else writes behind its back. On
912    /// AArch64 that is `x18`, which rucc keeps off every target because Windows and Apple give it
913    /// to the platform, and which mingw-w64's `winnt.h` declares this way so that `NtCurrentTeb`
914    /// reads the thread's TEB out of it.
915    ///
916    /// The stack pointer is the other one, on both machines. Nothing hands it out and nothing
917    /// writes it behind the program's back, and the Linux kernel declares `current_stack_pointer`
918    /// as `rsp`, `esp` or `sp` this way, to read it and to hand it to the `asm` statements that make a
919    /// call so that the call is made from a frame that is set up.
920    #[must_use]
921    pub fn keeps_register_for_the_program(&self, name: &str) -> bool {
922        match self.tuple.arch() {
923            tuple::Arch::Aarch64 => matches!(name, "x18" | "sp"),
924            tuple::Arch::X86_64 => name == "rsp",
925            tuple::Arch::X86 => name == "esp",
926            _ => false,
927        }
928    }
929
930    /// What a flag output, `"=@cc<cond>"`, turns into on this target: the constraint of an output in
931    /// a register and the instructions that leave the condition in it, which go after the rest of
932    /// the template. The output is operand `index` and its type is `bits` wide.
933    ///
934    /// gcc does the same thing. The template leaves the answer in the flags, and gcc writes the
935    /// `set<cond>` or `cset` that reads it after the template, into a register it picked for the
936    /// output. The kernel's `CC_SET` and `CC_OUT` are the way it gets at this on both machines, and
937    /// `test_bit` and every atomic that answers whether it reached zero are written with them.
938    ///
939    /// Nothing for a condition the target has no name for, and for a target with no flag outputs.
940    #[must_use]
941    pub fn flag_output(
942        &self,
943        cond: &str,
944        index: usize,
945        bits: u32,
946    ) -> Option<(&'static str, String)> {
947        match self.tuple.arch() {
948            tuple::Arch::X86_64 | tuple::Arch::X86 => {
949                const CONDITIONS: &[&str] = &[
950                    "a", "ae", "b", "be", "c", "e", "g", "ge", "l", "le", "na", "nae", "nb", "nbe",
951                    "nc", "ne", "ng", "nge", "nl", "nle", "no", "np", "ns", "nz", "o", "p", "pe",
952                    "po", "s", "z",
953                ];
954                if !CONDITIONS.contains(&cond) {
955                    return None;
956                }
957                // `set<cond>` writes one byte, and the rest of a wider output is cleared the way gcc
958                // clears it, with a move that writes the low 32 bits and so the whole register.
959                let mut text = format!("\n\tset{cond} %b{index}");
960                if bits > 8 {
961                    text.push_str(&format!("\n\tmovzbl %b{index}, %k{index}"));
962                }
963                Some(("=q", text))
964            }
965            tuple::Arch::Aarch64 => {
966                const CONDITIONS: &[&str] = &[
967                    "eq", "ne", "cs", "hs", "cc", "lo", "mi", "pl", "vs", "vc", "hi", "ls", "ge",
968                    "lt", "gt", "le",
969                ];
970                if !CONDITIONS.contains(&cond) {
971                    return None;
972                }
973                // `cset` into the 32 bit register clears the top half as well, so one width does
974                // for every type.
975                Some(("=r", format!("\n\tcset %w{index}, {cond}")))
976            }
977            _ => None,
978        }
979    }
980
981    /// How an `asm` template with operands on this target writes the register called `name`,
982    /// which is `%%rsp` on x86, where one `%` would start an operand, and as it is on AArch64.
983    #[must_use]
984    pub fn register_in_text(&self, name: &str) -> String {
985        match self.tuple.arch() {
986            tuple::Arch::X86_64 | tuple::Arch::X86 => format!("%%{name}"),
987            _ => name.to_owned(),
988        }
989    }
990
991    /// Whether an unnamed bit-field raises the record's alignment under `style`, which is the
992    /// target's own rule or the one a `gcc_struct` or `ms_struct` attribute chose.
993    ///
994    /// Under the target's own rule it is [`TargetInfo::unnamed_bit_field_aligns`]. Microsoft's rule
995    /// says yes everywhere. The Itanium rule that `gcc_struct` asks for on Windows says what it
996    /// says on the same architecture's other rows: no on x86-64, and yes on AArch64, where AAPCS64
997    /// says so. So `struct { char c; int :20; } __attribute__((gcc_struct))` is four bytes aligned
998    /// to one from mingw-w64 gcc on x86-64 and four aligned to four from llvm-mingw's clang on
999    /// AArch64, which is also what gcc for AArch64 Linux makes of it without the attribute.
1000    #[must_use]
1001    pub fn unnamed_bit_field_aligns_under(&self, style: BitFieldStyle) -> bool {
1002        if style == self.bit_field_style {
1003            return self.unnamed_bit_field_aligns;
1004        }
1005        match style {
1006            BitFieldStyle::Microsoft => true,
1007            BitFieldStyle::Itanium => {
1008                matches!(
1009                    self.tuple.arch(),
1010                    tuple::Arch::Aarch64 | tuple::Arch::Arm | tuple::Arch::Arm64Ec
1011                ) && !self.tuple.os().is_darwin()
1012            }
1013        }
1014    }
1015
1016    /// The description of `target`.
1017    ///
1018    /// Every row of the target table has one of these, whether or not there is a backend that can
1019    /// emit code for it, because laying a record out and reading a header are questions that do
1020    /// not need a backend. The fields that genuinely need one say so: [`TargetInfo::regs`] is
1021    /// empty and [`TargetInfo::call_regs`] is [`None`] for an architecture whose register file is
1022    /// not written down.
1023    #[must_use]
1024    pub fn for_tuple(target: TargetTuple) -> Self {
1025        // Every size, alignment and signedness below is `rucc-abi`'s answer over the ten field
1026        // tuple rather than a match written out here. They were written out here, and the copy was
1027        // wrong about `x86_64-apple-darwin`, whose `long double` is the eighty bit x87 format in
1028        // sixteen bytes and not a `double`: Apple made that change on AArch64 and left the Intel
1029        // answer alone, and a rule keyed on the operating system takes both.
1030        let layout = DataLayout::for_target(target);
1031        // RISC-V and everything else with a row and no backend have register files and this crate
1032        // has not written them down yet. They arrive with the backends that need them. AArch64's is
1033        // here ahead of its backend, because the convention over it is what the ABI tests and the
1034        // debugging information read, and [`TargetInfo::regs`] having it does not make anything
1035        // try to generate code: that is `rucc_codegen::Machine::for_target`'s decision.
1036        let regs = match target.arch() {
1037            tuple::Arch::X86_64 => &x86_64::REGS,
1038            tuple::Arch::Aarch64 => &aarch64::REGS,
1039            tuple::Arch::X86 => &x86::REGS,
1040            _ => &RegFile::EMPTY,
1041        };
1042        let call_regs = match (target.arch(), target.os(), target.env()) {
1043            // The environment, and this is the one question it decides about a convention. What the
1044            // two Windows runtimes disagree about is the name of the routine a large frame reaches
1045            // its pages by calling, which is in the runtime rather than in the compiler, so a build
1046            // against mingw-w64 and a build against Microsoft's runtime want different names for the
1047            // same routine.
1048            (tuple::Arch::X86_64, tuple::Os::Windows, tuple::Env::Gnu) => Some(&x86_64::MINGW64),
1049            (tuple::Arch::X86_64, tuple::Os::Windows, _) => Some(&x86_64::WIN64),
1050            // Apple's x86-64 follows SysV, and its divergences from it are on AArch64.
1051            (tuple::Arch::X86_64, _, _) => Some(&x86_64::SYSV),
1052            // Windows on AArch64 reserves `x18`, passes every argument of a variadic function in
1053            // the x registers and homes them at the top of the callee's frame.
1054            (tuple::Arch::Aarch64, tuple::Os::Windows, _) => Some(&aarch64::WINDOWS),
1055            (tuple::Arch::Aarch64, os, _) if os.is_darwin() => Some(&aarch64::DARWIN),
1056            (tuple::Arch::Aarch64, _, _) => Some(&aarch64::AAPCS64),
1057            // Windows is cdecl over the same registers, with an ABI of its own for each runtime
1058            // and a routine of its own for a large frame. Position independent code wants
1059            // [`x86::SYSV_PIC`], which is the code generator's to pick, since whether code is
1060            // position independent is a flag and not the target.
1061            (tuple::Arch::X86, tuple::Os::Windows, tuple::Env::Msvc) => Some(&x86::MSVC32),
1062            (tuple::Arch::X86, tuple::Os::Windows, _) => Some(&x86::MINGW32),
1063            (tuple::Arch::X86, _, _) => Some(&x86::SYSV),
1064            _ => None,
1065        };
1066        // The same rule as the register file. A model is a measurement of a processor, and there
1067        // is nothing to measure until there is a backend emitting instructions for it.
1068        let timing = match target.arch() {
1069            tuple::Arch::X86_64 => Some(&x86_64::TIMING),
1070            _ => None,
1071        };
1072        Self {
1073            tuple: target,
1074            scalars: layout,
1075            pointer_width: bits(layout.pointer_size),
1076            little_endian: target.is_little_endian(),
1077            char_is_signed: layout.char_is_signed,
1078            long_width: bits(layout.long_size),
1079            long_double_width: bits(layout.long_double.size),
1080            long_double_format: layout.long_double.format,
1081            float64x_format: float64x_format(target),
1082            has_float16: has_float16(target),
1083            has_float128: has_float128(target),
1084            has_decimal_float: matches!(
1085                (target.arch(), target.os()),
1086                (tuple::Arch::X86_64, tuple::Os::Linux)
1087            ),
1088            wchar_width: bits(layout.wchar_size),
1089            wchar_is_signed: layout.wchar_is_signed,
1090            bit_int_granule: bit_int_granule(target),
1091            // Eight bytes everywhere, for the reason the field gives: it is the widest access this
1092            // compiler writes an instruction for, and every one of these machines has a wider one
1093            // that nothing here reaches. It is a claim about the code this compiler emits, so the
1094            // day a backend emits a sixteen byte atomic is the day this stops being one number.
1095            lock_free_width: 64,
1096            object_format: ObjectFormat::from_tuple(target.object_format()),
1097            landing_pads: x86_64_elf(target, layout.pointer_size)
1098                || (target.arch() == tuple::Arch::Aarch64
1099                    && target.object_format() == tuple::ObjectFormat::Elf),
1100            relative_tables: x86_64_elf(target, layout.pointer_size),
1101            bit_field_style: bit_field_style(target),
1102            unnamed_bit_field_aligns: unnamed_bit_field_aligns(target),
1103            // The environment and not the operating system, so `x86_64-windows-gnu` keeps GCC's
1104            // zero while `x86_64-windows-msvc` takes clang's four.
1105            empty_record_size: match target.env() {
1106                tuple::Env::Msvc => 4,
1107                _ => 0,
1108            },
1109            va_list: va_list(target),
1110            regs,
1111            call_regs,
1112            regparm: 0,
1113            reg_struct_return: false,
1114            timing,
1115        }
1116    }
1117
1118    /// The same target with the first `registers` words of every function's arguments in
1119    /// registers, which is `-mregparm=`, and [`None`] where gcc has no such option or refuses
1120    /// the number.
1121    #[must_use]
1122    pub fn with_regparm(mut self, registers: u8) -> Option<Self> {
1123        if self.tuple.arch() != tuple::Arch::X86 || self.tuple.os() == tuple::Os::Windows {
1124            return (registers == 0).then_some(self);
1125        }
1126        self.call_regs = Some(x86::regparm(registers, self.reg_struct_return)?);
1127        self.regparm = registers;
1128        Some(self)
1129    }
1130
1131    /// The same target with a structure of one, two, four or eight bytes returned in registers,
1132    /// which is `-freg-struct-return`, or through memory, which is `-fpcc-struct-return`.
1133    ///
1134    /// Only i386 System V changes. Every other target's ABI already says where a small structure
1135    /// comes back and gcc takes the flag there without doing anything, which this does too.
1136    #[must_use]
1137    pub fn with_reg_struct_return(mut self, in_registers: bool) -> Self {
1138        if self.tuple.arch() != tuple::Arch::X86 || self.tuple.os() == tuple::Os::Windows {
1139            return self;
1140        }
1141        if let Some(regs) = x86::regparm(self.regparm, in_registers) {
1142            self.call_regs = Some(regs);
1143            self.reg_struct_return = in_registers;
1144        }
1145        self
1146    }
1147
1148    /// The convention a function of this type is called with, given what its type says and
1149    /// whether it is variadic.
1150    ///
1151    /// Only `regparm` changes anything. A function type that says nothing has the unit's own
1152    /// count, one that says `regparm(n)` has `n`, and a variadic one has none whatever it says,
1153    /// which is what gcc does. A count that is the unit's own is written [`Convention::Target`],
1154    /// so a `regparm(3)` written in a unit built with `-mregparm=3` changes nothing.
1155    #[must_use]
1156    pub fn convention_for(&self, convention: Convention, variadic: bool) -> Convention {
1157        let registers = match convention {
1158            Convention::Target if self.regparm == 0 => return convention,
1159            Convention::Target => self.regparm,
1160            Convention::Regparm(registers) => registers,
1161            other => return other,
1162        };
1163        let registers = if variadic { 0 } else { registers };
1164        if registers == self.regparm { Convention::Target } else { Convention::Regparm(registers) }
1165    }
1166
1167    /// The largest an object may be on this target, in bytes.
1168    ///
1169    /// `PTRDIFF_MAX`, which is what C 6.5.6 needs it to be: subtracting two pointers into one
1170    /// object has to have an answer, and the answer has a `ptrdiff_t` to fit in. So an object
1171    /// of exactly this many bytes is allowed and one byte more is not, which is the line GCC
1172    /// draws too. It is the only size limit in the compiler and every layout question that has
1173    /// one asks here rather than at whatever its own arithmetic happens to overflow at.
1174    #[must_use]
1175    pub const fn max_object_size(&self) -> u64 {
1176        (1u64 << (self.pointer_width - 1)) - 1
1177    }
1178}
1179
1180/// The format `_Float64x` is, where the target has one.
1181fn float64x_format(target: TargetTuple) -> Option<Format> {
1182    match target.arch() {
1183        // The x87 unit is on the machine whatever the operating system says a `long double` is,
1184        // so `x86_64-apple-darwin` and `x86_64-windows-msvc` both have an eighty bit `_Float64x`
1185        // and an eight byte `long double`.
1186        tuple::Arch::X86_64 | tuple::Arch::X86 => Some(Format::X87Extended),
1187        tuple::Arch::Aarch64
1188        | tuple::Arch::Riscv64
1189        | tuple::Arch::Riscv32
1190        | tuple::Arch::LoongArch64
1191        | tuple::Arch::S390x
1192        | tuple::Arch::PowerPc64 => Some(Format::Quad),
1193        // Nothing on these machines is wider than a `double`, so there is no type here to
1194        // describe and neither reference defines the macros that would describe it.
1195        tuple::Arch::Arm | tuple::Arch::Arm64Ec | tuple::Arch::Wasm32 => None,
1196    }
1197}
1198
1199/// Whether the target has `_Float16`.
1200fn has_float16(target: TargetTuple) -> bool {
1201    match target.arch() {
1202        // Half precision is in the baseline of these: SSE2 on x86-64, the FP16 storage format
1203        // every ARMv8 has, and RISC-V, where gcc gives the type whether or not the hardware has
1204        // the instructions to go with it.
1205        tuple::Arch::X86_64
1206        | tuple::Arch::Aarch64
1207        | tuple::Arch::Arm64Ec
1208        | tuple::Arch::Riscv64
1209        | tuple::Arch::Riscv32 => true,
1210        // i686 for the reason the field gives, which is the baseline and not the chip, and the
1211        // rest are machines gcc 13 has not written the type for.
1212        tuple::Arch::X86
1213        | tuple::Arch::Arm
1214        | tuple::Arch::LoongArch64
1215        | tuple::Arch::PowerPc64
1216        | tuple::Arch::S390x
1217        | tuple::Arch::Wasm32 => false,
1218    }
1219}
1220
1221/// Whether the target has `_Float128`.
1222fn has_float128(target: TargetTuple) -> bool {
1223    match target.arch() {
1224        // Either the machine already has quad precision, which is the AArch64, RISC-V, s390x and
1225        // PowerPC answer, or the compiler provides it in software, which is what x86 does.
1226        tuple::Arch::X86_64
1227        | tuple::Arch::X86
1228        | tuple::Arch::Aarch64
1229        | tuple::Arch::Arm64Ec
1230        | tuple::Arch::Riscv64
1231        | tuple::Arch::Riscv32
1232        | tuple::Arch::LoongArch64
1233        | tuple::Arch::PowerPc64
1234        | tuple::Arch::S390x => true,
1235        // The same two rows that have no `_Float64x`, and for the same reason: nothing on the
1236        // machine is wider than a `double` and neither reference offers a type that is.
1237        tuple::Arch::Arm | tuple::Arch::Wasm32 => false,
1238    }
1239}
1240
1241/// The granule a `_BitInt` wider than 64 bits is laid out in, in bits.
1242fn bit_int_granule(target: TargetTuple) -> u32 {
1243    match target.arch() {
1244        // AAPCS64 says a `_BitInt` above sixty four bits is an array of `__int128`, which is the
1245        // one psABI that departs from the register width here.
1246        tuple::Arch::Aarch64 | tuple::Arch::Arm64Ec => 128,
1247        // Everywhere else it is the width of a general purpose register, which is what the psABIs
1248        // that have written the rule down all say and what both references do on the rows that
1249        // have not.
1250        tuple::Arch::X86 | tuple::Arch::Arm | tuple::Arch::Riscv32 => 32,
1251        tuple::Arch::X86_64
1252        | tuple::Arch::Riscv64
1253        | tuple::Arch::LoongArch64
1254        | tuple::Arch::PowerPc64
1255        | tuple::Arch::S390x
1256        | tuple::Arch::Wasm32 => 64,
1257    }
1258}
1259
1260/// How this target allocates bit-fields into storage.
1261///
1262/// Keyed on the operating system rather than the environment, because mingw's answer here is
1263/// Microsoft's and not GCC's. That is the whole reason it is not a guess: a rule keyed on
1264/// `Env::Msvc` gets `x86_64-windows-gnu` wrong by four bytes on a struct of an `unsigned :3` and a
1265/// `char`, and gets it wrong quietly.
1266fn bit_field_style(target: TargetTuple) -> BitFieldStyle {
1267    match target.os() {
1268        tuple::Os::Windows => BitFieldStyle::Microsoft,
1269        _ => BitFieldStyle::Itanium,
1270    }
1271}
1272
1273/// Whether an unnamed bit-field raises the record's alignment the way a named one does.
1274///
1275/// AAPCS says it does, on both widths of ARM, and Apple and Microsoft each dropped that rule.
1276/// Microsoft then put its own rule in the same place for a `struct`, so Windows says yes again by
1277/// a different route, and says something else entirely for a `union`, which [`BitFieldStyle`]
1278/// carries rather than this.
1279fn unnamed_bit_field_aligns(target: TargetTuple) -> bool {
1280    match (target.arch(), target.os()) {
1281        (_, tuple::Os::Windows) => true,
1282        // A freestanding ARM target is AAPCS proper, so it says yes: there is no operating system
1283        // there to have dropped it.
1284        (tuple::Arch::Aarch64 | tuple::Arch::Arm | tuple::Arch::Arm64Ec, os) => !os.is_darwin(),
1285        _ => false,
1286    }
1287}
1288
1289/// What `__builtin_va_list` is on this target, where this crate can build the type.
1290fn va_list(target: TargetTuple) -> Option<VaList> {
1291    match (target.arch(), target.os()) {
1292        // Windows passes every argument in one place and spills the register ones next to the
1293        // stack ones, so the list is an address, and Apple does the same on AArch64.
1294        (_, tuple::Os::Windows) => Some(VaList::CharPointer),
1295        (tuple::Arch::Aarch64, os) if os.is_darwin() => Some(VaList::CharPointer),
1296        (tuple::Arch::Aarch64, _) => Some(VaList::Aapcs),
1297        // The x32 ABI's list is the same structure with four byte pointers in it, which is what
1298        // building it out of this target's pointer type gives, so it is the same answer.
1299        (tuple::Arch::X86_64, _) => Some(VaList::SysV),
1300        (tuple::Arch::X86, _) => Some(VaList::CharPointer),
1301        (tuple::Arch::Riscv64 | tuple::Arch::Riscv32 | tuple::Arch::LoongArch64, _)
1302        | (tuple::Arch::Wasm32, _) => Some(VaList::VoidPointer),
1303        // 32-bit ARM's is a structure of one pointer, s390x's is a structure of four members, and
1304        // PowerPC's is a structure of five. None of them is any of the four types above and this
1305        // crate does not build them, so it says so rather than naming a neighbour's.
1306        (
1307            tuple::Arch::Arm | tuple::Arch::S390x | tuple::Arch::PowerPc64 | tuple::Arch::Arm64Ec,
1308            _,
1309        ) => None,
1310    }
1311}
1312
1313#[cfg(test)]
1314mod tests {
1315    use super::*;
1316
1317    #[test]
1318    fn parses_a_four_field_triple() {
1319        let t: Triple = "x86_64-unknown-linux-gnu".parse().unwrap();
1320        assert_eq!(t, Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
1321    }
1322
1323    #[test]
1324    fn parses_a_triple_with_no_vendor() {
1325        let t: Triple = "aarch64-linux-musl".parse().unwrap();
1326        assert_eq!(t, Triple::new(Arch::Aarch64, Os::Linux, Env::Musl));
1327    }
1328
1329    #[test]
1330    fn accepts_the_common_aliases() {
1331        let a: Triple = "arm64-apple-darwin".parse().unwrap();
1332        let b: Triple = "aarch64-apple-darwin".parse().unwrap();
1333        assert_eq!(a, b);
1334        assert_eq!(a.env, Env::None);
1335    }
1336
1337    #[test]
1338    fn fills_in_the_default_environment() {
1339        let t: Triple = "x86_64-unknown-linux".parse().unwrap();
1340        assert_eq!(t.env, Env::Gnu);
1341        let w: Triple = "x86_64-pc-windows".parse().unwrap();
1342        assert_eq!(w.env, Env::Gnu);
1343    }
1344
1345    #[test]
1346    fn rejects_what_it_does_not_support() {
1347        let e = "sparc64-unknown-linux-gnu".parse::<Triple>().unwrap_err();
1348        assert_eq!(e.reason, "unknown architecture");
1349        let e = "x86_64-unknown-plan9".parse::<Triple>().unwrap_err();
1350        assert_eq!(e.reason, "unknown operating system");
1351    }
1352
1353    #[test]
1354    fn displays_in_a_normalised_form() {
1355        let t: Triple = "amd64-linux-gnu".parse().unwrap();
1356        assert_eq!(t.to_string(), "x86_64-unknown-linux-gnu");
1357    }
1358
1359    #[test]
1360    fn display_round_trips_through_parse() {
1361        for s in [
1362            "x86_64-unknown-linux-gnu",
1363            "aarch64-unknown-darwin-none",
1364            "riscv64-unknown-linux-musl",
1365        ] {
1366            let t: Triple = s.parse().unwrap();
1367            assert_eq!(t.to_string().parse::<Triple>().unwrap(), t);
1368        }
1369    }
1370
1371    #[test]
1372    fn char_signedness_follows_the_psabi() {
1373        let x86 = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1374        let arm = TargetInfo::new("aarch64-unknown-linux-gnu".parse().unwrap());
1375        let mac = TargetInfo::new("aarch64-apple-darwin".parse().unwrap());
1376        assert!(x86.char_is_signed);
1377        assert!(!arm.char_is_signed);
1378        assert!(mac.char_is_signed, "Apple overrides AAPCS64 back to a signed char");
1379    }
1380
1381    #[test]
1382    fn windows_is_llp64() {
1383        let win = TargetInfo::new("x86_64-pc-windows-msvc".parse().unwrap());
1384        assert_eq!(win.pointer_width, 64);
1385        assert_eq!(win.long_width, 32);
1386    }
1387
1388    #[test]
1389    fn the_largest_object_is_ptrdiff_max() {
1390        // Half the address space less one, which is what a pointer subtraction across the whole
1391        // of one object has to fit in. gcc 16 on x86-64 prints this same number when it refuses
1392        // an array, and takes an object of exactly this many bytes.
1393        for triple in ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-pc-windows-msvc"]
1394        {
1395            let target = TargetInfo::new(triple.parse().unwrap());
1396            assert_eq!(target.max_object_size(), 9_223_372_036_854_775_807, "{triple}");
1397        }
1398    }
1399
1400    #[test]
1401    fn apple_long_double_is_double() {
1402        let mac = TargetInfo::new("aarch64-apple-darwin".parse().unwrap());
1403        assert_eq!(mac.long_double_width, 64);
1404        assert_eq!(mac.long_double_format, Format::Double);
1405        let linux = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1406        assert_eq!(linux.long_double_width, 128);
1407    }
1408
1409    #[test]
1410    fn apples_x86_64_is_not_one_of_the_targets_that_narrowed_long_double() {
1411        // The bug the layout facts moving to `rucc-abi` fixed. This crate used to decide the
1412        // width from the operating system, which took both Apple targets, and Apple made the
1413        // change on AArch64 only. `facts/x86_64-macos.facts` in tamnd/rucc-cross records
1414        // `long_double_format=x87_extended` with `sizeof_long_double=16`, from a reference
1415        // compiler, and this used to answer a sixty four bit `double`.
1416        //
1417        // It is the quiet kind of wrong. `sizeof(long double)` came out at eight where the
1418        // headers say sixteen, so `printf("%Lf")` read the wrong bytes and every structure with
1419        // a `long double` in it laid out differently from the system's own.
1420        let mac = TargetInfo::new("x86_64-apple-darwin".parse().unwrap());
1421        assert_eq!(mac.long_double_width, 128);
1422        assert_eq!(mac.long_double_format, Format::X87Extended);
1423
1424        let linux = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1425        assert_eq!(
1426            (mac.long_double_width, mac.long_double_format),
1427            (linux.long_double_width, linux.long_double_format)
1428        );
1429    }
1430
1431    #[test]
1432    fn every_triple_describes_a_machine() {
1433        // `Triple::tuple` panics on a pair that is not a machine and this is what says there is
1434        // no such pair. All sixty four combinations, including the ones the parser will produce
1435        // from a string somebody can type and no machine has, such as a Darwin target claiming
1436        // glibc.
1437        let mut built = 0;
1438        for arch in [Arch::X86_64, Arch::Aarch64, Arch::Riscv64, Arch::X86] {
1439            for os in [Os::Linux, Os::Darwin, Os::Windows, Os::None] {
1440                for env in [Env::None, Env::Gnu, Env::Musl, Env::Msvc] {
1441                    let triple = Triple::new(arch, os, env);
1442                    let tuple = triple.tuple();
1443                    assert_eq!(tuple.pointer_width(), arch.pointer_width(), "{triple}");
1444                    // The one field the narrowing has to preserve, because mingw and MSVC are the
1445                    // same operating system with two different `long double`s.
1446                    if os == Os::Windows {
1447                        let expected = match env {
1448                            Env::Gnu => rucc_tuple::Env::Gnu,
1449                            _ => rucc_tuple::Env::Msvc,
1450                        };
1451                        assert_eq!(tuple.env(), expected, "{triple}");
1452                    }
1453                    built += 1;
1454                }
1455            }
1456        }
1457        assert_eq!(built, 64);
1458    }
1459
1460    #[test]
1461    fn from_tuple_undoes_the_narrowing() {
1462        // Every triple's tuple comes back as a triple describing the same machine. It is not
1463        // always the triple it started as, because the narrowing is many to one: a Darwin target
1464        // claiming glibc and the same one claiming nothing are one machine, and the answer is the
1465        // spelling that names no libc.
1466        for arch in [Arch::X86_64, Arch::Aarch64, Arch::Riscv64, Arch::X86] {
1467            for os in [Os::Linux, Os::Darwin, Os::Windows, Os::None] {
1468                for env in [Env::None, Env::Gnu, Env::Musl, Env::Msvc] {
1469                    let triple = Triple::new(arch, os, env);
1470                    let back = Triple::from_tuple(triple.tuple())
1471                        .unwrap_or_else(|| panic!("{triple} has a tuple and no way back"));
1472                    assert_eq!(back.tuple(), triple.tuple(), "{triple}");
1473                    assert_eq!(back.arch, arch, "{triple}");
1474                    assert_eq!(back.os, os, "{triple}");
1475                }
1476            }
1477        }
1478    }
1479
1480    #[test]
1481    fn from_tuple_gives_the_canonical_environment() {
1482        let musl = Triple::from_tuple("aarch64-linux-musl".parse().unwrap()).unwrap();
1483        assert_eq!(musl, Triple::new(Arch::Aarch64, Os::Linux, Env::Musl));
1484        let gnu = Triple::from_tuple("x86_64-linux-gnu".parse().unwrap()).unwrap();
1485        assert_eq!(gnu, Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
1486        // Darwin and freestanding name no libc, so the answer does too, even though the parser
1487        // will hand this type a Darwin triple with `gnu` on the end.
1488        let macos = Triple::from_tuple("aarch64-macos".parse().unwrap()).unwrap();
1489        assert_eq!(macos, Triple::new(Arch::Aarch64, Os::Darwin, Env::None));
1490        let bare = Triple::from_tuple("riscv64-none".parse().unwrap()).unwrap();
1491        assert_eq!(bare, Triple::new(Arch::Riscv64, Os::None, Env::None));
1492        // The two Windows environments stay apart, which is the whole reason the narrowing keeps
1493        // the environment there and nowhere else.
1494        let mingw = Triple::from_tuple("x86_64-windows-gnu".parse().unwrap()).unwrap();
1495        assert_eq!(mingw.env, Env::Gnu);
1496        let msvc = Triple::from_tuple("x86_64-windows-msvc".parse().unwrap()).unwrap();
1497        assert_eq!(msvc.env, Env::Msvc);
1498        // A version on either side narrows to the same triple as the tuple without it.
1499        let pinned = Triple::from_tuple("aarch64-macos.13".parse().unwrap()).unwrap();
1500        assert_eq!(pinned, macos);
1501        let old = Triple::from_tuple("x86_64-linux-gnu.2.28".parse().unwrap()).unwrap();
1502        assert_eq!(old, gnu);
1503    }
1504
1505    #[test]
1506    fn from_tuple_says_no_rather_than_saying_something_near() {
1507        // Most of the forty two rows have no triple, and the answer is `None` rather than
1508        // a neighbour. `rucc-abi` knows the scalar layout of every one of these and this type
1509        // cannot hold any of them, which is the gap the record layout engine inherits.
1510        for tuple in [
1511            "armv7-linux-gnueabihf",
1512            "s390x-linux-gnu",
1513            "powerpc64le-linux-gnu",
1514            "loongarch64-linux-gnu",
1515            "x86_64-linux-gnux32",
1516            "aarch64-linux-android",
1517            "aarch64-ios",
1518            "wasm32-wasip1",
1519            "x86_64-freebsd",
1520        ] {
1521            let target = tuple.parse().unwrap();
1522            assert_eq!(Triple::from_tuple(target), None, "{tuple}");
1523        }
1524        // i686 has a triple now, and it is the machine and not x86-64's.
1525        let i686 = Triple::from_tuple("i686-linux-gnu".parse().unwrap()).unwrap();
1526        assert_eq!(i686, Triple::new(Arch::X86, Os::Linux, Env::Gnu));
1527        assert_eq!(i686.to_string(), "i686-unknown-linux-gnu");
1528    }
1529
1530    #[test]
1531    fn mingw_and_msvc_are_one_operating_system_with_two_long_doubles() {
1532        // The narrowing in `Triple::tuple` keeps the environment on Windows for this reason and
1533        // throws it away everywhere else. GCC's Windows targets keep the eighty bit `long double`
1534        // and Microsoft's make it a `double`, on the same processor and the same OS.
1535        let mingw = TargetInfo::new("x86_64-pc-windows-gnu".parse().unwrap());
1536        assert_eq!(mingw.long_double_width, 128);
1537        assert_eq!(mingw.long_double_format, Format::X87Extended);
1538
1539        let msvc = TargetInfo::new("x86_64-pc-windows-msvc".parse().unwrap());
1540        assert_eq!(msvc.long_double_width, 64);
1541        assert_eq!(msvc.long_double_format, Format::Double);
1542
1543        // And they agree about everything the operating system does decide.
1544        assert_eq!(mingw.long_width, msvc.long_width);
1545        assert_eq!(mingw.wchar_width, msvc.wchar_width);
1546        assert_eq!(mingw.object_format, msvc.object_format);
1547    }
1548
1549    #[test]
1550    fn wchar_t_divides_the_targets_in_two_directions_at_once() {
1551        // Windows narrows it to sixteen bits, which makes a wide string UTF-16 there and
1552        // UTF-32 everywhere else, and AArch64 Linux makes it unsigned without narrowing it.
1553        let windows = TargetInfo::new("x86_64-pc-windows-msvc".parse().unwrap());
1554        assert_eq!((windows.wchar_width, windows.wchar_is_signed), (16, false));
1555        let arm = TargetInfo::new("aarch64-unknown-linux-gnu".parse().unwrap());
1556        assert_eq!((arm.wchar_width, arm.wchar_is_signed), (32, false));
1557        let linux = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1558        assert_eq!((linux.wchar_width, linux.wchar_is_signed), (32, true));
1559        // Apple keeps it signed on the same processor where Linux does not, in the same way it
1560        // keeps plain `char` signed there.
1561        let mac = TargetInfo::new("aarch64-apple-darwin".parse().unwrap());
1562        assert_eq!((mac.wchar_width, mac.wchar_is_signed), (32, true));
1563    }
1564
1565    #[test]
1566    fn va_list_is_the_psabis_type_and_not_one_type_with_four_spellings() {
1567        let linux = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1568        assert_eq!(linux.va_list, Some(VaList::SysV));
1569        // x86-64 Darwin follows SysV here, and AArch64 Darwin does not follow AAPCS64.
1570        let mac = TargetInfo::new("x86_64-apple-darwin".parse().unwrap());
1571        assert_eq!(mac.va_list, Some(VaList::SysV));
1572        let arm_mac = TargetInfo::new("aarch64-apple-darwin".parse().unwrap());
1573        assert_eq!(arm_mac.va_list, Some(VaList::CharPointer));
1574        let arm = TargetInfo::new("aarch64-unknown-linux-gnu".parse().unwrap());
1575        assert_eq!(arm.va_list, Some(VaList::Aapcs));
1576        // Windows passes everything one way on both processors, so both get the simple one.
1577        let win = TargetInfo::new("x86_64-pc-windows-msvc".parse().unwrap());
1578        assert_eq!(win.va_list, Some(VaList::CharPointer));
1579        let arm_win = TargetInfo::new("aarch64-pc-windows-msvc".parse().unwrap());
1580        assert_eq!(arm_win.va_list, Some(VaList::CharPointer));
1581        let riscv = TargetInfo::new("riscv64-unknown-linux-gnu".parse().unwrap());
1582        assert_eq!(riscv.va_list, Some(VaList::VoidPointer));
1583    }
1584
1585    #[test]
1586    fn two_targets_agree_on_the_width_of_long_double_and_not_on_the_type() {
1587        // Sixteen bytes on both, and a different number in them: the x87 format has sixty four
1588        // bits of significand and quad precision has a hundred and thirteen, so a constant
1589        // converted for one is the wrong bits for the other.
1590        let x86 = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1591        let arm = TargetInfo::new("aarch64-unknown-linux-gnu".parse().unwrap());
1592        assert_eq!(x86.long_double_width, arm.long_double_width);
1593        assert_eq!(x86.long_double_format, Format::X87Extended);
1594        assert_eq!(arm.long_double_format, Format::Quad);
1595        assert_eq!(x86.long_double_format.precision(), 64);
1596        assert_eq!(arm.long_double_format.precision(), 113);
1597        // Windows keeps the name and drops the type, the way Apple does.
1598        let windows = TargetInfo::new("x86_64-pc-windows-msvc".parse().unwrap());
1599        assert_eq!(windows.long_double_format, Format::Double);
1600    }
1601
1602    #[test]
1603    fn float64x_follows_the_processor_where_long_double_follows_the_operating_system() {
1604        // `_Float64x` is the widest format the hardware has, and no ABI takes it away the way
1605        // Apple and Windows take `long double` away. So the two fields say the same thing on
1606        // Linux and disagree everywhere else, which is the whole reason there are two of them.
1607        let x86 = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1608        assert_eq!(x86.float64x_format, Some(Format::X87Extended));
1609        let arm = TargetInfo::new("aarch64-unknown-linux-gnu".parse().unwrap());
1610        assert_eq!(arm.float64x_format, Some(Format::Quad));
1611        let riscv = TargetInfo::new("riscv64-unknown-linux-gnu".parse().unwrap());
1612        assert_eq!(riscv.float64x_format, Some(Format::Quad));
1613
1614        let mac = TargetInfo::new("aarch64-apple-darwin".parse().unwrap());
1615        assert_eq!(mac.long_double_format, Format::Double);
1616        assert_eq!(mac.float64x_format, Some(Format::Quad));
1617        let windows = TargetInfo::new("x86_64-pc-windows-msvc".parse().unwrap());
1618        assert_eq!(windows.long_double_format, Format::Double);
1619        assert_eq!(windows.float64x_format, Some(Format::X87Extended));
1620    }
1621
1622    #[test]
1623    fn the_named_floating_types_are_not_on_every_machine() {
1624        // gcc 13, measured with the cross compilers rather than reasoned about. `_Float16` is on
1625        // three of these seven and `_Float128` is on six, and the two lists are not the same
1626        // list, which is why there are two fields.
1627        // The three field triple spells three architectures, and four of these rows are not
1628        // among them, so this asks the tuple the way the layout tests do.
1629        let of = |tuple: &str| TargetInfo::for_tuple(tuple.parse().expect("a row in the table"));
1630        let rows = [
1631            ("x86_64-linux-gnu", true, true),
1632            ("i686-linux-gnu", false, true),
1633            ("aarch64-linux-gnu", true, true),
1634            ("armv7-linux-gnueabihf", false, false),
1635            ("powerpc64le-linux-gnu", false, true),
1636            ("riscv64-linux-gnu", true, true),
1637            ("s390x-linux-gnu", false, true),
1638        ];
1639        for (tuple, float16, float128) in rows {
1640            let target = of(tuple);
1641            assert_eq!(target.has_float16, float16, "{tuple} `_Float16`");
1642            assert_eq!(target.has_float128, float128, "{tuple} `_Float128`");
1643        }
1644        // The operating system has nothing to do with it, the way it has nothing to do with
1645        // `_Float64x`, so Apple and Windows keep both types.
1646        assert!(of("aarch64-apple-darwin").has_float16);
1647        assert!(of("x86_64-pc-windows-msvc").has_float128);
1648    }
1649
1650    #[test]
1651    fn the_decimal_types_are_on_the_one_row_the_back_end_calls_routines_for() {
1652        let of = |tuple: &str| TargetInfo::for_tuple(tuple.parse().expect("a row in the table"));
1653        assert!(of("x86_64-linux-gnu").has_decimal_float);
1654        for tuple in ["aarch64-linux-gnu", "x86_64-pc-windows-msvc", "aarch64-apple-darwin"] {
1655            assert!(!of(tuple).has_decimal_float, "{tuple}");
1656        }
1657    }
1658
1659    #[test]
1660    fn the_object_format_follows_the_operating_system() {
1661        assert_eq!(Os::Linux.object_format(), ObjectFormat::Elf);
1662        assert_eq!(Os::Darwin.object_format(), ObjectFormat::MachO);
1663        assert_eq!(Os::Windows.object_format(), ObjectFormat::Coff);
1664    }
1665
1666    #[test]
1667    fn a_target_carries_its_registers_and_says_so_when_it_has_none() {
1668        let of = |triple: &str| TargetInfo::new(triple.parse().unwrap());
1669        let linux = of("x86_64-unknown-linux-gnu");
1670        assert_eq!(linux.regs.reg_named("rdi"), Some((x86_64::GPR, x86_64::RDI)));
1671        assert_eq!(linux.call_regs.map(|regs| regs.int_args[0]), Some(x86_64::RDI));
1672        // Apple's x86-64 is SysV and Windows is the one that is not.
1673        let apple = of("x86_64-apple-darwin");
1674        assert_eq!(apple.call_regs.map(|regs| regs.int_args[0]), Some(x86_64::RDI));
1675        let windows = of("x86_64-pc-windows-msvc");
1676        assert_eq!(windows.regs.len(x86_64::GPR), 16);
1677        assert_eq!(windows.call_regs.map(|regs| regs.int_args[0]), Some(x86_64::RCX));
1678        let arm = of("aarch64-unknown-linux-gnu");
1679        assert_eq!(arm.regs.len(aarch64::GPR), 32);
1680        assert_eq!(arm.call_regs.map(|regs| regs.int_args[0]), Some(aarch64::x(0)));
1681        assert_eq!(arm.call_regs.map(|regs| regs.red_zone), Some(0));
1682        assert_eq!(of("aarch64-apple-darwin").call_regs.map(|regs| regs.red_zone), Some(128));
1683        // Another stack boundary is the same convention with one number changed, made once.
1684        let sysv = linux.call_regs.expect("a convention");
1685        let eight = sysv.aligned_to(8);
1686        assert_eq!((eight.stack_align, eight.int_args), (8, sysv.int_args));
1687        assert!(std::ptr::eq(eight, sysv.aligned_to(8)));
1688        assert!(std::ptr::eq(sysv, sysv.aligned_to(16)));
1689        // Windows on AArch64 has registers of its own rather than Linux's, both runtimes alike.
1690        for triple in ["aarch64-pc-windows-msvc", "aarch64-pc-windows-gnu"] {
1691            let regs = of(triple).call_regs.expect("a convention");
1692            assert!(std::ptr::eq(regs, &aarch64::WINDOWS), "{triple}");
1693        }
1694        // i386 has its registers everywhere and a convention wherever `rucc-abi` has one.
1695        let i386 = of("i686-unknown-linux-gnu");
1696        assert_eq!(i386.regs.reg_named("ebx"), Some((x86::GPR, x86::EBX)));
1697        assert!(std::ptr::eq(i386.call_regs.expect("i386 SysV"), &x86::SYSV));
1698        let i686_windows = of("i686-pc-windows-gnu");
1699        assert_eq!(i686_windows.regs.len(x86::GPR), 8);
1700        assert!(std::ptr::eq(i686_windows.call_regs.expect("mingw"), &x86::MINGW32));
1701        let i686_msvc = of("i686-pc-windows-msvc");
1702        assert!(std::ptr::eq(i686_msvc.call_regs.expect("msvc"), &x86::MSVC32));
1703        let riscv = of("riscv64-unknown-linux-gnu");
1704        assert!(riscv.regs.is_empty());
1705        assert!(riscv.call_regs.is_none());
1706    }
1707
1708    #[test]
1709    fn regparm_is_the_unit_s_count_and_a_variadic_function_has_none() {
1710        let target = |triple: &str| TargetInfo::new(triple.parse().expect("a triple"));
1711        let unit = target("i686-unknown-linux-gnu").with_regparm(3).expect("i386 has the flag");
1712        assert_eq!(unit.regparm, 3);
1713        let three = x86::regparm(3, false).expect("three");
1714        assert!(std::ptr::eq(unit.call_regs.expect("i386"), three));
1715        assert_eq!(unit.convention_for(Convention::Target, false), Convention::Target);
1716        assert_eq!(unit.convention_for(Convention::Regparm(3), false), Convention::Target);
1717        assert_eq!(unit.convention_for(Convention::Regparm(0), false), Convention::Regparm(0));
1718        assert_eq!(unit.convention_for(Convention::Target, true), Convention::Regparm(0));
1719        let plain = target("i686-unknown-linux-gnu");
1720        assert_eq!(plain.convention_for(Convention::Target, true), Convention::Target);
1721        assert_eq!(plain.convention_for(Convention::Regparm(2), true), Convention::Target);
1722        assert_eq!(plain.convention_for(Convention::Regparm(2), false), Convention::Regparm(2));
1723        assert!(plain.clone().with_regparm(4).is_none());
1724        assert!(target("x86_64-unknown-linux-gnu").with_regparm(3).is_none());
1725        assert!(target("x86_64-unknown-linux-gnu").with_regparm(0).is_some());
1726    }
1727
1728    /// The two maps from a triple, held against each other.
1729    ///
1730    /// A target's registers and a target's ABI are chosen by two separate matches, one here and one
1731    /// in `rucc_abi::abis::for_target`, and [`CallRegs::abi`] is the link between them. Two matches
1732    /// that can disagree are the thing this crate must not have, so every triple with registers is
1733    /// asked both questions and the answers have to be the same description. What it catches is a
1734    /// target added to one match and not the other, which is a compiler that puts the value in the
1735    /// register one ABI names and the form another one asked for.
1736    #[test]
1737    fn the_registers_and_the_abi_a_target_gets_are_the_same_convention() {
1738        let mut checked = 0;
1739        for arch in [Arch::X86_64, Arch::Aarch64, Arch::Riscv64, Arch::X86] {
1740            for os in [Os::Linux, Os::Darwin, Os::Windows, Os::None] {
1741                for env in [Env::None, Env::Gnu, Env::Musl, Env::Msvc] {
1742                    let triple = Triple::new(arch, os, env);
1743                    let info = TargetInfo::new(triple);
1744                    let Some(regs) = info.call_regs else { continue };
1745                    let described = rucc_abi::abis::for_target(info.tuple)
1746                        .unwrap_or_else(|| panic!("{triple} has registers and no ABI"));
1747                    assert!(
1748                        std::ptr::eq(regs.abi, described),
1749                        "{triple} has the registers of {} and the ABI of {}",
1750                        regs.abi.name,
1751                        described.name
1752                    );
1753                    checked += 1;
1754                }
1755            }
1756        }
1757        assert!(checked > 0, "no target has registers, so this asserted nothing");
1758    }
1759
1760    /// The timing model, which follows the register file: an architecture with no backend has
1761    /// nothing to measure and says so rather than borrowing a neighbour's numbers.
1762    #[test]
1763    fn a_target_carries_the_model_its_schedules_were_chosen_with() {
1764        let of = |triple: &str| TargetInfo::new(triple.parse().unwrap());
1765        let linux = of("x86_64-unknown-linux-gnu");
1766        let timing = linux.timing.expect("x86-64 has a backend and so has a model");
1767        assert!(timing.model.contains("Skylake"), "{}", timing.model);
1768        assert!(!timing.accurate, "and it says it is not a cycle accurate one");
1769        assert_eq!(timing.of("x64.imul_rr_64").map(|cost| cost.unit), Some(Unit::Mul));
1770
1771        // The same model whatever the operating system, since a model is about the processor.
1772        assert_eq!(of("x86_64-apple-darwin").timing, linux.timing);
1773        assert_eq!(of("x86_64-pc-windows-msvc").timing, linux.timing);
1774
1775        assert!(of("aarch64-unknown-linux-gnu").timing.is_none(), "nobody has measured it here");
1776    }
1777
1778    #[test]
1779    fn the_host_triple_is_one_we_support() {
1780        // Every host in spec/15-testing.md section 15.7 must be recognised, and CI runs on
1781        // all three, so a failure here means a host we claim support for stopped resolving.
1782        let host = Triple::host().expect("the host must be a supported target");
1783        assert_eq!(host.to_string().parse::<Triple>().unwrap(), host);
1784    }
1785}