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}