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