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